MVVM:通知UI中的属性更改

时间:2015-08-06 17:09:06

标签: c# wpf mvvm binding inotifypropertychanged

我正在建立一个记录器。 UI(LoggerView)绑定到LogItem的三个属性:DateTimeStatusMessage。当我运行程序时,会创建日志记录事件并正确登录到UI。但是,当我关闭UI,记录更多其他事件并再次打开UI时,其他事件会记录到events,但它们不会出现在UI中。

问题是,当新的LogItems被添加到集合时,不会通知UI。如何在添加其他LogItems时通知用户界面?

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 Logic:

[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();
    }
}

更新:

我注意到CustomLogger中的静态方法GetLogItems()仅在第一次打开UI时执行。在GetLogItems()更新后,events不会执行。

有没有办法在事件中通知财产变更?

要解决这个问题,我认为最好在触发UI时触发LogItems,DateTime,Status和Message的setter。 MenuItem_Click方法负责触发UI。是否可以在NotifyPropertyChanged之前调用window.Show()

1 个答案:

答案 0 :(得分:2)

我认为您在解析绑定后正在替换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>