即使集合没有改变

时间:2018-04-16 19:37:55

标签: c# wpf mvvm datagrid wpfdatagrid

我正在编写一个应用程序来阅读和分析我公司软件使用的一些日志。有多种类型的日志,但为了描述我的问题,我们只需要两个。即,TypeATypeB的日志。我设计了一个类来保存一行日志数据,名为LogLine,如下所示。

public class LogLine
{
    public long LineNum { get; set; }
    public string Msg { get; set; }
}

所以这是我的问题/要求。

在我的主ViewModel中,我想在应用程序加载时只读取每种类型的日志一次。一次阅读TypeA个日志,并存储在ObservableCollection LogLine个实例中,对TypeB执行相同操作。然后根据我的选择,DataGrid显示来自一种类型的日志,如果我在任何时候单击一个按钮,则相同的DataGrid应该显示来自另一种类型的日志。请注意,我的日志数据没有变化,我只想显示我选择的日志。

为此,我创建了三个类,即ControllerMainControllerAControllerB。最后两个来自前者,如此:

public class ControllerMain
{
    public ControllerMain()
    {
        LogLineList = new ObservableCollection<LogLine>();
    }

    private ObservableCollection<LogLine> logLineList;
    public ObservableCollection<LogLine> LogLineList
    {
        get { return logLineList; }
        set { logLineList = value; }
    }
}

public class ControllerA : ControllerMain
{
    public ControllerA() { }
    // More stuff here
}

public class ControllerB : ControllerMain
{
    public ControllerB() { }
    // More stuff here
}

您可以猜测ControllerA旨在保存TypeA的日志,以及这些日志特有的关联属性和方法。 TypeB日志也是如此。

在我的ViewModel中,我有上面每个类的实例,并且在应用程序加载时,我读取日志数据并存储在适当的类对象中。

    public ControllerMain COMMON_LOG { get; set; }
    public ControllerA A_LOG { get; set; }
    public ControllerB B_LOG { get; set; }

    public ViewModelMain()
    {
        isAType = true;
        ClickCommand = new CustomCommand(ClickCmd, CanClickCmd);

        A_LOG = new ControllerA
        {
            // This simulates reading logs from files - done only once
            LogLineList = DataService.GetAData()
        };
        B_LOG = new ControllerB
        {
            // This simulates reading logs from files - done only once
            LogLineList = DataService.GetBData()
        };

        // This simulates switching to already loaded logs.
        // When I do this the log lines don't change, but I want to refresh the datagrid and display correct info.
        LoadAppropriateLog();
    }

    private void LoadAppropriateLog()
    {
        if (isAType)
        {
            COMMON_LOG = A_LOG;
            isAType = false;
        }
        else
        {
            COMMON_LOG = B_LOG;
            isAType = true;
        }
    }

我的View绑定到COMMON_LOG实例,如下所示:

<DataGrid Grid.Row="0" Margin="5"
          Name="dgLogs"
          AutoGenerateColumns="False" SelectionUnit="CellOrRowHeader"
          ItemsSource="{Binding COMMON_LOG.LogLineList}">

然后,只需点击一下按钮,我就会调用上面的LoadAppropriateLog()方法,因此它只会将适当类型的实例分配给COMMON_LOG,这是我用于数据的实例绑定。

问题在于,当我这样做时,由于每个实例LogLineList中的实际数据都没有变化,DataGrid不会自动更新以反映我选择的日志。

每次切换日志类型后,有没有办法从DataGrid手动刷新ViewModel

如果您想运行该项目,请参阅下面的链接:

Download the VS Project

1 个答案:

答案 0 :(得分:2)

如果你在XAML中绑定了一个类的属性,那么

  1. 在绑定首次看到它之后,属性不应该更改其值,并且通常应该只是为了避免意外 - 或者
  2. 该类应实现INotifyPropertyChanged,该属性应在其setter中引发PropertyChanged
  3. 在您的情况下,您正在更改COMMON_LOG的值,并且您永远不会更改其LogLineList的值。

    tl; dr:因此,您的主视图模型需要实现INotifyPropertyChanged,并在PropertyChanged的设置器中引发COMMON_LOG任何不做的事情那些东西不是视图模型。

    作为LogLineList

    ObservableCollection将无法完成任何操作:该类所做的是在添加,删除或替换项目时发出通知。在绑定看到它之后的任何时候都不会发生这种情况。那些ObservableCollection的实例甚至不知道主视图模型甚至存在,因此当它们的属性发生变化时,它们肯定不会引发通知事件。它们也不应该:每个人都对自己的通知负责。

    事实上,如果您做出设计决定,初始化后这些集合永远不会更改,请使用ReadOnlyCollection代替ObservableCollection。创建一个很简单:致电List<T>.AsReadOnly<T>()。对于任何IEnumerable<T>,只需致电e.ToList().AsReadOnly()即可。 ObservableCollection表示“你可以添加内容”。但没有人应该。所以不要给他们想法。