【问题标题】:Why is my ObservableCollection going unobserved?为什么我的 ObservableCollection 没有被观察到?
【发布时间】:2014-11-02 08:42:56
【问题描述】:

我在用户控件中有以下元素,作为默认 Grid 的唯一子元素:

<ListView ItemsSource="{Binding LogCollection}" Name="LogView">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn DisplayMemberBinding="{Binding Level}" Header="Level"/>
                <GridViewColumn DisplayMemberBinding="{Binding FormattedMessage}" Width="500" Header="Message"/>
                <GridViewColumn DisplayMemberBinding="{Binding Exception}" Header="Exception"/>
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

在后面的代码中,我正在处理来自自定义 NLog Target 的以下事件。我只是因为给我一个例子而处理它,一个完全 WPF 菜鸟,但它似乎还可以。

private void EventReceived(LogEventInfo message)
{
    Dispatcher.Invoke(() =>
        {
            if (LogCollection.Count >= 50)
            {
                LogCollection.RemoveAt(LogCollection.Count - 1);
            }
            LogCollection.Add(message);
        });
}

事件触发良好,当我单步执行代码时,可以调用Add。当我在执行时将鼠标悬停在 XAML 视图中的 LogCollection 上时,我在 Addcall, yet myListView` 之后看到一个“Count = 1”,但我完全没有意识到它应该向我显示一个日志事件。

好的,好的,我的用户控件背后的删节代码是:

public partial class LoggingControl : UserControl
{
    public ObservableCollection<LogEventInfo> LogCollection { get; set; }
    public LoggingControl()
    {
        LogCollection = new ObservableCollection<LogEventInfo>();
        InitializeComponent();

        foreach (Target target in LogManager.Configuration.AllTargets)
        {
            var memoryEventTarget = target as MemoryEventTarget;
            if (memoryEventTarget != null)
            {
                memoryEventTarget.EventReceived += EventReceived;
            }
        }
    }

    private void EventReceived(LogEventInfo message)
    {
        Dispatcher.BeginInvoke(new Action(() =>
                    {
                        LogCollection.Add(message);
                    }));
    }
}

【问题讨论】:

  • 双向绑定与此场景无关。
  • @MilanNankov 我同意,但还是给了它一个狂欢。我在必须绑定回集合的列表中没有做任何事情,它只能读取它。
  • 顺便说一句,@MilanNankov 和我的 cmets 仅在另一个上下文中,因为已删除,建议 TwoWay 的评论。
  • LogCollection 可能是 ViewModel 上的一个属性?如果是这样,请确保该属性在您设置它时引发 PropertyChanged,因此每当您重新分配(或初始化)该属性时,绑定都会更新一个新的 Observable 实例。确保发布 ViewModel 的代码。
  • @ErnodeWeerd 不,LogCollection 是我声明 ListView 的控件的属性。我知道,我应该使用 ViewModel,但事情必须先正常工作才能正常工作。

标签: c# wpf xaml nlog


【解决方案1】:

正如 cmets 中所述,ListView.ItemsSource 绑定的绑定上下文似乎是错误的。 DataContext。在您的情况下,手动设置 DataContext 可以解决问题

this.DataContext = this; 

但这需要在创建 LogCollection 之后完成,因为属性本​​身不会引发 INotifyPropertyChanged.PropertyChanged 事件。

并回答为什么 DataContext 默认不设置为 self 是因为 DataContext 在整个可视化树中被继承。所以UserControl 和其他FrameworkElement 一样,默认情况下会从放置它的元素继承DataContext

另一件值得注意的事是DataContext 值只能有一个。它将从可视化树继承或在您的UserControl 中手动设置。您可以使用继承的上下文,但仍然可以使用RelativeSourceElementName 绑定绑定到UserControl 属性。

<UserControl ... x:Name="myUserControl">
    <!--  --->
    <ListView ItemsSource="{Binding LogCollection, ElementName=myUserControl}" Name="LogView">

【讨论】:

  • 是的,谢谢,我刚刚意识到在UserControl 中我必须将 dc 设置为 Self 非常有意义,否则它会从其父级继承。 +1 以获得关于更显式绑定的额外提示。
【解决方案2】:

您确定您实际添加项目的 LogCollection 是您将网格绑定到的那个吗?我创建了一个包含您的代码但按预期工作的示例应用程序?也许您需要发布更多代码。

Here is my sample application

也许您的 ItemsSource 绑定现在可以正常工作了。您在控制台中看到任何绑定错误吗?类似于“BindingExpression 路径错误:在 'object' 上找不到 'LogCollection' 属性”

【讨论】:

  • 是的,只有一个LogCollection。我在输出窗口中看不到任何错误,只有日志消息,因为我的自定义日志目标也会写入其他任何地方。当我初始化它时,我会尝试直接添加到集合中,看看它是否获得了初始内容。然后我会检查你的示例,然后进行一些大规模的编辑以发布更多代码,而不会过多地乱扔这个页面。
  • 可能您的绑定不起作用,因为您的 DataContext 设置不正确 - 您的 LogCollection 不是您的 DataContext 的属性。您已经说过 LogCollection 属性是在您拥有 GridView 的控件上定义的。如果是这种情况,只需在控件的构造函数中将控件本身设置为 DataContext。比如在控件的构造函数中 -> this.DataContext = this;
  • 我说过LogCollection 是我的用户控件的属性,而不是我的用户控件或其Grid 中的任何内容。我已经说过 ListView 是我的用户控件上唯一的子控件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-14
  • 2021-09-11
  • 1970-01-01
  • 2017-05-06
  • 1970-01-01
  • 2015-10-25
相关资源
最近更新 更多