Using MVVMLight toolkit in a WPF app.
I have a ViewModel class that encapsulates the application's settings, and a corresponding View to allow the user to change them. The settings are stored in the registry.
I'm adding some status indicators to the main View to reflect whether some settings are toggled on or off. It makes sense to bind the visibility of these to properties in the MainViewModel.
Is it possible to bind properties in the MainViewModel to properties in the SettingsViewModel, or should I use messages to tell the MainViewModel that something has changed in the SettingsViewModel?
Thanks for any suggestions...
A simple way to do it by exposing the SettingsViewModel as a property on your MainViewModel like this:
Public SettingsViewModel Settings
{
get
{
return settingsViewModel;
}
}
Then in your MainView.xaml your binding can do this:
"{Binding Path=Settings.ASettingOnTheSettingsVM}"
If your SettingsViewModel is launched from the MainViewModel and you have implemented the INotifyPropertyChanged interface then you can just listen to the SettingsViewModel.PropertyChanged event:
In MainViewModel:
SettingsViewModel settingsViewModel = new SettingsViewModel();
settingsViewModel.PropertyChanged = SettingsViewModel_PropertyChanged;
...
private void SettingsViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Monitor property changes here
}
Related
I need to connect methods in View (WPF window) to events in ViewModel. Is it violation of MVVM pattern to DirectCast Object DataContext in view to concrete VM type and connect its events? If yes, is there better way to do it?
First look at what the methods in the view do. If they manipulate the view, consider adding properties to the viewmodel that you change in the events in the viewmodel and bind the view to. This way, by binding the view to properties you eliminate the need for code in the view.
If the methods contain other logic consider moving that logic to the viewmodel.
In other cases casting a DataContext to a viewmodel or interface can be a valid option and is not a violation of the MVVM pattern.
When adding code to a view, do consider testing. Automated/unit testing a view is harder than testing a viewmodel.
It is not a violation of the MVVM pattern, but the more abstract the better, of course (not because of MVVM but as a general good practice).
If you're setting your DataContext on XAML, you may be able to keep it abstract by using Interactivity EventTrigger and CallMethodAction... Maybe. But if you're setting it on code-behind (via injection or whatever), you're left with either casting the DataContext to a known type, or using Reflection (I wouldn't >_>).
Generally, creating an interface for your ViewModel, so you keep a decent level of abstraction and only expose what the view needs to know instead of its whole implementation, is good enough for most scenarios.
public interface IMyViewModel
{
event EventHandler MyEvent;
}
public class MyViewModel : IMyViewModel
{
public event EventHandler MyEvent;
// More viewmodel related stuff
protected virtual void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
public class MyWindow : Window
{
public MyWindow(IMyViewModel viewModel)
{
this.DataContext = viewModel;
InitializeComponent();
(this.DataContext as IViewModel).MyEvent += MyEventHandler;
}
private void MyEventHandler(object sender, EventArgs e)
{
// Do view related stuff
}
}
Using MVVM pattern and WPF, I would like to bind my controls to two properties. Let's say I have a label that is bind to property on VM1 and I would like to bind it as well to the property on VM2 to send received value from VM1 to VM2.
I could use messenger and for multiple items Tuple Class, but I was wondering if there is another solution for that. Multibinding? but then I need ValueConverter.
Thank you for any suggestions.
Since The View-Model is an abstraction of the view that exposes public properties and commands, it doesn't make a lot of sense for a view to have two view-models the way you explained. It'll be more rational if there is a VM class as the view-model of your view that has two properties of type VM1 and VM2. Then the binding would be on VM.VM1.YourText and you can notify VM2 via events like this:
in VM1:
public event EventHandler<EventArgs> ValueChanged;
string _yourText;
public string YourText
{
get
{
return _yourText;
}
set
{
_yourText= value;
if (ValueChanged != null)
ValueChanged(_yourText, new EventArgs());
}
}
In VM:
public VM1 Vm1 {get; set;}
public VM2 Vm2 {get; set;}
public VM()
{
InitializeComponent();
Vm1 = new VM1();
Vm2 = new VM2();
Vm1.ValueChanged += Item_ValueChanged;
DataContext = this;
}
void Item_ValueChanged(object sender, EventArgs e)
{
VM2.YourAnotherText = sender.ToString();
}
If 2 properties are connected, usually INotifyPropertyChanged can be utilized to notify about a change of 2 or more properties if on the same ViewModel.
What I understand is that you want also to notify a View attached to a ViewModel about a change of a property on another ViewModel. This is usually done by letting ViewModels exchange information.
If that's a one rare case, using message bus for that might be an overkill. Usually keeping a reference to each view model and changing properties from outside should be all right. To keep separation of concerns you can create an interface on one or both viewmodels and reference this interface instead of a concrete type.
Overall keeping a single binding between a control and property keeps it simple and easy to understand and you should worry about making sure that this property handles all changes to/from other VMs.
I created a Task List control. I have an AddTask method on it. I'd like to call this method from the host Window.
I found a few posts here in SO and other sites that suggest using an interface, then looping over all the controls in the window to find the control, then getting a reference to it and using that to call the method. Here's an example:
Call method on various user controls
But is it possible to call a method somehow with binding? Assume someone is using MVVM and the Window's VM wants to fire the control's AddTask method. is this possible?
Thanks!
If you really want to do it (in a possible) the right way i'd tell you to write about MVVM.
Binding and methods work very well in MVVM using Commands
Here it is my solution
Create a ViewModel class
Create a nested class MyCommandBehaviour that implements ICommand (some people create the class in a different class)
In the view model create a property MyCommandBehaviour MyCommand
In the constructor of the view model instantiate that property
In The XAML bind the button {Binding MyCommand}
Set the DataContext of the window (or user control) to the view model
Note: I usually create the Command nested class with a constructor that accepts the 'parent' view model. Because the class is nested it can directly access the view model private members
public class OkCommand : System.Windows.Input.ICommand
{
private MyViewModel _vm;
public OkCommand(MyViewModel vm)
{
this._vm = vm;
}
public bool CanExecute(object parameter)
{
return true;//I never use this and the event below
}
#pragma warning disable 0067
public event EventHandler CanExecuteChanged;
#pragma warning restore 0067
public void Execute(object parameter)
{
//do your stuff. Note you can access the MyViewModel members here via _vm
}
}
In MEFedMVVM viewmodels are instantiated using ViewModelLocator. MEFedMVVM is really powerful, since you can have an arbitrary constructor for your viewmodels:
[ExportViewModel("MyViewModel")]
public class MyViewModel : NotifyPropertyChangedBase
{
[ImportingConstructor]
public MyViewModel(IMediator mediator, IContainerStatus containerStatus, IDispatcherService dispatcherService)
{
}
}
IMediator, IContainerStatus and IDispatcherService are service interfaces which are instantiated through MEF. Obviously, I can create my own services if needed.
The problem
When my view has loaded it needs to assign a member of one of its children's readonly property with data from the viewmodel. Ideally, I would bind this variable directly in XAML, but I cannot do that since the property is readonly and its member is not an attachable property. Currently, I have an ugly workaround:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var viewModel = DataContext as MyViewModel;
this.child.SomeReadonlyProperty.Data = viewModel.MyData;
}
I want to get rid of this coupling of the view and the viewmodel. Both MEFedMVVM and Prism provide different patterns that might be helpful, but I have no idea which to use - and how. Is it ok to let services have access to both the view and the viewmodel?
Q: What pattern should I use to remove the coupling?
I think I would be inclined to create an interface which your view can implement that will describe the behavior you need to set the property:
interface IMyView
{
void SetReadOnlyProperty(object value);
}
You can then create your implementation of SetReadOnlyProperty in the view, export the view as type IMyView and finally, create an import on your viewmodel of type IMyView which will expose your method and allow you to perform calls from the viewmodel. I believe this doesn't go against the ethos of MVVM because the viewmodel isn't aware of the view, it just has an interface that will expose what you need.
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>