MVVM Locator class in silverlight class libraries - silverlight

I'm experimenting with the MVVM pattern, specifically I am trying to determine how I'm going to work with separate class libraries that contain the views (and view models). Looking on the net it looks like this is being done in different ways with both prism and MEF, and it might be that is what I will end up doing. Initially however, I'm working with the ServiceLocator sample that John Papa created .
In that sample, the location of the ViewModelLocator is defined in app.xaml as a static resource:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MVVMLib="clr-namespace:MVVMLib;assembly=MVVMLib"
x:Class="VMLocatorDemo.App">
<Application.Resources>
<MVVMLib:ViewModelLocator x:Key="VMLocator"/>
<MVVMLib:IndexerConverter x:Key="VMIndexerConverter" />
</Application.Resources>
</Application>
and because view.xaml is in the same project, it can refer to the locator and everything is fine:
<Grid x:Name="LayoutRoot" Background="White"
DataContext="{Binding Source={StaticResource VMLocator}, Converter={StaticResource VMIndexerConverter}, ConverterParameter=MainPageViewModel}">
<TextBlock Text="{Binding Path=CompanyName}"/>
</Grid>
However, with separate class libraries, I can't do this because view.xaml doesn't know where the locator is, um, located. I need the definition to remain in xaml so that I can continue to work with the different runtime/design time view models.
I guess my question is, how do I define the Locator class in the class library, or, is this precisely why other tools such as MEF and/or prism are required?
Many thanks
Jason

Here is one using behaviors, and uses convention by default to find a view. We have service locator, but I guess it could be improved to not have dependency on this static class
public class ViewModelLocatingBehavior : Behavior<FrameworkElement>
{
#region Overrides
protected override void OnAttached()
{
base.OnAttached();
var viewType = AssociatedObject.GetType();
var viewName = viewType.Name;
var viewModelName = viewName.EndsWith("View")
? viewName + "Model"
: viewName + "ViewModel";
AssociatedObject.DataContext = ServiceLocator.Current.GetInstance<object>(viewModelName);
}
protected override void OnDetaching()
{
base.OnDetaching();
}
#endregion
}
in XAML
<i:Interaction.Behaviors>
<u:ViewModelLocatingBehavior/>
</i:Interaction.Behaviors>

Related

MVVM WPF design related query : use of UserControls

I have one query related to designing WPF using MVVM
Here is the scenario :
1> I have one WPF screen which contains various user controls which are reusable in some other screens too.
2> Can i have separate ViewModel class for each of those user controls , what could be ideal design in this scenario
3> Should i separate my Viewmodel based on individual screen or on UserControls .
4> If i create separate viewmodels based on UserControls how i should integrate it .
Is there any design guidelines around this !!
Urgent Help appreciated ..
This post describes what I do in certain scenario, I don't know if it is a best practice or not but it works for me.
I create ViewModel for my Window that holds all the user controls, so this called ContainerViewModel and I create an instance of that Viewmodel and put it in the DataContext of the Window. From that moment all the UserControls can access that ViewModel with Binding.
The next thing to do is to create a property on my ContainerViewModel for everty UserControl that holds the ViewModel for each UserControl.
Then use binding to attach the usercontrols ViewModel to the DataContext property of the Usercontrol.
example of the viewmodels and a window with 2 listboxes instead of usercontrols:
Viewmodel classes without any implementation but just empty classes to show the concept:
public class ContainerViewModel
{
public ContainerViewModel()
{
ViewModelForControl1 = new Control1ViewModel();
ViewModelForControl2 = new Control2ViewModel();
}
public Control1ViewModel ViewModelForControl1 { get; set; }
public Control2ViewModel ViewModelForControl2 { get; set; }
}
public class Control1ViewModel { }
public class Control2ViewModel { }
Window xaml:
<Window x:Class="ConfigHellp.UI.Windows.ContainerWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ConfigHellp.UI.ViewModel"
mc:Ignorable="d"
DataContext="{DynamicResource ContainerViewModel}" >
<Window.Resources>
<vm:ContainerViewModel x:Key="ContainerViewModel" d:IsDataSource="True" />
</Window.Resources>
<StackPanel>
<ListBox DataContext="{Binding ViewModelForControl1}" />
<ListBox DataContext="{Binding ViewModelForControl2}" />
</StackPanel>
</Window>
this depends on how complex the embedding of the UserControl into the environment is. If you think that its to much effort to build the view model logic for your user control again and again (which is also a very nice source for mistakes), you should infact encapsulate the logic in a single viewmodel for your control. If the user control will be an ListItem for example, i generally suggest you to build an own viewmodel for the control.
The infrastructure will be than:
A general viewmodel for your WPF screen, which holds instances of the viewmodels for your usercontrols. That DataContext of the screen will be the general viewmodel. The users controls's DataContext will be a Binding to the PropertyPath of the user control viewmodel in your general viewmodel. e.g:
In WPF Screen:
<ListBox DataContext="{Binding}" ItemsSource="{Binding Path=ItemList}">
<ListBox.ItemTemplate>
<yourControls:YourUserControl />
</ListBox.ItemTemplate>
</ListBox>
In the general viewmodel:
public class ScreenViewModel : INotifyPropertyChanged
{
private ObservableCollection<YourUserControlViewModel> _itemList =
new ObservableCollection<YourUserControlViewModel>();
public ObservableCollection<YourUserControlViewModel> ItemList
{
get { return _itemList; }
set { _itemList = value; }
}
}
This will automatically generate a your user control for each viewmodel in the ItemList of your general view model.

