【问题标题】:Schrodinger's Object: WPF property won't bind/update unless I check its value code-side?薛定谔的对象:除非我在代码端检查它的值,否则 WPF 属性不会绑定/更新?
【发布时间】:2014-03-21 19:15:10
【问题描述】:

我有一个带有 Parent 的 Node 对象,我已经像这样绑定在我的 xaml 中,

<Label>
    <Hyperlink>
        <TextBlock Text="{Binding Path=Node.Parent.Name}"/>
    </Hyperlink>
</Label>

我的 ViewModel 看起来像这样

public class NodeViewModel
{
    public Node Node { get; set; }

    public NodeViewModel(Node model)
    {
        Node = model;
        if(model.Parent != null) { } // Check if it's null, then do nothing.
        // When the above line is commented out, my label displays nothing.
    }

}

为什么当我的 if 语句被注释掉时,标签/文本块是空白的?难道我做错了什么?在我检查它是否为空之前,我的对象是否既存在又不存在?

编辑:
忘了提一下,我的节点类非常简单,并且确实为 Name 属性实现了INotifyPropertyChanged

第二次编辑:添加了我的简单节点类。

[ImplementPropertyChanged] // From Fody.PropertyChanged
public class Node
{
    public int? ParentID { get; set; }
    public Node Parent { get; set; }
    public string Name { get; set; }

    public Node Node(Node p = null)
    {
        Parent = p;
    }
}

【问题讨论】:

  • 贴出Node类的代码。
  • 对象是否被某种实现延迟加载的 ORM(如 NHibernate 或实体框架)代理?
  • @Matthew 实际上是。它由实体框架代理。这可能是问题吗?如果是这样,有没有更简单的方法来解决这个问题?

标签: c# wpf entity-framework fody-propertychanged


【解决方案1】:

来自 cmets:

确实如此。它由实体框架代理。这可能是问题吗?如果是这样,是否有一种不那么骇人听闻的方法来解决这个问题?

如果问题确实出在代理上,那么,不。使用代理/包装器、WPF 和更改通知您会遇到困难,真的。

WPF 的绑定引擎不适用于代理。完全没有。好吧,也许除非它们写得很好而且很复杂。所以,通常,从不。这来自于 INPC 接口的工作原理:

void PropertyChanged(object sender, .... args)

WPF 跟踪 senderBinding.Source。如果绑定的源是一个名为“Z”的对象,即对象“A”的代理,那么由代理“Z”转发到 WPF 引擎的任何源自“A”的通知都将被丢弃,因为对于 WPF,“Z”是源,它与通告的发件人“A”不匹配。

我与这个问题斗争了很长时间,我找到的唯一解决方案是让代理翻译 P-Changed 事件,以便@987654324 @ 替换为 Z。如果没有正确写入,这可能会导致一些令人讨厌的内存泄漏。这是因为很难将一个代表“映射”到一个新的代表并为他们提供适当的处置和 GCing。通常只有当你用 ie 构建你自己的代理时,这才是可能的。城堡或类似的图书馆。我还没有找到任何支持sender-replacement 开箱即用的动态代理库。

如果你知道 - 请告诉我!

无论如何,请注意术语。我的意思是,代理。一个对象“Z”,它对另一个不同的原始对象“A”进行包装、修剪或扩展操作。您的情况可能不同。如果您所谓的“代理”是您的类型的动态子类,并且它覆盖了您在原始类中拥有的任何虚拟方法/属性/事件,那么它不是我的意思的代理。在这种情况下,“代理”和原始对象是同一个对象,只是您将其视为类 A,而实际类是 Z:A,而对于 WPF,Sourcesender 匹配。如果我没记错的话,这就是 EF 通常的工作方式。

因此,我要检查的第一件事是检查谁是真正的罪魁祸首。也许 Fody 不是 EF?尝试移除魔法ImplementPropertyChanged,尝试在所有类中手动实现INPC。我的意思是所有。在NodeNodeViewModel 中。如果您很懒惰,可以propdp 并改用DependencyProperty。如果它有效 - 然后开始删除手动 INPC 并用 ie 替换它。来自福迪的。当事情开始破裂时,收集所有结果并重新分析。

另外,看看 Fody 做了什么——也许它在某些时候提供了一个透明的代理/包装器?

编辑:考虑到当您“触摸”Fody'ied 对象时您的代码开始工作,我会开始责怪 Fody。也许是因为NodeViewModel.Node 属性目前没有被 Fody 跟踪?你也试过用ImplementPropertyChanged 标记它吗?这是一个纯粹的猜测,这意味着这个库有一些严重的问题,但它很快并且值得一试。

【讨论】:

  • 这是个好建议,我会在可能的时候报告手动INPC 实施的结果。 编辑:事实上,我的视图模型中已经有[ImplementPropertyChanged],没有任何变化。
  • 似乎有比我想象的更多的因素让它显示。例如,Node Parent 必须是虚拟的,并且必须打开延迟加载。如果延迟加载被关闭,或者 Node Parent 不再是虚拟的,或者两者兼而有之,它都不起作用。
  • @CharlesW:很高兴你注意到了。如果virtual 有所作为,则可以肯定没有代理/包装器,并且 WPF 不应该有任何通知问题。这些库是您的类的子类,并使用 virtual'ness 来提供它们的扩展。您可能需要将每个属性都虚拟化,否则库将无法将自己附加到那里。使用“子类代理”时很常见,我不知道如何称呼它:)。然而,一般来说,Lazy'ness 可能会令人惊讶 -
  • 但在使用 EF 的“子类代理”时并不令人惊讶。如果我没记错的话,EF 不会覆盖您的属性,除非它们要求“延迟”。当一个属性或关系是“正常的”时,它会被急切地加载,在第一次加载时填充,并且永远不需要更新。在这种情况下,EF 将实现剥离到最低限度(不覆盖,保留您的普通属性)以提供代理的最低性能影响。但是,如果关系延迟或属性延迟 - 那么它必须覆盖它以提供加载,然后才注入更多逻辑
  • 您可以通过以下方式验证它:如果普通的虚拟 nonLazy 属性不起作用 (public virtual string Name{get;set}),则尝试将其设为虚拟 nonLazy 和 INPC (private string _name; public virtual string Name{get{return _name;} set{_name=value;raiseInpc("Name");}})。不懒惰,EF 不会覆盖它,但仍然必须使用你的 getter/setter,它会通知并且现在可以工作。不过我没试过。而且我不知道 Fody'a 属性如何与这些东西一起使用。也许plain+nonLazy+virtual+FodyINPC可以吗? ([ImplementPropChanged]public virtual string Name{get;set;})
【解决方案2】:

我认为您缺少在ViewModel 上实现INPC。当您执行Node = node 时,绑定不会更新回UI。在 Node 设置器上引发 property changed 事件。

【讨论】:

    猜你喜欢
    • 2015-07-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 2018-07-24
    • 1970-01-01
    • 2020-09-21
    相关资源
    最近更新 更多