Скажем, у меня есть класс Logger
класс LoggerViewModel
и MainWindow
с TextBox
. Класс Logger
- это потокобезопасный синглтон, поэтому у меня есть только его экземпляр в домене приложения.
public sealed class Logger : INotifyPropertyChanged
{
private static readonly Logger _Instance = new Logger();
private static readonly object _SyncLock = new object();
private static List<LogEntry> _Data = new List<LogEntry>();
/// <summary>
///
/// </summary>
private Logger() { ; }
/// <summary>
///
/// </summary>
public static Logger Instance
{
get { return _Instance; }
}
/// <summary>
///
/// </summary>
/// <param name="entry"></param>
public void Write(LogEntry entry)
{
lock (_SyncLock)
{
_Data.Add(entry);
}
this.RaiseNotifyPropertyChanged("Entries");
}
/// <summary>
///
/// </summary>
/// <param name="component"></param>
/// <param name="message"></param>
public void Write(string component, string message)
{
LogEntry entry = LogEntry.Create(component, message);
Write(entry);
}
/// <summary>
///
/// </summary>
public IList<LogEntry> Entries
{
get
{
lock (_SyncLock)
{
return new ReadOnlyCollection<LogEntry>(_Data);
}
}
}
/// <summary>
///
/// </summary>
/// <param name="property"></param>
private void RaiseNotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
/// <summary>
///
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
Уникальный экземпляр Logger
обновляется большим количеством потоков во время работы приложения, поэтому я обновляю TextBox
на MainWindow
всякий раз, когда изменяется модель (то есть одноэлементный класс Logger
).
Как подключить модель и ViewModel между ними? Я подчеркиваю, что модель изменена только несколькими потоками приложений, поэтому она доступна только для чтения с точки зрения пользовательского интерфейса.
Я предоставил LoggerText
имущество в LoggerViewModel
класса, так как я думал, что следующий рабочий механизм. 1. Когда модель (экземпляр Logger
) изменяется, она уведомляет ViewModel. 2. ViewModel получает уведомление с помощью модели и создает новую string
содержащую все сообщения из регистратора. 3. ViewModel уведомляет View.
public class LoggerViewModel : INotifyPropertyChanged
{
Logger _LoggerModel;
/// <summary>
///
/// </summary>
public LoggerViewModel()
{
_LoggerModel = Logger.Instance;
}
/// <summary>
///
/// </summary>
public string LoggerText
{
get
{
string text = "";
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text += entry.ToString();
text += "\n";
}
return text;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Как ViewModel может перехватывать уведомления, отправленные моделью?
Прежде всего мне не нравится, что вы используете синглтон. При использовании одноэлементного шаблона вы усложняете себя при тестировании или повторном использовании контроллеров представлений. Вместо этого я бы LoggerViewModel
зависимость Logger
в ваш класс LoggerViewModel
.
Помимо этого один из способов решения вашей проблемы - зарегистрировать обработчик события PropertyChanged
в вашем Logger
и построить текст, когда событие запускается для свойства " Entries
.
В LoggerViewModel
вы затем добавляете обработчик свойств и обновляете свойство LoggerText
мере необходимости.
public LoggerViewModel(Logger loggerModel /* Dependency injection*/)
{
_LoggerModel = loggerModel;
_LoggerModel.PropertyChanged += this.LoggerModel_PropertyChanged;
}
private void LoggerModel_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Entries")
{
StringBuilder text = new StringBuilder(); // Use StringBuilder for performance
List<LogEntry> entries = new List<LogEntry>(_LoggerModel.Entries);
foreach (LogEntry entry in entries)
{
text.AppendLine(entry.ToString());
}
this.LoggerText = text.ToString();
}
}
private string _loggerText;
public string LoggerText
{
set
{
_loggerText = value;
RaisePropertyChanged("LoggerText");
}
get
{
return _loggerText;
}
}
Отказ от ответственности: приведенный выше код написан без компилятора.
Logger
в модель представления и привязку данных к свойствуEntries
в подходе пользовательского интерфейса ? Еслиstatic
класс вызывает у вас проблемы, просто добавьте свойство в обычный класс и просто используйте ваш класс Singleton для обновления нормального класса.LoggerViewModel
классаLoggerViewModel
. Я быLoggerText
свойствоLoggerText
в пользовательском интерфейсе при изменении модели.