WPF & RelayCommand - Button always firing - wpf

I have been working through some of the examples of MVVM & WPF and while doing some debugging I find that the RelayCommand associated with a button on my view is constantly firing (executing the associated ImportHoursCommand) as soon as the program starts.
Here are the code snippets:
View
<Button x:Name="ImportHoursButton" Content="Import Hours"
Command="{Binding ImportHoursCommand}"
Height="25" Width="100" Margin="10"
VerticalAlignment="Bottom" HorizontalAlignment="Right"
Grid.Row="1" />
ViewModel
private RelayCommand _importHoursCommand;
public ICommand ImportHoursCommand
{
get
{
if (_importHoursCommand == null)
{
_importHoursCommand = new RelayCommand(param => this.ImportHoursCommandExecute(),
param => this.ImportHoursCommandCanExecute);
}
return _importHoursCommand;
}
}
void ImportHoursCommandExecute()
{
MessageBox.Show("Import Hours",
"Hours have been imported!",
MessageBoxButton.OK);
}
bool ImportHoursCommandCanExecute
{
get
{
string userProfile = System.Environment.GetEnvironmentVariable("USERPROFILE");
string currentFile = #userProfile + "\\download\\test.txt";
if (!File.Exists(currentFile))
{
MessageBox.Show("File Not Found",
"The file " + currentFile + " was not found!",
MessageBoxButton.OK);
return false;
}
return true;
}
}
If I put a breakpoint on the 'string userProfile = ...' line and run the program, Visual Studio will stop on the breakpoint and continue to stop on the breakpoint everytime I click the debug 'Continue' button. If I don't have a breakpoint the program appears to run OK but should this command always be checking if it can execute?
I am using the RelayCommand from Josh Smith's article here.

If a Button is bound to a Command, the CanExecute() determines if the Button is enabled or not. This means the CanExecute() is run anytime the button needs to check it's enabled value, such as when it gets drawn on the screen.
Since you're using a breakpoint in VS, I am guessing that the application is getting hidden when VS gains focus, and it is re-drawing the button when you hit the Continue button. When it re-draws the button, it is evaluating CanExecute() again, which goes into the endless cycle you're seeing
One way to know for sure is to change your breakpoint to a Debug.WriteLine, and watch your output window when your application is running.
As a side note, you could also change your RelayCommand to Microsoft Prism's DelegateCommand. I haven't looked at the differences too closely, however I know RelayCommands automatically raise the CanExecuteChanged() event when certain conditions are met (properties change, visual invalidated, etc) while DelegateCommands will only raise this event when you specifically tell it to. This means CanExecute() only evaluates when you specifically tell it to, not automatically, which can be good or bad depending on your situation.

That's perfectly normal; WPF reevaluates whether the command can be executed very often, for instance when the focus control changes, or when the window takes focus. Every time you click "Continue", the window takes the focus again, which reevaluates the CanExecute of your command, so your breakpoint is hit again.

Related

In what scenarios might ICommand CanExecute not update?

