Trouble applying MVVM pattern - wpf

i am having some troubles applying the MVVM pattern, for start i am following this example to learn how to apply and use the pattern...
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
So my problem is establishing the "connection" between the View with the ViewModel...
In the example we have a View with a CollectionViewSource where the Source is the AllCustomers property:
<UserControl
x:Class="DemoApp.View.AllCustomersView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
<UserControl.Resources>
<CollectionViewSource x:Key="CustomerGroups" Source="{Binding Path=AllCustomers}"/>
</UserControl.Resources>
<DockPanel>
<ListView AlternationCount="2" DataContext="{StaticResource CustomerGroups}" ItemsSource="{Binding}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=DisplayName}"/>
<GridViewColumn Header="E-mail" DisplayMemberBinding="{Binding Path=Email}"/>
</GridView>
</ListView.View>
</ListView>
</DockPanel>
</UserControl>
who belongs to the ViewModel AllCustomersViewModel:
public class AllCustomersViewModel : WorkspaceViewModel
{
(...)
public ObservableCollection<CustomerViewModel> AllCustomers { get; private set; }
(...)
}
but he uses a ResourceDictionary where he applies a DataTemplate between the View and the ViewModel:
<DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
<vw:AllCustomersView />
</DataTemplate>
but my problem is because i am not using a ResourceDictionary, and because of that i thought that i can put the DataTemplate in the Resources of the Window where i will have my View (for me is the more logic place to put the DataTemplate)... But for some reason the Data isn't appearing in the ListView, and so i ask why?

I wouldn't attach the view model in the Xaml. This hard-codes the Xaml to a specific view model.
Instead, I'd override the Application startup:
private void OnStartup(object sender, StartupEventArgs e)
{
Views.MainView view = new Views.MainView();
view.DataContext = new ViewModels.MainViewModel();
view.Show();
}
See this article:
http://www.codeproject.com/KB/WPF/MVVMQuickTutorial.aspx
This approach is also helpful if you use some sort of tool to dynamically bind your view models in the future, like MEF, Castle.Windsor or Prism.

I never bind my view models to my view in XAML. I prefer to have control over when that binding is set myself. I always put a property on the View that contains the reference to the view Model. That way I can change the view model out if needed without any unsuspecting consequences from XAML binding. Plus doing it this way allows me to put an INotify handler on the view model so all changes are updated automatically when the view model is switched.

DataTemplates without key are applied implicitly whereever data of the specified DataType is used as content that is to be displayed. That means that your ViewModel needs to appear somewhere, for example as the content of a ContentControl or an item of an ItemsControl.
So if you have a window you need to have an instance of the ViewModel in it:
<Window ...>
<Window.Resources>
<DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
<vw:AllCustomersView />
</DataTemplate>
</Window.Resources>
<Window.Content>
<vm:AllCustomersViewModel />
</Window.Content>
</Window>
Normally you only use windows as shells though, and the content is set dynamically.

Related

WP7 XAML Bind to property in parent

I'm writting an app in WP7 (Silverlight 3). I have a view model that looks like this
public class MainViewModel
{
public List<ActivityTypes> ActivityTypes{get;set;}
public RelayCommand LoadActivity{get;set;}
}
My pages datacontext is set to the view model and I have a listbox with it's item source set to the ActivityTypes collection. In the listbox I'm trying to render a list of buttons who's command is bound to the LoadActivity property on the viewmodel. RelayCommand is part of the MVVM Light toolkit in case you are wondering what it is.
The problem I am having is I can't find a way to bind my button command to the LoadActivity property as my listbox has it's itemsource set to the Activitytypes collection and this property is in the parent. I've read about FindAncester but it doesn't look like this is supported in Silverlight 3.
My XAML looks like this
<UserControl.Resources>
<DataTemplate x:Key="ActivityTypeListTemplate">
<StackPanel>
<Button Command="{Binding LoadActivity}"> <!--binding not found as we're in the collection-->
<TextBlock Text="{Binding Name}" FontSize="50"/>
</Button>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ListBox Margin="0" ItemsSource="{Binding ActivityTypes}" ItemTemplate="{StaticResource ActivityTypeListTemplate}"/>
</Grid>
What's the best way to code something like this?
There is no "direct" way to do this. You can set the Buttons DataContext to your MainViewModel (preferably as a StaticResource link) and the it would work.
Take a look at Bind to parent object in xaml

