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

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.

Related

UserControl, ContentControl and MVVM

I'm kind of a beginner with the WPF platform. I've done a few Windows Forms apps that were very basic and now I'm working on a much more complex app for my current work.
I'd like to implement an MVVM model, but I'm getting lost in the different articles on how to do so.
Here's a screenshot of the app interface:
The section on the left is a ListView containing 6 sections that correspond to different UserControl. I would like the section on the right to display the UserControl that corresponds to the selected Item.
Each UserControl is stored in a separate XAML file.
By looking online, I found that I should use a ContentControl in my MainWindow, but every attempt I've made has been unfruitful. I know there's more than one way to skin a cat.
Which method would you use? Do you have any concrete examples or sources where I can find how to make it work?
The only difference between UserControl and ContentControl is the OnCreateAutomationPeer method. This method is responsible for UI automation.
Although this method is rarely used in practice, it is customary to use UserControl to represent some complex data types.
A typical use for a UserControl is to retrieve data (in normal CLR types) through the data context.
And the Content property specifies (using XAML) a visual tree to represent that context.
And each such UserControl is declared as a derived type (just like a Window).
If necessary, additional properties (CLR and DP) and other members (event handler methods, for example) are added to such a UserControl.
The base class ContentControl itself and others of its successor are used somewhat differently.
The data in them goes directly to the Content property.
And their visualization is set by the data template in the ContentTemplate property.
While DataTemplate's can be quite complex, they are usually much simpler than the XAML markup of the UserControl.
And, besides the task of visualization, you cannot add additional logic (properties, fields, methods).
Here's a photo of the app interface: ...
In this UI, I don't see where the ContentControl can be applied.
On the left is a navigation bar consisting of buttons.
It is usually implemented directly in the Window from an ItemsControl or ListBox.
On the right (while an empty area) is the region for the page content.
Usually, when you click on a button in the navigation bar, the corresponding UserControl is set to this region.
At a lower level, deeper in the visual tree, for smaller elements, it is quite possible that a ContentControl is needed.
P.S. Just in case, I will clarify, in view of the comment below.
By area for pages, I do not in any way mean the use of Frame + Page.
This is a very specific pair and it is extremely rarely justified to use it.
In combination with MVVM, its use is even more difficult, since this pair does not support DataContext inheritance.
And this is the main way for the View to interact with the ViewModel.

How to access Parent's DataContext in Windows 8 store apps

This is a common situation in XAML based apps (WPF/Silverlight/WinRT).
WPF related link -> WPF Databinding: How do I access the "parent" data context?
RelativeSource with AncestorType, and Mode=FindAncestor usually comes to rescue in WPF.
Both of these are missing in WinRT API. How to access the Parent's (may not be immediate one), DataContext?
(I am aware of TemplateBinding, and ElementBinding but both are not suitable mostly in DataTemplate).
I just had the same problem. Presumably this is common??
Here is a crude solution that works:
Bind the Tag property of a top level element to the DataContext
<Grid Name="gridTop" Tag="{Binding}" />
Bind the property you want via ElementName in nested element, ie
{Binding Tag.SomeProp, ElementName=gridTop}
ElementName binding is still possible and might work in your case. Otherwise you'd need to implement an attached behavior.
There are several ways you can deal with this issue:
ElementName binding is the most common approach, as Filip pointed out.
You could walk visual tree till you find the parent. That is what FindAcestor does internally. You could dress it up in behavior for easy reuse.
If you use view models you could use messages instead of bindings or you could add parent context to each child view model.
Picking the best solution will depend on your specific circumstances.

Extending user controls in WPF

