📜 ⬆️ ⬇️

Commands in MVVM


Introduction


Using the example of an application that uses the MVVM pattern (Model View View-Model), consider working with commands (Commands).

The examples I use have the same logic regardless of the platform: WPF, SilverLight, Windows Phone. First of all, we will consider several options for using commands in any MVVM application. First, examples, then debriefing.

All the code used is part of my Apex MVVM library, but all parts will be provided in full, which will allow you to easily integrate these methods into your project or library, or you can simply connect the link to Apex and get started right away.

image
Screenshot 1: Commands in WPF

image
Screenshot 2: Commands in SilverLight

image
Screenshot 3: Commands in Windows Phone

What are teams?

Commands are bound objects, which allows you to separate the logic and user interface from each other.
')
If we look at the commands in more detail, they are the following:

Examples

To begin, consider the cases when it becomes necessary to use commands. Each case will be discussed in more detail later in the corresponding paragraph.

Important : each View Model in this part of the examples contains observable collections of strings with the name “Messages” - each sample application shows a message that displays information about what is happening.

Consider the basic View Model:

public class MainViewModel : ViewModel { public MainViewModel() { } private ObservableCollection<string> messages = new ObservableCollection<string>(); public ObservableCollection<string> Messages { get { return messages; } } } 


This view model inherits from ViewModel ', which in turn implements the INotifyPropertyChanged interface, but you can use any other available type of view model implementation.

Example 1 - Simple Command Usage
Objective : Call the appropriate view model function when the user clicks an item.

