4

I have a Node object with a Parent that i have bound in my xaml like so,

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

My ViewModel looks something like this

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.
    }

}

Why is it that when my if statement is commented out, the label/textblock is blank? Am I doing something wrong? Does my object both exist and not exist until I check if it's null?

Edit:
Forgot to mention, my node class is pretty simple, and does implement INotifyPropertyChanged for the Name property.

2nd Edit: Added my simple Node class.

[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;
    }
}
Charles W
  • 2,262
  • 3
  • 25
  • 38

2 Answers2

3

From comments:

It is actually. It is proxied by Entity Framework. Could that be the issue? If so, is there a less hackish way to fix this?

If the problem really lies in the proxy, then, well, no. You'll get a hard time with proxies/wrappers and WPF and change-notifications, really.

WPF's Binding engine does not work with proxies. At all. Well, maybe unless they are really well written and complex. So, usually, never. This comes form the fact how INPC interface works:

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

WPF tracks the sender and Binding.Source. If a Source of a Binding is an object called "Z", that is a Proxy to an object "A", then any notifications originating from "A" that are forwarded by proxy "Z" to the WPF's engine are .. discarded because for WPF the "Z" is the Source and it does not match the advertised sender "A".

I battled with this issue for quite a long time, and the only solution I found is to have the Proxy translate the P-Changed event so that the sender=A is subsituted for Z. This can have some nasty memory leaks when not written properly. It's because it's hard to "map" a delegate to a new delegate and provide proper disposal and GCing of both of them. And usually even this is only possible if you build your own proxies with ie. Castle or similar library. I have not found any dynamic-proxy library that supports sender-replacement out of the box.

If you know any - pleease let me know!

Anyways, be careful with the terminology. I mean, proxies. An object "Z" that wraps and trims or extends operations on another different original object "A". Your case may be different. If your so-called "proxy" is a dynamic subclass of your type and if it overrides any virtual methods/properties/events you have in your original class, then it is not the Proxy I meant. In this case, the 'proxy' and the original object is the same object, it's just that you see it as a class A and the actual class is Z:A, and for WPF the Source matches the sender. If I remember correctly, this is how EF often works.

Therefore, the first thing I'd check is to inspect who is really the culprit. Maybe Fody not EF? Try to remove that magic ImplementPropertyChanged, try to implement INPC manually in ALL classes. And I mean ALL. Both in Node and in NodeViewModel. If you are lazy, you can propdp and use DependencyProperty instead. If it works - then start removing the manual INPC and replacing it with ie. that from Fody's. When things start to break, gather all results and reanalyze.

Also, check what Fody does - maybe it provides a transparent proxy/wrapper in some point?

EDIT: Considering that your code starts working when you 'touch' the Fody'ied object, I'd start blaming Fody. Maybe it is because the NodeViewModel.Node property is currently not tracked bu Fody? Have you tried marking it with ImplementPropertyChanged too? It is a pure guess and it'd mean that this library has some serious problems, but it's quick and worth trying.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • This is good advice, I'll report back when I can with the results of manual `INPC` implementation. **Edit:** I do in fact have `[ImplementPropertyChanged]` already in place in my viewmodel with no change. – Charles W Mar 21 '14 at 20:15
  • 1
    It seems that there are more factors that go into making it display than I thought. For instance, Node Parent must be virtual, and lazy loading must be turned on. If either Lazy loading is turned off, or Node Parent is no longer virtual, or both, it does not work. – Charles W Mar 21 '14 at 21:18
  • @CharlesW: it's great that you've noticed it. If `virtual` makes difference, it's sure that there are no proxies/wrappers and WPF shouldn't have any problems with notifications. The libraries are subclassing your classes and use the virtual'ness to provide their extensions. You will probably need to have every property virtual, or else the libs will not able to attach themselves there. It's common when working with 'subclassing proxies', I dont know how to call that:). However, the Lazy'ness might be suprising in general terms - – quetzalcoatl Mar 21 '14 at 22:43
  • but is not suprising when working with EF 'subclassing proxies'. If I remember correctly, EF **does not override** your properties unless they **require** to be "delayed". When a property or relation is "normal", then it is loaded eagerly, filled during first load, and never needs updates. In this case, EF keeps the implementation stripped to minimum (does not override, keeps your plain properties) to provide the lowest performance impact of proxying. However, if the relation is delayed or property is lazy - then it **must** override it to provide that loading, and only then injects more logic – quetzalcoatl Mar 21 '14 at 22:47
  • You could verify it by for example: if plain virtual nonLazy property does not work (`public virtual string Name{get;set}`), then try making it virtual nonLazy and also INPC (`private string _name; public virtual string Name{get{return _name;} set{_name=value;raiseInpc("Name");}}`). Being not-lazy, EF wont override it, but still must use your getters/setters and it will notify and may now work. I've not tried though. And I don't know how Fody'a attributes work with such things. Maybe plain+nonLazy+virtual+FodyINPC is OK? (`[ImplementPropChanged]public virtual string Name{get;set;}`) – quetzalcoatl Mar 21 '14 at 22:50
0

I think you are missing to implement INPC on the ViewModel. The binding won't update back to the UI when you did Node = node. Raise a property changed event on the Node setter.

123 456 789 0
  • 10,565
  • 4
  • 43
  • 72