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.
Related
some advice from he WPF/MVVM gurus, pls.
I come from a Windows Forms background and I am porting a personal project from VB.NET to WPF. I started with a straight re-write with the logic going in the code-behind files, albeit with a discrete Data Access Layer.However then I discovered MVVM and that I was 'doing it all wrong' and should be abstracting the logic into a View Model accessing a data model. I think I've absorbed the new paradigm and my code behind files are now (nearly) empty.
(As an aside, I buy into the rationale behind MVVM, but I'm not sure about the 'easier to test and debug' argument, due to all the 'plumbing' of routed events and commands etc, I appreciate the need but it seems to me sometimes to obscure rather than clarify what is going on. But I a very much a novice, maybe it will come with practice.)
Anyhow - here's what has got me scratching my head; the app is a dive planning/recording tool for scuba diving centres. Logically, its like a calendar or diary app, with the need to record the time and site of each dive and who went on each dive. The main screen resembles one page of a diary and I have a user control that encapsulates the dive info. On change of selected date, the diary page is cleared and re-populated with the dives for the new selected date. The View Model retrieves and exposes a list of 'Dive' objects (classes) for the new date. The container is a Stackpanel and I want to clear its children and then create and add the new Dive user controls to the child controls collection.
My question is - where is the appropriate place to do this - View code behind or View Model? The former is relatively easy but seems to me to break the pattern, but I am stumped by how I would achieve it in the view model. The sequence needs to be
User selects a new date (calendar control)
Handle the selected date change event
Clear Stackpanel child controls
Retrieve list of dives for new date from db and generate a user control for each
Add user controls as stackpanel child controls.
I guess what I'm asking is - is there a way of binding the collection of child controls to a collection of objects in the view model such that it responds dynamically - and is this a sensible approach?
The appropriate way to do this would be to wrap your StackPanel in an ItemsControl and bind the ItemsSource to your Dives of the selected day. When your DivesOfThatDay is changed (make sure to implement INotifyChanged or a DependencyProperty, or use an ObservableCollection), every single entry will be generated automatically.
<ItemsControl ItemsSource="{Binding DivesOfThatDay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Dive}">
<!-- Your Template -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In MVVM you should always avoid generating Controls in Code-Behind. Your View should take the Data from your ViewModel and do that on its own - otherwise MVVM would be kind of useless. In your case it should work like this:
User selects a new date in the view
Because the date control in the view is bound to some property in your viewmodel, that property gets updated.
The viewmodel reacts to this change by clearing its current collection of dives and fetching a new one.
The model data (from the database) gets encapsulated in a viewmodel (a Dive) and added to the dive collection in the viewmodel
The view (ItemsControl) gets notified of this change (via INotifyPropertyChanged, INotifyCollectionChanged or whatever) and tells its ItemContainerGenerator to update the controls.
The ItemsContainerGenerator generates a view for every Dive in your viewmodel and adds it to the stackpanel.
Or in short V → VM → M → VM → V
I have an application that uses Caliburn.Micro. My View contains a user control which contains e.g. a tab control. I want to be able to access that tab control from the outer ViewModel to select a particular tab. Is it possible?
Thanks.
The standard MVVM way is to have the TabControls SelectedItem property bound to a property on your viewModel.
<TabControl ItemsSource="{Binding PropertyToYourViews}"
SelectedItem="{Binding PropertyToYourSelectedView}">
</TabControl>
If you do it this way your ViewModel does not have to know about the existence of the TabControl.
The next step is dependant on your implementation. Your outer ViewModel could simply keep a reference to the child viewModels SelectedView property and access it directly however,
If you want to keep your ViewModels decoupled then you will need to implement some sort of notification system. I'm not sure of the specifics of Caliburn.Micro but most MVVM frameworks offer some kind of solution for this.
Implementation would depend on exactly how you have it set up, but you can bind a variable in your view model to the SelectedItem of the TabControl
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.
I have a collection of objects that I want to present. How can I do this? A listbox would do but each object has many attributes which I want to present. I already bound a listbox to a collection and I have all my objects listed. My question is regarding the visualization of the listbox and if the listbox is the correct thing to use or there is something else that I should use.
I find the existing answers to be a bit lacking, normally one would not process collections or boil their items down to a string, at the very best you would do some dynamic manipulation using a CollectionView (e.g. sorting, grouping), but normally you use Data Templating to display the individual items which allows you to use all their properties.
Further there are several controls which work well with collections, firstly you need to know if you want selection, if not an ItemsControl is a good choice, otherwise you should use a ListBox or ListView.
ListViews are normally employed if you have different views for your objects, e.g. a details view and a thumbnail view. You can use the ListView.View for this, there is one existing view in the framework, the GridView, which offers columns. What Matthew Ferreira suggested is exactly what you should not do with a ListView since you want to make the templates dependent on the current view, in fact that code does not even compile since DataTemplate can only have one child.
ListViews are supposed to encapsulate the view logic in their view so it can be changed at will. If you decide to use a ItemsControl or ListBox then setting the ItemTemplate is what you want to do. Read the Data Templating overview i linked to, it makes for a good starting point.
You might want to consider using a ListView control instead. ListView has support for columns if you are planning on showing several properties from your object. You can use the ItemTemplate property to format the display of your object. For example:
<ListView ItemsSource="{Binding Path=myObjectCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
<CheckBox IsChecked="{Binding Path=ShouldCheck}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This example assumes that your object has the properties Title and ShouldCheck.
Your collection of object is probably to be viewed as your model. The usual thing in WPF is to add a ViewModel that translates and exposes the model data into a form suitable for binding. Depending on what you want to do, your VM could e.g. format each object into a string representation and then expose it as a collection of strings that the Listbox can bind to and display.
I have a bit of a problem with my Silverlight application, and my usage of the MVVM pattern.
In my View I have a DataGrid. The ItemsSource would normaly be bound to the ViewModel, but in my specific case I need the columns to be dynamic and my items collection consists of a Dictionary for each item, so I have no class properties to show. My solution was to generate all this in codebehind, since the actual design of the DataGrid has nothing to do with my ViewModel. This was the only solution I could think of since the columns can't be databound.
I have got all of this to work. My problem is that I'm using RIA and the view has no idea when the items collection has finished loading. I tried my design out by putting an ordinary button on the view to trigger the codebehind function, but obviously this solution is no good. I need my codebehind function to run as soon as my item collection has finished loading.
Can I make my codebehind listen to the ViewModel?
I have a feeling that you are messing up things somewhere.
For your question I think you can solve it by having an event in the ViewModel.
Subscribe to that event in your view's view_Loaded event and call the codebehind function in the handler.
I would recommend you to recheck your design and to see if this is really necessary.
I understand what you mean, we once had to do the same thing generating random columns which is a PIA in silverlight because you would need some kind of object that has a dynamic set of properties.
I see you've found the Dictionary solution. What I would suggest, which isn't per sé the cleanest solution but it is cleaner then putting the stuff in the code behind, is to add this in a converter. Then bind the collection to the itemssource of an itemscontrol and then when the list propertychanged is raised you assemble the datagrid in the converter.
small example:
<ItemsControl Grid.Row="1" ItemsSource="{Binding theListOfEntities, Converter={StaticResource theconverter}}"/>