how to load wpf usercontrol in MVVM pattern

I'm creating a wpf user control which is in mvvm pattern.
So we have : view(with no code in codebehind file), viewmodel,model,dataaccess files.
I have MainWindow.xaml as a view file, which I need to bind with MainWindowModel.cs.
Usually, in a a wpf application we can do this with onStartUp event in App.xaml file. But in user control, as we do not have App.xaml...How do I achieve it ?
Please help :(...Thanks in Advance !!!
You can use a ContentControl, with a DataTemplate to bind the UserControl (View) to the ViewModel :
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<v:MyUserControl />
</DataTemplate>
...
<ContentControl Content="{Binding Current}" />
WPF will pick the DataTemplate automatically based on the type of the Content
I know this is an old, answered question, but I have a different approach. I like to make implicit relationships in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
With this, there is no need to set a DataContext anywhere.
UPDATE >>>
In response to #Vignesh Natraj's request, here is a fuller explanation:
Once you have set up the DataTemplate in a Resources element, you can display the KioskView in this example by adding an instance of the KioskViewModel anywhere in your XAML. This could be filling the MainWindow, or just inside a particular section of the screen. You could also host multiple instances of the KioskViewModel in a ListBox and it will generate multiple KioskView instances.
You can add an instance of the KioskViewModel to your XAML in a couple of ways, depending on your requirements. One way is to declare the XML namespace for the project that contains the KioskViewModel.cs file and simply add an instance of it in a ContentControl to the page where you want your view to appear. For example, if you had a UserControl called MainView and the KioskViewModel.cs file was in a Kiosk.ViewModels namespace, you could use basic XAML like this:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<UserControl.Resources>
<ViewModels:KioskViewModel x:Key="KioskViewModel" />
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{StaticResource KioskViewModel}" />
</UserControl>
I prefer to use the MVVM design pattern with WPF, so I would have a base view model class providing useful functionality such as implementing the essential INotifyPropertyChanged interface. I then have a property called ViewModel in the main (top level) view model of type BaseViewModel. This provides me with a nice way to change the ViewModel property to any view model that has derived from BaseViewModel and therefore to be able to change the associated view from the view model.
For example, in the MainViewModel.cs class that is bound to MainView there is a field and relating property:
private BaseViewModel viewModel = new KioskViewModel();
public BaseViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; NotifyPropertyChanged("ViewModel"); }
}
As you can see, it starts off as a KioskViewModel instance, but can be changed to any other view at any time in response to user interaction. For this setup, the XAML is very similar, but instead of declaring an instance of the view model in the Resources element, we bind to the property in the MainViewModel:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<ContentControl Content="{Binding ViewModel}" />
</UserControl>
Note that for this example, we would need to declare two (or more to make this approach useful) DataTemplates in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
I've been using MVVM Light Toolkit which has a ViewModelLocator class that you can put properties to the viewmodels in. You then create a reference to the ViewModelLocator in your Mainwindow.xaml like so:
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/>
In the grid panel, or whatever you're using, you can then set the datacontext like this:
<Grid DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
...
</Grid>
You could also go with MEFedMVVM which potentially adds a bit more flexibility in terms of being able to swap different viewModel implementations into the view.
The flexibility in both of these libraries is that you don't have to use their ViewModel base classes if you don't want to - the ViewModelLocator and the MEFedMVVM can work with any class.
There are endless ways to do it, wich all fall in one of the two categories:"view first" or "model first".
In a "view first" mode the view (e.g. your mainwindow) is created first and then (e.g. in the codebehind) the View instantiates the ViewModel and sets it as its datacontext):
private void WindowLoaded(object sender, EventArgs args)
{
this.DataContext = ViewModelService.GetViewModelX();
}
In a "model first" mode the ViewModel is there first and then instanciated the View.
// method of the viewmodel
public void LoadView()
{
// in this example the view abstracted using an interface
this.View = ViewService.GetViewX();
this.View.SetDataContext(this);
this.View.Show();
}
The examples given here are just one way of many. You could look at the various MVVM frameworks and see how they do it.
We can use ObjectDataProvider to call a method inside an object ..as follows :
<ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
MethodName="ConvertTemp"
x:Key="convertTemp">
Is there anyway to do the same using DataTemplate
You can probably look at MSDN. I find it as a good resource, though it doesn't explain how to use usercontrols,you will find your way out.