I've been using Josh Smith's implementation of RelayCommand in a couple of large projects for some years now. However today I've come across a scenario where the CanExecute on one of my commands isn't refreshing. I'm at a loss as to what's causing it - the view-model isn't doing anything that I haven't done dozens of times already.
The VM's constructor creates a couple of commands. The first one is a "start" command:-
StartCommand = new RelayCommand(o => StartAsync(), o => true);
The StartAsync() method looks like this:-
private async void StartAsync()
{
IsRunning = true;
await Task.Run(() => SomeLongRunningProcess(); }
IsRunning = false;
}
There is also a "save" command:-
SaveCommand = new RelayCommand(o => Save(), o => !IsRunning);
('IsRunning' is a bog-standard property, implementing INotifyPropertyChanged. As well as being used for the "CanExecute" delegate, it's also bound to the IsEnabled property of a few controls in the view to enable/disable them).
When I click my "Start" button (bound to 'StartCommand'), the "Save" button is correctly disabled. The b/g process runs to completion, then IsRunning is set to false, but this doesn't trigger the "Save" button to become enabled. It only enables if I click somewhere on my view.
(The controls whose IsEnabled property is bound to the VM IsRunning property do enable and disable correctly, by the way).
I've come across a few SO articles about this, but nothing really explains why this happens. My workaround was to bind the button's IsEnabled property to 'IsRunning', but it's frustrating that this particular view refused to play ball. Any thoughts on what might be causing this? Common sense says it's something specific to this view/VM, but I'm stumped (and I'm not going to post the code here - there's too much of it).
Yes, because the version of RelayCommand you're using is depending on CommandManager.RequerySuggested event and it's not accurate.
Its documentation states that
Occurs when the CommandManager detects conditions that might change
the ability of a command to execute.
Basically it guesses all the possible events where your data could be changed. It can never know when your ViewModel/Model is changed. It isn't listening for the property change notifications.
If you want to react immediately without waiting for CommandManager to guess, you need to fire the ICommand.CanExecuteChanged manually yourself when model is updated.
You saw that the event not fired unless you click the window or something but do note that it could fire several times too

Double click WPFNotifyIcon with Caliburn Micro

I'm trying to use WPF NotifyIcon and Caliburn Micro together. My end goal is that I double click on the icon in my system tray to restore my window from a minimized state. Feel free to tell me if the way I'm trying to go about it is totally wrong.
I can see that there is a DoubleClickCommand on the TaskbarIcon (I don't see any DoubleClick event, which I would actually be happy to use, if it were available). Normally when working with commands while using Caliburn Micro, I would simply create, say, a Restore() method and a CanRestore property which would automatically be called by a button named Restore. How would I go about hooking things up so that Restore() is called upon a double click on the icon in the system tray?
EDIT - As per gleng's suggestion, I have now added an attached event, but double clicking the icon in the system tray still seems to do nothing. Code below.
xaml -
<tb:TaskbarIcon cal:Message.Attach="[Event DoubleClick] = [Action Restore]" />
VM -
public void Restore()
{
MessageBox.Show("moo"); // breakpoint on this line is never hit
}
public bool CanRestore
{
get { return true; }
}
Try the following:
<TaskbarIcon cal:Message.Attach="[Event TrayMouseDoubleClick] = [Action DoubleClickFunc]"/>
Notice that the event name is TrayMouseDoubleClick which I found by looking at the source code of the control, specifically the file named TaskBarIcon.Declarations.cs.
This file contains all dependency property definitions and routed events that are defined for the TaskBarIcon class.
How about you create an attached event?
<TaskbarIcon cal:Message.Attach="[Event DoubleClick] = [Action DoubleClickFunc]"/>
Then in your view model you'll have:
public void DoubleClickFunc()
{
// Your custom logic here
}
I've done this in the past and it works quite well.

Unexpected behaviour of System.Windows.Window.ShowDialog() when no other windows are open. Any idea why?

I picked up on this (with some effort) when my WPF MVVM application tried to show two consecutive error dialog windows before the main window was launched: After OKing the first window, the application went into a loop and the second error dialog never showed up.
I fixed the problem, but I was hoping someone could enlighten me as to why this happened.
It seems that, if there are no non-modal open windows, if one dialog window has been closed, all new dialog windows are immediately closed, without displaying.
Its very easy to reproduce, so here is some highly conceited code to illustrate the problem. This code is complete, so using just this, you should be able to reproduce it.
Create a Window control for the dialog window, with no code behind, and just the following XAML:
<Window x:Class="ForumExampleShowDialogIssue.OKDialogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OKDialogWindow" Height="300" Width="300">
<StackPanel>
<TextBlock Text="This is a Window with a single button. The button is set to Cancel, so it closes the window."
TextWrapping="Wrap"
Margin="5"/>
<Button Content="OK" IsCancel="True" IsDefault="True"
Margin="5"/>
</StackPanel>
Next, use the standard WPF App class, with nothing new in the XAML, but with the following in the code behind:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
OKDialogWindow alwaysOpen = new OKDialogWindow();
alwaysOpen.Show();
while (true)
{
OKDialogWindow dialogWindow = new OKDialogWindow();
Console.WriteLine("Before show");
dialogWindow.ShowDialog();
Console.WriteLine("After show");
}
}
Delete the MainWindow.XAML if it exists, and remove the reference to it from the App.XAML header.
Run. (the program, not like Forest).
This works as expected. The alwaysOpen window remains open, while one after the other dialogWindow instances appear in dialog mode, closing when OK is clicked, then showing the next one.
HOWEVER, this breaks when you change OnStartup to the following:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
while (true)
{
OKDialogWindow dialogWindow = new OKDialogWindow();
Console.WriteLine("Before show");
dialogWindow.ShowDialog();
Console.WriteLine("After show");
}
}
When there is no constantly open window, the first dialog window is the only one that works. After that, countless "Before show" and "After show" messages are printed to the console, but no new dialog windows appear - they are closed automatically as soon as they are shown.
Surely this can't be the intended behaviour? Do you get the same result? Any idea why this happens?
This is the intended behavior.
by Default the first open window is the MainWindow.
By default the only window in the list becomes the MainWindow (if others are to be removed).
Application Class is designed to exit if no windows are present in
windows list.
Check this:
http://www.ageektrapped.com/blog/the-wpf-application-class-overview-and-gotcha/
Resolve
You can goto the App.xaml file and add this in the root <Application> node:
ShutdownMode="OnExplicitShutdown"
This means that even when you close all the windows, the application still runs until you explicitly call the InvokeShutdown() Method.
The default value of ShutdownMode is OnMainWindowClose。
Explanation
In your first snippet, you create a window first that never close. This is the MainWindow and it never close. Thus Application never shutdown. But in your second snippet, your MainWindow is the first dialog that you have created. Application will shutdown as soon as the window close. Your other dialogs would never show after Application shutted down, right?
See MSDN: https://msdn.microsoft.com/en-us/library/system.windows.application.shutdownmode(v=vs.110).aspx.

