I am currently writing a WPF application which has a navigation panel on its left side (to which I bind a navigationViewModel) and a content presenter on its right side (which I bind to the previously mentioned VM's UserControl member 'CurrentView'.
For each item of this navigation panel, I created a corresponding user control and for each of these user controls, I bind an instance of a corresponding ViewModel.
Clicking on a navigation panel's items sets its ViewModel's UserControl member CurrentView to the instance of the corresponding UC, which is then displayed in the content presenter mentioned above.
The first navigation item is some "select or create a new client" form. When this operation is done, I would like to set some wide app resource id to which I would bind the other navigation items Enabled state. Thus, if the wide app resource is null, they are disabled, as soon as it is set to anything, they are enabled. There would also be some mechanism which would allow the corresponding ViewModel to be notified of this situation.
I am wondering if this would be considered a good practice?
Furthermore, I'd like to know if I can simply declare an int resource in the app.xaml and bind it to the navigation items Enabled property, would setting this resource to anything immediately refresh this property? Or is there a better, simpler or cleaner way?
An alternative could be to nest the two viewmodels (navigation and currentview) in a third viewmodel (say mainviewmodel)
This main viewmodel could then keep state that should be available across these viewmodels and across instances of currentviews.
This way you do not need to have global state in the application and you can simply set the datacontext of the Window to the main viewmodel and bind the navigation and content views to properties of the main viewmodel.
This also allows you to have a proper place for navigating to a different content view.
Here is what I ended up doing:
In my app.xaml; I declare the following resource:
<Application x:Class="MyProject.GUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.GUI.ViewModels"
StartupUri="MainWindow.xaml">
<Application.Resources>
<vm:MainViewModel x:Key="MainViewModel" />
</Application.Resources>
</Application>
This MainViewModel exposes a static property as follow:
static bool _myStaticProperty;
public static bool MyStaticProperty
{
get
{
return _myStaticProperty;
}
set
{
_myStaticProperty = value;
NotifyStaticPropertyChanged("MyStaticProperty");
}
}
And the following static INPC mechanism:
#region Static property INPC mechanism
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
{
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
And my different viewmodels:
FirstChildViewModel _firstChildViewModel;
public FirstChildViewModel FirstChildViewModel
{
get
{
if (_firstChildViewModel == null)
_firstChildViewModel = new FirstChildViewModel();
return _firstChildViewModel;
}
}
//then a second one, a third one and so on
Which are binded to my user controls as follow
<UserControl x:Class="MyProject.GUI.Views.FirstChildControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyProject.GUI.ViewModels"
mc:Ignorable="d"
DataContext="{Binding Path=FirstChildViewModel,
Source={StaticResource MainViewModel}}">
In my user controls' xaml code, I declare command bindings etc. which basically do the following in their ViewModel
MainViewModel.MyStaticProperty = myBoolValue;
Related
I have a custom control which will have properties that can be set which will affect the logic of how the control is handled. How should this be handled in MVVM?
Currently I'm stuck trying to pass a DependencyProperty to the ViewModel.
Example code:
CustomControl.xaml
<UserControl x:Name="Root" ...>
<UserControl.DataContext>
<local:CustomControlViewModel SetDefaultValue="{Binding ElementName=Root, Path=SetDefaultValue, Mode=TwoWay}"/>
</UserControl.DataContext>
...
</UserControl>
CustomControl.xaml.cs
...
public static readonly DependencyProperty SetDefaultValueProperty = DependencyProperty
.Register("SetDefaultValue",
typeof(bool),
typeof(CustomControl),
new FrameworkPropertyMetadata(false));
public string SetDefaultValue
{
get { return (string)GetValue(SetDefaultValueProperty ); }
set { SetValue(SetDefaultValueProperty , value); }
}
...
CustomControlViewModel.cs
...
private bool setDefaultValue;
public bool SetDefaultValue
{
get { return setDefaultValue; }
set
{
if (setDefaultValue!= value)
{
setDefaultValue= value;
OnPropertyChanged("SetDefaultValue"); // INotifyPropertyChanged
}
}
}
...
My goal with this property specifically is to be able to set a default value (getting the default value requires running business logic). So in another view I would use this control like this:
<local:CustomControl SetDefaultValue="True"/>
(Before I answer I want to point out that what you have here is actually a user control, not a custom control. That's not nit-picking on my part; A user control is something derived from the UserControl class and it typically has an associated XAML file. A custom control just derives from the Control class and has no associated XAML file. A custom control requires you set to a control template. Custom controls can be styled. User controls cannot.)
The thing about UserControl is that sometimes we create one assuming one specific DataContext, of one type and then we make all of its XAML bind to that object type. This is good for big, main pages of an application that are not meant to be re-used in too many places
But another approach -- that you have started to do here -- is to give our user controls their own dependency properties. So in this case, why not dispense with the need for this control to have any specific DataContext altogether? This is the first step to making user controls truly re-usable in many places.
Unless this control is huge, there's a good chance that When you are laying out its XAML, it can get everything that XAML needs to bind to in just a few properties. So why not make all those properties into dependency properties and make the control's XAML bind to itself?
Make the class be its own DataContext. Set that property on the root UI element of the control's layout and then every Binding should work well.
To illustrate, I've renamed your control class MyUserControl I've renamed your "SetDefaultValue" property to just be "BoolOption" Let's assume that all it needs to show is a checkbox, representing the bool value and a string label on the checkbox. We can do this with just two dependency properties. (In effect, this entire control is now just a pointless, glorified CheckBox control but please ignore that)
MyUserControl.xaml.cs
public static readonly DependencyProperty BoolOptionProperty =
DependencyProperty.Register(nameof(BoolOption),
typeof(bool),
typeof(MyUserControl),
new FrameworkPropertyMetadata(false));
public string BoolOption
{
get { return (string)GetValue(BoolOptionProperty ); }
set { SetValue(BoolOptionProperty , value); }
}
public static readonly DependencyProperty CheckBoxLabelProperty =
DependencyProperty.Register(nameof(CheckBoxLabel),
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(string.Empty));
public string CheckBoxLabel
{
get { return (string)GetValue(CheckBoxLabelProperty ); }
set { SetValue(CheckBoxLabelProperty , value); }
}
// Constructor. Here we set the control to be its own UI's DataContext
public MyUserControl()
{
InitializeComponent();
// Make us be the UI's DataContext. Note that I've set the
// x:Name property root Grid in XAML to be "RootUiElement"
RootUiElement.DataContext = this;
}
MyUserControl.xaml
<UserControl x:Class="MyCompany.MyApp.MyUserControl"
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:local="clr-namespace:MyCompany.MyApp.Controls"
x:Name="Root"
d:DesignHeight="450"
d:DesignWidth="800"
d:DataContext="{d:DesignInstance {x:Type local:MyUserControl}}"
mc:Ignorable="d">
<Grid x:Name="RootUiElement">
<CheckBox IsChecked="{Binding BoolOption}"
Content="{Binding CheckBoxLabel"
/>
</Grid>
</UserControl>
Finally you could use the control anywhere you wanted, no matter what your current DataContext is, like this
<local:MyUserControl BoolOption="True" CheckBoxLabel="Is Option A enabled?"/>
<local:MyUserControl BoolOption="False" CheckBoxLabel="Is Option B?"/>
Or even bind it to some other DataContext where you're using it like this. Suppose my current DataContext is a view-model that has a boolean UserBoolOptionC property
<local:MyUserControl BoolOption="{Binding UseBoolOptionC}" "Is Option C enabled?"/>
I have a WPF application with two pages, now I wanted to navigate to the other page when the button in first the page is clicked (I wrote the command for button in the first page), but the logic should be through the viewmodel. How to achieve this?
When I write WPF applications that need to navigate to different pages, I like to follow Rachel Lim's method to implement it using DataTemplates and ViewModels. You can follow the link to her page to get the exact code for the solution, but I'll give a little summary of her method here.
In her method, she creates a ViewModel that represents the application and has a property called CurrentPage which holds a ViewModel. You can then create a command on the ApplicationViewModel called ChangePage. This command will take the ViewModel that is passed as a parameter and sets it to the CurrentPage.
The xaml takes the responsibility of switching out the correct views. When using this method, I put a ContentControl in my MainWindow and bind the Content property to ApplicationViewModel.CurrentPage. Then in the resources of the MainWindow, I create DataTemplates to tell the view "When I try to display this ViewModel, put that View on the screen".
You don't really provide any code. But I assume your Navigation is in your code behind. You could do this by binding a Command OneWayToSource.
XAML
<local:MainWindow x:Class="WpfNameSpace.MainWindow"
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:local="clr-namespace:WpfNameSpace"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
NavigatePageCommand="{Binding Path=MyViewModel.NavigateCommand, Mode=OneWayToSource}"
Title="MainWindow" Height="600" Width="800">
<Grid>
</Grid>
</local:MainWindow>
Please take a look at local:MainWindow.
C#
public partial class MainWindow : Window
{
public ICommand NavigatePageCommand
{
get { return (ICommand) GetValue(NavigatePageCommandProperty); }
set { SetValue(NavigatePageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigatePageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigatePageCommandProperty =
DependencyProperty.Register("NavigatePageCommand", typeof(ICommand), typeof(MainWindow),
new PropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
NavigatePageCommand = new RelayCommand(Navigate);
}
public void Navigate()
{
//Do Navigation here
}
}
I assume you are familiar with Commands, ViewModels and Bindings and you get the idea.
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>
Let's say I have a custom control which wraps another control (for example MyCustomButton). I expose a property Content, which wraps the inner control:
public object Content
{
get { return innerControl.Content; }
set { innerControl.Content = value; }
}
In order for a consumer to bind to this property, I need to define a DependencyProperty for it:
public static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyCustomButton));
but now I need my property definition to use GetValue/SetValue:
public object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
so I'm not wrapping the value of the inner control anymore.
I can define PropertyMetadata to handle the PropertyChanged event of the DependencyProperty, but then I need a bunch of plumbing code to keep the values in sync and prevent infinite loopbacks on changed.
UPDATE: I can't just derive from Button because my UserControl has various other concerns.
Is there a better way to do this?
Well, depending on the particulars of why you're wrapping a button with a user control, you could define a custom control that inherits from button. Then, instead of wrapping the button and exposing the wrapped methods and properties that you want, you can simply override methods and properties whose behavior you want to define the custom control. This way, you'll get all of the functionality of button without the need to reinvent the wheel.
Here's a google link that walks you through it (one of the first that I found - there are plenty): http://knol.google.com/k/creating-custom-controls-with-c-net#
If the user control has other concerns, this may not be an option for you, but I'm offering this answer because the only purpose that you've mentioned for it is wrapping the button. I'd personally favor creating a custom control and inheriting rather than a user control and wrapping if the control in question is simply meant to be a more specific kind of wrapped/inherited control (i.e. button in your case).
Edit: In light of updated question...
You could do something along these lines. Here is the XAML of the client of your user control:
<Grid>
<local:MyControl ButtonContent="Click Me!"/>
</Grid>
</Window>
Here is the XAML for the user control itself:
<UserControl x:Class="GuiScratch.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:GuiScratch"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<ContentControl Content="Asdf"/>
<Button Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}},Path=ButtonContent}"/>
</StackPanel>
</Grid>
</UserControl>
And, here is the code behind:
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(object), typeof(MyControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public object ButtonContent
{
get { return (object)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
So, you don't need to handle the binding at all through code. Your client XAML binds to your dependency property, as does the XAML of the user control itself. In this fashion, they share the dependency property setting. I ran this in my little scratchpad, and the result is (at least my understanding of) what you're looking for. The main window displays the user control as a stack panel with the text "Asdf" and then a button with the text "Click Me!"
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!