WPF: Composite Application with Page Navigation

I am currently writing an application to which the composite methodology fits like a glove.... almost!
I also need a way to navigate between views, including maintaining a journal for navigation backward and forward.
What is the best way to combine these two methodologies, on one hand the single Window based CAG shell with its UserControl derived views, and on the other hand the convenient NavigationWindow shell with its Page derived views and journal?
Thanks!
You can display anything in a NavigationWindow, not just Pages. A simple way to make it work is to define in the NavigationWindow's resources a DataTemplate for each ViewModel you want to display. Bind the Content property of the NavigationWindow to a property of your main ViewModel, and you're done : changing that property will update the NavigationWindow content, and the appropriate DataTemplate will be picked automatically
UPDATE
I just looked at the code of a project of mine where I used a NavigationWindow. Actually I was mistaken, it doesn't work by binding the Content (or maybe it works, but that's not what I did). Instead I created a INavigationService interface, implemented by my App class, which handles the navigation by calling the NavigationWindow.Navigate method. That way, the navigation history is maintained by the NavigationWindow.
Here's an extract from my project
MainWindow.xaml :
<NavigationWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModel"
xmlns:view="clr-namespace:MyApp.View"
Title="{Binding Content.DisplayName, RelativeSource={RelativeSource Self}, FallbackValue=The Title}"
Height="600" Width="800">
<NavigationWindow.Resources>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<view:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<view:CustomerView />
</DataTemplate>
</NavigationWindow.Resources>
</NavigationWindow>
App.xaml.cs :
...
private void Application_Startup(object sender, StartupEventArgs e)
{
LoadConfig();
MyApp.MainWindow window = new MainWindow();
INavigationService navigationService = this;
HomeViewModel viewModel = new HomeViewModel(navigationService);
this.MainWindow = window;
window.Navigate(viewModel);
window.Show();
}
When I need to navigate to another view, I just call the Navigate method with the ViewModel as a parameter, and WPF automatically picks the appropriate DataTemplate from the resources.

GlobalSettings ViewModel

I have some global settings that should come from the ViewModel and should be available to all DataTemplates across all UserControls. It includes things like GlobalButtonMargin, GlobalStrokeWidth or GlobalWorkspaceBackgroundColor. Those things are in the viewmodel because the user can edit these settings at runtime.
How would you go about implementing this in a good MVVM fashion?
I thought about having a Singleton GlobalSettingsViewModel. Is this the preferred approach? If so how can I acess the singleton instance from XAML?
Another way would be to pass the GlobalSettings to all ViewModel instances that exist in my application so I can access from the viewmodels I create DataTemplates for. But that feels unclean.
A third approach would be to ditch the ViewModel approach alltogether define that as XAML resources and set the resources dynamically at runtime using FindResource.
Could you sketch out, how you would design your application to support this scenario?
You could use a static you can read from and bind to, using the x:Static in your XAML. I do not like doing static global settings as it's more of an anti-pattern.
I think you should look into inversion of control/dependency injection. There are many IoC containers out there, but I usually use Unity for my dependency injection. It's available at http://prism.codeplex.com
Using IoC, you could register you settings class, and within your VM that need the data, they can easily pull out the settings you want. Your code would look something similar to this (if using unity).
var vm = container.Resolve<SomeViewModel>();
public class SomeViewModel
{
public SomeViewModel(IUnityContainer container)
{
ISomeSettings settings = container.Resolve<ISomeSettings>();
}
}
EDIT: Or here is another solution you may be looking for:
Create your singleton:
class GlobalSettings : ViewModel
{
private Thickness m_globalGirth;
private static GlobalSettings m_instance = new GlobalSettings();
public GlobalSettings()
{
GlobalGirth = new Thickness(2, 2, 2, 2);
}
public Thickness GlobalGirth
{
get { return m_globalGirth; }
set
{
m_globalGirth = value;
InvokePropertyChanged("GlobalGirth");
}
}
public static GlobalSettings Instance
{
get { return m_instance; }
set { m_instance = value; }
}
}
Then setup your bindings:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication3="clr-namespace:WpfApplication3"
Title="Window1" Height="300" Width="300" MouseDoubleClick="Window_MouseDoubleClick">
<Window.Resources>
<WpfApplication3:GlobalSettings x:Key="settings" />
</Window.Resources>
<Grid>
<Border BorderThickness="{Binding Source={StaticResource settings}, Path=Instance.GlobalGirth}"
BorderBrush="Black"
Width="100"
Height="100" />
</Grid>
</Window>
I would create the type to represent your ViewModel as a class and then define the instance of it as a resource at the ApplicationLevel. That guarentees a single instance for the entire application and you will now be able to refer to those settings using StaticResource. So for example:
<Application xmlns:myNS="clr-namespace:MyNamespace;assembly=MyAssembly" ...>
<Application.Resources>
<myNS:MySettings x:Key="Settings" />
</Application.Resources>
</Application>
And then in windows/controls/templates/etc. you can access the MySettings instance using:
{Binding Source={StaticResource Settings}, Path=MyProperty}