I have built a User Control composed, as usual, of a XAML part and a code-behind part. Now, I need to create another User Control which shares some of the functionalities of the former, but which has different looks and might also have additional features in the code-behind.
My first idea was to create an interface to gather the common functionalities of the two types of controls. Is this the right way to go? And how would I manage the different XAML parts I should have? Any advice is welcome.
I would like to add another contribution which I think might be useful to others encountering my same situation.
One other possible solution, which in my opinion is suitable if the controls you are going to create are not particularly complex, is to create a base User Control containing the common features that you want to share: such control will be written entirely in C#.
This, in fact, allows inheritance in User Controls composed of both XAML and code behind. In the XAML of the inherited control, rather than having
<UserControl> ... </UserControl>
You will have
<MyProject: MyBaseControl x:Class="MyProject.MyExtendedControl"> ... </MyProject: MyBaseControl>
and then in the code behind you will also need to specify the following:
class MyExtendedControl : MyBaseControl
Hope this helps.
User controls don't lend themselves well to inheritance in WPF, you can do some hacks to make it work, but overall you'll probably be better off using a Custom Control instead. It's easier to specify different XAML for inherited controls using this technique. The easiest way to get started with a custom control is to add a new item to your project of type "Custom Control (WPF)" and it will add the control and the XAML gets added to Themes/generic.xaml for you.
To inherit from this control, the easiest way is to add another new Custom Control (WPF) and then change the code behind to inherit from your parent control instead of Control.
There are a number of ways to split up the pieces to make them easier to work with.
MVVM, as mentioned in a comment, relies upon Data Binding to separate the input controls from logic. By setting the appropriate class which implements INotifyPropertyChanged into the DataContext of you control, you can change the behaviour. Your DataContext class or ViewModel could implement Visibility properties to show and hide different parts of the input if the differences are not too great between the uses.
Another way to break apart functionality is by Aggregation. See StackOverflow 269496
You could build smaller controls that implement common functionality and then build larger special purpose controls by combining the smaller controls (i.e. aggregating them) to make the larger control. Aggregation will work for both the code-behind and the Data Binding approaches.

WPF: Adorning a ViewModel?

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

How to implement menuitems that depend on current selection in WPF MVVM explorer-like application

I am new to WPF and MVVM, and I am working on an application utilizing both. The application is similar to windows explorer, so consider an app with a main window with menu (ShellViewModel), a tree control (TreeViewModel), and a list control (ListViewModel). I want to implement menu items such as Edit -> Delete, which deletes the currently selected item (which may be in the tree or in the list).
I am using Josh Smith's RelayCommand, and binding the menuitem to a DeleteItemCommand in the ShellViewModel is easy. It seems like implementing the DeleteItemCommand, however, requires some fairly tight coupling between the ShellViewModel and the two child view models (TreeViewModel and ListViewModel) to keep track of the focus/selection and direct the action to the proper child for implementation. That seems wrong to me, and makes me think I'm missing something.
Writing a focus manager and/or selection manager to do the bookkeeping does not seem too hard, and could be done without coupling the classes together. The windowing system is already keeping track of which view has the focus, and it seems like I'd be duplicating code.
What I'm not sure about is how I would route the command from the ShellViewModel down to either the ListViewModel or the TreeViewModel to do the actual work without making a mess of the code. Some day, the application will be extended to include more than two children, and I want the shell to be as ignorant of the children as possible to make that extension as painless as possible.
Looking at some sample WPF/MVVM applications (Karl Shifflett's CipherText, Josh Smith's MVVM Demo, etc.), I haven't seen any code that does this (or I didn't understand it).
Regardless of whether you think my approach is way off base or I'm just missing a small nuance, please share your thoughts and help me get back on track. Thanks!
There are some inherent issues with Josh Smith's implementation of MVVM. Take a look at Ward Bell's post on the subject: http://neverindoubtnet.blogspot.com/2010/03/mvvm-josh-smiths-way.html. You may want to take a look at some alternative MVVM frameworks such as Caliburn that take a ViewModel first approach and break this coupling.
The RelayCommand is just a way to get a command in your ViewModel that can be bound to your View.
I think I would be inclined to step back from all of the different MVVM architectural variations and sample apps, and just use good old OOD. Why not have a ViewModel base class of some sort (ie, DetailsViewModelBase) for TreeViewVm and ListViewVm. Put a DeleteCommand in there with CanDelete and Delete methods that have as much implementation as the subclasses share (or abstract if none), and a SelectedItem as well. Then bind the SelectedItem to the controls similar to the xaml below:
<ListView AlternationCount="2" MinHeight="250" MaxHeight="400"
ItemsSource="{Binding Projects.View}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedProject, Mode=TwoWay}"
behaviors:SelectionBehavior.DoubleClickCommand="{Binding PickCommand}"
ItemContainerStyle="{StaticResource listingRowStyle}"
>
The key bindings being SelectedItem and IsSynchronizedWithCurrentItem.
HTH,
Berryl
I found a blog post by Kent Boogaart that describes what he calls an ActiveAwareCommand. This seems to do what I was looking for, although I haven't yet tried it. A comment on the post mentions Prism's IActiveAware as having similar behavior.

Resources