I am looking for a way to be able to have an event run after a command executes. I am working with EditingCommands (ToggleBold, ToggleItalic...ect.) and would like to be able to have a method called directly after the command finishes whatever it is doing.
An example would be that I have some text selected and press Ctrl+B and that executes EditingCommands.ToggleBold. Right after the text is toggled I want to call an method that will update a ToggleButton that is connected with the selection FontWeight.
I tried using the Executed event but that is unfortunately for me called before the text is affected and consequently updates the button with information that will change in just a second. Does anyone know a way around this?
A workaround is to queue another message in your Executed handler:
void copy_Executed(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new ThreadStart(delegate()
{
//do update of bold button here
}), null);
}
This will ensure your work is added to the back of the queue, and will be executed after other messages of the same or higher priority.
However, I'd like to suggest a better solution. If you think about it, the bold button is responsible for executing two different commands: make bold, and make normal. It switches between these two commands based on the currently selected text/caret position. Therefore, you could write a custom ICommand implementation that encapsulates two sub-commands (completely untested code):
public class TogglingCommand : ICommand
{
private readonly ICommand _command1;
private readonly ICommand _command2;
private ICommand _activeCommand;
public TogglingCommand(ICommand command1, ICommand command2)
{
_command1 = command1;
_command2 = command2;
}
public ICommand ActiveCommand
{
get { return _activeCommand; }
}
public bool CanExecute(object parameter)
{
if (_command1.CanExecute(parameter))
{
_activeCommand = _command1;
}
else if (_command2.CanExecute(parameter))
{
_activeCommand = _command2;
}
else
{
_activeCommand = null;
}
return _activeCommand != null;
}
public void Execute(object parameter)
{
_activeCommand.Execute(parameter);
}
}
You can then construct a TogglingCommand with two commands: one to bolden and one to unbolden text. Then you can bind the Button in your UI to the ActiveCommand property to change it in anyway you like based on what will happen when you click the command. For example, if you're using a ToggleButton you would bind IsChecked to ActiveCommand and convert to true is the active command is unbolden. Of course, the bolden and unbolden commands need CanExecute logic of their own that inspects the selected text.
Can you use the Executed (past tense) Routed Event handler? (Or maybe that's what you're saying you tried)
public partial class CustomerWindow : Window
{
public CustomerWindow()
{
InitializeComponent();
CommandBinding binding = new CommandBinding(ApplicationCommands.Copy);
binding.Executed += new ExecutedRoutedEventHandler(this.copy_Executed);
this.CommandBindings.Add(binding);
}
void copy_Executed(object sender, RoutedEventArgs e)
{
MessageBox.Show("Executed the Copy command");
}
}
Related
I'm used to developing WPF applications by setting up events in xaml and linking to code behind on the MainWindow. This makes it really easy to enable and disable events as needed. I've recently been learning MVVM pattern and have developed an application which uses commands to handle events. The problem with this is I have no idea how to disable an event command.
My specific scenario is this:
I need to disable some filtering events when data is loading or refreshing so that they don't continuously fire... The data is already being bound so no filtering events should be happening.
In the past, I have done it something like this:
listView.SelectionChangedEvent -= new SelectionChangedEvent(event_method);
//Perform actions...
listView.SelectionChangedEvent += new SelectionChangedEvent(event_method);
My understanding with MVVM is that I can use RelayCommand or DelegateCommand and pass in a predicate which should disable the command based on a boolean value. Then I just need to call RaiseCanExecuteCommandChanged() to update the Command after I have changed the boolean value. However, all of my tests to make this work just plain don't work...
Can anyone post a brief example of how to handle what I have described in MVVM? All the examples I can find are for enabling/disabling controls such as Button ... I haven't been able to find anything to actually disable a Command from executing...
To ensure that a command will not be executed if CanExecute evaluates to false, you can write a decorator:
public class LimitExecuteIfCanCommandDecorator : CommandDecoratorBase
{
public LimitExecuteIfCanCommandDecorator( ICommand command ) : base( command )
{
}
public override void Execute( object parameter )
{
if ( CanExecute(parameter) ) // check if it evaluates to true
{
base.Execute( parameter );
}
}
}
public abstract class CommandDecoratorBase : ICommand
{
protected CommandDecoratorBase(ICommand command)
{
_command = command;
}
private readonly ICommand _command;
public event EventHandler CanExecuteChanged
{
add
{
_command.CanExecuteChanged += value;
}
remove
{
_command.CanExecuteChanged -= value;
}
}
public virtual bool CanExecute( object parameter )
{
return _command.CanExecute( parameter );
}
public virtual void Execute( object parameter )
{
_command.Execute( parameter );
}
}
Now use that decorator to wrap all your commands.
I'm learning about wpf, delegates, event and I have some clue on what does what, but I'm a little lost when it comes to implementing ICommand
I have a class the implements the ICommand interface like this
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute) : this (execute, null)
{
}
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
{
this._execute = execute;
this._canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
//throw new NotImplementedException();
return true;
}
public void Execute(object parameter)
{
//throw new NotImplementedException();
this._execute(parameter);
}
public void OnCanExecute()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
And then my ViewModel that uses i.
class PersonViewModel
{
public ICommand ICommandPresenter { get; set; }
public PersonModel PM;
private string _btnString = "";
#region Propertys
public string ButtonString
{
get {return _btnString; }
set
{
if (_btnString.Equals(value))
{
return;
}
_btnString = value;
}
}
public string Name { get { return PM.Name; } set
{
PM.Name = value;
}
}
#endregion Propertys
public PersonViewModel()
{
PM = new PersonModel();
ICommandPresenter = new RelayCommand(ChangeName);
}
public void ChangeName(object a)
{
string s = a as string;
MessageBox.Show("Hello from PersonViewModel Commander: " + s);
}
}
This is where it gets confusing for me. In the RelayCommand class I have an event CanExecuteChanged but that event is never executed/fired. From what I've understood from my reading on events is that you don't "need" to have subscribers to an event, but if you're going to have an event you should atleast have somewhere in the code that executes it. But I don't have that in my code but for some reason my button still does what I command it to do. I still understand that I've clicked the button but I don't have anything in my code that is subscribed to that button.
Why is my button able to execute my code even though I don't have an event connected to it?
Since I don't have any subscribers connected to CanExecuteChanged event does it becomes useless?
Is the command acting like an event? if so, please describe the whole process from clicking the button to executing the command.
CanExecuteChanged is member of ICommand class and, simplifying the things, is used by wpf framework to enable/disable your button depending on result of CanExecute() method. CanExecute is not tight to the code you want to execute when you click the button, but to the condition, when it's legal to be done.
Command executes your code, because you send pointer to your method(ChangeName method) here:
ICommandPresenter = new RelayCommand(ChangeName);
So you are not using CanExecuteChange at all, because you are invoking this constructor:
public RelayCommand(Action<object> execute).
To have CanExecute you have to invoke overloaded constructor that accepts CanExecute predicate:
public RelayCommand(Action<object> execute, Func<object,bool> canExecute)
And to invoke it just pass some function that returns a bool as second parameter:
ICommandPresenter = new RelayCommand(ChangeName, ()=>MyCustomLogicWhenButtonIsActive());
Based on what I saw in .Net's source code, the command assigned to the button's Command property is executed in the following way:
Clicking the button calls the button's OnClick() method (located in the button's ButtonBase base class).
The OnClick() method (see source here) calls the Commands.CommandHelpers.ExecuteCommandSource() method, passing this as the command source parameter.
The ExecuteCommandSource() (see source here) further calls the CriticalExecuteCommandSource() method, passing the same command source as a parameter.
Finally, the CriticalExecuteCommandSource() method (see source here) accesses the command source's Command member, and:
checks if the command's CanExecute() method returns true,
if it's true, it calls the command's Execute() method.
(If you use a RelayCommand implementation, then obviously, the RelayCommand class relays the call to the specific method you passed to its constructor when you instantiated it.)
So, to answer your question whether commands are fired through an event:
The source indicates that the OnClick() method executes the command directly, not through event handling. As you can see in the source, this method does raise the Click event, but it does so separately from the command execution.
Though, it remains unanswered how the OnClick() method is being called in the first place by the framework; I have no idea about that.
I got a WPF application that shows a button bound to a command like that:
<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">
The command is defined like that:
public ICommand TestrunStartCommand
{
get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}
public bool IsTestrunInProgress
{
get{
return _isTestrunInProgress;
}
set{
_isTestrunInProgress = value;
RaisePropertyChanged(IsTestrunInProgressPropertyName);
}
}
The problem is, the button won't be enabled immediately after I set IsTestrunInProgress to false, but only after I click inside the application window.
Could you help me understand this behaviour and show me how to fix this?
Further reading:
wpf command pattern - when does it query canexecute
The ICommand interface exposes an event ICommand.CanExecuteChanged which is used to inform the UI when to re-determine the IsEnabled state of command driven UI components.
Depending upon the implementation of the RelayCommand you are using, you may need to raise this event; Many implementations expose a method such as RelayCommand.RaiseCanExecuteChanged() which you can invoke to force the UI to refresh.
Some implementations of the RelayCommand make use of CommandManager.RequerySuggested, in which case you will need to call CommandManager.InvalidateRequerySuggested() to force the UI to refresh.
Long story short, you will need to call one of these methods from your property setter.
Update
As the state of the button is being determined when the active focus is changing, I believe the CommandManager is being used. So in the setter of your property, after assigning the backing field, invoke CommandManager.InvalidateRequerySuggested().
Update 2
The RelayCommand implementation is from the MVVM light toolkit. When consumed from WPF/.NET, the implementation wraps the methods and events exposed from the CommandManager. This will mean that these commands work automagically in the majority of situations (where the UI is altered, or the focused element is changed). But in a few cases, such as this one, you will need to manually force the command to re-query. The proper way to do this using this library would be to call the RaiseCanExecuteChanged() method on the RelayCommand.
This is so important and easy to miss, I am repeating what #Samir said in a comment. Mr Laurent Bugnion wrote in his blog:
In WPF 4 and WPF 4.5, however, there is a catch: The CommandManager will stop working after you upgrade MVVM Light to V5. What you will observe is that your UI elements (buttons, etc) will stop getting disabled/enabled when the RelayCommand’s CanExecute delegate returns false.
If you are in a hurry, here is the fix: In any class that uses the RelayCommand, replace the line saying:
using GalaSoft.MvvmLight.Command;
with:
using GalaSoft.MvvmLight.CommandWpf;
You can try with CommandManager.InvalidateRequerySuggested.
Anyway this did not help me sometimes in the past. For me the best solution turned out to be to bind the boolean property to the Button.IsEnabled dependency property.
In your case something like
IsEnabled={Binding IsTestrunInProgress}
The issue is, the ICommand Property TestrunStartCommand is always returning a new command object whenever it is accessed.
A simple fix is to create the ICommand object once and use it again and again.
private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
get
{
return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress));
}
}
This was quite a simple fix and it worked for me.
Addition to Riegardt Steyn's answer above: https://stackoverflow.com/a/33503341/1964969
If you don't want to change Command to CommandWpf usage (as that two RelayCommand versions are not compatible inbetween), another workaround could be to not instantiate a command at the declaration place. Use constructor code instead:
public class SomeVMClass
{
// CanExecute won't work:
// Declaration and instantiation same place
public RelayCommand MyCommand1 => new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
// CanExecute will work
// Declaration only
public RelayCommand MyCommand2 { get; private set; }
public SomeVMClass()
{
// Let's instantiate our declared command
MyCommand2 = new RelayCommand(MyBusinessLogic, MyCanExecuteValidator);
...
Blockquote
In your Command class change CanExcutedChanged to this
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
This is example of my command class
public class SaveConfigCommand : ICommand
{
public MyViewModel VM { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public SaveConfigCommand(MyViewModel vm)
{
VM = vm;
}
public bool CanExecute(object? parameter)
{
MyObjectModel model = parameter as MyObjectModel;
if (model == null)
return false;
// Validate others properties here
return true;
}
public void Execute(object? parameter)
{
VM.MyMethodInViewModel();
}
}
Here, I have a bit confusion about UI language. If language is changed then what happens? The whole folder gets changed or Culture gets loaded? I cannot get what is actually happening.
Properties.Strings.MainWindow_Language_Selection_English_Label="English"
Properties.Strings.MainWindow_Language_Selection_Gujarati_Label="ગુજરાતી"
Please explain what is happening.
private void LanguageSelection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBoxItem item = LanguageSelection.SelectedItem as ComboBoxItem;
if (item.Content.ToString() == Properties.Strings.MainWindow_Language_Selection_English_Label)
{
CultureManager.UICulture = new System.Globalization.CultureInfo("en");
}
else if (item.Content.ToString() == Properties.Strings.MainWindow_Language_Selection_Gujarati_Label)
{
CultureManager.UICulture = new System.Globalization.CultureInfo("gu");
}
Settings.Default["UILanguage"] = CultureManager.UICulture.Name;
Settings.Default.Save();
}
In general, setting the culture on application thread will be effective on the next form that is displayed, so to make this work you probably need a login/language selection window where you set the main thread's culture and then show application's main window.
There were a few attempts around this to make language selection take effect immadiately (easier in WPF) but this is how it works out of the box.
In WPF, however, if you are directly binding UI elements to resources you can make the UI update by raising a property change event on your resource property. The easiest way to achieve this (other than creating a new code generator for the .resx file) would be to wrap your resources in a model class like this:
public class StringRes : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate {};
public string Login
{
get { return Properties.Strings.Login; }
}
public string Password
{
get { return Properties.Strings.Password; }
}
public void NotifyLanguageChanged()
{
PropertyChanged(this, new PropertyChangedEventArgs("Login"));
PropertyChanged(this, new PropertyChangedEventArgs("Password"));
}
}
public class MainWindow
{
private StringRes _resources;
private void LanguageSelection_SelectionChanged()
{
System.Threading.Thread.CurrentThread.CurrentUICulture = GetCurrentCulture();
_resources.NotifyLanguageChanged();
}
}
If you have bound your UI elements to the instance of the StringRes class, they will be updated when you raise the notification change event in your model.
I have a group of usercontrols that I use multiple instances of through out my form.
The usercontrols have contain either a textbox, combobox, or checkbox and a get value method to return the value of it's repective control. Usually I have a button on the form whose clicked event calls the usercontrols getValue function, but now I need for something to happen on the form whenever the usercontrols controls changed event happens. Something like the following.
In form1.cs
form1.Controls.Add(UserControl1);
form1.Controls.Add(UserContorl2);
// gets called every time the combobox on UserControl1 has it's
// ValueChanged event raised
private void UserControl1_Changed(object Sender, EventArgs e)
{
form1.property1 = UserControl1.getValue();
}
// gets called everytime the textbox on UserControl2 has it's
// textChanged event raised
private void UserControl2_Changed(object Sender, EventArgs e)
{
form1.property2 = UserControl2.getValue();
}
I can't figure out how to throw/catch that event in form. I'm using VS 2005.
here is the code in one of my usercontrols. txtValue is a textbox
public partial class StringParameterControl : BaseParameterControl
{
public StringParameterControl(string aName, string aValue)
: base(aName)
{
InitializeComponent();
txtValue.Text = aValue;
}
public StringParameterControl(string aName)
: base(aName)
{
InitializeComponent();
}
public StringParameterControl()
: base()
{
InitializeComponent();
}
public void SetValue(string aValue)
{
txtValue.Text = aValue;
}
public override object GetValue()
{
return txtValue.Text;
}
}
UserControl1.Changed += UserControl1_Changed;
Update your control to include the following:
// A delegate type for hooking up change notifications.
// This is _what kind_ of event you want. It sets the signature your event handler methods must have.
public delegate void ChangedEventHandler(object sender, EventArgs e);
//the actual event
public event ChangedEventHandler Changed;
// Method to raise/fire the Changed event. Call this whenever something changes
protected virtual void OnChanged(EventArgs e)
{
ChangedEventHandler handler = Changed;
if (handler != null) handler(this, e);
}
//and update your existing SetValue() function like so:
public void SetValue(string aValue)
{
txtValue.Text = aValue;
OnChanged(EventArgs.Empty);
}
You can change your event signature to pass any information you want — for example the old or new value of the property (or both). I just used the standard event arguments for the example.
And speaking or properties, don't write separate Get/Set methods in C# like you just did. If you find yourself doing that, you probably want to use a property instead, which will enforce the correct get/set semantics automatically:
public string Value
{
get { return txtValue.Text;}
set {txtValue.Text = value; OnChanged(EventArgs.Emtpy); }
}
As far as I understand the usercontrols you are using do not fire events whenever their value changes, so you can't just subscribe to some "ValueChanged" event.
A possible solution might be to find the control you are interested in (Combobox, Textbox, etc.) in the usercontrols' "Controls" collection and directly subscribe to its appropriate events.
Or you can do with type inference style.
UserControl.Changed = (sender, e) => this.controlFired = true; //or whatever
The Changed is the public event you expose through a property in your control with the type of the delegate (void(object sender, EventArges e)). You can look up how to publish the event on msdn - there is plenty of articles on that.