Diagram model binding mvvm way

Hello,

I've set up a data template for dock view containing diagram as follows:

, the underlying view model defines Model property as:

public IDiagramModel Model { get { return _model; } set { _model = value; OnPropertyChanged(); } }

that is initialized in the constructor:

Model = new GraphLinksModel...

In this case data binding does not work - getter is fired but afterwards nothing happens and diagram still has default model value (UniversalGraphLinksModel). If i set the binding as OneWayToSource - property setter is fired but value is null. Is it supposed to work like that? Additionaly binding to part manager failed as well as setting model or graph manager in xaml - silently, without any error.

Do You have any suggestions? Model is required to be accessed from vm because it has to manage all of the node operations.

thank You kindly for any help provided

Grieg

I have a few comments that I hope will help you.

First, the model type should probably be GraphLinksModel<LogicDiagramNodeData, long, string, LogicDiagramLinkData>, since port identifiers are best handled as strings.

Second, you might as well be more specific about the type of your view model’s Model property, to be GraphLinksModel<LogicDiagramNodeData, long, string, LogicDiagramLinkData> instead of the more general IDiagramModel.

Third, in that Model setter you should check whether the value has actually changed before deciding to call OnPropertyChanged. You aren’t passing the property name either, I notice.

Fourth, I think there is a conflict between your binding both Diagram.NodesSource and Diagram.Model. (And Diagram.LinksSource is just like Diagram.NodesSource.) You should choose one or the other. If you choose binding the Model, I suspect it should be Mode=TwoWay.

Thanks for your reply,

however:

[quote]First, the model type should probably be GraphLinksModel, since port identifiers are best handled as strings.[/quote]

Does that mean that long is not supported? What means best handled? I've changed the type to string and Model is still not bound properly to the Diagram.

[quote]Second, you might as well be more specific about the type of your view model's Model property, to be GraphLinksModel instead of the more general IDiagramModel.[/quote]

According to documentation Model property type of the Diagram is IDiagramModel so, as I understand, the only gain here is to simplify using Diagram on the vm side. It doesn't affect Model binding, which fails even if I change the type.

[quote]Third, in that Model setter you should check whether the value has actually changed before deciding to call OnPropertyChanged. You aren't passing the property name either, I notice.[/quote]

I probably should be more specific on this one so here is base implementation:

[code] protected void OnPropertyChanged([CallerMemberName] string propertyName = null) {

var handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); }[/code]

Still - even if getter is fired diagram property doesn't change and has its default value. What about OneWayToSourceBinding firing setter with null value I mentioned?

[quote]Fourth, I think there is a conflict between your binding both Diagram.NodesSource and Diagram.Model.[/quote]

Because of Model binding fail binding to NodesSource and LinksSource is my only option so far to get diagram to show anything at all. I've tried to remove binding to both sources leaving only Model binding and it still fails.

My last try was:

[code]

public class LogicDiagram : Diagram { public LogicDiagram() { PartManager = new LogicDiagramPartManager(); Model = new LogicDiagramModel(); } }

[/code]

and using this class in xaml with Model binding to vm. Although Diagram having Model property set the binding still fails firing vm setter with null value.

Please do share any more suggestions you might have especially on the Model dependancy property and why might its setter fail.

Could you share what XAML you are now using for the Diagram, and how you are now defining your view model?

Many customers use a DiagramModel as part of their view model.

The samples in the Demo app are organized into pairs of files per sample (*.XAML and *.XAML.CS) to make it easier to separate out the individual samples. This is clearly not a pure M-V-VM organization, but most of the samples can easily be reorganized to be that way.

Hello again,

I've created a simple project to reproduce the issue. The structure is most similar to actual project I'm working on - without unnecessary parts like dock manager. I hope it will be helpful. Please share any suggestions you might have

I don’t know why the Diagram.Model Binding isn’t working the way that one would expect – the Model property setter isn’t being called at all. Similarly with the other XAML declaration for the Diagram, the NodesSource and LinksSource property setters aren’t being called.

But if you replace the in MainWindow.xaml with the contents of the LogicPlanViewTemplate, everything works fine, whether binding the Model or the NodesSource/LinksSource.

So at the moment I cannot explain why dynamically defining the visual tree via a DataTemplate causes the Diagram’s property bindings not to be evaluated. I tried adding a TextBlock and a string property to the LogicPlanViewModel, but that binding worked fine. I tried adding a boolean property to the LogicPlanViewModel and binding Diagram.AllowDelete to it, and that worked too.

This will require further investigation.

Hello

[quote]I don't know why the Diagram.Model Binding isn't working the way that one would expect -- the Model property setter isn't being called at all. Similarly with the other XAML declaration for the Diagram, the NodesSource and LinksSource property setters aren't being called.[/quote]