Binding the ViewModel itself inside a DataTemplate with Caliburn Micro Conventions

I'm a newcomer to Caliburn.Micro and there are a few things I'm still not getting.
ViewModel first:
First of is a ViewModel that manages a Collection of other ViewModels:
public class NavigationBarViewModel : PropertyChangedBase
{
public BindableCollection<IHaveDisplayName> Items { get; set; }
}
I've got a ItemsControl (it's Telerik RadOutlookBar if that matters) as the root of a UserControl
of that view and I set the ItemTemplate too ensure that the ViewModels I insert into the collection are wrapped in a corresponding RadOutlookBarItem ( should I use ItemContainer instead of ItemTemplate here? ).
<telerik:RadOutlookBar x:Name="Items">
<telerik:RadOutlookBar.TitleTemplate>
<DataTemplate>
<ContentControl Content="{Binding Path=DisplayName}" />
</DataTemplate>
</telerik:RadOutlookBar.TitleTemplate>
<telerik:RadOutlookBar.ItemTemplate>
<DataTemplate>
<telerik:RadOutlookBarItem cal:Bind.Model="{Binding}"
Header="{Binding Path=DisplayName}">
<ContentControl />
</telerik:RadOutlookBarItem>
</DataTemplate>
</telerik:RadOutlookBar.ItemTemplate>
</telerik:RadOutlookBar>
This way I wan't the ViewModels in the collection to appear where the ContentControl is. I Bind the model to the root item of the DataTemplate to ensure conventions will work but have no idea how to bind to the ContentControl with convention. The DataContext inside the DataTemplate is of course the ViewModel itself. Using normal WPF standard I would put Content="{Binding}".
Now the model is there inside the RadOutlookBarItem but it's view doesn't get applied. Not even View can't be found, only a string with the class name.
Isn't this the proper way to do this?
As I answered here: Dynamic Telerik RadOutlookBar headers come out wrong with ItemTemplate in which I thought was a unrelated matter I was using the wrong property. ItemTemplate controls the picker and contentTemplate what comes up when you select. Here is the code that works:
<telerik:RadOutlookBar x:Name="Items">
<telerik:RadOutlookBar.ContentTemplate>
<DataTemplate >
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
</telerik:RadOutlookBar.ContentTemplate>
<telerik:RadOutlookBar.TitleTemplate>
<DataTemplate>
<TextBlock x:Name="DisplayName"
cal:Bind.Model="{Binding}" />
</DataTemplate>
</telerik:RadOutlookBar.TitleTemplate>
<telerik:RadOutlookBar.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="DisplayName"
cal:Bind.Model="{Binding}" />
</DataTemplate>
</telerik:RadOutlookBar.ItemTemplate>
</telerik:RadOutlookBar>

MVVM View Binding

Lets say I have a list of items. So I have 2 views: ListView and ItemView.
I also have 2 ViewModels: ListViewModel and ItemViewModel.
In ListViewModel I create and initialize ObservableCollection of ItemViewModels.
In ListView I have ListBox with ItemTemplate -> ItemView, and ItemSource binded to the ObservableCoolection into the ListViewModel.
I want to bind each ItemView to each ItemViewModel so I will be able to use the binding into ItemView.xaml, but I cannot achive it.
What I understood from your question is you want to bind ItemViewModel to ItemView.xaml and your ListBox resides in some other xaml(say ListBox.xaml). You don't want to apply binding related to ItemViewmodel and ItemVIew in ListBox.xaml. If this was the issue, you
can easily create a DataTemplate mapping to achieve so:
<Window
xmlns:vw="namespace of your view files (i.e. xaml files. ListBox.xaml and ItemView.xaml)"
xmlns:ViewModels="namespace of your view model files (i.e. ItemViewModel.cs, ListBoxViewModel.cs)">
<Window.Resources>
<DataTemplate DataType="{x:Type ViewModels:ItemViewModel}">
<vw:ItemView />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Path=List of ItemViewmodel in ListBoxViewModel.cs}"
</Grid>
</Window>
Then, you ItemViewModel class will be mapped with ItemView as it is specified in Resources without any key. Now you can apply binding in ItemView.xaml. Look, you do not need to assign Itemtemplate of ListBox here, that will be resolved automatically. If you want to specify the ItemTemplate anyway (and do not want to specify in resource), then write this:
<ListBox.ItemTemplate>
<DataTemplate>
<vw:ItemView />
</DataTemplate>
</ListBox.ItemTemplate>
Either way should work :)