Error when binding a Dependency Property on a custom Behavior

I am exploring the Silverlight attached behaviors mechanism in order to use the Model-View-ViewModel pattern within my Silverlight applications. To start with, I am trying to get a simple Hello World working, but I am completely stuck in an error for which I'm not able to find a solution.
What I have right now is a page that just contains a button which should display a message when clicked. The click event is handled by using a class derived from Behavior, and the message is specified as a dependency property of the behavior itself. The problem comes when trying to bind the message property to a property on a viewmodel class used as the data context: I get an exeption in the call to InitializeComponent in the view.
Here is all the code I'm using, as you can see it is rather simple. First the markup of the main page and the view it contains:
MyPage
<UserControl x:Class="MyExample.MyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyExample"
>
<Grid x:Name="LayoutRoot">
<local:MyView/>
</Grid>
</UserControl>
MyView (the TextBlock is there just to check that the binding syntax is correct)
<UserControl x:Class="MyExample.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:MyExample"
Width="400" Height="300">
<StackPanel Orientation="Vertical" x:Name="LayoutRoot" Background="White">
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
</StackPanel.Resources>
<TextBlock Text="This button will display the following message:"/>
<TextBlock Text="{Binding MyMessage, Source={StaticResource MyResource}}" FontStyle="Italic"/>
<Button x:Name="MyButton" Content="Click me!">
<i:Interaction.Behaviors>
<local:MyBehavior Message="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</i:Interaction.Behaviors>
</Button>
</StackPanel>
</UserControl>
Now the code, there are two classes: one for the behavior and another one for the viewmodel:
MyViewmodel
public class MyViewmodel
{
public string MyMessage
{
get { return "Hello, world!"; }
}
}
MyBehavior
public class MyBehavior : Behavior<Button>
{
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message",
typeof(string), typeof(MyBehavior),
new PropertyMetadata("(no message)"));
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += new RoutedEventHandler(AssociatedObject_Click);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Click -= new RoutedEventHandler(AssociatedObject_Click);
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Message);
}
}
Simple enough, but this code throws an AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 15 Position: 43] (right at the start of the value being set for the Message property) exception when ran. I'm sure that I'm missing something, but what?
Additional information: if I remove the binding from the Message property on MyBehavior (that is, if I set its value to any static string), it works fine. Also, I'm targeting silverlight 3 RTW.
Thanks a lot!
UPDATE
It seems that unlike WPF, Silverlight does not support data binding on any object deriving from DependencyObject, but only on objects deriving from FrameworkElement. This is no the case for Behavior, hence binding does not work.
I have found a workaround here, in the form of something named surrogate binders. Basically you specify the element and property to be binded, as well as the value, as attributes of the FrameworkElement containing the non-FrameworkElement object.
UPDATE 2
The surrogate binder does not work when the FrameworkElement contains an Interaction.Behaviors sub-element.
I have found another solution here, and this one seems to work. This time, the trick used is a DeepSetter. You define one of such setters as a static resource on the containing StackPanel, and then reference the resource from the behavior. So in my example, we should expand the StackPanel resources section as follows:
<StackPanel.Resources>
<local:MyViewmodel x:Key="MyResource"/>
<local:DeepSetter
x:Key="MyBehaviorSetter"
TargetProperty="Message"
BindingExport="{Binding MyMessage, Source={StaticResource MyResource}}"/>
</StackPanel.Resources>
...and modify the button's behavior declaration as follows:
<local:MyBehavior local:DeepSetter.BindingImport="{StaticResource MyBehaviorSetter}"/>
UPDATE 3
Good news: data binding for any DependecyObject will be available on Silverlight 4: http://timheuer.com/blog/archive/2009/11/18/whats-new-in-silverlight-4-complete-guide-new-features.aspx#dobind
To get the DataBinding support the class should inherit from FrameworkElement.Hoping MSFT will give support in Silverlight 4

Resources