WPF MVVM event binding

Event transfer command

Most of our controls with Command dependent properties inherit the ICommandSource interface, which has three function members: ICommand interface type property Command, object type property CommandParameter,IInputElement type property CommandTarget, ButtonBase and MenuItem basically inherit the two basic classes of ICommandSource interface. Therefore, buttons, checkboxes and RadioButton s inherit from ButtonBase and have Command dependent properties, and MenuItem is the same. But there are no textboxes we often use.

Now we have this demand. We need to add a second Textbox on the basis of this interface. When the text of the Textbox changes, we need to merge and update the Name of the button and the text string of the second Textbox to the first Textbox. Our first intuition will certainly think of using the TextChanged event of the Textbox, so how to turn TextChanged into a command?

First, we introduce the following in the xmal interface:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

The assembly system Windows. Interaction DLL is in the Expression Blend SDK, and Prism package also includes it, so we can directly import it, and then we add the code of the second Textbox:

 <Grid>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="12,44,0,0" TextWrapping="Wrap" Text="{Binding CurrentTime2,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="TextChanged">
                    <i:InvokeCommandAction Command="{Binding DelegateCommand1}" CommandParameter="{Binding ElementName=mybtn}"></i:InvokeCommandAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </TextBox>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding CurrentTime}" VerticalAlignment="Top" Width="125"/>
        <Button Content="Button" Command="{Binding DelegateCommand}" Name="mybtn" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}" HorizontalAlignment="Left" Margin="11,75,0,0" VerticalAlignment="Top" Width="75"/>
        <CheckBox Content="CheckBox" IsChecked="{Binding Ischecked}" HorizontalAlignment="Left" Margin="91,79,0,0" VerticalAlignment="Top"/>

    </Grid>

Background code

public class MainWindowViewModel : BindableBase
    {
        private string _title = "Prism Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
        private string _currentTime;

        public string CurrentTime
        {
            get { return _currentTime; }
            set { SetProperty(ref _currentTime, value); }
        }
        private bool _ischecked;

        public bool Ischecked
        {
            get { return _ischecked; }
            set { SetProperty(ref _ischecked, value); }
        }
        private string _currentTime2;

        public string CurrentTime2
        {
            get { return _currentTime2; }
            set { SetProperty(ref _currentTime2, value); }
        }
        private DelegateCommand<object> delegateCommand1;
        public DelegateCommand<object> DelegateCommand1 =>delegateCommand1 ?? (delegateCommand1 = new DelegateCommand<object>(ShowText));
        private DelegateCommand<object> delegateCommand;
        public DelegateCommand<object> DelegateCommand => delegateCommand ?? (delegateCommand = new DelegateCommand<object>(ShowButton).ObservesCanExecute(()=>Ischecked));
        private void ShowButton(object obj)
        {
            this.CurrentTime = ((Button)obj).Name + DateTime.Now.ToString();
        }
        private void ShowText(object obj)
        {
            this.CurrentTime = CurrentTime2+((Button)obj).Name + DateTime.Now.ToString();
        }
        public MainWindowViewModel()
        {

        }
    }

In the xaml code above, we added listening to the Blend EventTrigger of TextBox's TextChanged event. Whenever this event is triggered, InvokeCommandAction will call the TextChanged command

Pass the EventArgs parameter to the command

The event binding described above is not enough to deal with all situations, because in many cases, we also need to obtain data from the EventArgs of the event, such as mouse position and key status from the MouseMove event parameter, but the parameter passed by InvokeCommandAction to the Execute method without binding the CommandParameter is null. Therefore, we need to write our own class to handle the binding of events to commands.

 

Take a look at the InvokeCommandAction we used above. It inherits from TriggerAction < dependencyobject >. TriggerAction is an abstract class. We just need to inherit this class and implement the Invoke method. TriggerAction is introduced in MSDN as follows:

TriggerAction class (system. Windows. Interaction) | Microsoft docs

I have simply implemented the following code, as shown in the figure below. The dependency attribute is generated with the help of propdp code segment. Otherwise, I can't remember. It's troublesome to enter so many codes. When used, it is used to replace the previous InvokeCommandAction. If the CommandParameter is not bound, the event parameters are passed. If CommandParameter is bound, the bound parameter is passed.

Example of event binding

<esri:MapView x:Name="MyMap" Style="{DynamicResource MapViewStyle}"  Map="{Binding Map, Source={StaticResource MapViewModel}}" >
             <i:Interaction.Triggers>
                <i:EventTrigger EventName="GeoViewTapped">
                    <commands:MyEventCommand Command="{Binding GetmapTappedCommand,Source={StaticResource MapViewModel}}" >
                        
                    </commands:MyEventCommand>
                    <!--<i:InvokeCommandAction >
                    </i:InvokeCommandAction>-->
                </i:EventTrigger>
                <i:EventTrigger EventName="GeoViewTapped" >
                    <i:InvokeCommandAction  Command="{Binding GetmapTappedCommande,Source={StaticResource MapViewModel}}" CommandParameter="{Binding ElementName=MyMap }">
                    </i:InvokeCommandAction>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </esri:MapView>
   private DelegateCommand<GeoViewInputEventArgs> _getmapTapped;
        public DelegateCommand<GeoViewInputEventArgs> GetmapTappedCommand =>
            _getmapTapped ?? (_getmapTapped = new DelegateCommand<GeoViewInputEventArgs>(ExecuteGetCurrentTimeCommand));

 

Keywords: WPF microsoft

Added by Democreous on Sun, 09 Jan 2022 08:35:05 +0200