Now consider the simplest example of using commands. First, add a Command object to our view model — an associated function that is called when the command is invoked:
 public class MainViewModel : ViewModel { public MainViewModel() { //   -  DoSimpleCommand. simpleCommand = new Command(DoSimpleCommand); } /// <summary> /// The SimpleCommand function. /// </summary> private void DoSimpleCommand() { //   Messages.Add(" 'DoSimpleCommand'."); } /// <summary> ///    /// </summary> private Command simpleCommand; /// <summary> ///    /// </summary> public Command SimpleCommand { get { return simpleCommand; } } } 

Now we will add a command to the “Command '” property of the control button (button).
 <Button Content="Simple Command" Command="{Binding SimpleCommand}" /> 

That's all. The simplest example is made. We attached a command object to the Command element's interface property. When an element is activated, that is, when a button is pressed, a command is called (the corresponding DoSimpleCommand function is called).

Example 2 - Simple use of Command paired with lambda functions
Purpose : To call the function of the view model during activation or clicking on an element of the interface.

However, this is a very simple function, which I prefer not to write explicitly and instead use the lambda expression.

In all examples of this manual, you can explicitly create a function, for example, as in Example 1, or use lamdas, which somewhat reduces the code and makes it more elegant. The choice is yours.
 public MainViewModel() { //      .      . lambdaCommand = new Command( () => { Messages.Add("    .      . "); }); } /// <summary> /// The command object. /// </summary> private Command lambdaCommand; /// <summary> /// Gets the command. /// </summary> public Command LambdaCommand { get { return lambdaCommand; } } 

Again, repeat the process of binding the command to the Command property of our button:
 <Button Content="Lambda Command" Command="{Binding LambdaCommand}" /> 


Example 3 - Simple use of Command with parameters
Objective : To create a command that uses the parameter passed to it when binding data.

In this example, we use the Command object (it is also possible to use the AsynchronousCommand object, which we will see a little later). The passed parameter can be used in the called function.
 public class MainViewModel : ViewModel { public MainViewModel() { //    parameterizedCommand = new Command(DoParameterisedCommand); } /// <summary> ///   /// </summary> private void DoParameterisedCommand(object parameter) { Messages.Add("   –  : '" + parameter.ToString() + "'."); } /// <summary> /// The command object. /// </summary> private Command parameterizedCommand; /// <summary> /// Gets the command. /// </summary> public Command ParameterisedCommand { get { return parameterizedCommand; } } } 

Repeat the command binding to the button or any other element. Just do not forget to add a parameter:
 <Button Content="Parameterized Command" Command="{Binding ParameterizedCommand}" CommandParameter={Binding SomeObject} /> 

Wherever commands are used, it is possible to pass parameters to them. When creating a command, you can use Action (command function without parameters) or Action <object> (command function with an object type parameter). It is also possible to use lamdas:
 //       parameterizedCommand = new Command( (parameter) => { Messages.Add("   –  : '" + parameter.ToString() + "'."); }); 


Example 4 - Enabling and disabling Command
Objective : Add the ability to enable / disable commands from code or XAML

Each command contains the CanExecute property, which, when set to true, enables the command, and if set to false, disables the command, as well as the associated control. In the following example, when the command is disabled, the button from the user interface will also disappear:
 public class MainViewModel : ViewModel { public MainViewModel() { //  /  enableDisableCommand = new Command( () => { Messages.Add("/ ."); }, false); } private void DisableCommand() { //   EnableDisableCommand.CanExecute = false; } private void EnableCommand() { //   EnableDisableCommand.CanExecute = true; } /// <summary> /// The command object. /// </summary> private Command enableDisableCommand; /// <summary> /// Gets the command. /// </summary> public Command EnableDisableCommand { get { return enableDisableCommand; } } } 

Make a binding to a button (or another element):
 <Button Content="Enable/Disable Command" Command="{Binding EnableDisableCommand}" /> 

It also remains possible to bind the property of the CanExecute command.
In this example, to control the state of the command in the code, we assign the corresponding value to the CanExecute property. And to control through the interface (XAML), we use the additional CheckBox element:
 <CheckBox IsChecked="{Binding EnableDisableCommand.CanExecute, Mode=TwoWay}" Content="Enabled" /> 

Regardless of where and how we created the command object, we always have the opportunity to pass as a second parameter a boolean value that controls the CanExecute property. By default, it is set to false. In the example, pass true to enable.

Example 5 - Command calling events
Objective : To find out when the command was executed, or the moment of its execution.

Each command has two events: Executed - called when the command has completed, and Executing - at the time of execution. The Executing event also allows you to cancel a command.

Important : There are many situations where the above events become very useful. For example, you want to show a pop-up window that asks if you want to continue at the time of the command. And where to place such code? In the command code? A bad idea, since you have to create the interface code in the data model, which first of all litters the code, and secondly, which is much more important, it will not allow to test it. The best would be to place the code in the View. And with the help of events you can do it.
A second example of the demonstration of the usefulness of such events can be a situation where it becomes necessary to shift the focus to another element after the execution of a command. You cannot do this in the view model, as it does not have access to controls (controls), but by subscribing to events you can accomplish this without any problems.
 public class MainViewModel : ViewModel { public MainViewModel() { //     eventsCommand = new Command( () => { Messages.Add("   ."); }); 

Now we tie to the button:
 <Button Content="Events Command" Command="{Binding EventsCommand}" /> 

At the moment there is no difference from the code in the previous examples. Subscribe to events in View.
Important: in my view, a DataContext named viewModel:
 /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); viewModel.EventsCommand.Executing += new Apex.MVVM.CancelCommandEventHandler(EventsCommand_Executing); viewModel.EventsCommand.Executed += new Apex.MVVM.CommandEventHandler(EventsCommand_Executed); } void EventsCommand_Executed(object sender, Apex.MVVM.CommandEventArgs args) { viewModel.Messages.Add("   .  View!"); } void EventsCommand_Executing(object sender, Apex.MVVM.CancelCommandEventArgs args) { if (MessageBox.Show(" ?", "Cancel?", MessageBoxButton.YesNo) == MessageBoxResult.Yes) args.Cancel = true; } } 

Finally, we are getting close to understanding the full power of the team implementation. It is possible to subscribe to the Executed and Executing events in the view (or even in another ViewModel, or object), and also know when it occurs. The Executing event passes the CancelCommandEventArgs object - this is the property with the name “Cancel”. If it is set to true, the command will not be executed. Both the CommandEventArgs and CancelCommandEventArgs objects support another property, the parameter. This is the parameter that can be passed to Command (if there is one at all).

Example 6 - Asynchronous Command
Purpose : If commands are executed long enough, the interface is blocked. It must be able to run them asynchronously.

You can create something like a background process and run a command there. However, several questions immediately arise:
- What happens if we want to update the ViewModel in a function that is in the stream? We cannot do this without resorting to actions in the user interface.
- How to make sure that one command did not accidentally cause several threads when only one impact occurred on the interface?
- How not to litter the View Model, if there are a lot of commands that should be executed in separate threads?
- How to ensure compatibility with WP and Silverlight, if there are different options for streams on each system?

The AsynchronousCommand object was created taking into account all these nuances and not only. We get acquainted:
 public class MainViewModel : ViewModel { public MainViewModel() { //    asyncCommand1 = new AsynchronousCommand( () => { for (int i = 1; i <= 10; i++) { //    . asyncCommand1.ReportProgress(() => { Messages.Add(i.ToString()); }); System.Threading.Thread.Sleep(200); } }); } /// <summary> /// The command object. /// </summary> private AsynchronousCommand asyncCommand1; /// <summary> /// Gets the command. /// </summary> public AsynchronousCommand AsyncCommand1 { get { return asyncCommand1; } } } 

Binding to a XAML element:
 <Button Content="Asynchronous Command" Command="{Binding AsyncCommand1}" /> 

After the command is invoked, the corresponding function is invoked, which we have specified using the lamda expression, which in turn is called in a separate thread from the thread pool.

If there is a need to do something with View Model objects (which can be associated with user interface elements), we can use the ReportProgress function:
 asyncCommand1.ReportProgress(() => { Messages.Add(i.ToString()); }); 

ReportProgress ensures that the transmitted code will be run in the right stream, which makes it quite easy to change the user interface if necessary.

Example 7 - Asynchronous Command Updates User Interface (UI)
Purpose : The team runs a long time. Show progress bar.

In AsynchronousCommand there is a property called "IsExecuting". If it is set to true, then the command is in progress. Since AsynchronousCommand implements the INotifyPropertyChanged interface, which implies that we have the ability to bind to this property and show the progress.
 public class MainViewModel : ViewModel { public MainViewModel() { //    asyncCommand2 = new AsynchronousCommand( () => { for (char c = 'A'; c <= 'Z'; c++) { //    asyncCommand2.ReportProgress(() => { Messages.Add(c.ToString()); }); System.Threading.Thread.Sleep(100); } }); } /// <summary> /// The command object. /// </summary> private AsynchronousCommand asyncCommand2; /// <summary> /// Gets the command. /// </summary> public AsynchronousCommand AsyncCommand2 { get { return asyncCommand2; } } } 

The team will associate with the button. And we will bind the IsExecuting property to another interface element, for example, a StackPanel, which will contain the TextBlock and the ProgressBar respectively:
 <Button Content="Asynchronous Command" Command="{Binding AsyncCommand2}" Visibility="{Binding AsyncCommand2.IsExecuting, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}" /> <StackPanel Visibility="{Binding AsyncCommand2.IsExecuting, Converter={StaticResource BooleanToVisibilityConverter}}"> <TextBlock Text="The command is running!" /> <ProgressBar Height="20" Width="120" IsIndeterminate="True" /> </StackPanel> 

In this example, as soon as the command begins to run, the button disappears, and the text and progress bar appear. Note that we bind to the IsExecuting property of the command.
 asyncCommand1.ReportProgress(() => { Messages.Add(i.ToString()); }); 

Note : The Invert parameter can be passed to the BooleanToVisilityConverter, since it is an enhanced version of the standard BooleanToVisibilityConverter, which is defined in Apex.Converters. Inverts the result. Very useful thing in certain moments.

Example 8 - Asynchronous Command with the ability to cancel
Objective : To allow the user to cancel the process of executing an asynchronous command.

Let's use some AsynchronousCommand features. Each AsynchronousCommand object also contains a command named CancelCommand. And it can be tied to the user's UI or called inside the code in the right place. When calling this command, the IsCancellationRequested property of the AsynchronousCommand object is set to true (note that the property uses INotifyPropertyChanged and you have the opportunity to bind to it). You can periodically call the CancelIfRequested function, and if it suddenly returns true, the command will stop.
 public class MainViewModel : ViewModel { public MainViewModel() { //      cancellableAsyncCommand = new AsynchronousCommand( () => { for(int i = 1; i <= 100; i++) { // ? if(cancellableAsyncCommand.CancelIfRequested()) return; // . cancellableAsyncCommand.ReportProgress( () => { Messages.Add(i.ToString()); } ); System.Threading.Thread.Sleep(100); } }); } /// <summary> /// The command object. /// </summary> private AsynchronousCommand cancellableAsyncCommand; /// <summary> /// Gets the command. /// </summary> public AsynchronousCommand CancellableAsyncCommand { get { return cancellableAsyncCommand; } } } 

Let's tie the command to the button, and the IsExecuting property to the StackPanel:
 <Button Content="Cancellable Async Command" Command="{Binding CancellableAsyncCommand}" Visibility="{Binding CancellableAsyncCommand.IsExecuting, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=Invert}" /> <StackPanel Visibility="{Binding CancellableAsyncCommand.IsExecuting, Converter={StaticResource BooleanToVisibilityConverter}}"> <TextBlock Margin="4" Text="The command is running!" /> <ProgressBar Margin="4" Height="20" Width="120" IsIndeterminate="True" /> <Button Margin="4" Content="Cancel" Command="{Binding CancellableAsyncCommand.CancelCommand}" /> </StackPanel> 

In this example, we show the Cancel button during the execution of a command. This button is associated with the CancellableAsyncCommand.CancelCommand property. Due to the fact that we use the CancelIfRequested function, we have a beautiful opportunity to stop the execution of an asynchronous command.

Note : When you stop the execution of an asynchronous command, the Executed event is not raised. However, the Cancelled event is raised instead, which can take the same parameters.

Example 9 - Binding Events to Command
Purpose : To invoke a command when activating a custom item that does not have the Command property set, but an event is set.

In this case, you can use the EventBindings attachment property. It is located in the class Apex.Commands. EventBindings accepts an EventBindingCollection, which is a simple collection of EventBinding objects. Each EventBinding takes two parameters: the name of the event and the name of the command to be invoked.
 public class MainViewModel : ViewModel { public MainViewModel() { //    EventBindingCommand = new Command( () => { Messages.Add("  ."); }); } /// <summary> /// The command object. /// </summary> private Command eventBindingCommand; /// <summary> /// Gets the command. /// </summary> public Command EventBindingCommand { get { return eventBindingCommand; } } } 

A team link with an event occurs as follows:
 <Border Margin="20" Background="Red"> <!—  EventBindingCommand   MouseLeftButtonDown. --> <apexCommands:EventBindings.EventBindings> <apexCommands:EventBindingCollection> <apexCommands:EventBinding EventName="MouseLeftButtonDown" Command="{Binding EventBindingCommand}" /> </apexCommands:EventBindingCollection> </apexCommands:EventBindings.EventBindings> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="Left Click on Me" FontSize="16" Foreground="White" /> </Border> 

When using EventBindings it is allowed to attach any command to any event.

How it works - Command Class
Consider the Command class, which was used in examples 1-5.
 /// <summary> ///  ViewModelCommand –   ICommand,   . /// </summary> public class Command : ICommand { /// <summary> ///       <see cref="Command"/>. /// </summary> /// <param name="action">.</param> /// <param name="canExecute">  <c>true</c> [can execute] ( ).</param> public Command(Action action, bool canExecute = true) { // Set the action. this.action = action; this.canExecute = canExecute; } /// <summary> ///       <see cref="Command"/> class. /// </summary> /// <param name="parameterizedAction"> .</param> /// <param name="canExecute">    <c>true</c> [can execute]( ).</param> public Command(Action<object> parameterizedAction, bool canExecute = true) { // Set the action. this.parameterizedAction = parameterizedAction; this.canExecute = canExecute; } 

First of all, we create two overloaded constructors to which the action is passed without parameters: Action, or with parameters: Action <object>, where object is a type.

Next, set the flag canExecute, responsible for the ability to run the command. After changing the canExecute flag, you must call canExecuteChanged.
 /// <summary> /// (  )     . /// </summary> protected Action action = null; protected Action<object> parameterizedAction = null; /// <summary> ///  ,     . /// </summary> private bool canExecute = false; /// <summary> ///  /  ,      /// </summary> /// <value> /// <c>true</c>   ;   - <c>false</c>. /// </value> public bool CanExecute { get { return canExecute; } set { if (canExecute != value) { canExecute = value; EventHandler canExecuteChanged = CanExecuteChanged; if (canExecuteChanged != null) canExecuteChanged(this, EventArgs.Empty); } } } 

Next, we implement the ICommand interface
 /// <summary> ///  , ,        /// </summary> /// <param name="parameter">   . ///      , ///        null.</param> /// <returns> /// >    ;   - false. /// </returns> bool ICommand.CanExecute(object parameter) { return canExecute; } /// <summary> ///  ,      . /// </summary> /// <param name="parameter">    . ///      , ///        null.</param> void ICommand.Execute(object parameter) { this.DoExecute(parameter); } 

We will analyze the DoExecute function a little later.
 /// <summary> /// ,      /// </summary> public event EventHandler CanExecuteChanged; /// <summary> ///      /// </summary> public event CancelCommandEventHandler Executing; /// <summary> /// ,    /// </summary> public event CommandEventHandler Executed; 

Now let's implement the Invoke function for each event. Thus, we will have the opportunity to call them from derived classes.
 protected void InvokeAction(object param) { Action theAction = action; Action<object> theParameterizedAction = parameterizedAction; if (theAction != null) theAction(); else if (theParameterizedAction != null) theParameterizedAction(param); } protected void InvokeExecuted(CommandEventArgs args) { CommandEventHandler executed = Executed; //    if (executed != null) executed(this, args); } protected void InvokeExecuting(CancelCommandEventArgs args) { CancelCommandEventHandler executing = Executing; // Call the executed event. if (executing != null) executing(this, args); } 

To the note : InvokeAction causes either an action without parameters, or with parameters, depending on which of them is set.
 /// <summary> ///   /// </summary> /// <param name="param">The param.</param> public virtual void DoExecute(object param) { //       CancelCommandEventArgs args = new CancelCommandEventArgs() { Parameter = param, Cancel = false }; InvokeExecuting(args); //     - . if (args.Cancel) return; //    /  ,    .   . InvokeAction(param); // Call the executed function. InvokeExecuted(new CommandEventArgs() { Parameter = param }); } 

DoExecute is pretty simple. Just raises the corresponding event with the ability to cancel the execution.

The above class implements the ICommand interface and provides all the necessary functionality used in examples 1-5.

How it works - Class asynchronous Command
Examples 6-8 use the class AsynchronousCommand, which in turn inherits the Command class described above.

First, we announce the cash register and constructor:
 /// <summary> ///   -  ,        .. /// </summary> public class AsynchronousCommand : Command, INotifyPropertyChanged { /// <summary> ///       <see cref="AsynchronousCommand"/>. /// </summary> /// <param name="action">.</param> /// <param name="canExecute">    /// <c>true</c>   .</param> public AsynchronousCommand(Action action, bool canExecute = true) : base(action, canExecute) { //   Initialise(); } /// <summary> ///      <see cref="AsynchronousCommand"/>. /// </summary> /// <param name="parameterizedAction"> .</param> /// <param name="canExecute">    <c>true</c> [can execute] ( ).</param> public AsynchronousCommand(Action<object> parameterizedAction, bool canExecute = true) : base(parameterizedAction, canExecute) { //   Initialise(); } 

INotifyPropertyChanged IsExecuting. Initialise, :
 /// <summary> ///   /// </summary> private Command cancelCommand; /// <summary> ///   . /// </summary> public Command CancelCommand { get { return cancelCommand; } } /// <summary> /// / , ,     /// </summary> /// <value> /// <c>true</c>     ;   - <c>false</c>. /// </value> public bool IsCancellationRequested { get { return isCancellationRequested; } set { if (isCancellationRequested != value) { isCancellationRequested = value; NotifyPropertyChanged("IsCancellationRequested"); } } } /// <summary> ///   /// </summary> private void Initialise() { //    cancelCommand = new Command( () => { // Set the Is Cancellation Requested flag. IsCancellationRequested = true; }, true); } 

, , — IsCancellationRequested true. . IsCancellationRequested, , .

, . :
 /// <summary> /// , ,     . /// </summary> private bool isExecuting = false; /// <summary> /// / ,  ,     .. /// </summary> /// <value> /// <c>true</c>    ;  <c>false</c>. /// </value> public bool IsExecuting { get { return isExecuting; } set { if (isExecuting != value) { isExecuting = value; NotifyPropertyChanged("IsExecuting"); } } } 

We continue. Cancelled PropertyChanged ( INotifyPropertyChanged):
 /// <summary> /// The property changed event. /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// ,   . /// </summary> public event CommandEventHandler Cancelled;      DoExecute. /// <summary> ///  . /// </summary> /// <param name="param">.</param> public override void DoExecute(object param) { //     ,  . if (IsExecuting) return; //   ,     . CancelCommandEventArgs args = new CancelCommandEventArgs() { Parameter = param, Cancel = false }; InvokeExecuting(args); //   - . if (args.Cancel) return; //   . IsExecuting = true; 

, , .
 //   . #if !SILVERLIGHT callingDispatcher = Dispatcher.CurrentDispatcher; #else callingDispatcher = System.Windows.Application.Current.RootVisual.Dispatcher; #endif 

, , .

, Silverlight WPF.
 // Run the action on a new thread from the thread pool // (this will therefore work in SL and WP7 as well). ThreadPool.QueueUserWorkItem( (state) => { //  . InvokeAction(param); // Fire the executed event and set the executing state. ReportProgress( () => { //     . IsExecuting = false; //  , //    - ,   –  . if(IsCancellationRequested) InvokeCancelled(new CommandEventArgs() { Parameter = param }); else InvokeExecuted(new CommandEventArgs() { Parameter = param }); //    . IsCancellationRequested = false; } ); } ); } 

. , InvokeAction , . , ReportProgress , Executed. ( ), IsExecuting, : Cancelled Executed. , ReportProgress:
 /// <summary> /// Reports progress on the thread which invoked the command. /// </summary> /// <param name="action">The action.</param> public void ReportProgress(Action action) { if (IsExecuting) { if (callingDispatcher.CheckAccess()) action(); else callingDispatcher.BeginInvoke(((Action)(() => { action(); }))); } } 


– Command
EventBindings - WPF Silverlight. WPF EventBindingsCollection FreezableCollection, . Silverlight FreezableCollection, .
 public static class EventBindings { /// <summary> ///  Event Bindings. /// </summary> private static readonly DependencyProperty EventBindingsProperty = DependencyProperty.RegisterAttached("EventBindings", typeof(EventBindingCollection), typeof(EventBindings), new PropertyMetadata(null, new PropertyChangedCallback(OnEventBindingsChanged))); /// <summary> /// Gets the event bindings. /// </summary> /// <param name="o">The o.</param> /// <returns></returns> public static EventBindingCollection GetEventBindings(DependencyObject o) { return (EventBindingCollection)o.GetValue(EventBindingsProperty); } /// <summary> /// Sets the event bindings. /// </summary> /// <param name="o">The o.</param> /// <param name="value">The value.</param> public static void SetEventBindings(DependencyObject o, EventBindingCollection value) { o.SetValue(EventBindingsProperty, value); } /// <summary> /// Called when event bindings changed. /// </summary> /// <param name="o">The o.</param> /// <param name="args">The <see /// cref="System.Windows.DependencyPropertyChangedEventArgs"/> /// instance containing the event data.</param> public static void OnEventBindingsChanged(DependencyObject o, DependencyPropertyChangedEventArgs args) { // Cast the data. EventBindingCollection oldEventBindings = args.OldValue as EventBindingCollection; EventBindingCollection newEventBindings = args.NewValue as EventBindingCollection; // If we have new set of event bindings, bind each one. if (newEventBindings != null) { foreach (EventBinding binding in newEventBindings) { binding.Bind(o); #if SILVERLIGHT // If we're in Silverlight we don't inherit the // data context so we must set this helper variable. binding.ParentElement = o as FrameworkElement; #endif } } } } 

EventBinding.Bind:
 public void Bind(object o) { try { //        EventInfo eventInfo = o.GetType().GetEvent(EventName); // Get the method info for the event proxy. MethodInfo methodInfo = GetType().GetMethod("EventProxy", BindingFlags.NonPublic | BindingFlags.Instance); // Create a delegate for the event to the event proxy. Delegate del = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, methodInfo); // Add the event handler. (Removing it first if it already exists!) eventInfo.RemoveEventHandler(o, del); eventInfo.AddEventHandler(o, del); } catch (Exception e) { string s = e.ToString(); } } /// <summary> /// Proxy to actually fire the event. /// </summary> /// <param name="o">The object.</param> /// <param name="e">The <see /// cref="System.EventArgs"/> instance /// containing the event data.</param> private void EventProxy(object o, EventArgs e) { #if SILVERLIGHT // If we're in Silverlight, we have NOT inherited the data context // because the EventBindingCollection is not a framework element and // therefore out of the logical tree. However, we can set it here // and update the bindings - and it will all work. DataContext = ParentElement != null ? ParentElement.DataContext : null; var bindingExpression = GetBindingExpression(EventBinding.CommandProperty); if(bindingExpression != null) bindingExpression.UpdateSource(); bindingExpression = GetBindingExpression(EventBinding.CommandParameterProperty); if (bindingExpression != null) bindingExpression.UpdateSource(); #endif if (Command != null) Command.Execute(CommandParameter); } 


Source: https://habr.com/ru/post/196960/


All Articles