MVVM:通知UI中的属性更改
本文关键字:属性 通知 UI MVVM | 更新日期: 2023-09-27 18:06:54
我正在构建一个日志记录器。UI(LoggerView)绑定到LogItem的三个属性:DateTime
, Status
和Message
。当我运行程序时,日志事件确实被创建并正确地登录到UI中。但是,当我关闭UI,记录更多额外的事件,并再次打开UI时,额外的事件确实被记录到events
中,但它们没有出现在UI中。
问题是,当新的LogItems
被添加到集合中时,UI没有得到通知。当添加额外的LogItems
时,我如何通知UI ?
LoggerViewModel:
[Export]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.Shared)]
public class LoggerViewModel : ViewModelBase
{
protected static readonly ILog log = LogManager.GetLogger(typeof(LoggerViewModel));
private Hierarchy h = LogManager.GetRepository() as Hierarchy;
[ImportingConstructor]
public LoggerViewModel()
{
LogItems = new ObservableCollection<LogItem>();
foreach (var customLog in CustomLogger.GetLogItems())
{
var logItem = new LogItem(customLog.TimeStamp, customLog.Status, customLog.Message);
LogItems.Add(logItem);
}
}
private ObservableCollection<LogItem> _logItems;
public ObservableCollection<LogItem> LogItems
{
get { return _logItems; }
set { _logItems = value; }
}
private DateTime _timeStamp;
public DateTime TimeStamp
{
get { return _timeStamp; }
set
{
if (value == _timeStamp)
return;
_timeStamp = value;
NotifyPropertyChanged("TimeStamp");
}
}
private Level _status;
public Level Status
{
get { return _status; }
set
{
if (value == _status)
return;
_status = value;
NotifyPropertyChanged("Status");
}
}
private string _message;
public string Message
{
get { return _message; }
set
{
if (value == _message)
return;
_message = value;
NotifyPropertyChanged("Message");
}
}
}
LoggerView XAML:
<ListView Grid.Row="1" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible" VerticalContentAlignment="Bottom" ItemsSource="{Binding LogItems}">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Date And Time" DisplayMemberBinding="{Binding Path=DataContext.TimeStamp, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="50" Header="Status" DisplayMemberBinding="{Binding Path=DataContext.Status, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="1800" Header="Message">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Path=DataContext.Message, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
LoggerView逻辑:
[Export]
[PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
public partial class LoggerView : Window
{
private LoggerViewModel _viewModel;
protected static readonly ILog log = LogManager.GetLogger(typeof(LoggerView));
private Hierarchy h = LogManager.GetRepository() as Hierarchy;
[ImportingConstructor]
public LoggerView(LoggerViewModel viewModel)
{
_viewModel = viewModel;
this.DataContext = _viewModel;
InitializeComponent();
}
CustomLogger: LogItems
正在从events
创建
public class CustomLogger
{
protected static readonly ILog log = LogManager.GetLogger(typeof(CustomLogger));
static MemoryAppender memoryAppender = new MemoryAppender();
private Hierarchy h = LogManager.GetRepository() as Hierarchy;
public CustomLogger()
{
log4net.Config.XmlConfigurator.Configure(new System.IO.FileInfo(@"C:'Users'username'Documents'GitHub'MassSpecStudio'MassSpecStudio 2.0'source'MassSpecStudio'Core'app.config"));
memoryAppender = h.Root.GetAppender("MemoryAppender") as MemoryAppender;
}
public static ObservableCollection<LogItem> GetLogItems()
{
var events = memoryAppender.GetEvents();
ObservableCollection<LogItem> LogItems = new ObservableCollection<LogItem>();
foreach (LoggingEvent loggingEvent in events)
{
DateTime TimeStamp = loggingEvent.TimeStamp;
Level Status = loggingEvent.Level;
string Message = loggingEvent.RenderedMessage;
LogItems.Add(new LogItem(TimeStamp, Status, Message));
}
return LogItems;
}
}
LogItem:
public class LogItem : INotifyPropertyChanged
{
private DateTime _datetime;
private Level _status;
private string _message;
public LogItem(DateTime datetime, Level status, string message)
{
this._datetime = datetime;
this._status = status;
this._message = message;
}
public enum LogLevel
{
Debug = 0,
Info = 1,
Warn = 2,
Error = 3,
Fatal = 4,
}
public DateTime TimeStamp
{
get
{
return _datetime;
}
set
{
if (_datetime != value)
{
_datetime = value;
RaisePropertyChanged("DateTime");
}
}
}
public Level Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
_status = value;
RaisePropertyChanged("Status");
}
}
}
public string Message
{
get
{
return _message;
}
set
{
if (_message != value)
{
_message = value;
RaisePropertyChanged("Error");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string info)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); }
}
}
LoggerMenuItem:
[Export]
public partial class LoggerMenuItem : MenuItem
{
protected static readonly ILog log = LogManager.GetLogger(typeof(LoggerMenuItem));
private readonly IServiceLocator _serviceLocator;
[ImportingConstructor]
public LoggerMenuItem(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
InitializeComponent();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
LoggerView window = _serviceLocator.GetInstance<LoggerView>();
window.Owner = Application.Current.MainWindow;
window.Show();
}
}
更新:我注意到我的静态方法GetLogItems()
从CustomLogger只在第一次打开UI时执行。GetLogItems()
在events
更新后不执行
是否有一种方法可以通知事件中的属性更改?
要解决这个问题,我认为最好是让LogItems, DateTime, Status和Message的setter在UI被触发时被触发。MenuItem_Click
方法负责触发UI。是否可以在window.Show()
之前调用NotifyPropertyChanged
?
我认为你正在替换LogItems ObservableCollection后绑定已解决,你没有调用NotifyPropertyChanged通知UI。
尝试更改LogItems属性以调用NotifyPropertyChanged:
private ObservableCollection<LogItem> _logItems;
public ObservableCollection<LogItem> LogItems
{
get { return _logItems; }
set
{
_logItems = value;
NotifyPropertyChanged("LogItems");
}
}
我认为你需要像这样改变列:
<GridView>
<GridViewColumn Width="150" Header="Date And Time" DisplayMemberBinding="{Binding TimeStamp}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" TextAlignment="Center" Text="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="50" Header="Status" DisplayMemberBinding="{Binding Status}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" TextAlignment="Center" Text="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="1800" Header="Message" DisplayMemberBinding="{Binding Message}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock TextWrapping="WrapWithOverflow" Text="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>