WPF MVVM cancel Window.Closing event - wpf

In WPF application together with MVVMLight Toolkit, I would like to see your opinion, what is the best way to implement if I need to Cancel the Window Close event.
In Window.Closing event I can set the e.Cancel = true, which prevents closing the form. To identify if the Close is allowed, or should be prevented is in the ViewModel context.
One solution could be if I define an Application variable, and I can query this in the normal event handler in view code behind?
thanks

With MVVM Light you got EventToCommand:
So you could in xaml wire up the closing event to the VM.
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding ClosingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the VM:
public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }
ctor() {
ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}
If you do not want to pass CancelEventArgs to the VM:
You could always take the similar approach with a Behavior and just use a simple bool from the VM(bind this bool to the Behavior) to indicate the closing event should be cancelled.
Update:
Download Link for following example
To do this with a Behavior you could just have a Behavior such as:
internal class CancelCloseWindowBehavior : Behavior<Window> {
public static readonly DependencyProperty CancelCloseProperty =
DependencyProperty.Register("CancelClose", typeof(bool),
typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));
public bool CancelClose {
get { return (bool) GetValue(CancelCloseProperty); }
set { SetValue(CancelCloseProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
}
}
Now in xaml:
<i:Interaction.Behaviors>
<local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>
Where CancelClose is a bool property from the VM which indicates if the Closing event should be cancelled or not. In the attached example I have a Button to toggle this bool from the VM that should let you test the Behavior

You could to control this using Messages, for instance:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<CloseApplicationMessage>(this, m => Close());
Loaded += MainWindowLoaded;
Closing += MainWindowClosing;
}
private void MainWindowClosing(object sender, CancelEventArgs e)
{
//Ask for saving
var closingMessage = new ClosingApplicationMessage();
Messenger.Default.Send(closingMessage);
if (closingMessage.Cancel)
e.Cancel = true;
}
...
The mvvm message:
public class ClosingApplicationMessage
{
public bool Cancel { get; set; }
}
In this way, in any place you are listening to the ClosingApplicationMessage, you can control when the application is going to close, and may to cancel it.
Hope this helps...

Related

WPF ProgressBar not updating

