Memory leak in WPF app due to DelegateCommand - c#

I just finished desktop apps written in WPF and c# using MVVM pattern. In this app I used Delegate Command implementation to wrap the ICommands properties exposed in my ModelView. The problem is these DelegateCommands prevent my ModelView and View from being garbage collected after closing the view. So it stays larking until I terminate the whole application. I profile the application I find it’s all about delegatecommand that keeping the modelview in memory.
How could I avoid this situation and is this in nature of mvvm pattern, or it’s about my implantation of the pattern?. Thanks.
Edit: this is small but complete portion of how i implement MVVM pattern
First: CommandDelegte class
class DelegateCommand:ICommand
{
private Action<object> execute;
private Predicate<object> canExcute;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExcute = canExecute;
}
public bool CanExecute(object parameter)
{
if (this.canExcute != null)
{
return canExcute(parameter);
}
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Second: ModelView Class
public class ViewModel:DependencyObject, INotifyPropertyChanged
{
private DelegateCommand printCommand;
public ICommand PrintCommand
{
get
{
if (printCommand == null)
{
printCommand = new DelegateCommand(Print, CanExecutePrint);
}
return printCommand;
}
}
void Print(object obj)
{
Console.WriteLine("Print Command");
}
bool CanExecutePrint(object obj)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnProeprtyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Third: Window code behind
public MainWindow()
{
InitializeComponent();
base.DataContext = new ViewModel();
}
Forth: My XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.InputBindings>
<KeyBinding Key="P" Modifiers="Control" Command="{Binding Path=PrintCommand}"/>
</Window.InputBindings>
<StackPanel>
<Button Content="Print - Ctrl+P" Width="75" Height="75" Command="{Binding Path=PrintCommand}"/>
</StackPanel>

In your case, what contains a reference to what?
DelegateCommand contains a reference to ViewModel - its execute and canExecute properties contain references to a methods of the ViewModel instance.
ViewModel contains a reference to DelegateCommand - its PrintCommand property.
The view contains any number of references to the ViewModel.
The CommandManager contains a reference to DelegateCommand in its RequerySuggested event.
That last reference is a special case: CommandManager uses a WeakReference in its RequerySuggested event, so despite the fact that DelegateCommand registers for that event, it can still be garbage-collected.
Given all this, you shouldn't be having a problem. If the view gets disposed, neither the ViewModel nor the DelegateCommand should be reachable.
You say you've profiled the application and DelegateCommand is holding a reference to ViewModel. It seems to me that the logical next question should be: what's holding a reference to DelegateCommand? It shouldn't be CommandManager. Do you have something else in your application that's referencing your commands?

I think that in this code there is a circular reference which is causing the ViewModel to never be garbage collected.
I know this is an old question, but I will point out that some implementations of DelegateCommand or RelayCommand hold a WeakReference to the action. Your use of the DelegateCommand here is typical, but unfortunately will cause memory leaks with this implementation because when the ViewModel's method is passed into the DelegateCommand's constructor, a reference to the class containing that method is automatically captured by the delegate.
If you implemented IDispose on your ViewModel and cleared the references to the DelegateCommands explicitly in Dispose, then you could continue to use this implementation. Your view that's constructing your ViewModel would also have to Dipose of it, however.

After reading this post, I then came across a web page that had some relating information. It is a page on CodePlex called Memory Leak caused by DelegateCommand.CanExecuteChanged Event.
Reported by : huetter
Updated by : dschenkelman
When profiling my application I noticed that plenty of EventHandlers
had never been deregistered from DelegateCommand's
CanExecuteChanged-Event. So those EventHandlers were never been
garbage-collector, which caused a severe memory leak.
As registering CanExecuteChanged-EventHandles is done outside
application code scope I had expected them to be deregistered
automatically as well. At this point I thought this might as well be
a ThirdParty WPF control issue, but digging further I read a blog
post stating that "WPF expects the ICommand.CanExecuteChanged-Event
to apply WeakReferences for EventHandlers". I had a look into
RoutedCommand, and noticed it uses WeakReferences as well.
I adapted DelegateCommand to use an implementation similar to
RoutedCommand's CanExecuteChanged-Event, and the memory leak was
gone. The same is true for CompositeCommand.
Closed Nov 3, 2009 at 6:28 PM by This issue was fixed in the
Prism-v2.1 release, so the Workitem is closed now. Prism 2.1 can be
downloaded from here:
http://www.microsoft.com/downloads/details.aspx?FamilyID=387c7a59-b217-4318-ad1b-cbc2ea453f40&displaylang=en

Related

Delegate Command - WPF Button Binding Not Working [duplicate]

I'm having some difficulty with Context Menu commands on my View Model.
I'm implementing the ICommand interface for each command within the View Model, then creating a ContextMenu within the resources of the View (MainWindow), and using a CommandReference from the MVVMToolkit to access the current DataContext (ViewModel) Commands.
When I debug the application, it appears that the CanExecute method on the command is not being called except at the creation of the window, therefore my Context MenuItems are not being enabled or disabled as I would have expected.
I've cooked up a simple sample (attached here) which is indicative of my actual application and summarised below. Any help would be greatly appreciated!
This is the ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
The DisplayValueCommand is such:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
And finally, the view is defined in Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
To complete Will's answer, here's a "standard" implementation of the CanExecuteChanged event :
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(from Josh Smith's RelayCommand class)
By the way, you should probably consider using RelayCommand or DelegateCommand : you'll quickly get tired of creating new command classes for each and every command of you ViewModels...
You have to keep track of when the status of CanExecute has changed and fire the ICommand.CanExecuteChanged event.
Also, you might find that it doesn't always work, and in these cases a call to CommandManager.InvalidateRequerySuggested() is required to kick the command manager in the ass.
If you find that this takes too long, check out the answer to this question.
Thank you for the speedy replies. This approach does work if you are binding the commands to a standard Button in the Window (which has access to the View Model via its DataContext), for example; CanExecute is shown to be called quite frequently when using the CommandManager as you suggest on ICommand implementing classes or by using RelayCommand and DelegateCommand.
However, binding the same commands via a CommandReference in the ContextMenu
do not act in the same way.
In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
However, binding the same commands via a CommandReference in the
ContextMenu do not act in the same way.
That's a bug in CommandReference implementation. It follows from these two points:
It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged
The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.
The fix is to hold a strong ref to the handler in CommandReference:
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
In order for the same behaviour, I must also include the EventHandler
from Josh Smith's RelayCommand, within CommandReference, but in doing
so I must comment out some code from within the OnCommandChanged
Method. I'm not entirely sure why it is there, perhaps it is
preventing event memory leaks (at a guess!)?
Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.
UPDATE: submitted an issue on CodePlex
An easier solution for me, was to set the CommandTarget on the MenuItem.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
More info: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html

Using ApplicationCommands in WPF PRISM

After studying several Q&A on stackoverflow, some tutorials and of course the official documentation, I trying to use ApplicationCommands in my WPF Prism MVVM application.
My current approach
After trying different solutions I found out, I ended up with following constellation:
I am using the AttachCommandBindingsBehavior class mentioned in this answer, which will be used like this in the view:
<UserControl>
<i:Interaction.Behaviors>
<localBehaviors:AttachCommandBindingsBehavior CommandBindings="{Binding CommandBindings}"/>
</i:Interaction.Behaviors>
</UserControl>
MyViewModel contains a CommandBindingCollection property, which will be populated in the constructor:
public CommandBindingCollection CommandBindings { get; } = new CommandBindingCollection();
public MyViewModel()
{
this.CommandBindings.AddRange(new[]
{
new CommandBinding(ApplicationCommands.Save, this.Save, this.CanSave),
new CommandBinding(ApplicationCommands.Open, this.Open)
});
}
The UserControl MyView contains two buttons:
<Button Command="ApplicationCommands.Open" Content="Open" />
<Button Command="ApplicationCommands.Save" Content="Save" />
My first question at this point is: Are the Executed() and CanExecute() methods already bound to the Command-DependencyProperty of the Button? Since it does not work, what did I forgot or made wrong?
My second question is: How can I trigger the CanExecute of the Command the Button is bound to? The actual use-case: MyViewModel.CanSave() returns true, when the user successfully executed the MyViewModel.Open() method. Usually, I would call an DelegateCommand.RaiseCanExecuteChanged(), but calling ApplicationCommands.Save.RaiseCanExecuteChanged() does not execute MyViewModel.CanSave().
Feel free to ask for more information. I will really appreciate your answers. Thank you!
Since it does not work, what did I forgot or made wrong?
The CommandBindings property on your behavior is an ObservableCollection<CommandBinding>, but you're binding it to a CommandBindingCollection in your view model. Change your view model's property to a ObservableCollection<CommandBinding>.
There are also some problems with the AttachCommandBindingsBehavior you linked to. I'm not sure why the answer was accepted, because it's actually quite broken. The tweaked version below should work, though.
public class AttachCommandBindingsBehavior : Behavior<FrameworkElement>
{
public ObservableCollection<CommandBinding> CommandBindings
{
get => (ObservableCollection<CommandBinding>)GetValue(CommandBindingsProperty);
set => SetValue(CommandBindingsProperty, value);
}
public static readonly DependencyProperty CommandBindingsProperty =
DependencyProperty.Register(
"CommandBindings",
typeof(ObservableCollection<CommandBinding>),
typeof(AttachCommandBindingsBehavior),
new PropertyMetadata(null, OnCommandBindingsChanged));
private static void OnCommandBindingsChanged(
DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
var b = sender as AttachCommandBindingsBehavior;
if (b == null)
return;
var oldBindings = e.OldValue as ObservableCollection<CommandBinding>;
if (oldBindings != null)
oldBindings.CollectionChanged -= b.OnCommandBindingsCollectionChanged;
var newBindings = e.NewValue as ObservableCollection<CommandBinding>;
if (newBindings != null)
newBindings.CollectionChanged += b.OnCommandBindingsCollectionChanged;
b.UpdateCommandBindings();
}
protected override void OnAttached()
{
base.OnAttached();
UpdateCommandBindings();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.CommandBindings.Clear();
}
private void UpdateCommandBindings()
{
if (AssociatedObject == null)
return;
AssociatedObject.CommandBindings.Clear();
if (CommandBindings != null)
AssociatedObject.CommandBindings.AddRange(CommandBindings);
CommandManager.InvalidateRequerySuggested();
}
private void OnCommandBindingsCollectionChanged(
object sender,
NotifyCollectionChangedEventArgs e)
{
UpdateCommandBindings();
}
}
How can I trigger the CanExecute of the Command the Button is bound to?
You can suggest that WPF's routed command system reevaluate all commands by calling CommandManager.InvalidateRequerySuggested(). The actual reevaluation will occur asynchronously at the Background dispatcher priority. This is the explicit way to do it, but you should know that this already happens implicitly upon certain actions, like focus changes and mouse/keyboard button-up events. The WPF developers tried to make automatic command requerying as seamless as possible, so it "just works" most of the time.
The actual use-case: MyViewModel.CanSave() returns true [...]
Just to be clear, as a CanExecuteRoutedEventHandler, your CanSave method should return void and set CanExecute to true on the event argument.

WPF binding to a global variable to update UI

Let's say I have two windows and a trayicon context menu. Each of the windows has a togglebutton and the context menu has a checkable menu item. All three controls are designed to display and toggle the status of the same value.
How can I bind, in this case IsChecked, of the three controls to a single global variable that when one of the controls is checked/unchecked that the other controls will update accordingly? Should I just do an invoke or is there an MVVM solution? I'm new to WPF so I'm not sure the best/most correct way to accomplish this.
Lets say you have WindowA, WindowB, ..., WindowN and assume that they all are of different type.
Create a class, lets say CommonState, that encapsulates all common properties, commands, etc. and implements INotifyPropertyChanged
public class CommonState : INotifyPropertyChanged
{
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
if (value != _isChecked)
{
_isChecked = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then declare an interface:
public interface ICommonStateWindow
{
CommonState { get; set; }
}
Make each window implement this interface:
public partial class WindowA : Window, ICommonState
{
public WindowA()
{
InitializeComponent();
}
// This property will be injected, do not re-assign
public CommonState CommonState { get; set; }
}
Inject the common state in each window prior to showing it, for example:
public partial class App : Application
{
private CommonState _state;
protected override void OnStartup(StartupEventArgs e)
{
_state = new CommonState() {IsChecked = true};
var wndA = new WindowA() { CommonState = _state };
var wndB = new WindowB() { CommonState = _state };
wndA.Show();
wndB.Show();
}
}
Remember to keep at least one reference to the created CommonState in some long living object (like App or the main window), so it does not get garbage collected at some point.
In the XAML you should bind using a RelativeSource, so that each new type of window you create can have its own independent ViewModel (DataContext):
<Window x:Class="Example.WindowA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowA" Height="300" Width="300">
<Grid>
<CheckBox IsChecked="{Binding CommonState.IsChecked, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
The example, I've demonstrated is not the only way and I won't say "the best", but it solves the following problems:
Encapsulates the common (shared) state
Synchronizes the state between different instances (or types) of windows
Allows the CommonState to be extended independently of the window implementation (only the XAML needs to be updated)
Another possible solution is to register a singleton instance of the CommonState into a statically exposed inversion of control container (IoC) and make each concrete window's ViewModel obtain an instance to it. In this way you will avoid the injection step. This would be an overkill for small projects
I anyone is trying to run the above code, remember to remove StartupUri="MainWindow.xaml" from App.xaml
You can add to your codebehind bool IsChecked property and use it for all component you want. And you can change it components' event method to true or false.

C# WPF - Write to Listview from any class

I have a UI in the MainWindow.xaml and in the window I have a ListView which I'd like to use for all logging.
How do I write to this ListView from any class without having to pass the window object the whole way through the system?
I've tried making a method in the MainWindow code behind called Log(string) and then accessing it from another class like MainWindow.Log("some text") but no joy!
Perhaps I'm just not entirely grasping the whole object oriented part of this problem :(
Help much appreciated!
Cheers,
Dave
You can implement simple binding in the following way:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ListView ItemsSource="{Binding Model.Items}" />
</Grid>
</Window>
Then add your model class:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> items = new ObservableCollection<string>();
public ObservableCollection<string> Items
{
get { return items; }
set
{
items = value;
OnPropertyChanged("Items");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And use this model i.e. in your code behind. This is little hacky and should be moved to some Controller or something like that, but this is out of the scope of this question.
public partial class MainWindow : Window
{
public MainWindowViewModel Model { get; set; }
public MainWindow()
{
Model = new MainWindowViewModel();
InitializeComponent();
Model.Items.Add("one");
Model.Items.Add("two");
}
}
So we have View in xaml that is bounded to its "code behind" property called Model. This is why we have 'DataContext' property set in our . Then, we have our ViewModel that holds data for our View. In MVVM pattern we call it ViewModel.
Of course you could also implement some ViewModel base and move there INotifyPropertyChange implementation but it's up to you. You can implement also implement MVVM pattern in any other way but core mechanisms are the same.
You are using WPF ! So do not use any UI control type instances inside your back end language.
Use data-binding, which WPF is mainly tailored to, to bind your list view to the instance of a back end class. And pass all around of your back-end an instance of that class .
The concrete basic implementation is not suitable for SO contest, but basic idea may look like
class Log {
.....
List<string> logData;
public List<string> LogData { //PROPERTY ACTUALLY BOUND TO LIST VIEW UI
get {
return logData;
}
}
public void AddLog(string s) {
logData.Add(s);
NotifyPropertyChanged(.. LogData ..);
}
}
after in some shared space of your code is created Log log.
Anyone who will execute AddLog, will add the string to the lost of log, and raise an event to update UI.
For data binding examples may look on:
A Very Simple Example of Data Binding in WPF
Data Binding Overview
or just google for simpler or more suitable examples to you.
You can access the MainWindow instance from everywhere in your application by Application.Current.MainWindow. It returns an object of type window so you have it to your main window class. Usually MainWindow. All together:
(Application.Current.MainWindow as MainWindow).Log("some text").

Designs of a WPF MVVM Application

I have been doing tons of research using MVVM (Model View ViewModel) with WPF. I am developing a desktop application. This application consists of a main window. This main window has some buttons which do something. Also, there is a button that opens a OpenFileDialog box.
Currently, this is my ViewModel to which the main window binds to:
MainWindowPresenter Class
namespace BMSVM_Simulator.ViewModel
{
class MainWindowPresenter : ObservableObject
{
private bool logLoaded; // true if a log is currently loaded, false otherwise
public MainWindowPresenter()
{
logLoaded = true;
}
public ICommand load_data_button_pressed
{
get { return new DelegateCommand(doLoadData); }
}
private void doLoadData()
{
// DO LOAD DATA COMMANDS
}
public ICommand exit_button_pressed
{
get { return new DelegateCommand(doExit); }
}
private void doExit()
{
// DO EXIT COMMANDS
}
}
}
QUESTION 1: I am concerned that this is the "wrong" implementation. Is it correct (per MVVM) for each button to have a property of type ICommand and then a corresponding method implementing the functionality? A main window with a lot of buttons would have a very large ViewModel class, no?
QUESTION 2: If one of the buttons was a File->Open File button. So, in that case it would open up a new OpenFileDialog window. Would this be done in the same way I previously done it above (i.e. have a public ICommand open_file_dialog_button_pressed property and a corresponding public void doOpenFileDialog() method? This seems like I am mixing the "view" of the open file dialog into the ViewModel, although the view is already defined by the built in wpf OpenFileDialog class.
QUESTION 3: Is it true that each "view" of our application should have only a single "presenter" class (which is part of the ViewModel) to which that view binds to? In the example above, my main window view binds to only the MainWindowPresenter class. If I were to make another view (say a graph generated with Microsoft's Dynamic Data Display library in it's own popout window), I would need an additional "presenter" class in my ViewModel, correct?
Thank you very much!
Rich
For reference, I've included these classes, but they may not be useful:
DelegateCommand Class
namespace BMSVM_Simulator.ViewModel
{
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
}
}
ObservableObject Class
namespace BMSVM_Simulator.ViewModel
{
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//basic ViewModelBase
internal void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
}
}
1) Yes that's correct. You need to create a command property for each command. But thanks to your relay command you don't need to implement it directly. To prevent your ViewModel from busting i would recommend to move all commands into a separate CommandsViewModel serving as command source. Your View then binds to it.
2) Opening the Dialog can be achieved in XAML via routed commands using the CommandBinding property. So the tasks remains in the view. You basically try to avoid the dependency on any view related object. .NET provides some ready to use commands for common purposes (MSDN - ApplicationCommands)
3) You can share ViewModels among Views of course. That's one reason you structure your implementation into Model View ViewModel to be independent from changes and for reusability. It can become critical when more than one view is updating the same source simultaneously.

Categories