如何在模型更改后更新视图

本文关键字:更新 新视图 模型 | 更新日期: 2023-09-27 18:18:34

假设我有一个Logger类,一个LoggerViewModel类和一个MainWindow与一个TextBoxLogger类是一个线程安全的单例,所以我在应用程序域中只有它的一个实例。

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唯一实例在应用程序运行时由更多线程更新,因此每当模型(即Logger单例类)更改时,我会在MainWindow上更新TextBox

如何在它们之间连接模型和ViewModel ?我要强调的是,Model只会被几个应用程序线程修改,所以从UI的角度来看,它是只读的。

我在LoggerViewModel类中提供了LoggerText属性,因为我认为下面的工作机制。 1。模型 (Logger实例)改变时,它通知ViewModel 2。ViewModel接收Model发出的通知,并创建一个包含来自记录器的所有消息的新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如何拦截由Model发送的通知?

如何在模型更改后更新视图

首先,我不喜欢你使用单例。当使用单例模式时,你在测试或重用视图控制器时就会变得很困难。相反,我将把Logger依赖注入到你的LoggerViewModel类中。

除此之外,解决问题的一种方法是在Logger上注册PropertyChanged事件的处理程序,并在事件为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;
    }
}

免责声明:上面的代码没有编译器。