For example, my ViewModel do something. After that, I wanted to notify my View in order to do something that should only be done in View.
Something like;
public class MyViewModel : ViewModelBase{
...
private void DoSomething(){
//raise the event here and notify the View
}
}
then in View;
public MyView(){
InitializeComponent();
...
}
private void ViewModelSaidDoSomething(){
//The View Model raises an event, do something here about it...
}
is that possible without breaking the MVVM concept?
Yes, it's possible. Usually, what you can do is define the event from your ViewModel, then let your View subscribe from that event.
For example, in your ViewModel.
public class MyViewModel : ViewModelBase{
...
//Define your event.
public delegate void YourEventAction(string your_argument);
public event YourEventAction? YourEvent;
private void DoSomething(){
YourEvent?.Invoke(your_argument); //raise the event here, any subscriber will received this.
}
}
Then you can subscribe for that event in your View.
public MyView(){
InitializeComponent();
...
DataContextChanged += ViewModelSaidDoSomething; //subscribe to DataContextChanged.
}
private void ViewModelSaidDoSomething(){
var viewModel = (MyViewModel)DataContext; //Get your ViewModel from your DataContext.
viewModel.YourEvent += (your_argument) =>{ //Subscribe to the event from your ViewModel.
//The View Model raises an event, do something here about it...
};
}
Hope that helps.
My primary candidate for any viewmodel <-> view communication would be binding.
This is a weak event pattern, late binding and the view doesn't need to be aware of what type the viewmodel is. They went to a load of trouble to implement binding and it works well IMO. Anyhow, binding means view and viewmodel are as loosely coupled as you can get whilst retaining reasonable practicality.
Here's an example.
The purpose is to allow a viewmodel to close a window.
The view functionality is encapsulated in a control.
Only code.
public class CloseMe : Control
{
public bool? Yes
{
get
{
return (bool?)GetValue(YesProperty);
}
set
{
SetValue(YesProperty, value);
}
}
public static readonly DependencyProperty YesProperty =
DependencyProperty.Register("Yes",
typeof(bool?),
typeof(CloseMe),
new PropertyMetadata(null, new PropertyChangedCallback(YesChanged)));
private static void YesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool?)e.NewValue != true)
{
return;
}
CloseMe me = (CloseMe)d;
Window parent = Window.GetWindow(me) as Window;
parent.Close();
}
}
Anywhere in a window or usercontrol goes in the window you want to close:
<local:CloseMe Yes="{Binding CloseYes, Mode=TwoWay}"/>
That binds to a bool property CloseYes in the viewmodel. Set that true and the window closes.
We can imagine encapsulating some data in a more complicated viewmodel object instead of just a boolean. That would not be quite so elegant though.
A view isn't supposed to process data, so I'd question what you're doing if you're sending complex data.
Setting those doubts aside though.
If you wanted to transfer a complex type from A to B with loose coupling then the pub sub pattern is a good candidate.
The mvvm toolkit is my framework of preference and there's an implementation in there which you can use. Messenger:
https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/messenger
The message sent is an object you define and you can use WeakReferenceMessenger. Which does what it implies and relies on a weak reference. Thus reducing the risk of memory leaks.
Other frameworks have similar mechanisms so you could take a look at what's in the one you prefer.
Related
I am working on an application and trying to follow MVVM as much as possible and thus have many Views with corresponding ViewModes. I am deserializing a ViewModel which is instantiated in the View using XAML. For example, if the View is called "ExampleView" and the ViewModel is called "ExampleViewModel". The ViewModel is instantiated in the ExampleView by doing this...
<UserControl.Resources>
<local:ExampleViewModel x:Key="ViewModel" />
</UserControl.Resources>
With the following code behind to get/set the ViewModel from the View (normally this is only a get, but I tried the set to set the ViewModel after deserialization).
public ExampleViewModel ViewModel
{
get { return (ExampleViewModel)this.Resources["ViewModel"]; }
set
{
if (this.Resources["ViewModel"]!=value)
{
this.Resources["ViewModel"] = value;
}
}
}
This didn't work, but I figured the reason is that PropertyChanged wasn't being fired. So in ExampleViewModel I put in a method to refresh each of the Properties. For example ...
public void RefreshAllProperties()
{
NotifyPropertyChanged("Property1");
NotifyPropertyChanged("Property2");
...
}
where NotifyPropertyChanged is ...
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
While this doesn't pass the code smell test, I was trying to understand on my way to find something more elegant. However, I was surprised to find it didn't work.
I would prefer to keep the ViewModel instantiated in the XAML. Is there a best practice to re-instantiate the ViewModel after deserialization?
Modified follow on question
Any comments on moving the ViewModel creating into the constructor of the View class? Is this a better design pattern?
ExampleViewModel exampleViewModel;
public ExampleView()
{
InitializeComponent();
ExampleViewModel = new ExampleViewModel();
this.DataContext = ExampleViewModel;
}
public ExampleViewModel ViewModel
{
get { return exampleViewModel; }
set
{
if (exampleViewModel!=value)
{
exampleViewModel = value;
NotifyPropertyChanged();
}
}
}
I haven't seen a ViewModel defined in a ResourceDictionary before. I tend to initialize my ViewModels in the code-behind (I know you mentioned you wanted to keep it in XAML) since I can more directly control the page DataContext and when it ultimately updates (like after deserialization in your case). Modifying the ResourceDictionary at runtime seems like a dangerous approach and the ResourceDictionary in WPF does not implement INotifyPropertyChanged or any other change interface like INotifyCollectionChanged, meaning that it will not notify anything that one of its key-value pairs has changed in some way.
In summary: My answer is to not define your VM in a ResourceDictionary, but to manage it in your page code-behind where you can ensure the DataContext is appropriately updated when the VM or its state changes.
I'm relatively new to WPF and Behaviors.
I have this behavior, I need to execute DoSomething() every time I set IsRedundant in the ViewModel.
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if ture => set it to false, If false => set it to true). IsRedundant only used to raise the property changed event and for nothing else.
Is there a better way of achieving this ?
Any ideas ?
wpf
<i:Interaction.Behaviors>
<local:UIElementBehavior Redundant="{Binding IsRedundant, Mode=TwoWay}"/ >
</i:Interaction.Behaviors>
C#
class UIElementBehavior : Behavior<UIElement>
{
public static readonly DependencyProperty RedundantProperty = DependencyProperty.Register(
"Redundant",
typeof(bool),
typeof(UIElementBehavior),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, DoSomething));
public bool Redundant
{
get { return (bool)GetValue(RedundantProperty); }
set { SetValue(RedundantProperty, value); }
}
private static void DoSomething(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do something on the AssociatedObject
}
}
Each time I need to trigger DoSomething, I would need to change the value of the property and this is confusing (if true => set it to false, If false => set it to true)
The problem is that you are using binding. Binding required target to be dependency property. And those are special, their setters aren't called, so you have to use callback to get informed when their value is changed via binding.
Moreover there is internally a check if value is different, for performance reasons callback is not called if value is the same, so you must change it as you do already.
An alternative solution is to simply add event in the view model:
public class ViewModel: INotifyPropertyChanged
{
public EventHandler SomethingHappens;
// call this to tell something to listener if any (can be view or another view model)
public OnSomethingHappens() => SomethingHappens?.Invoke(this, EventArgs.Empty);
...
}
Now you can subscribe/unsubscribe in the view to/from this event and do something in the event handler. If you are purist, then refactor code from the view into reusable behavior.
Is it shorter? Nope. Is it more clear? Yes, compared to using bool and such "wonderful" code:
IsRedundant = false;
IsRedundant = true; // lol
I was using bool properties like you do to inform the view in the past.
Then I used events.
Now I use combination of both. Every view model already implements INotifyPropertyChanged so why not use it?
Think about IsRedundant as a state. It can be used not only to trigger some method, but also used by the view to run animations via data triggers, control visibility of dynamic layout, etc. So you need a normal bool property in view model.
The view then can subscribe to/unsubscribe from PropertyChanged and simply have to check:
if(e.PropertyName == nameof(ViewModel.IsRedudant)) { ... }
I'm looking for a simple way to call a method in my Main Window, but I want to call it from my View Model. Basically, I'm looking for some king of "this.parent" sort of thing to put in the View Model to reference the Main Window.
Or, if you want to check out the reason I want to do this and tell me another way to go about my problem:
I'm working with an app that constantly gets information fed to it. In the viewmodel, the information is processed. I want to make a notification every time a piece of information comes in that satisfies some qualification.
Initially, I had a dictionary in the viewmodel that stored info about that information, and I accessed that dictionary in the MainWindow so that I could make the window flash and send other notifications. But I was getting issues with the viewmodel's dictionary being continuously changed while I was accessing it in the MainWindow.
Sorry if this question sounds stupid. I just started with WPF two months ago, and didn't have a great background in programming even before that, either.
VM should "know" nothing of your View or Window, the way VM typically "communicates" with V in WPF/MVVM is by rasing events. That way VM remains ignorant of/decoupled from the V and since VM is already DataContext of V it's not hard to subscribe to VM's event.
Example:
VM:
public event EventHandler<NotificationEventArgs<string>> DoSomething;
...
Notify(DoSomething, new NotificationEventArgs<string>("Message"));
V:
var vm = DataContext as SomeViewModel; //Get VM from view's DataContext
if (vm == null) return; //Check if conversion succeeded
vm.DoSomething += DoSomething; // Subscribe to event
private void DoSomething(object sender, NotificationEventArgs<string> e)
{
// Code
}
first of all, it's not a stupid question. Most of MVVM starters came from winforms and it's normal to have the tendency to bring in your winforms practices and work on code behind. Now all you have to do is forget that and think MVVM.
Going back to your question, you have a dictionary that your VM is processing and you are accessing that dictionary from the view. Your view should not have any idea about your viewmodel except through binding.
Making a window flash when there are changes in the viewmodel sounds like an attached behavior to me. Here's a good read about attached behavior.
http://www.codeproject.com/Articles/28959/Introduction-to-Attached-Behaviors-in-WPF
To make it easier, I'll try to give you a very simple example that will somehow be relevant to your case.
Create an attached behavior class where you have an IEnumerable where in whenever you add something a messagebox will appear on the screen. Just change the messagebox code to whatever flashing animation you would like to do on notify.
public class FlashNotificationBehavior
{
public static readonly DependencyProperty FlashNotificationsProperty =
DependencyProperty.RegisterAttached(
"FlashNotifications",
typeof(IEnumerable),
typeof(FlashNotificationBehavior),
new UIPropertyMetadata(null, OnFlashNotificationsChange));
private static void OnFlashNotificationsChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var collection = e.NewValue as INotifyCollectionChanged;
collection.CollectionChanged += (sender, args) =>
{
if (args.Action == NotifyCollectionChangedAction.Add)
{
foreach (var items in args.NewItems)
MessageBox.Show(items.ToString());
}
};
}
public static IEnumerable GetFlashNotifications(DependencyObject d)
{
return (IEnumerable)d.GetValue(FlashNotificationsProperty);
}
public static void SetFlashNotifications(DependencyObject d, IEnumerable value)
{
d.SetValue(FlashNotificationsProperty, value);
}
}
In your viewmodel, you can create an ObservableCollection property, you need an observable collection so there is a collection changed event notification. I also added a command for adding so that you can test it.
public class MainViewModel : ViewModelBase
{
ObservableCollection<string> notifications;
public ObservableCollection<string> Notifications
{
get { return notifications; }
set
{
if (notifications != value)
{
notifications = value;
base.RaisePropertyChanged(() => this.Notifications);
}
}
}
public ICommand AddCommand
{
get
{
return new RelayCommand(() => this.Notifications.Add("Hello World"));
}
}
public MainViewModel()
{
this.Notifications = new ObservableCollection<string>();
}
}
And here's a view where you can bind it the Notifications proeprty from your view model.
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication7.ViewModel"
xmlns:local="clr-namespace:WpfApplication7"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<StackPanel>
<ListBox ItemsSource="{Binding Notifications}"
local:FlashNotificationBehavior.FlashNotifications="{Binding Notifications}"></ListBox>
<Button Command="{Binding AddCommand}" >Add Something</Button>
</StackPanel>
</Grid>
Everytime you add something in the ObservableCollection, you will get a messagebox notifying the user that something has been added to your collection.
I hope that I helped in your problem. Just tell me if you need some clarifications.
I'm a student learning C# with WPF using the MVVM pattern. Recently I have been working on a [art of my application (a custom splash screen) that should not be closed when I don't want it to.
I have been searching the web for a good way of doing this without code-behind. Unfortunately after days I still did not find a satisfying way.
Then I came to think of a way to do it myself, with help of just one line of code in the constructor of my view. It still makes my code testable and decouples the code from the View. The question is, is there a better way of doing what I'm trying to do:
My interface for my ViewModel
public interface IPreventCloseViewModel
{
bool PreventClose { get; set; }
}
The extension for the View
public static class PreventCloseViewModelExtension
{
/// <summary>
/// Use this extension method in the constructor of the view.
/// </summary>
/// <param name="element"></param>
public static void PreventCloseViewModel(this Window element)
{
var dataContext = element.DataContext as IDisposable;
if (dataContext is IPreventCloseViewModel)
{
element.Closing += delegate(object sender, CancelEventArgs args)
{
if (dataContext is IPreventCloseViewModel)
{
args.Cancel = (dataContext as IPreventCloseViewModel).PreventClose;
}
};
}
}
}
The code-behind for the View
public partial class SplashScreen
{
public SplashScreen()
{
InitializeComponent();
this.PreventCloseViewModel();
}
}
MVVM does not mean that you cannot use Code-Behind.
MVVM means that your application logic should not be tied to UI elements.
You can perfectly well handle events in code behind (such as Window.Closing), and "send messages" or execute methods in the ViewModel to react to that.
Here, you are not breaking MVVM by placing the event handler in code behind. You would be breaking MVVM if you were placing the logic that determines whether the application can be closed in code behind. That is a responsibility of the application logic, and the application logic lives in ViewModels, not Views.
I usually have a generic Shell class which subclasses Window and does something like:
public Shell()
{
InitializeComponent();
this.Closing += (s,e) =>
{
var canClose = Content as ICanClose;
if (canClose != null)
e.Cancel = !canClose.CanClose;
}
}
That way it does not matter what kind of view model you put in, if it implements the interface that will be taken into account.
Don't see much point in externalizing the logic, and it's fine in terms of the MVVM pattern.
I have a lot of existing business objects with many properties and collections inside which I want to bind the userinterface to. Using DependencyProperty or ObservableCollections inside these objects is not an option. As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this. As an extra I also don't know which UI controls bind to these objects and to what properties.
Here is a simplified code of what I tried to do by now:
public class Artikel
{
public int MyProperty {get;set;}
}
public partial class MainWindow : Window
{
public Artikel artikel
{
get { return (Artikel)GetValue(artikelProperty); }
set { SetValue(artikelProperty, value); }
}
public static readonly DependencyProperty artikelProperty =
DependencyProperty.Register("artikel", typeof(Artikel), typeof(MainWindow), new UIPropertyMetadata(new Artikel()));
public MainWindow()
{
InitializeComponent();
test.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
artikel.MyProperty += 1;
// What can I do at this point to update all bindings?
// What I know at this point is that control test or some of it's
// child controls bind to some property of artikel.
}
}
<Grid Name="test">
<TextBlock Text="{Binding Path=artikel.MyProperty}" />
</Grid>
This is, I tried to pack my object into a DependencyProperty and tried to call UpdateTarget on this, but didn't succeed.
What could I do to update the corresponding UI controls?
I hope I described my situation good enough.
Using INotifyPropertyChanged is a good alternative to DependencyProperties.
If you implement the interface you can raise the PropertyChanged event with null as parameter to notify the UI that all properties changed.
(I'm going to assume you can't add INotifyPropertyChanged to your business objects either, and that you don't want to add another "view of the data model" layer of wrapper objects a la MVVM.)
You can manually update bound properties from their data source by calling BindingExpression.UpdateTarget().
myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
To update all bindings on a control or window, you could use something like this:
using System.Windows.Media;
...
static void UpdateBindings(this DependencyObject obj)
{
for (var i=0; i<VisualTreeHelper.GetChildrenCount(obj); ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is TextBox)
{
var expression = (child as TextBox).GetBindingExpression(TextBox.TextProperty);
if (expression != null)
{
expression.UpdateTarget();
}
}
else if (...) { ... }
UpdateBindings(child);
}
}
If you're binding a diverse set of properties then rather than handling them individually as above, you could combine the above with this approach to enumerate all dependency properties on a control and then get any BindingExpression from each; but that relies on reflection which will not be particularly performant.
As a footnote, you can also use BindingExpression.UpdateSource() if you want to explicitly write back to the data source. Controls usually do this anyway when their value changes or when they lose focus, but you control this and do it by hand with {Binding Foo, UpdateSourceTrigger=Explicit}.
As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this.
You will find that the most straightforward and maintainable way to deal with this is to implement view model classes for each class you want to present in the UI. This is probably true if you can modify the underlying classes, and almost certainly true if you can't.
You don't need to be using dependency properties for this. Dependency properties are only necessary on the targets of binding, which is to say the controls in the UI. Your view model objects are the source; they need only implement INotifyPropertyChanged.
Yes, this means that you will need to build classes that contain a property for each property exposed in the UI, and that those classes will need to contain observable collections of child view models, and you'll have to instantiate and populate those classes and their collections at runtime.
This is generally not as big a deal as it sounds, and it may be even less of one in your case. The traditional way to build a view model that's bound to a data model is to build properties like this:
public string Foo
{
get { return _Model.Foo; }
set
{
if (value != _Model.Foo)
{
_Model.Foo = value;
OnPropertyChanged("Foo");
}
}
}
But if, as you've claimed, you know when the objects are being updated, and you just want to push the updates out to the UI, you can implement read-only properties, and when the underlying data model gets updated make the view model raise PropertyChanged with the PropertyName property of the event args set to null, which tells binding, "Every property on this object has changed; update all binding targets."