I am new to WPF. I'm trying to build an application which has a function (call it Initialisation) where a user has to fill in a lot of data and some parts of the form are repeated. We're rewriting a legacy app that has quite a long wizard in although we will probably use collapsible panels in one window rather than next/previous pages. Also some parts are repeated e.g. the user can specify a number of items, if they say 3 they will need to fill in some configuration info for each, so those controls would need to be repeated three times.
I'm using MVVM and am using this example here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
The old wizard had about 4 pages so I'm intending to have one user control (Initialisation) that contains 4 child user controls to break the xaml up a bit.
So far I have the Initialisation (its ViewModel inherits from Workspace ViewModel as in the above example) and it contains one child which is working:
<Expander ExpandDirection="Down" Header="ChildOne">
<view:ChildOne />
</Expander>
I will have separate ViewModels for each child and for Intialisation and this brings me to my problem.
The problem I am having is that ChildOne contains a dropdown which I am trying to bind like so:
<ComboBox x:Name="textMessageTypeCmb" ItemsSource="{Binding Path=TextMessageSelectionOptions, Mode=OneTime}"/>
TextMessageSelectionOptions is a public property in ChildOne's ViewModel. This results in no errors but an empty dropdown - that property getter is never called. If I move that property getter code into the Initialisation's ViewModel instead it works but I'm trying to keep my code in manageable chunks so I'd like to put hat code back in ChildOne's ViewModel. It also works if in my MainWindow I create ChildOne as a workspace instead of Initialisation like this
ChildOneViewModel ws = this.Workspaces.FirstOrDefault(vm => vm is ChildOneViewModel) as ChildOneViewModel;
Can anyone advise whether I am taking the right approach (by dividing it up into several user controls) and what I need to do in the binding to make this work? I don't really understand any of this yet especially binding.
It seems to me that your ChildOne view's DataContext is still this Initialisation vm.
You can bind it the views Datacontext to a ChildOneViewModel object
...
<view:ChildOne DataContext={Binding PropertyReturnsChildOneViewModellObject/>
...
or specify the path for the combobox ItemsSource prop.
<ComboBox x:Name="textMessageTypeCmb" ItemsSource="{Binding Path=PropertyReturnsChildOneViewModellObject.TextMessageSelectionOptions, Mode=OneTime}"/>
Note: PropertyReturnsChildOneViewModellObject is a property of the Initialisation vm.
Related
I've been experimenting with WPF, Xaml, MVVM, and DependencyInjection lately. Consequently, I am creating a UI using MVVM principles. A certain portion of the UI is designed to act like a wizard wherein not all of the available options are presented to the user at the same time. Each section of options is its own View (sub-View) with a single View (Parent View) hosting these sub-Views in a ContentControl. The user sets certain options and uses buttons to move from one section to the other.
View Navigation
To switch between these views I'm using a DataTemplateSelector with each sub-View defined as a DataTemplate in my Xaml resources.
Content Control in the Main View:
<ContentControl Content="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
Example sub-View Data Template:
<DataTemplate x:Key="SubViewATemplate">
<local:SubViewAView x:Name="SVAView" DataContext="{Binding ElementName=ParentViewControl, Path=DataContext}" ViewState="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay }" />
</DataTemplate>
On the Parent View and each sub-View I've created a Dependency Property called ViewState (an enum). These bind to each other through the DataTemplates. In each View's code-behind I update this ViewState Property based off user input and it propagates up to the Parent View which in turn triggers the DataTemplateSelector. So far, so good. The navigation works beautifully.
ViewModel Info
The Parent View has a ViewModel which implements INotifyPropertyChanged as its DataContext. I'm attempting to use this single ViewModel to bind Properties to the Parent View and the sub-Views. The problem is the DataContext binding in the DataTemplate snippet above does not work. (Which is odd to me since the ViewState binding does.) After various attempts to get this to work, the DataContext on a sub-View is either null or the ViewState control variable.
I am currently using the UnityContainer as my dependency injector.
Various Attempts
Here are the various other things I've tried that have all failed:
1) Registered the ViewModel as a singleton in the UnityContainer thereby using Constructor Injection on the sub-Views to set the DataContext. (Does not work because there must be a Parameter-less Constructor for the DataTemplate resource.)
2) Registered the ViewModel as a singleton in the UnityContainer and then using Property Injection on the sub-Views to set the DataContext. (Does not work. I think this is due to the UnityContainer not working when an object is instantiated in Xaml.)
3) Creating sub-ViewModels for each sub-View that needs a ViewModel to display properties that would have existed on the Parent ViewModel. I've used this before to get around the Xaml instantiation problem with the UnityContainer. I then replace the DataContext binding in the DataTemplate with the associated sub-ViewModel. (Does not work because for some reason the DataContext of my Parent View is getting set to the ViewState variable instead of remaining my ViewModel which I've set in the view's Constructor. This in turn means the sub-ViewModel property on my Parent ViewModel can't be found to bind to the DataContext of the sub-View.) Are the Content and DataContext of UserControls the same thing? Does setting one affect the other?
4) Moving the ViewState dependency property from the View to the ViewModel and then setting the ContentControl's Content to bind to the ViewModel. This violates MVVM principles but by this time I was trying anything to get this to work. (It doesn't work because when ViewState is changed in the code-behind of the view the ViewModel does not trigger as changed.) I haven't gone any further with this one because I didn't want to go deeper violating MVVM.
Conclusion
I've found most of these attempted solutions on this site over the last couple days. I haven't had any formal training in WPF, Xaml, and MVVM so I suspect I'm missing something obvious, or am attempting to do something that isn't possible. I'm going to keep attempting variations on the above and researching until I find something that works, but I thought I would tap into the collective knowledge here to help me find a solution.
What I'd prefer is to have the group of Views use the single ViewModel as their DataContext so I can bind properties to their controls. And have the Views' navigation be controlled by a DataTemplateSelector. Is there a way to do this that I'm not seeing?
Thank you for your time!
I have had similar issues before, I have had good luck using a RelativeSource binding. Maybe try something like this:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=DataContext}"
Just a thought.
I am working on a an WPF MVVM application where I need to have a Main Window with just a logo and it has to show child views inside it. I don't have any controls in Main Window all the controls reside in child view for example Buttons like Next, Back, Cancel and some text blocks etc. Now If users select Next button on the child view I have to draw or load the next child view inside the Main Window. If Back button is clicked I have to go back to the previous child view. So basically I am changing the child views depending on which button is clicked. Also I am maintaining different view models for every child view. Now the problem is I am not able to figure how should I link the child views to there respective view models. This application is similar to some Installation applications where different dialogs are shown depending on the selection and the button clicked by the user.I am new to this wpf and don't want to use MVVM Light , Prism etc. Any detailed help will be greatly appreciated. Thanks in advance.
One of the easiest ways to associate any data type with XAML controls is to use a DataTemplate. Therefore, you can simply add something like this into your Application.Resources and as long as you do not set the x:Key properties on the DataTemplates, then they will be explicitly applied by the Framework whenever it comes across instances of your view models:
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
Then displaying the view is as simple as this:
<ContentControl Content="{Binding YourViewModelProperty"} />
In code behind, or your view model:
YourViewModelProperty = new MainViewModel();
It's often handy to create a base class for your view models and then the YourViewModelProperty can of that type and you will be able to interchange them using the same property and ContentControl.
UPDATE >>>
The general idea is that you have one MainViewModel class with one BaseViewModel property data bound to one ContentControl in MainWindow.xaml... the navigation controls should also be in MainWindow.xaml and not in the views themselves. In this way, the MainViewModel class is responsible for changing the property to the relevant view model instances when it receives navigation Commands from the MainWindow.xaml.
I'm trying to alter an existing WPF application and my lack of WPF experience (I'm WINFORMS) is making this difficult.
I've come across a situation where I need to reuse a UserControl and I'm not sure how to do this in terms of modifying the xaml. I'll explain.
UserControlA has the following code:
<Grid Name="gdMain" Style="{StaticResource ContentRoot}">
<content:MonitorAlarmsPage />
</Grid>
Now, "MonitorAlarmsPage" is an AXML document that defines another UserControl - UserControlB. This UserControl, once created has to persist for the lifetime of the application.
So, I could have many UserControlAs, but only ONE UserControlB.
I've created a static class that has an appropriate UserControlB field which is updated when UserControlB is created, but how do I modify the content:MonitorAlarmsPage so that the content of the grid is replaced by the existing UserControlB as referenced in this static class and not by the XAML file that defines UserControlB? Can this actually be done? i.e. essentially, insert pre created user controls inside an XAML page.
To make things a bit clearer, UserControlB is essentially a page that can sit inside another page. The page is complex and there is a massive overhead incurred when it is created and so must only be created once.
WPF is very different to WinForms, so you certainly have your work cut out for you. The basic idea for the solution to your problem is this. You'll need to add a DependencyProperty to the MonitorAlarmsPage control. This will enable you to data bind to this property from outside the control.. This property should be of a type of custom class that you define, that contains all of the properties required in the inner control.
The next stage is to develop a DataTemplate that defines how WPF should display your custom class when it comes across an instance of it. In this DataTemplate, you declare your inner control, so that when WPF sees the custom class, it displays the inner control and sets the custom class as the DataContext for the inner control:
<DataTemplate DataType="{x:Type YourXmlNamespacePrefix:YourCustomClass}">
<YourControlsXmlNamespacePrefix:YourInnerControl DataContext="{Binding}" />
</DataTemplate>
Finally, you'll need to data bind your custom class (the static class) to the outer UserControl:
<content:MonitorAlarmsPage YourCustomClassProperty="{Binding YourStaticClass}" />
So just to be clear... this static class should be a data class, not a UI class. When using WPF, we manipulate data, not UI elements. We let the wonderful templating system generate the UI for us.
I am constructing my app infra structure, and finding it hard to achieve a very basic behavior - I want to raise events from different user controls in the system and being able to catch those events on some other user controls that listens to them. For example i have a user control that implements a TreeView. I have another user control that implmements a ListView. Now, i want my ListView to listen to the TreeView, and when the selection is changed on the TreeView, i want to repopulate my ListView accordingly.
I also want this to happen even if the ListView is not located within the TreeView on the WPF logical tree.
PLEASE HELP!!
Thanks,
Oran
Use data binding.
If the content of the list view is stored inside the object shown in the tree view you can just bind into the tree SelectedItem property.
Otherwise bind the tree SelectedItem to a property in your view models (or your window!) and in the setter of this property change the list that is bound to the list view ItemSource property.
You can see the technique in this series on my blog the post I linked to is the last post with the code download link, you'll need to read from the beginning of the series if you want the full explanation.
EDIT: Here's how I did it in one project: (the GridView definition removed since it's not relevant here)
<TreeView
Name="FolderTree"
Width="300"
ItemsSource="{Binding Root.SubFolders}"
ItemTemplate="{StaticResource FolderTemplate}"/>
<ListView
Name="FileView"
ItemsSource="{Binding ElementName=FolderTree, Path=SelectedItem.Files}">
</ListView>
The list bound into the tree view's ItemsSource is of objects that have 3 properties: Name (that is bound to a TextBlock in the FolderTemplate), SubFolders (that is likewise bound to the HierarchicalDataTemplate.ItemsSource property) and Files that is bound to the ListView using {Binding ElementName=FolderTree, Path=SelectedItem.Files}
Note that non of the lists are observable collections (because in this project they never change) but are loaded lazily (on-demand) by the properties getters (because in this project they are expensive to load).
This is the point where the added complexity of MVVM (Model-View-ViewModel pattern) can start to pay off. What you need is a publish/subscribe infrastructure, and MVVM Light has that, along with good MVVM structure that doesn't get overly complex. Prism is another good WPF/Silverlight infrastructure foundation with publish and subscribe support.
I have following questions
Should the consumer of my usercontrol assign the usercontrol's DataContext or set some dependency property. (related to #3 : if DataContext then my individual items need to bind directly to the object given in DC, if DP then I have the luxury to have bind to any VM)
If they set property, and if I am using 3 primitive items, should I accept them as individual properties or combine them together to a Model for my usercontrol
Should I ask the consumer of my usercontrol to send me model or viewmodel ( I say viewmodel but for all the controls I have used so far, I have never seen anybody asking me to send them VM - I am sure some could be implementing MVVM internally
Your consumer wants a user control. So I presume the user control should be able to work in any context/application(WPF). So, to answer your questions
1) The consumer should set dependency properties which is defined in the user control. By using the datacontext you will be coupling the usercontrol to the consumer.
2)Take them as individual primitive properties, otherwise the consumer needs to create an object unnecessarily to cater with your model(coupling again-why should the consumer need to know about your model?).
3)No, you should not ask the cosumer to send you the view model.Why do you need to know which consumer is using your "generic" user control.
If you cannot do any of the above because of practical considerations - then dont worry about breaking any/all the rules because your user conrol is coupled with a specific context-it is not generic any more. If you write a generic user control, any WPF application can use your user control. It is your call.
1.
I would say this depends on the kind of UserControl, if it is "generic" you should be able to change the DataContext as the control internally should not have anything to do with the DataContext. For example if i create an ImageButton UserControl which exposes the properties Caption and ImageSource then those should be bound internally independent of the DataContext, the on the instance those can be bound and the DataContext may be changed as well, e.g.
<uc:ImageButton Caption="{Binding ButtonInfo.Caption}"
ImageSource="{Binding ButtonInfo.Image}"/>
Here one could then change the DataContext to simplify the bindings a bit:
<uc:ImageButton DataContext="{Binding ButtonInfo}"
Caption="{Binding Caption}"
ImageSource="{Binding Image}"/>
If on the other hand the UserControl is a view for a viewmodel i would expect the UserControl to bind to viewmodel properties internally, relative to the DataContext.
So in a DataTemplate where the current DataContext is already the viewmodel of that view a simple instance without anything should do, i.e.
<v:StatisticsView />
If the viewmodel to be passed is in a property of the current DataContext you may bind the DataContext as well:
<v:StatisticsView DataContext="{Binding StatisticsViewModel}"/>
2.
This can be handled either way i would say, especially if you have only three properties its not too much of a hassle to create those. You might want to consider some aspects like dependency, e.g. does it make sense to group all three propeties in an object?
3.
As noted in 1. this should be apparent from the UserControl itself, if it's a StatisticsView the consumer should pass in a StatisticsViewModel (either implicitly by inheriting the current DataContext or by binding it explicitly).