Oddly enough the getter on vm is being called but considering I cannot peek into your code I can't provide much more information, only that i suspect dependancy property setter.

[quote]But if you replace the in MainWindow.xaml with the contents of the LogicPlanViewTemplate, everything works fine, whether binding the Model or the NodesSource/LinksSource.[/quote]

I'm afraid i cannot do that because diagram should be lying on the top of dock view and I can only specify templates for dock views in order not to introduce unnecessary class coupling.

[quote]This will require further investigation.[/quote]

I really appreciate that. Moreover I'm pretty surprised I'm first to report that issue. MVVM model isn't that new after all. Or maybe it's just our design that is strange? But again, if repaired, this could be pretty neat feature.

Our dependency property setters are not being called, so I don’t think the problem is in the implementation of those setters. I suspect that the problem is with the order in which bindings are being established and evaluated when dynamically creating content due to a binding change. But I’m just guessing at this time.

I know a lot of our customers use a no-code-behind approach to M-V-VM. Actually, we designed GoXam before M-V-VM became popular, but we have not had to make any changes to accommodate that pattern.

Hello,

how is the investigation? I managed to overcome previous problem by introducing another layer of UserControl and placing Diagram there. Binding works fine however I wasn't able to create links by dragging on the diagram. Is there special property to enable dragging links to nodes? Or am I supposed to add another component there? Test project available here

You need to set model.Modifiable = true; the default value is false.

Hello,

thank you for your consideration but have you actually tried to set this variable and run the project to try if this works? I've tried setting Modifiable on Model, AllowLink on Diagram and setting custom linking tool with overrided methods to see if this is ever called - all for nothing. Test project with beforementioned property set available here. Could you please try to investigate the problem further than that?

No, I had not tried your project yet, because I was at home on a mobile device trying to give helpful answers sooner rather than later. Apparently I was wrong about that in your case. I will try your project very soon.

The problem is basically that the ListBox is handling all of the mouse events, so the Diagram does not get a chance to do so.

That is why the Dynamic Ports sample defined these classes:

[code] public class InertListBox : ListBox {
protected override DependencyObject GetContainerForItemOverride() {
return new InertListBoxItem();
}
}

public class InertListBoxItem : ListBoxItem {
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
base.OnMouseLeftButtonDown(e);
e.Handled = false;
}
}[/code]
If you then replace all of your elements with diagramTest:InertListBox, the user will be able to draw new links.

Hello,

thank you for quick reply. It really was ListBox eating mouse events, however i managed to solve the issue by replacing it with ItemsControl. Is this common practice? Unfortunately I've come across another issue. If I supply LinkTool by binding - it doesn't work. Here is the project with issue reproduced. Upper diagram works well while that below is not. Could you please suggest any fixes? Binding components is really essential for our design.

(Again I am answering quickly, before having a chance to try your project.)

Are you sharing tools, such as a LinkingTool, between multiple Diagrams? As Introduction to Tools warns, that might happen unintentionally in various manners.

No, I do not share tools. The only difference between diagrams is that in one case LinkingTool is initialized in xaml and in the other is binded from viewmodel.

The immediate problem is caused by the LinkingTool="{Binding …}"assigning Diagram.LinkingTool = null initially. This causes the default LinkingTool to be removed from the Diagram.MouseMoveTools list.

Then the data context is established, causing your LogicDiagramLinkingTool to be assigned to Diagram.LinkingTool. But the old LinkingTool had been removed, so the property setter just adds the LogicDiagramLinkingTool to the end of the Diagram.MouseMoveTools list.

That means that all of the other tools in the Diagram.MouseMoveTools list take precedence over your LogicDiagramLinkingTool. So your tool is there, ready to run, but another tool, in particular the DraggingTool, starts running before your tool gets a chance.

Rather than suggest a kludgy way around this, let’s step back. I think it is a bad idea to put FrameworkElements into your view model. Your view model won’t be serializable in any reasonable fashion. It can’t be shared by multiple views. Tools and the CommandHandler are really part of the Diagram, just as Nodes and Links are part of the Diagram and not part of the view model.

It is unusual to have validation methods be part of your view model, but you could do it. You can define a custom LinkingTool, as you have, but not part of “DiagramData”. You would declare it as you did above in the XAML, without a Binding. Its override of IsValidLink would call a predicate that you would define on your view model. That predicate could be implemented as a virtual method or it could be implemented as a functional property or it could be implemented by any number of means. That includes having multiple predicates that you could choose from at run-time based on some model property, a “predicate selector”.

But instead of defining a custom LinkingTool I would recommend overriding GraphLinksModel.CheckLinkValid to do what you want. Of course that method will be operating on model data, not Nodes and FrameworkElements as LinkingBaseTool.isValidLink does, but I think that would be more appropriate anyway.
I hope I have explained myself adequately.

2 posts were split to a new topic: Binding models to diagrams in TabControls