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

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.

Related

Mvvm light wpf navigation

What I want to achieve. A navigation service for MVVM Light WPF where I can easily say NavigateTo(View URI or ViewModel and a string name of the framework element that should present the View or ViewModel).
I first tried the View First approach from here.
https://stackoverflow.com/a/28968560/5272185
Problems I found with it was that the Frame frameworkelement seems to run sandboxed, and even though I set the view's datacontext to bind to a viewmodel retrieved from my viewmodellocator I seem to get a different instance inside the Frame. Also styles seem not to be inherit down into the Frame and the MVVM Light Messenger's messages do not seem to bubble up from the View shown inside a Frame.
Also the code to find the Frame element failes if the Frame is located within a ViewBox. I have no clue why but if I just remove the ViewBox it finds it correctly.
I ignored the ViewBox issue (removed the ViewBox) and changed the NavigationService to store ViewModelBase types instead of URI, and I changed the code to find a FrameworkElement of type ContentControl instead of Frame, set the ContentControl's content instead of a Frame's source. This so I should not run into the sandbox problem. This worked, but I now have no good way of switching to a detail view (using the same ViewModel as datacontext for multiple views).
Questions
When using a ViewModel first NavigationService as explained above, is there an easy way to show a detail view (sharing a ViewModel) without code duplication?
In case of a View first navigationservice exactly like the code in the link. Is there a way to make the Frame inherit the correct datacontext and styles (not running sandboxed at all)?
Can someone explain why the VisualTreeHelper code in the provided link does not find a framework element if the element is inside a ViewBox?
I should also mention that I have tried the approach to make a CurrentVM property and CurrentDetailMode property on the ViewModel and bind a ContentControl's content to the CurrentVM property and used DataTemplates with DataTriggers (bind to CurrentDetailMode) to set the corresponding View(s) to the ViewModels. According to this post.
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
It works well and supports detail views. My goal with the navigationservice is that I want to add all views or viewmodels to the service in one place. Not having to create the CurrentVM and CurrentDetailMode properties on every ViewModel that should be able to show different content / views. And I would prefer an easier solution than manually setting up Datatemplates with special DataTriggers for supporting "Detail" views. Also the navigation service discussed has a history queue so one can go back.
I managed to solve / answer my own questions
Just create a dumb ViewModel class for the Detail view, provide it with the (Main)ViewModel instance and make that ViewModel instance a property so you can bind to it. Done! Easy after all, and with Dependency injection it can become a breath.
See this link page.DataContext not inherited from parent Frame?
I solved this one and tracked it in the following separate question. See answer here
WPF ViewBox blocks VisualTreeHelper search

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.

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

Where should I set the DataContext - code behind or xaml?

(honestly I searched and read all the 'related questions' that seemed relevant - i do hope i didn't "miss" this question from elsewhere but here goes...)
There are two different ways (at least) to set the DataContext. One can use XAML or one can use the code behind.
What is the 'best practice' and why?
I tend to favor setting it in XAML because it allows a designer to define collections on their own but I need 'ammunition' on why it's a best practice or why I'm crazy and the code behind is the bomb...
A third way you might look at is using a locator service. I usually have one class that is responsible for the creation of all my DataContext(VM's in most cases for me) and I create an instance of that class in the App.xaml Resources. Then I bind the DataContext in the XAML of each individual page.
i.e.
<Page DataContext="{Binding ViewModel,Source={StaticResource Locator}}" >
I think it depends on what you are setting the DataContext to, and ultimately personal preference.
I personally always do it in the code behind of my views because I find it overall cleaner, and it was how I was taught MVVM. Another thing to keep in mind is, there are times you may need to change your datacontext depending on what you are working with. If this is the case it's much cleaner/easier to do in the code behind rather than in XAML.
As you can see by the answers so far opinion is divided. In truth there is no best practice (I do get bee in my bonet about discusions of "best practice" in the Silverlight world, its way too young for best practice to be truely known.)
The reality actually is that you can't set the "data context" in Xaml. Unless you actually construct an object instance like this:-
<UserControl>
<UserControl.DataContext>
<local:MyDataProviderThing />
Ultimately something external has to assign either the DataContext property directly or indirectly via another property or via binding (as in Stephan's answer). Its this external context which is dictates whether it makes sense to do it in Xaml or not. Many MVVM solutions use a binding in Xaml, in some cases simply to avoid there having to be any code at all in code-behind rather than it truely being "better". Others set up the DataContext in code using a base class that your control derives from.
DataContext of the user control/view I assume? One advantage of setting data context in the code behind is the availability of dependency injection. Your DI container can take care of any dependencies for you dynamically at run-time.
With this pattern, I frequently set a view's Blend design DataContext in xaml using d:DataContext. The "design version" can provide mock data for use in Blend, while the true implementation is resolved at run-time.

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