WPF: Adorning a ViewModel? - wpf

I have these ViewModels: RecordViewModel, ComponentViewModel where RecordViewModel essentially is a container for several ComponentViewModels.
The display of these ViewModels is currently handled by DataTemplates that look something like this:
<DataTemplate DataType="{x:Type vm:RecordViewModel}" >
<ItemsControl ItemsSource={Binding Components} />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ComponentViewModel}" >
<TextBox Text={Binding Name} />
</DataTemplate>
What I wanted to provide now is a way to change the order the ComponentViewModels are displayed and to remove a certain ComponentViewModel from the list. I started out doing that by manipulating the DataTemplate of the ComponentViewModel and adding buttons that provided these functions (the click would then trigger a method on the ComponentViewModel that would (through a reference "Parent" to the RecordViewModel) call a method on the RecordViewModel to perform the operation (like component.Parent.DeleteComponent(this)).
The problem with this in my oppinion is that it is really the Record that should manipulate the Components position/remove a Component and not the Component itself.
So I thought about using an adorner that attaches to the RecordViewModel and renders the buttons to provide the functionality (remove, move up, move down) for each of the ComponentViewModels.
The problem however is that these adorners need to take a reference to a Control-derivate which they adorn (which was ok I would just bind to the ItemsControl in the Record-DataTemplate) however the problem appears when I want to show the buttons in the right position for each ComponentViewModel. I only have a reference to the given ComponentViewModels and not to their visual representation (the thing defined in the DataTemplate) so I have no way of knowing where to place the 3 buttons.
Is there a way to work around this? Or is it possible that for these requirements using ViewModels/DataTemplates is just not a good idea and should I therefore use Control-derivates/ControlTemplates?
Thanks in advance!

Coming up with wacky architectural hacks that you can employ to keep your view model elegant and simple is missing the point. The view model is the wacky architectural hack.
The only reason - seriously, the only reason - that the view model exists is to model the view. Does the view have buttons that trigger commands? The commands belong in the view model.
Thinking, "it's really the Record's responsibility to move Components" seems sensible on its face, but it's actually an indication that you're losing track of why you even created a view model in the first place. Does the Component view have a "Move Up" button? Then the Component view model needs a "Move Up" command that you can bind the button to. Because that's what the Component view model is for.
I'm being emphatic about this because this is the third or fourth question I've seen this week from WPF developers who seem to have gone down so deeply down the rabbit hole of the MVVM pattern that they've forgotten why it exists.

If your goal is to have a Command on the parent ViewModel that acts on an element of the child ViewModel, you can do this by using a RelativeSource binding on Command and passing the item as Command Parameter:
<DataTemplate DataType="{x:Type vm:ComponentViewModel}" >
<Button
Command="{Binding DataContext.RemoveCommand,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
Content="{Binding Name}"/>
</DataTemplate>
The RelativeSource binding will find the ItemsControl, so the DataContext property will be your RecordViewModel. The CommandParameter will be the individual ComponentViewModel, so your ICommand implementation would be:
DeleteComponent((ComponentViewModel)parameter);

it is really the Record that should manipulate the Components position/remove a Component and not the Component itself
As far as your model objects go, that's probably true. However, the ViewModels are all about presentation, and the buttons are kind of part of a Component's presentation. So I think it could be acceptable for the ComponentViewModel to have a reference to its parent RecordViewModel, to enable this scenario, even if it wouldn't be appropriate for the Component to have a reference to its parent Record.
But consider that, in your scenario, maybe the ComponentViewModel has too many responsibilities. It belongs to the collection (because it's mutating the collection), and it belongs to the element in the collection (because it's showing the Component's name in a TextBox). It sounds like it's this dual responsibility that's bothering you. So break it up. Make RecordViewModel contain RecordElementViewModels, each of which knows how to remove itself from the Record; and each RecordElementViewModel contains a ComponentViewModel. On the view side, it sounds like your UI would be composed the same way: an outer panel with a Delete button, and then another control or panel inside that, presenting the Component's properties.
Now, for the example you posted, where Component's view is just a TextBox, I wouldn't bother splitting the ViewModel into two parts. But for a more complex example, it might make a lot of sense.

To specifically answer your question about adorning:
You're getting into changing the way a DataTemplate-d element is laid out, which means you're not just layering an adorner on top of the element, you're actually wanting to insert a panel into the visual tree that imposes its own layout onto the DataTemplate (which becomes a child of the new panel). I'll admit that I haven't used adorners, but that doesn't seem to be what they're for.
The best way to do this, IMO, is to have your DataTemplate generate the parent panel, buttons and all -- which leads back to wanting the functionality on the ComponentViewModel, or perhaps splitting ComponentViewModel's responsibilities (see my other answer).

Related

How to avoid WPF to make Arrangements/Measurements on my own controls?

I have developed a 3D-engine in C# and I want it to be usable from within a WPF application through classes that can be used just like any other WPF Control.
First implementation
I created a Panel (let's call it EnginePanel) that extends the Grid class, and a set of controls to put inside like SceneNodeControl, GeometryControl, etc... These controls inherit the FrameworkElement WPF class.
For the SceneNodeControl, I exposed a Children property (UIElementCollection) and overrode that Logical/Visual management methods so that they look inside this collection.
I also overrode the ArrangeOverride and MeasureOverride methods so that we call Arrange/Measure on each child of the SceneNodeControl and then return a constant Size of zero pixels.
This implementation works and allows me to use ItemsControl, ContentControl and DataTemplates to populate my scene graph with a classical MVVM pattern.
Problem
My problem is that if I create a massive scene graph with hundreds of SceneNodeControls, the application dramatically slows down.
A quick check with the Visual Studio Profiler informed me that the Measure method from SceneNodeControl is responsible for 80% to 90% of the CPU usage (in terms of time spent).
OK, no problem. What I have to do is to remove these calculations that are too heavy and moreover useless in my case.
Solution 1
I tried to simply remove the call to the Arrange/Measure methods on the children of the SceneNodeControl.
This does not work. As specified in MSDN, the implementations of these methods MUST call the Arrange/Measure on the children.
Solution 2
Inherits the FrameworkContentElement. Indeed, this class does not have layouting algorithms.
But it does not have any Visual tree management, which makes it unusable with ItemsControls, ContentControl and DataTemplates.
Other solutions
I don't have other solutions... so this is why I'm here now!
The question is How to avoid WPF to make Arrangements/Measurements on my own controls?
Thanks!
Edit
Solution 3
I found another solution to my problem (but it produces more and more questions).
My controls can inherit the DependencyObject class. In that way, it should be possible to use the DataContextes from other WPF controls and more generally their DependencyProperties.
Problem number 1: I cannot use the existing DataTemplate, ItemsControl and ContentControl classes, but I probably can reimplement them...
Problem number 2: I cannot tell a DependencyObject to be a 'parent' of another DependencyObject. I found some hacks that 'reflect' this class and expose hidden members to try to manage the InheritanceContext and InheritanceParent. But since no one seems to do that and since Microsoft obviously don't want us to use it, it's really hard...
I will probably open a new question about this second problem. (Edit: It's here.)
Edit: to focus my question
What I want is writing something like that:
<controls:SceneNodeControl NodeName="Root">
<ItemsControl ItemsSource="{Binding SceneNodes}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:SceneNodeViewModel}">
<controls:SceneNodeControl NodeName="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</controls:SceneNodeControl>
and, at runtime, don't execute Measure/Arrange algorithms on the SceneNodeControls but build their Logical/Visual trees.
And here is my little test project.
If you don't want measure and Arrange affect the logic tree starting in NodeSceneControl and don't want to render it, just set its Visibility property to Visibility.Collapsed. This way, this node will be left out of the layout processing.

Split view into two separate views

I'm building a WPF application which very simplified looks something like this:
I have an ApplicationView which holds the menu and a ContentControl.
The ContentControl binds to the property CurrentViewModel which is set by the menu and rendered by its related View (Views and ViewModels are coupled by DataTemplates defined in the App.xaml).
I found this approach on Rachel Lim's blog
So in this example my View contains a list of duties as well as a "Details" window of the currently selected duty.
This setup works fine, but I think my ViewModels are getting too fat!
The non-simplified version of this ViewModel is up at around 500 lines of code, for handling:
Initializing filters
Logic for filtering list
Displaying duty details
Add/Update/Cancel/Delete logic
Now I'm very new to WPF but that seems like too much code, yea?
And it will be even bigger before I'm finished with it.
Anyways, I was thinking that I could split the ViewModel into two separate ViewModels; one for holding list and filters and one for showing the details. But how is this best accomplished?
I have thought of two approaches, but don't know which is preferable:
Create a DutyMasterView whose sole purpose is to hold two ContentControls for the actual Views (ie DutyListView and DutyDetailView each with their own ViewModel)?
I'm using MVVM Light as my framework so I suppose I could use the messaging service to tell the DutyDetailViewModel which Duty to display, right?
Alternately create a DutyMasterViewModel which exposes the selected duty.
Ditch the DutyMasterView and nest the DutyDetailView in the DutyListView.
Does it make sense to split my ViewModel into two or should I just stick with my fat ViewModel?
If splitting the ViewModel is recommended which of my suggestions makes most sense?
Are there other approaches that I should consider?
If you're still looking for opinion, I'd do it almost like you mentioned in point 1 but you don't need any messaging.
You create two VMs. Let's say DutiesVM and DutyDetailVM. DutyDetailsVM contains just some string properties for ID and Name.
In DutiesVM you create two properties:
ObservableCollection<DutyDetailVM> DutiesList
DutyDetailVM SelectedDuty
Your DutiesView can look like this:
<DockPanel>
<v:DutyDetailV DockPanel.Dock="Right" DataContext="{Binding SelectedDuty}">
<ListBox ItemsSource="{Binding DutiesList}" SelectedItem="{Binding SelectedDuty}"/>
</DockPanel>
Now you can create ListView ItemTemplate that binds to DutyDetailVM Properties.
is usercontrol that defines the DutyDetail view. Selecting the item in the list updates the details control automatically.
That's just the sketch but I think you can get the point from it.

Using WPF without databinding?

I need to create an application that will take an .ini file which will contain
min
max
default
values for the elements, allows user to edit these values and save a new .ini file. Since .ini files can not contain different elements in the specified groups the GUI needs to be generated dynamically.
From what I have read about WPF it largely stands on data-binding and Notifying Property changes.
Since my view model needs to accommodate different numbers of variables I am not going to have the ability to bind to properties, i was planning to attach one event handler to all text boxes which will pick the corresponding validation rule when the TextBox loses focus or Enter is pressed. After that, it should update the model accordingly if it passes validation and update the View using the model for the corresponding value.
I was wondering whether this sounded like a valid idea, whether there is similar design pattern I should read about or should I just steer away from WPF altogether?
You can still use bindings - since WPF supports item templating, and since you are using an MVVM pattern you can just create a VM for each sub-item in the list (you don't even need to do this you can bind directly in the template of each list item to a DTO or business object)
I'm currently doing a similar thing now - I have a list of material tests for a client, they want to have a variable number and type of tests for each material, but also be able to tweak and change those tests per order for their customer
They actually have two test types, but to describe the simpler of the two cases (which doesn't require child VMs as such) I just created an ItemsControl that has an item template:
<ItemsControl ItemsSource="{SomeBinding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{PropertyDescription}" />
<TextBox Text="{PropertyValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In this case the model just contains a list of property names/values and displays them in a stackpanel (you may need to set the ItemPanel using an ItemsPanelTemplate. Obviously you could have an extended ItemsControl that allows a DataTemplateSelector to display a different data template per type (in fact WPF already support per-type data templates).
I'm currently using Caliburn.Micro which actually does a lot of setting up child-templates for you - so if you create the bound items as a VM you can do something as simple as this:
<ItemsControl x:Name="SomeBinding" />
And CM takes care of the rest as long as the child items in the SomeBinding property are VMs themselves (though that's another story :P)
Wrap this library with a class that implements INotifyPropertyChanged so WPF can update itself once properties change. That way you can effectively databind to an INI file.

How to create databinding over two xaml files?

I am trying to come to a working understanding of how databinding works, but even after several tutorials I only have a basic understanding of how databinding works. Thus this question might seem fundamental to those more familiar with silverlight. Even if it is trivial, please point me to some tutorial that deals with this problem. All that I could find simply solved this via adding the data binding on a parent page.xaml (that i must not use in my case).
For the sake of this example let us assume, that we have 5 files:
starter.cs
button1.xaml + codeBehind
button2.xaml + codeBehind
The two buttons are generated in code in the starter(.cs) file, and then added to some MapLayer
button1 my_button1 = new button1();
button2 my_button1 = new button2();
someLayer.Children.Add(my_button1);
someLayer.Children.Add(my_button2);
My aim is to connect the two buttons, so that they always display the same "text" (i.e. my_button1.content==my_button2.content = true;). Thus when something changes my_button1.content this change should be propagated to the other button (two way binding).
At the moment my button1.xaml looks like this:
<Grid x:Name="LayoutRoot">
<Button x:Name="x_button1" Margin="0,0,0,0" Content="{Binding ElementName=x_button2, Path=Content}" ClickMode="Press" Click="button1_Click"/>
</Grid>
But everthing that i get out of that is a button with no content at all, it is just blank as the binding silently fails.
How could I create the databinding in the context I described? Preferably in code and not XAML ;)
Thanks in advance
The chunk of documentation you need to read is this: XAML Namescopes
Your button1 xaml has a binding looking for an element with the name "x_button2". However in a real application there can be many controls which in turn have nested controls. All of these controls have all manner of UI elements some of which may have names.
It would be impossible to get anything done if all names throughout the entire application had be unique. Yet that would need to be true if it were for your button1 to be able to hunt down the existence of another control somewhere in the visual tree outside of that which it actually knows (its own xaml).
Hence each loaded Xaml document exists in its own "namescope" and the search for other elements with other names is limited to that "namescope".
The are various solutions to this problem depending on what you real requirements are as opposed to the simplified problem in your question.
Typically you give each of your controls a DependencyProperty to which the inner button Content property binds. In "MapLayer" as call it, could then bind the propert on one of your button controls to the other.

Xaml - add existing control to grid

Trying to develop using MVVM:
I have this Csla.PropertyStatus control that is created in my ViewModel.
I want to add it in the xaml (via binding, i think) and set some additional properties there (such as the target).
I don't know how i could do that, not even if that is possible.
I was looking for something like
<csla:PropertyStatus Instance="{Binding Path=MyStatus}"
Target="{Binding ElementName=txtTextBox}"
Grid.Column="2" Grid.Row="0"/>
Is that possible somehow?... i NEED to create it in the ViewModel because i need to set some property that the view (XAML) is not allowed to know about.
Edit:
The property i'm talking about is the model. The PropertyStatus needs a reference to the business object to perform the validation.
But considering the paradigm of MVVM (or as i understand it), the view should be decoupled from the model, and should only know about the ViewModel. I might change that approach, though...
You CAN'T do this. BTW, the view is all about UI controls and their properties, so saying the view is not allowed to "know about" a property on one of its elements is a bit... extreme. A description of what you are trying to accomplish here might help in providing you with a decent answer.

Resources