Silverlight mvvm dynamic controls

How do create controls dynamically in the mvvm pattern?
The code I'm trying to port:
Parent control:
ObservableCollection History = new ObservableCollection();
private void Save_Click(object sender, RoutedEventArgs e)
{
ChildControl cc = new ChildControl();
History.Add(cc);
}
if you use ContentControl, you can simply bind to your History collection
<ListBox ItemsSource="{Binding History}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ContentControl Content="{Binding }"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The above will show a list of your controls.
One thing to consider though is that with your implementation, the VM knows about View Objects, It is cleaner to use pure data in your VM and have the view worry about how to display itself.
Assuming that "ChildControl" derives from UserControl, the XAML from each of these controls will be automatically displayed when the ItemsSource is set.
<ListBox ItemsSource="{Binding History}">
</ListBox>

How do GroupStyles work?

I have a ListView Control bound to a ListCollectionView in a ViewModel.
I wanted to try to group these items but having some problems.
I set the Property grouping in the VM to begin with and then added a GroupStyle.
C#:
ListCollectionView.GroupDescriptions.Add(new PropertyGroupDescription("Category"));
XAML:
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
However the list is now just the category names, no way to see the items themselves.
I'm not really understanding completely what is going on here. When I create a Template for the GroupStyle what am I really binding to? Are there other properties besides Name ?
I just added the GroupStyle to a ListView I has already created where I for example included a ItemTemplate. Is that something that is messing with the GroupStyle?
What if the Items in the list belong to another class and I wan't to group based on what instance of class they belong to (it has an ID). I would then have the group name as a property on this parent class. Is that possible?
PARTIAL SOLUTION:
Problem was with the style applied on the ListView. I have no idea what about the style was interefering.
FULL SOLUTION
I wasn't using a ItemsPresenter in my listbox ControlTemplate opting to use a Panel with IsItemsHost set to true. It seems ItemsPresenter must be used for GroupStyling to work correctly.
I think the error lies elsewhere in your code.
Usually, you expose a collection of Models on your ViewModel
namespace Derp
{
public sealed class ViewModel
{
public ObservableCollection<Model> Items {get;set;}
// initialization code not shown
}
public sealed class Model
{
public string GroupName {get;set;}
public string ModelName {get;set;}
}
}
In your View, you bind a CollectionViewSource to this collection:
<Window.DataContext>
<ViewModel xmlns="clr-namespace:Derp" />
</Window.DataContext>
<Window.Resources>
<CollectionViewSource
Source="{Binding Items}"
x:Key="GroupedItems">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription
PropertyName="GroupName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
Next, we bind our list control to this CollectionViewSource (using a combo in this example):
<ComboBox
ItemsSource="{Binding Source={StaticResource GroupedItems}}"
DisplayMemberPath="ModelName">
<ComboBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ComboBox.GroupStyle>
</ComboBox>
Where it can get confusing is that, within the GroupStyle, you aren't binding against your Model, you are binding against a collection of Models which is grouped on (in this case) the property "GroupName". The CollectionViewSource groups your Models into collections that extend CollectionViewGroup. These groups have a property called Name, which contains the common value on which your Models are grouped (the value of the GroupName property). So, in the HeaderTemplate, you are binding to CollectionViewGroup.Name.

Resources