using routed events within Silverlight user controls

within my current project file I have a user control that has a storyboard animation applied to the control. When a button is clicked in the page the storyboard starts and basically visually presents the control to the user. The storyboard resides in the current page as a resource
<navigation:Page.Resources>
<Storyboard x:Name="PreferncesOpen">....</Storyboard x:Name="PreferncesOpen">
</navigation:Page.Resources>
Within the page I have button that I have a click event on that starts the storyboard
private void btnOpenPreferences_Click(object sender, RoutedEventArgs e)
{
preferencesPanel.Visibility = System.Windows.Visibility.Visible;
PreferncesOpen.Begin();
}
Within the userControl (preferencesPanel) I have a button that when clicked needs to close/collapse the user control. I plan to do this using Visibility.collapsed. I assume that I need to use routed commands since the button is within the user control but the actions need to be called within the page that contains the control? I'm still new to routed commands and I assume this is the correct approach. I'm just unsure how to click on a button within the user control and have it modify or execute commands that would impact how the page (in which this control resides) may change or for that part affect other elements within the page? For example when the button is clicked within the user control I would like the visibility of the user control to be set to collapsed. I also would like to have the width of one of the grid columns within the main page re-size. I have done this in the past using the code behind for the page but I am trying to separate some of this and I thought routed commands would be the way to go?
I'd greatly appreciate any tips.
Thank you in advance
The title is a bit misleading, you're asking about commands rather then routed events if I understand you correctly.
Here's an example of using a DelegateCommand<T> from the Prism library; It happens to be my personal preference.
Markup :
<Button x:Name="MyButton" Content="Btn" Command="{Binding DoSomethingCommand}"/>
Code-behind* or ViewModel :
(* if you're not using MVVM make sure to add MyButton.DataContext = this; so you're sure that the button can databind to your code behind effectively)
public DelegateCommand<object> DoSomethingCommand
{
get
{
if(mDoSomethingCommand == null)
mDoSomethingCommand = new DelegateCommand(DoSomething, canDoSomething);
return mDoSomethingCommand;
}
private DelegateCommand<object> mDoSomethingCommand;
// here's where the command is actually executed
void DoSomething(object o)
{}
// here's where the check is made whether the command can actually be executed
// insert your own condition here
bool canDoSomething(object o)
{ return true; }
// here's how you can force the command to check whether it can be executed
// typically a reaction for a PropertyChanged event or whatever you like
DoSomethingCommand.RaiseCanExecuteChanged();
The argument that's passed to the above function is the CommandParameter dependency property (in Prism it's an attached property as well as the Command property if memory serves me right).
When it's set, you can pass a value of your choosing to the command that you wish to execute.
Hope that helps.