This is casual and prototype code, hence me trying what I think should work, googling around if it doesn't, then asking here after perusing similar questions.
I have the following markup in my Shell view:
<StatusBarItem Grid.Column="0">
<TextBlock Text="{Binding StatusMessage}" />
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<ProgressBar Value="{Binding StatusProgress}" Minimum="0" Maximum="100" Height="16" Width="198" />
</StatusBarItem>
Then in ShellViewModel I have the following two properties and an event handler:
private string _statusMessage;
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
private double _statusProgress;
public double StatusProgress
{
get => _statusProgress;
set => SetProperty(ref _statusProgress, value);
}
private void OnFileTransferStatusChanged(object sender, FileTransferStatusEventArgs fileTransferStatusEventArgs)
{
StatusMessage = fileTransferStatusEventArgs.RelativePath;
StatusProgress = fileTransferStatusEventArgs.Progress;
}
The event is raised periodically, i.e. every n iterations, from a file download helper class.
Now the strange thing is this, when the event handler updates the vm properties, on the Shell view, the TextBlock bound to StatusMessage updates and displays correctly, but the ProgressBar bound to StatusProgress does not, and remains blank. If I put a break-point in the event handler, I can see the StatusProgress property being properly updated in various values from 0 to 100, yet this does not reflect on the ProgressBar.
The idea of the event handler executing on another thread, which often causes UI update problems, occurred to me, but why is one UI element updating properly and the other not?
NOTE: I have been monumentally stupid and not tested the ProgressBar statically, i.e. just set the viewmodel's StatusProgress to a value and get the shell window to display, without going through the download loop. If I do this, the progress bar displays a length that more or less corresponds to its Value property. None of the layout change suggestions made in comments or answers changes this. Statically it is always visible and always displays a value.
EXAMPLE: I created a small example that believe duplicates the problem. In the example the progress bar doesn't update until the waited on task has completed, and I believe this is the case with my main question, but it was a long download, and I didn't wait for it to complete before noticing the progress bar wasn't updating.
Here is the StatusBar in `MainWindow.xaml:
<StatusBar DockPanel.Dock="Bottom" Height="20">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="2">
<ProgressBar Value="{Binding StatusProgress}" Maximum="100" Minimum="0" Height="16" Width="198" />
</StatusBarItem>
</StatusBar>
With the code behind in MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.Download();
}
And the code in the MainWindowViewModel:
private string _statusMessage = "Downloading something";
public string StatusMessage
{
get => _statusMessage;
set
{
if (value == _statusMessage) return;
_statusMessage = value;
OnPropertyChanged();
}
}
private int _statusProgress;
public int StatusProgress
{
get => _statusProgress;
set
{
if (value == _statusProgress) return;
_statusProgress = value;
OnPropertyChanged();
}
}
public void Download()
{
var dl = new FileDownloader();
dl.ProgressChanged += (sender, args) =>
{
StatusProgress = args.Progress;
};
dl.Download();
}
And finally the code for FileDownloader:
public class ProgressChangedEventArgs
{
public int Progress { get; set; }
}
public class FileDownloader
{
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
public void Download()
{
for (var i = 0; i < 100; i++)
{
ProgressChanged?.Invoke(this, new ProgressChangedEventArgs{Progress = i});
Thread.Sleep(200);
}
}
}
In the example, the progress bar remains blank, until FileDownloader finishes its loop, and then suddenly the progress bar shows full progress, i.e. complete.
What's happening
Anything that is not about UI should be done in tasks, because, if not, you're blocking the UI thread and the UI.
In your case, the download was happening on you UI thread, the latter was waiting for the download to finish before updating your UI.
Solution
You need to do two things to solve your problem:
remove the work from the UI thread.
make sure the work can communicate with you UI thread.
So, first, start the download work as a Task like this:
private ICommand _startDownloadCommand;
public ICommand StartDownloadCommand
{
get
{
return _startDownloadCommand ?? (_startDownloadCommand = new DelegateCommand(
s => { Task.Run(() => Download()); },
s => true));
}
}
and connect the button to the command like this:
<Button Command="{Binding StartDownloadCommand}" Content="Start download" Height="20"/>
Then have you download method as such:
public void Download()
{
Application.Current.Dispatcher.Invoke(() => { StatusMessage = "download started"; });
var dl = new FileDownloader();
dl.ProgressChanged += (sender, args) =>
{
Application.Current.Dispatcher.Invoke(() => { StatusProgress = args.Progress; });
};
dl.Download();
Application.Current.Dispatcher.Invoke(() => { StatusMessage = "download DONE"; });
}
The dispatch will have your property (on UI thread) updated from a non UI thread.
And yet, the DelegateCommand helper class:
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute)
: this(execute, null) {}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
Remarks
In order to implement the MVVM pattern I had this code behind:
public partial class MainWindow : IView
{
public IViewModel ViewModel
{
get { return (IViewModel)DataContext; }
set { DataContext = value; }
}
public MainWindow()
{
DataContext = new MainWindowViewModel();
}
}
public interface IViewModel {}
public interface IView {}
and this View:
<Window x:Class="WpfApp1.MainWindow"
d:DataContext="{d:DesignInstance local:MainWindowViewModel,
IsDesignTimeCreatable=True}"
xmlns:local="clr-namespace:WpfApp1"
and this ViewModel:
public class MainWindowViewModel: INotifyPropertyChanged, IViewModel
This happens, because StatusBarItem default style sets its HorizontalContentAlignment to Left, which leads ProgressBar to get only a small amount of space horizontally.
You can make the ProgressBar to fill the StatusBarItem completely by setting StatusBarItem's HorizontalContentAlignment to Stretch or you can set the Width of the ProgressBar.
ProgressBar is a DispatcherObject, and DispatcherObject can be only accessed by the Dispatcher it is associated with.
If I understand your question well your OnFileTransferStatusChanged is being triggered on a background thread, so since you're not accessing controls using a Dispatcher (or from the UI thread) you're not guaranteed that the code will work.
The problem is that binding from a non-UI thread usually works until it doesn't - e.g. on a non-dev machine.
like first answer
be sure to be on the main UI thread, because OnFileTransferStatusChanged is on another thread. use this in your event
Application.Current.Dispatcher.Invoke(prio, (ThreadStart)(() =>
{
StatusMessage = fileTransferStatusEventArgs.RelativePath;
StatusProgress = fileTransferStatusEventArgs.Progress;
}));
I made few changes to your sample as your file downloaded is working on UI thread and application just freezes you can see it by changing focus to other application and trying to get back - window will not appear nor update.
changes:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() => ViewModel.Download());
}
forces download to execute in new thread.
public MainWindow()
{
InitializeComponent();
DataContext = ViewModel = new MainWindowViewModel();
}
public MainWindowViewModel ViewModel { get; }
removed cast and access to UI thread only property DataContext.
Now I can see progress bar filling up.
You can't see any changes because your Main thread AKA UI Thread is busy Sleeping
and it does not have time to update your UI
Let Task handle your lengthy job and Main thread for Updating UI
Wrap your code inside Task and you can see your progress bar progressing.
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
_viewModel.Download();
});
//_viewModel.Download(); //this will run on UI Thread
}

How to find whether button single clicked or double clicked while executing custom commands for button

My Requirement: I want to set custom command for my WPF button, inside the custom command execution I want to know whether the command executed by mouse single click or double click. Also when the customCommand's CanExecute returns false, I want the button to go on disable state. please refer below for more details.
Description:
Hi, In WPF I have set custom command for my button. When I click the button(For both single click and double click) the command gets executed. Inside the custom command I want to handle a separate action for single click and double click. Is it possible to find whetehr button single clicked or double this inside commands?? I use .Net 4.0, c#4.0
Note : I referred this How to bind a command in WPF to a double click event handler of a control? but I faced a limitation here.
Limitation:
When I set the custom command for my button then on CustomCommand CanExcute returns false the button goes to disable state. but As per the above Suggestion, by setting the command to mouse binding and setting the mouse binding to button works but when CanExecute returns false, the button doesnt goes to disable state. How to overcome this
public CustomCommand: ICommandd
{
public bool CanExecute(object parameter)
{
//arbitrary logic
}
public void Execute(object parameter)
{
if(MouseSingleClick)
{
perform ActionA;
}
if(MouseDoubleClick)
{
PerformActionB;
}
}
}
Thanks in Advance.
I was able to use this and tweak it to use it in MVVM friendly way.
I am giving a working example using Cinch framework.
I hoe this helps you give the idea to get going.
MyViewModel
public class MyViewModel : INotifyPropertyChanged
{
private static DispatcherTimer myClickWaitTimer =
new DispatcherTimer (
new TimeSpan (0, 0, 0, 0, 150),
DispatcherPriority.Background,
mouseWaitTimer_Tick,
Dispatcher.CurrentDispatcher);
private static void mouseWaitTimer_Tick (object sender, EventArgs e)
{
myClickWaitTimer.Stop ();
Debug.WriteLine ("Single Click Executed");//PerformActionA
}
public ICommand CinchSingleClickCommand { get; private set; }
public ICommand CinchDoubleClickCommand { get; private set; }
public MyViewModel ()
{
CinchSingleClickCommand = new SimpleCommand<object, EventToCommandArgs> (CanExecuteSingleCinch, ExecuteSingleCinch);
CinchDoubleClickCommand = new SimpleCommand<object, EventToCommandArgs> (CanExecuteDoubleCinch, ExecuteDoubleCinch);
myClickWaitTimer.Stop ();
}
private void ExecuteDoubleCinch (EventToCommandArgs obj)
{
if (obj.EventArgs is MouseEventArgs)
{
myClickWaitTimer.Stop ();
Debug.WriteLine ("Double Click Executed");//PerformActionB
var mouseEvent = obj.EventArgs as MouseEventArgs;
mouseEvent.Handled = true;
}
}
private bool CanExecuteDoubleCinch (object arg)
{
return true;
}
private void ExecuteSingleCinch (EventToCommandArgs obj)
{
if (!(obj.EventArgs is MouseEventArgs))
{
myClickWaitTimer.Start ();
var mouseEvent = obj.EventArgs as RoutedEventArgs;
mouseEvent.Handled = true;
}
}
private bool CanExecuteSingleCinch (object arg)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged (string propertyName)
{
var pc = PropertyChanged;
if (pc != null)
pc (this, new PropertyChangedEventArgs (propertyName));
}
}
You can play with the TimeSpan constructor to set how much delay do you want to keep between the single click and the double click.
The View
<Window x:Class="DataGridTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cinch="clr-namespace:Cinch;assembly=Cinch.WPF"
Title="MainWindow"
Height="350"
Width="525">
<DockPanel>
<Button x:Name="button"
Content="Test">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cinch:EventToCommandTrigger Command="{Binding CinchDoubleClickCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="Click">
<cinch:EventToCommandTrigger Command="{Binding CinchSingleClickCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DockPanel>
</Window>
The Code behind for the view
public partial class MainWindow : Window
{
public MainWindow ()
{
InitializeComponent ();
this.DataContext = new MyViewModel ();
}
}
I used Nuget Package Manager to pull the required dll's for Cinch, System.Windows.Interactivity and Microsoft.Expression.Interactions
The confusion is Button control's default click event shadows over other events like double click, mouse down etc. So May be using a Label is a good idea. You can camouflage it as a button and then use MouseDown and MouseDoubleClick events of the label to do your two different tasks. Use a timer to differentiate single click. Following link shows more detail
double click/ single click

How to set focus on AND SELECT ALL of an initial text box (MVVM-Style)?

I have a simple WPF page with one text box field that my client wants highlighted when the page shows up. In code behind, it would be three lines, but I'm sogging through MVVM (which I'm starting to think is a little over-rated). I've tried so many different variants of behaviors and global events and FocusManager.FocusedElement, but nothing I do will do this.
Ultimately the most of the code I've been using calls these two lines:
Keyboard.Focus(textBox);
textBox.SelectAll();
But no matter where I put these lines the text box is only focused; no text is selected. I have never had this much trouble with something so simple. I've been hitting my head against the internets for two hours. Does anyone know how to do this?
Again, all I want to do is have the text box focus and it's text all selected when the page is navigated to. Please help!
"Focus" and "Select All Text from a TextBox" is a View-specific concern.
Put that in code Behind. It does not break the MVVM separation at all.
public void WhateverControl_Loaded(stuff)
{
Keyboard.Focus(textBox);
textBox.SelectAll();
}
If you need to do it in response to a specific application/business logic. Create an Attached Property.
Or:
have your View resolve the ViewModel by:
this.DataContext as MyViewModel;
then create some event in the ViewModel to which you can hook:
public class MyViewModel
{
public Action INeedToFocusStuff {get;set;}
public void SomeLogic()
{
if (SomeCondition)
INeedToFocusStuff();
}
}
then hook it up in the View:
public void Window_Loaded(Or whatever)
{
var vm = this.DataContext as MyViewModel;
vm.INeedToFocusStuff += FocusMyStuff;
}
public void FocusMyStuff()
{
WhateverTextBox.Focus();
}
See how this simple abstraction keeps View related stuff in the View and ViewModel related stuff in the ViewModel, while allowing them to interact. Keep it Simple. You don't need NASA's servers for a WPF app.
And no MVVM is not overrated, MVVM is extremely helpful and I would say even necessary. You'll quickly realize this as soon as you begin working with ItemsControls such as ListBoxes or DataGrids.
Here are some workthroughs:
Use Interaction.Behaviors
You can install the NuGet package named Microsoft.Xaml.Behaviors.Wpf, and write your own Behavior:
using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
public class AutoSelectAllBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
}
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
if (AssociatedObject is TextBox box)
box.SelectAll();
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
}
}
and attach this behavior to the TextBox in the xaml:
<!-- xmlns:i="http://schemas.microsoft.com/xaml/behaviors" -->
<TextBox>
<i:Interaction.Behaviors>
<br:AutoSelectAllBehavior />
</i:Interaction.Behaviors>
</TextBox>
Use Interaction.Triggers
This is in the same package as mentioned in the last section. This special can be considered to let you be able to bind UIElement events to your ViewModel.
In your ViewModel, suppose you have an ICommand relay command (You may also need Microsoft.Toolkit.MVVM so that you can use some handy relay commands):
public ICommand SelectAllCommand { get; }
public ViewModel()
{
SelectAllCommand = new RelayCommand<TextBox>(box => box.SelectAll());
}
and then attach this command to the TextBox by setting the triggers:
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding SelectAllCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=TextBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Use Attached Property
You can also use attached property (write your own class derived from TextBox and use dependency property is quite similar):
using System.Windows;
using System.Windows.Controls;
public class TextBoxProperties
{
public static bool GetAutoSelectAll(DependencyObject obj)
{
return (bool)obj.GetValue(AutoSelectAllProperty);
}
public static void SetAutoSelectAll(DependencyObject obj, bool value)
{
obj.SetValue(AutoSelectAllProperty, value);
}
public static readonly DependencyProperty AutoSelectAllProperty =
DependencyProperty.RegisterAttached("AutoSelectAll", typeof(bool), typeof(TextBoxProperties), new PropertyMetadata(false, TextBoxProperties_PropertyChanged));
private static void TextBoxProperties_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (d is TextBox box)
{
box.GotFocus += TextBox_GotFocus;
}
}
}
private static void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
var box = sender as TextBox;
box.SelectAll();
}
}
Then you can use it like:
<!-- xmlns:ap="..." -->
<TextBox ap:TextBoxProperties.AutoSelectAll="True" />

When does the ui detach from commands?

I'm really scratching my head with this one. I have a mainwindow which opens a dialog. After the dialog closes, the CanExecute method on commands bound in the dialog are still executing. This is causing some serious problems in my application.
Example:
MainWindow has a button with a click handler. This is the click event handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
DialogWindow window = new DialogWindow();
window.ShowDialog();
}
In the dialog I bind an items control to a static resource in the dialog window, and each item in the list has a command:
<Window.Resources>
<Collections:ArrayList x:Key="itemsSource">
<local:ItemViewModel Description="A"></local:ItemViewModel>
<local:ItemViewModel Description="B"></local:ItemViewModel>
<local:ItemViewModel Description="C"></local:ItemViewModel>
</Collections:ArrayList>
<DataTemplate DataType="{x:Type local:ItemViewModel}">
<Button Grid.Column="1" Command="{Binding Path=CommandClickMe}" Content="{Binding Path=Description}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
</Button>
</DataTemplate>
</Window.Resources>
<Grid>
<ToolBar ItemsSource="{StaticResource itemsSource}"></ToolBar>
</Grid>
This is the viewmodel:
public class ItemViewModel
{
private RelayWpfCommand<object> _commandClickMe;
public RelayWpfCommand<object> CommandClickMe
{
get
{
if (_commandClickMe == null)
_commandClickMe = new RelayWpfCommand<object>(obj => System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
return _commandClickMe;
}
}
private bool CanClickMe()
{
return true;
}
public string Description { get; set; }
And this is the DelegateCommand implementation:
public class RelayWpfCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public RelayWpfCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
/// <summary>
/// Forces a notification that the CanExecute state has changed
/// </summary>
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
public bool CanExecute(T parameter)
{
return _canExecute(parameter);
}
public void Execute(T parameter)
{
_execute(parameter);
}
bool ICommand.CanExecute(object parameter)
{
if (!IsParameterValidType(parameter))
return false;
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
if (!IsParameterValidType(parameter))
throw new ArgumentException(string.Format("Parameter must be of type {0}", typeof(T)));
Execute((T)parameter);
}
private static bool IsParameterValidType(object parameter)
{
if (parameter != null && !typeof(T).IsAssignableFrom(parameter.GetType()))
return false;
return true;
}
}
Now, If I close the dialog window and set a breakpoint in the CanExecute (I'm using Prism DelegateCommand with weak event subscription) method on the viewmodel, I notice that it triggers although the dialog has been closed. Why on earth is the binding between the button in the dialog and the command on the ViewModel still alive?
And I am checking if its being executed by closing the window and at a later time setting a breakpoint in the "CanClickMe" method in the viewmodel. It will get executed for a while, then suddenly stop (probably due to GC). This non-determenistic behaviour is causing problems because in the real application the viewmodel might already bee disposed.
You may use the WeakEvent Pattern to mitigate this problem. Please refer to the following Stackoverflow question: Is Josh Smith's implementation of the RelayCommand flawed?
I've seen this catch many times in different projects, I'm not sure whether this creepy bug lurks in your app too, but it's worth checking.
There is a known memory leak issue in WPF 3.5 (including SP1), basically you can encounter it if you are binding to something that isn’t a DependencyProperty or doesn’t implement INotifyPropertyChanged. And this is exactly what your code is about.
Just implement INotifyPropertyChanged on ItemViewModel and see how it goes. Hope this helps.
You could clear the CommandBindings Collection of your window, when it closes.
rather than having your command as a property, could you try the following:
public ICommand CommandClickMe
{
get
{
return new RelayWpfCommand<object>((obj)=>System.Console.Out.WriteLine("Hei mom"), obj => CanClickMe());
}
}

How should I move a WPF Window using MVVM?

This is probably overkill on the MVVM pattern but it's new to me and I'm interested to see if it is possible.
If I attach to the MouseMove event for a Window and do DragMove, I can move a bordeless window. Can I achieve this by some other method in MVVM or should I just accept adding this code to the Window's codebehind?
This is pure UI logic and doesn't belong in a ViewModel. The only reason you wouldn't want to put this in your code-behind would be for re-use and that is better solved with a custom Window derived control.
Personally I think any solution using MVVM would not make this code any better. Also, this is typically something that's view related and hasn't got anything to do with the data you're displaying.
IMHO, unless this is something that effects your data (aka the Model) then it is View code and should be in the View's code-behind and not in the Model.
I'm going to actually answer your question. The answer is yes. I'm using Cinch to assist me in the event binding and view model creation. The solution uses DragMove, but not as part of the code-behind (which is what I believe you are asking).
Event binding in the XAML:
<Window
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cinchV2="clr-namespace:Cinch;assembly=Cinch.WPF"
...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cinchV2:EventToCommandTrigger Command="{Binding MouseLeftButtonDown}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
...
</Grid>
</Window>
In the ViewModel:
[ExportViewModel("MainViewModel")]
[PartCreationPolicy(CreationPolicy.NonShared)]
internal sealed class MainViewModel : ViewModelBase
{
public SimpleCommand<object, EventToCommandArgs> MouseLeftButtonDown { get; private set; }
[ImportingConstructor]
public MainViewModel(IUIVisualizerService uiVisualizerService)
{
...
MouseLeftButtonDown = new SimpleCommand<object, EventToCommandArgs>(OnMouseLeftButtonDown);
}
private static void OnMouseLeftButtonDown(EventToCommandArgs e)
{
((Window)e.Sender).DragMove();
}
}
Fairly simple, right? Any events that come from the UI contain the View as the sender. So, here, we simply call the method on the view within the event handler in the ViewModel.
The project I'm working on uses no code-behind (even if it is not recommended in MVVM).
I know that I am a little late to the question, but this is what I have been using for sometime now and it works like a charm.
DashboardViewModel viewModel;
public DashboardView()
{
InitializeComponent();
viewModel = new DashboardViewModel();
viewModel.RequestClose += (s, e) => Application.Current.Dispatcher.Invoke(this.Close);
viewModel.RequestMinimize += (s, e) => Application.Current.Dispatcher.Invoke(() => { this.WindowState = WindowState.Minimized; });
DataContext = viewModel;
}
and something like this in your viewModel
#region Public Event Handlers
public event EventHandler<EventArgs> RequestClose;
public event EventHandler<EventArgs> RequestMinimize;
#endregion
Using the ICommand interface...
#region ICommand Members
public ICommand CloseCommand { get; private set; }
public ICommand MinimizeCommand { get; private set; }
#endregion
Configure the commands...
private void SetupCommands()
{
CloseCommand = new RelayCommand(CloseApplication);
MinimizeCommand = new RelayCommand(MinimizeApplication);
}
Here is the RelayCommand class.
public class RelayCommand : ICommand
{
#region Private Readonly Properties
private readonly Action<object> executeCommand;
private readonly Predicate<object> canExecute;
#endregion
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
this.executeCommand = execute;
this.canExecute = canExecute;
}
#endregion
#region Public ICommand Members
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
executeCommand(parameter);
}
#endregion
}
And some example methods...
private void MinimizeApplication(object obj)
{
RequestMinimize(this, new EventArgs());
}
private void CloseApplication(object obj)
{
RequestClose(this, new EventArgs());
}
Hope this helps!
I know it's an old question. However I prepared another simple implementation. Use following behavior to make window moveable:
public class WindowMoveBehavior : Behavior<Grid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
}
protected override void OnDetaching()
{
AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
base.OnDetaching();
}
private void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Window.GetWindow(AssociatedObject).DragMove();
}
}
Xaml example:
<Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<i:Interaction.Behaviors>
<behaviors:WindowMoveBehavior/>
</i:Interaction.Behaviors>
<!-- different controls and content -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
11 years passed, but maybe someone is still interested in case to drag window using MVVM. This tricky solution is based on window's property "Tag" - almost no one use it but it's time to find out it's strength :) So all you need is System.Windows.Interactivity nuget, no Cinch or events!
Xaml:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<Window ... Tag="{Binding WindowTag}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding DragMoveWindowCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Let's find out your current window and move it. In ViewModel:
private object _windowTag;
public object WindowTag
{
get
{
return _windowTag;
}
set
{
_windowTag = value;
OnPropertyChanged("WindowTag");
}
}
private RelayCommand _dragMoveWindowCommand;
public RelayCommand DragMoveWindowCommand
{
get
{
return _dragMoveWindowCommand ??
(_dragMoveWindowCommand = new RelayCommand(obj =>
{
try
{
var window = WindowService.GetCurrentWindowByTag(WindowTag = 1);
window.DragMove();
}
catch (Exception ex)
{
}
}));
}
}
public static Window GetCurrentWindowByTag(object tag)
{
return (from Window w in App.Current.Windows
where w.Tag == tag
select w)?.FirstOrDefault();
}
Enjoy!)

Resources