This is what I'm trying to do:
I'm writing a UserControl that I want to be consumed by other developers.
I want end users to be able to use my control using Dependency Properties.
<lib:ControlView ControlsText={Binding Path=UsersOwnViewModelText} />
I'm using the MVVM pattern.
I'm binding my ViewModels to their View's using <DataTemplates>
<DataTemplate DataType="{x:Type local:ControlViewModel}">
<local:ControlView />
</DataTemplate>
So I have two questions:
Am I right in thinking that if a UserControl is being consumed in XAML then the UserControl must set the ViewModel as its DataContext when the control's Loaded event fires instead of using the <DataTemplate> method?
How do I allow users to data bind to my control's dependency properties while still being data bound to my ViewModel?
You should separate the two use cases:
The (user) control that will be consumed by other developers.
The user control that will be consumed by your application.
Importantly, the latter depends on the former - not vice versa.
Use case 1 would use dependency properties, template bindings, all the things that go into making a regular WPF control:
MyControl.cs:
public class MyControl : Control
{
// dependency properties and other logic
}
Generic.xaml:
<ControlTemplate Type="local:MyControl">
<!-- define the default look in here, using template bindings to bind to your d-props -->
</ControlTemplate>
You would then define use case 2 as:
MyViewModel.cs:
public class MyViewModel : ViewModel
{
// properties and business logic
}
MyView.xaml:
<UserControl ...>
<local:MyControl SomeProperty="{Binding SomePropertyOnViewModel}" .../>
</UserControl>
Best of both worlds with a clean separation. Other developers depend only on the control, which could (and probably should) be in a completely different assembly than your view model and view.
First off, I don't think MVVM is a good choice if you are developing a UserControl that will be consumed by others. A lookless control is what you really should be developing. Jeremiah Morrill has a blog post about this subject.
With that said, you can set the datacontext with XAML if you have a default public constructor.
Inside ControlView.xaml put:
<UserControl.DataContext>
<local:ControlViewModel />
</UserControl.DataContext>
Basically, instead of binding your UserControl's datacontext to the userControlViewModel, it's better to do it on the first child element of the user control. That way, all the references that you make within the control will be bound to the userControlViewModel, but the dependencies properties can be set from the data context set where you want to use your UserControl.
This is from a project I'm working at:
<UserControl x:Class="Six_Barca_Main_Interface.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Six_Barca_Main_Interface"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="900" d:DesignWidth="900">
<DockPanel x:Name="rootDock" >
<TextBlock>{Binding SomethingInMyUserControlViewModel}</TabControl>
</DockPanel>
</UserControl>
Then on the code behind:
public partial class MyUserControl : UserControl
{
UserControlViewModel _vm;
public MyUserControl()
{
InitializeComponent();
//internal viewModel set to the first child of MyUserControl
rootDock.DataContext = new UserControlViewModel();
_vm = (UserControlViewModel)rootDock.DataContext;
//sets control to be able to use the viewmodel elements
}
#region Dependency properties
public string textSetFromApplication
{
get{return (string)GetValue(textSetFromApplicationProperty);}
set{SetValue(textSetFromApplicationProperty, value);}
}
public static readonly DependencyProperty textSetFromApplicationProperty = DependencyProperty.Register("textSetFromApplication", typeof(string), typeof(MyUserControl), new PropertyMetadata(null, OnDependencyPropertyChanged));
private static void OnDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyUserControl)d)._vm.SomethingInMyUserControlViewModel =
e.NewValue as string;
}
#endregion
Then when you use this on your main view, you can set the dependency property with the value you want to pass to MyUSerControl
A UserControl is part of the "View" in "MVVM" just like the TextBox or ListView controls are part of the View.
Whether you decide to use MVVM to develop your UserControl itself or write it in QBASIC (not recommended) it does not break the MVVM pattern for the consumers of your UserControl so long as they can do every thing they need with your UserControl by binding to DependencyProperty's exposed on your UserControl. i.e. Your UserControl should expose the properties it is dependent upon (hence the name). Once you grasp this DependencyProperty's suddenly make a whole lot of sense and you want their helpful on changed event handlers and default values you specify in their constructor.
If your UserControl is in a different assembly or not I cannot see how that makes a difference.
That said many would advocate you build your UserControl using the MVVM pattern itself for all the good reasons MVVM brings e.g. helping another developer looking at your code. However some things simply are not possible and/or much harder more complex and less performant hacking the XAML to do this - I am not talking about your garden variety Add User Form but for example a UserControl handling the layout of thousands of visuals. Furthermore since you are working in your View you do NOT want your UserControl's ViewModels mixed in with you applications!
Basically I am saying it is well within MVVM not to use MVVM on your View!
Related
I am trying to create a user control using MVVM.
Basically I am trying to wrap a combobox that will pull data from a respository. This will allow me to use the same combobox in many different views in my application. There will be many of the wrapped comboboxes throughout the application.
I was easily able to create this control using a DependencyProperty and code-behind. I am now trying to convert this to MVVM and am having trouble figuring out how to get the value back to /from the ViewModel that in bound to the View where my combobox is located.
Any ideas or suggestions would be greatly appreciated at this point.
Thanks,
Eric
It is perfectly acceptable to use a UserControl that has code behind in it when using MVVM. If you really want to move the functionality out of the UserControl, then move it to whichever parent view models will require it. If you don't want to have the same code repeated in several places, you could encapsulate it in a class and add an instance of that class as a property to each of the relevant view models.
if you have a viewmodel that will pull data from a respository - you can use the same viewmodel in many different viewmodels in your application :)
and if you use a datatemplate your views know how to render this viewmodel
<DataTemplate DataType="{x:Type local:MyPullDataViewmodel}">
<view:MyCoolPullDataShowComboboxUserControl />
</DataTemplate>
It's pretty easy.
Let's say you have:
MyUserControlView.xaml
MYUserControlViewModel.cs
MyMainWindowView.xaml - For your MainWindow view (the one containing the UserControl)
MyMainWindowViewModel.cs - Your MainWindow ViewModel.
And you want to bind List<string> MyListToBind
And leave the code-behind completely empty.
MyUserControlViewModel.cs
public class MyUserControlViewModel
{
private List<string> _MyListToBind;
public List<string> MyListToBind { get; set;}
}
MyMainWindowViewModel.cs
public class MyUserControlViewModel
{
private MyUserControlViewModel _MyControlViewModel;
public MyUserControlViewModel MyControlViewModel { get; set;}
}
MyMainWindowView.xaml
<Window ...
xmlns:my="clr-namespace:NamespaceContainingYourUserControlView>
<my:MyUserControlView DataContext = "{Binding Path= MyControlViewModel}"/>
</Window>
MyUserControlView.xaml
<UserControl ...>
<DataGrid ItemsSource = "{Binding Path= MyListToBind}" .../>
...
</DataGrid>
</UserControl>
This doesn't support ViewModel updating View. To do that You have to use either DependencyProperties as you did instead of normal variables (as i did) or use INotifyPropertyChanged(google it, you'll get tons of examples) and OnPropertyChanged event.
You might read up on DataTemplates they are really useful in data binding.
You can find this usefeul:
http://www.youtube.com/watch?v=BClf7GZR0DQ
I sure as hell did !
Good luck.
Alright, this is somewhat related to this question: WPF Printing multiple pages from a single View Model
I tried to follow the advice given there but now I am stuck.
My application uses a MainView.xaml and the appropriate MainViewViewModel.cs, I am using MVVM Light in the background.
Now - according to the post - it seems I have to do the following:
Create a user control
Expose some properties from the user control
Make sure the view model shows these properties
The idea is clear but I am stuck when trying to notify each other.
My user control (UcTest.xaml) exposes a Dependency Property:
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set
{
SetValue(SpecialTextProperty, value);
}
}
// Using a DependencyProperty as the backing store for SpecialText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText", typeof(string), typeof(UcTest), new PropertyMetadata(new PropertyChangedCallback(SpecialTextChangedPropertyCallback)));
private static void SpecialTextChangedPropertyCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Do something
Debug.WriteLine("Ffgdgf");
}
Alright, so I do now have a user control which has some dependency properties. Yet, these properties are completely separated from my ViewModel properties (those are the ones which shall be displayed).
So basically I have two possibilities:
How can I now tell my ViewModel for the UserControl that some properties have changed?
Is there a possibility to forget about the dependency properties and access the view model directly?
Additional info #1:
I have uploaded a (simple) example of what I am trying to do here: Example Project. I would like to change the value of the label in UserControl1 (via the binding property in the ViewModel for UserControl1) from my MainViewViewModel.
You would usually bind the UserControl's property to the ViewModel property. A two-way binding would work in both directions, from ViewModel to View and vice versa.
<Window x:Class="TestApplication.MainWindow" ...>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
</Grid>
</Window>
To directly access the ViewModel object in the above example, you could simply cast the UserControl's DataContext property to the ViewModel type. The DataContext is inherited from the MainWindow.
var viewModel = DataContext as MyViewModel;
var property = viewModel.MyViewModelProperty;
You could of course also directly assign a specialized ViewModel instance to the UserControl's DataContext:
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
<local:UcTest.DataContext>
<local:UserControlViewModel/>
</local:UcTest.DataContext>
</local:UcTest>
or you may create the ViewModel instance as a resource in a resource dictionary and assign the DataContext like this
<local:UcTest DataContext="{StaticResource MyUserControlViewModel}"
SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
Alright, after hours of googling it appears that the "correct" approach to this is not to do it at all. The general approach is to keep the data in your MainViewModel and not use an additional ViewModel for the UserControl (which I find a little ... well .. not so good). The main problem is that there is no easy mechanism to get the Data from the Dependency Property to the ViewModel.
For printing, I have now gone back to doing it purely in code.
I'm working on a Surface WPF project where we try to implement the MVVM pattern. Within this project we are building a few custom controls which we bind to different viewmodels.
For example we have a settings control which has a settings viewmodel and we have a mainviewmodel which is the "overall" viewmodel.
In our surfacewindows.xaml page we are setting the datacontext to the main viewmodel by using the viewmodel locator in mvvm-light. Also on our surfacewindow.xaml we have added our settings control and on the control we have set the datacontext to the settings viewmodel.
Now we need both viewmodels to interact with each other: The current case is that we need to set the visibility of the settings control. We have a property on the main viewmodel that is a boolean (IsSettingsControlVisible) which is bound to the controls Visibility property by using a converter to convert the boolean to a visibility object.
The problem arises now when we need to set the visibility to not visible by clicking on a close button on the settings control. Because we have set the datacontext on the control to the settings viewmodel, we cannot access the mainviewmodel.
What we have thought of until now is adding the settings viewmodel as a property to the mainviewmodel and remove the datacontext from the settings control. In the settingscontrol we will than use the binding as SettingsProperty.Property. Than we can access the mainviewmodel too from the setttings control. Does that make sense? Are there better ways of doing these kind of interactions?
I really like to hear your ideas about how to make these interactions happen.
I tend to work with graphs of view models that are constructed using Castle Windsor. The top level view model uses constructor injection to receive the next level of view models that it requires. And in the views I bind content presenters to properties on the view models to create the corresponding view graph.
Doing this, it's quite easy for parent child view models to communicate, but a bit harder for sibling or more distant view models to communicate.
In these instances, I tend to use an event aggregator, or Messenger to allow the view models to communicate.
As you are already using MVVMLight, I'd suggest using the MVVM Light toolkits Messenger system. It's intended for message exchange between ViewModels.
The concept behind is the Mediator pattern where different objects exchange information without knowing each other.
Here's an example:
In the SettingsViewModel register to an event that tells to show the settings dialog
public SettingsViewModel()
{
Messenger.Default.Register<ShowSettingsMessage>(this, ShowSettingsDialog);
}
private void ShowSettingsDialog(ShowSettingsMessage showSettingsMessage)
{
// Set the visibility:
this.IsVisible = showSettingsMessage.Content;
}
In your MainViewModel you send the notification, wrapped in a Message:
// make the settings visible, e.g. the button click command:
Messenger.Default.Send(new ShowSettingsMessage(true));
And here's the message:
// the message:
public class ShowSettingsMessage : GenericMessage<bool>
{
public ShowSettingsMessage(bool isVisible)
: base(isVisible)
{ }
}
I wouldn't recommend making the SettingsViewModel a property of the Mainviewmodel as you lose the possibility to use the SettingsViewModel in a different context or even remove/exchange it.
Try to create a Dependency Property on the Settings control called IsSettingControlVisible and bind it with the parent viewModel.
EDIT:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0));
}
and use it like this...
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 MyProperty="{Binding Path=ParentViewModelProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</Window>
I am designing a WPF user control which contains other user controls (imagine a WidgetContainer, containing different Widgets) - using M-V-VM architecture.
During development, I have WidgetContainerView in a window, window (View) spawns a WidgetContainerViewModel as its resource, and in a parameterless constructor of WidgetContainerViewModel, I fill its exposed collection with some sample widgets (WidgetViewModels).
WidgetContainer control inherits the DataContext from window, and inside, there is a ListView, that binds Widgets to WidgetView control (which is inside ListView.ItemTemplate).
Now this works OK in my WindowView, as I see my sample widgets, but once I edit the WidgetContainerView or WidgetView, there is no content - at design time, controls are standalone, and they don't inherit any DataContext, so I don't see a content, and have troubles designing them (a ListView is empty, Widget's fields as well...).
I tried adding a sample widget to the WidgetView:
public partial class WidgetView : UserControl
{
public WidgetView()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
//btw, MessageBox.Show(...) here sometimes crashes my Visual Studio (2008), but I have seen the message - this code gets executed at design time, but with some lag - I saw the message on reload of designer, but at that time, I have already commented it - wtf?
this.DataContext = new WidgetViewModel(); //creates sample widget
}
}
}
but that didn't work - I still don't see anything in designer.
I also wanted to create a WidgetViewModel as a resource in WidgetView, like this:
<UserControl x:Class="MVVMTestWidgetsControl.View.WidgetView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="WidgetViewModel" //this doesn't work!
Height="Auto" Width="Auto">
<UserControl.Resources>
<ResourceDictionary>
<ViewModel:WidgetViewModel x:Key="WidgetViewModel" />
</ResourceDictionary>
</UserControl.Resources>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
</UserControl>
but I don't know how to assign a WidgetViewModel as a DataContext of a whole widget - I can't add DataContext attribute to UserControl, because WidgetViewModel is defined later in the code. Any ideas how to do this? I could use a sample data this way, and just override it in code so that it has the right content at runtime...
What are your best practices when developing user controls? Thank you, designing empty control is no fun :)).
In your second snippet, you should be able to refer to your DataContext as a DynamicResource:
DataContext="{DynamicResource WidgetViewModel}"
But most custom user controls have some sort of top level layout container, and you can set the DataContext on that container as a StaticResource.
In your case, however, you may want to consider dropping the VM portion of your code altogether since you're writing a custom UserControl. You should ask yourself what benefits are you gaining from a completely self-contained ViewModel with no real backing Model designed for just one View (i.e. the custom UserControl). Perhaps you could just define some DependencyProperties and use those?
I came up with several solutions: Add DC as resource (it will get automatically instantiated with parameterless constructor), and do the following in View's codebehind:
public PanelView()
{
InitializeComponent();
if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) //DeleteAtRelease:
{
//we are in runtime, reset DC to have it inherited
this.DataContextHolder.DataContext = DependencyProperty.UnsetValue;
}
}
Better way would be to only assign DC if we are at designtime, but VS didn't like it - it worked only sometimes, and quite nondeterministically, and once it even crashed.
Other check for design time is:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
this.DataContext = new WidgetViewModel();
}
I'm building a simple UserControl example with DependencyProperties so that the properties of the control can be changed in XAML (code below).
But of course in my application I don't want this control to have tightly-coupled code-behind, but instead the user control will be a view called "DataTypeWholeNumberView" and it will have its own ViewModel called "DataTypeWholeNumberViewModel".
So I am going to implement the DependencyProperty logic below into the ViewModel, but in ViewModels I usually inherit INotifyPropertyChanged which seems to give me the same functionality.
So what is the relationship between:
binding the DataContext of UserControl XAML to its code behind which has a DependencyProperties
binding the DataContext of UserControl XAML (View) to its ViewModel (which inherits from INotifyPropertyChanged) and has properties which implements INotifyPropertyChanged functionality?
XAML:
<UserControl x:Class="TestDependencyProperty827.SmartForm.DataTypeWholeNumber"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<TextBlock Text="{Binding Label}"/>
</StackPanel>
</StackPanel>
</UserControl>
Code Behind:
using System.Windows;
using System.Windows.Controls;
namespace TestDependencyProperty827.SmartForm
{
public partial class DataTypeWholeNumber : UserControl
{
public DataTypeWholeNumber()
{
InitializeComponent();
DataContext = this;
}
public string Label
{
get
{
return (string)GetValue(LabelProperty);
}
set
{
SetValue(LabelProperty, value);
}
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(DataTypeWholeNumber),
new FrameworkPropertyMetadata());
}
}
INotifyPropertyChanged is an interface that exists in .Net since 2.0. It basically allows objects to notify when a property has changed. An interested party can perform certain actions when this event is raised. The problem with it is that it only publishes the name of the property. So you end up using reflection or some iffy if statements to figure out what to do in the handler.
DependencyProperties are a more elaborate construct that supports default values, change notifications in a more memory-efficient and performant way.
The only relationship is that the WPF binding model supports binding to either DependencyProperties or to standard Clr properties, with an INotifyPropertyChanged implementation. Your ViewModel could be a DependecyObject as well and the third option would be to bind to the ViewModel's DependencyProperties!
Kent Boogaart wrote a very interesting article on having a ViewModel be a POCO vs a DependencyObject.
I don't really think there is a relationship between DependencyProperties and INotifyPropertyChanged. The only magic here is that the Binding classes/utils are smart enough to recognize a DependencyProperty and bind directly to it, or subscribe to the binding-target's notify-property-changed event and wait for that to fire.
With WPF, you can either bind to DependencyProperties or to Properties which implement INotifyPropertyChanged. It's a matter of choice.
So your question breaks into either to have them in code behind or in view model. Since you have mentioned that you do not want a tightly-coupled code behind, you are better off having a view model following the MVVM pattern.
You can use the DependencyProperties even in your view model just like you have done in your code behind.