How does one go about debugging databinding issues in MVVM?

I have rebuilt Josh Smith's CommandSink example from scratch and my version runs without error except that my command buttons are grayed out. I assume this is because there is something somewhere not set correctly so that the commands never get set to CanExecute = true or at some point get set to CanExecute = false.
But since the databinding is essentially going on in the XAML, I am not sure where to "set a breakpoint on the command" so I can see at what time a button is assigned CanExecute = false or e.g. is NOT assigned CanExecute = true.
Essentially I have these command bindings in a view:
<UserControl.CommandBindings>
<sink:CommandSinkBinding Command="vm:CustomerViewModel.CloseCommand"/>
<sink:CommandSinkBinding Command="vm:CustomerViewModel.ShowInformationCommand"/>
</UserControl.CommandBindings>
and in my CustomerViewModel the command is defined like this:
public static readonly RoutedCommand CloseCommand = new RoutedCommand();
public bool CanBeClosed
{
get { return _customer.IsOpen; }
}
public void Close()
{
_customer.IsOpen = false;
this.OnPropertyChanged("CanBeClosed");
this.OnPropertyChanged("CanBeApproved");
}
But since my understanding of MVVM right now is that you set up your M-VM-M, run your application and things "get data bound and just work".
I guess I am looking for something like a "Page Cycle" as in ASP.NET in which to step through to find out when my commands are CanExecute = true and when they are CanExecute = false.
How could one go about debugging an WPF/MVVM pattern like this where the databinding is not done explicitly in the code, and hence one cannot step-through debug in the classic sense?
Answer:
Although this article that Gishu mentioned was helpful in general regarding how to go about debugging databinding issues, and generally answered my question on how to do just that, it didn't help me in my particular case.
For what it's worth, I figured my particular issue with this code out by doing a line-per-line comparison with Josh Smith's original code and found these two lines that were missing from the CommandSinkBinding.OnCommandSinkChanged method:
if (!ConfigureDelayedProcessing(depObj, commandSink))
ProcessCommandSinkChanged(depObj, commandSink);
First point:
To debug XAML Binding you can add reference to Diagnostics from WindowsBase dll to the XAML file,
then when binding to some property add PresentationTraceSources.TraceLevel. When you run it check out the output window.
XAML sample :
<TextBlock Text="{Binding someProperty, diagnostics:PresentationTraceSources.TraceLevel=High}"/>
Second point:
your command being grayed out means that your binding works! What doesn't happen is that the command doesn't refresh it's state, meaning CanExe() method ran and set the command to "can't exe state", however it never does check again if it can toggle its state back to "can exe". There are many ways to do it, but basically when a certain property changes in your ViewModel, refresh your command state:
in Prism, for example you can call someCommand.RaiseCanExecuteChanged ();

Resources