Given a FrameworkElement, how do I traverse the DataConext "up" in C# code behind (not binding)? For example: A customer has multiple addresses, if I click on a button on the Address layout I would like to find the Customer. I have read the MSDN article on the VisualTree and the LogicalTree but I'm struggling to put it all together.
Please send codez :)
Well ideally your DataContext instances are all part of a hierarchy that have a parent and child relationship. That way you can always move up the parent chain. This is the advantage of a ViewModel approach where the base class implementation maintains a parent reference and automatically sets it up when adding children. When this is not the case...
Use the following static method to move up the chain...
VisualTreeHelper.GetParent
...note that the method expects a DependencyObject which is a base class of the FrameworkElement.
Related
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
Ok, I know that FrameworkElement, which is a direct subclass of UIElement, has a Parent property, but Panels have children of type UIElement, not FrameworkElement (Children is of type UIElementCollection) which seems it would mean you can add UIElements directly to a Panel.
That said, if you have a UIElement and want to see if it has a parent that's a panel, the only way I know how to test this is with the VisualTreeHelper, but that's the visual tree, not the logical tree. (At least we know a Panel doesn't have a template so maybe that's the way, but still...)
So asides from the VisualTreeHelper, does anyone know how to know which panel is the parent of a UIElement? (...and have any thoughts why they didn't just define a Parent property there instead of on FrameworkElement?)
Thanks!
Well, if you need to find a logical parent you can just use LogicalTreeHelper in the same manner as VisualTreeHelper.
As for "...and have any thoughts why they didn't just define a Parent property there instead of on FrameworkElement?". Basically, the notion of "Logical Tree" is introduced on the FrameworkElement level. See http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.aspx (see Remarks) for details of what FrameworkElement adds to UIElement. Here is what it says about Logical Trees:
The logical tree: The general WPF
programming model is often expressed
in terms of being a tree of elements.
Support for expressing the tree of
elements as a logical tree, and
accompanying support for defining that
tree in markup is implemented at the
FrameworkElement level. Note however
that FrameworkElement deliberately
does not define a content model, and
leaves that responsibility to derived
classes. For more information, see
Trees in WPF.
On my MainPage.xaml, I sometimes need to add a UserControl to the main Panel (a Canvas). That UserControl, when clicked, needs to access a routine from MainPage.xaml - it's parent's parent - which will either make the UserControl itself Visibility = Collapsed or removed all together from MainPage.xaml.
Right now in the code behind of that UserControl I am doing
Dim c = VisualTreeHelper.GetParent(Me)
Dim mp As MainPage = VisualTreeHelper.GetParent(c)
mp.MyRoutine(3)
This is working, but it makes me wonder if there is a more straight-forward way to do this or a more preferred way. Any ideas?
A more general way of navigating the visual tree is available via Linq To VisualTree, with this small framework you can perform all sorts of queries on your visual tree. Your example of getting parent would be as simple as:
var parent = me.Ancestors().First();
Or, if you want to find the first parent of a given type ...
var parent = me.Ancestors<MainPage>().Cast<MainPage>().First()
In general, I'd avoid this type of dependency. It dramatically reduces the usefulness and flexibility of your UserControl.
Instead, it might be better to push the dependency into the UserControl during construction (or even runtime). You could pass in a delegate referencing MyRoutine, or an interface the main page implements.
This way, the UserControl's dependency would be obvious (since it's part of the API), and it would be possible to use it with other applications/pages later.
That being said, if you decide to keep the above, I would make one change at a minimum. Instead of just expecting the parent's parent to always be "MainWindow", I would recursively check parents until you get to the MainWindow (or nothing) in a method on your class. This would, at a minimum, allow you the flexibility of moving your UserControl around in the hierarchy without breaking it.
The general way that I navigate the visual tree is with my own VisualTreeEnumeration code. However as I state in the blog there are other various utilities to do the same. Whatever you use you end up with an enumerable set of ancestors. However the most most flexiable approach would be to hunt for the MainPage type:-
(Note C#)
MainPage mp = this.Ancestors().OfType<MainPage>().FirstOrDefault();
Not sure what this would look like VB.
I'm developing a fairly simple WPF application to display an object hierarchy plus detail of the selected object, with a UserControl wrapping a TreeView in the left pane (the tree control), and another UserControl wrapping a ListView/GridView on the right (the details control).
The tree control uses MVVM following this Josh Smith article reasonably closely, and has a number of ViewModel types all derived from the same base type, TreeViewModel. The main window is set up using a MainWindowViewModel as in this Josh Smith article and exposes the TreeViewModel used to populate the first generation of the tree control.
However, when I want to populate the details pane on the right, I have the problem that the SelectedItem of the tree control is derived from TreeViewModel when I need a completely different type of ViewModel for the details pane which will expand the object into a table of properties/values using reflection.
So, questions:
Is it appropriate for the MainWindowViewModel to expose the TreeViewModel for the tree control? I believe that the answer here is yes, but am open to suggestions to the contrary.
How should the selected item in the tree control be adapted to the right ViewModel type for the details pane? One option seems to be that the MainWindowViewModel tracks the selected item in the tree and does the adaption, exposing it as another property, but I'm not sure if there is a better solution.
I'm new to WPF and the MVVM pattern, so please excuse the fairly basic nature of the question. I've done a fair bit of reading around the background of the pattern, looked at some sample apps etc. but I can't quite find anything specific enough to make me confident of the answer. I also realise that MVVM may be overkill for an app this simple, but I'm using it partly as a learning exercise.
1.Is it appropriate for the MainWindowViewModel to expose the
TreeViewModel for the tree control?
I belive yes. The model should hide the look from teh logic FOR THE LOOK, but it can not hide thigns like logical structure.
2.How should the selected item in the tree control be adapted to the right
ViewModel type for the details pane?
One option seems to be that the
MainWindowViewModel tracks the
selected item in the tree and does the
adaption, exposing it as another
property, but I'm not sure if there is
a better solution.
IMHO not.
Working on the similar problem I came to the conclusion that
object Tag { get; set; }
property is inevitable :( except maybe for some rare situations (only 1 type of objects in the entire treeview).
However, when I want to populate the details pane on the right, I have the problem that the SelectedItem of the tree control is derived from TreeViewModel when I need a completely different type of ViewModel for the details pane which will expand the object into a table of properties/values using reflection.
If you're really, really concerned about this, you can build a higher-order view model class that exposes two different properties - one of type TreeViewModel and one of type DetailsViewModel. Then the main window's view model will expose the same object to both the tree control and the details control, but the logical structure of the two view types will be decoupled from one another.
Logically, the selected item in the tree control and the item that's appearing in the details control are the same thing. While the details control doesn't present information about the thing's parent/child relationships, and the tree control doesn't present information about the thing's name/value pairs, it's still the same thing. There's probably not really any need to be too concerned over the fact that a single object representing a thing exposes a property that only one view of that thing uses.
i am working on MVVM and using commanding in silverlight(DelegateEvent and ICommand)
I want something like this ,(say) i have 2 usercontrols , parent and child .
Parent is hosting the child , both have thier own viewmodels .
On parent i have a button and it executes a simple command , on execute of that command i want to update text in the child control's textbox . also we should be able to change something in child which can propagate to the parent .
Is events the best answer for this or i can have commands to update child/notify parent in some way.
There are several ways to go about this.
First, it is completely appropriate to have ViewModels that are composed of other ViewModels, as long as you are OK with them being coupled in that way. When you do that, they can just talk to each other using regular method calls.
Next, you can decouple a bit and use events. Nothing wrong with that. There is still an Observer -> Observable coupling, but they are less dependent upon each other.
Next, you can completely decouple and use something like an EventAggregator (Prism has a good one you can use). Shoot a Publish a message. The other subscribes. They don't know about each other at all.
I have used commands for this as well... but for ViewModel to ViewModel communication, I find this to be a bit awkward.
You should probably start with most obvious implementation where parent viewmodel simply holds a reference to a child viewmodel, and child viewmodel holds a reference to a parent viewmodel. Then when a command is executed on parent viewmodel it simply sets a value on a child viewmodel to which textbox is bound to.
Adding a layer of abstraction between parent and child (e.g. events) adds a level of complexity and as a result it should be justified. If the value this indirection provides is higher than the cost of increased complexity of the code (e.g. it's now less clear what happens when command on a parent is executed, you will have to solve a problem how child gets subscribed to parent's event without obtaining the actual reference to it and vice-versa, adding additional dependencies between parent a child will require adding additional events, which pollutes the actual logic with all the plumbing, etc.) then certainly events (or something like PropertyObserver) might be a next logic step.
This seems like an ideal situation for using an EventAggregator like the one in the Composite Application Guidance / Prism.
In this model, you can set up a MessageBus in the root of the application (or other common area).
// in App.xaml.cs
public static IEventAggregator MessageBus = new EventAggregator();
Then set up a common Messages library
// in Messages.cs
public class SimpleCommand: CompositePresentationEvent<SimpleObject> { }
Where SimpleObject is a class or variable that contains all the info necessary to process this event.
// in control with button
App.MessageBus.GetEvent<Messages.SimpleCommand>().Publish(SimpleObject);
// anywhere in your app that you "care" about this
App.MessageBus.GetEvent<Messages.SimpleCommand>().Subscribe(ProcessingMethod);
Where ProcessingMethod is a method that takes a SimpleObject as a parameter.
Then you can spout off messages from anywhere and process them anywhere - across viewmodels, controls, etc. You can even pass MessageBuses between components if you are dynamically loading parts of the app. Works well.