I have a class derived from ItemsControl in which I implement my own selection-algorithm which uses the MouseLeftButtonDown to change the selection.
Now I needed a specific control to handle Mouseclicks in the ViewModel, so I wrote the following:
<controls:DraggableItemsContainer bla="blub">
<controls:DraggableItemsContainer.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DeselectSubGroupsCommand}" />
</controls:DraggableItemsContainer.InputBindings>
</controls:DraggableItemsContainer>
What happens now is, that I don't get the MouseLeftButtonDown-event anymore - which is comprehensable because the command "e.handles" the click.
But in this case, that's not what I want. Is there a way to fire the event anyway?
PS: Yes I need to do the selection in the MouseLeftButtonDown-event and not in any Preview-event
Two options:
You can have your custom ItemsControl hook PreviewMouseDown instead of MouseDown.
You can continue to hook MouseDown, but do it by calling AddHandler, and pass true for the handledEventsToo parameter.
If it's important that you get notified after the MouseBinding has done its work, then you need to use AddHandler. If you're okay with getting the message first, PreviewMouseDown is simpler.
I think your issue is that you try to catch the same event, on the same control, using two different approaches.
Try this:
<Grid>
<Grid.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding DeselectSubGroupsCommand}" />
</Grid.InputBindings>
<controls:DraggableItemsContainer bla="blub">
.....
</controls:DraggableItemsContainer>
</Grid>
Make sure that in your control you have e.Handled = false.
This should allow for your internal logic to run, and then execute the command. If you need it in the opposite order..... I don't know.
Related
I have a WPF app which uses DevExpress controls and MVVM with PRISM.
I'm using DockLayoutManager's 'DockOperationCompleted' event to invoke a command on my view model like this:
<dxd:DockLayoutManager x:Name="dockContainer">
<i:Interaction.Triggers>
<i:EventTrigger EventName="DockOperationCompleted">
<i:InvokeCommandAction Command="{Binding DataContext.SaveLayoutCommand, ElementName=dockContainer}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<dxd:LayoutGroup/>
</dxd:DockLayoutManager>
The purpose of the 'SaveLayoutCommand' command is to save the layout so it can be restored later in time.
The 'DockOperationCompleted' event is raised after a DockItem gets docked or closed (there are other cases but they are irrelevant).
The problem is that when I close the main window, the dock items in my DockLayoutManager are getting closed one by one and thus 'SaveLayoutCommand' gets invoked for every closed dock item and I don't want this to happen.
The 'DockOperationCompletedEventArgs' with which the event gets raised has a 'DockOperation' property which I can check agains, but I'm not sure where exactly should this code fit in.
What I am trying to achieve is that the command should be invoked only in one case - when the item is docked
My question is : is there a way to 'filter' when the command gets invoked based on the event's event args?
Thanks :)
In addition to aKzenTs answer I want to point out that with DevExpress its rather easy to pass the event args to a command.
You should use EventToCommand from their MVVM library (PassEventArgsToCommand-Property). If you want to keep your viewmodel clean of DevExpress you can additionally use a Converter to transform the event args to an arbitrary object.
<dxmvvm:Interaction.Triggers>
<dxmvvm:EventToCommand Command="{Binding YOURCOMMAND}"
EventName="THEEVENT"
EventArgsConverter="{StaticResource YOUREVENTARGSCONVERTER}"
PassEventArgsToCommand="true" />
</dxmvvm:Interaction.Triggers>
There is no builtin way to filter the events that are raised before invoking an action. You can however implement your own custom trigger action that does the filtering.
Unfortunately it's also not easy to access the event args and passing them to the command. See this question as a reference:
MVVM Passing EventArgs As Command Parameter
I'm little confused about RelayCommand and EventToCommand in Mvvmlight.
It seems that EventToCommand handle the EventTrigger and call a RelayCommand to do job.
Such as:
<i:Interaction.Triggers>
<i:EventTrigger x:Uid="i:EventTrigger_1" EventName="MouseLeftButtonUp">
<cmd:EventToCommand x:Uid="cmd:EventToCommand_1" Command="{Binding Form_MouseLeftButtonUpCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Is my understanding correct?
So, can we use RelayCommand directly with EventTrigger, no need to use EventToCommand?
Thanks for your help!
EventToCommand is a custom behavior. It is first provided by Expression blend team and now Part of WPF 4. If you're not using WPF4. you require Blend SDK from here.
Behaviors encapsulates functionality as reusable components. These are to be used when feature is not present by default. For example Adding Command support to Label, Combobox etc.
can we use RelayCommand directly with EventTrigger, no need to use EventToCommand?
No. RelayCommand is a shortcut to avoid code redudnency to define custom commands.It extends ICommand whose delegates can be attached for Execute(T) and CanExecute(T). It is similar to DelegateCommand of Prism library.
<cmd:EventToCommand x:Uid="cmd:EventToCommand_1" Command="{Binding Form_MouseLeftButtonUpCommand}" PassEventArgsToCommand="True"/>
In above line cmd:EventToCommand is additional feature to the underlying control. Form_MouseLeftButtonUpCommand is the Command it executes. This command can be encapsulated as RelayCommand.
Thank Tilak for your useful answer.
With the explanation from Tilak, I did mess up like putting a binding in a event handler (such as Button GotFocus="{Binding DoJob}") --> Build failed and found that Event Handler like this does not support Binding. We can only bind in Command (such as Button Command="{Binding DoJob}" /> and default event is invoked in this situation, with button, it should be Click event).
Do something stupid will help me to understand the life more - LOL
can we use RelayCommand directly with EventTrigger, no need to use
EventToCommand?
Actually, I intend NOT to use EventToCommand, and I found the solution for that: use InvokeCommandAction instead (belongs to System.Windows.Interactivity - Mvvm-light also refers to this assembly).
I have an application where I have a UserControl with a toolbar in it. This toolbar has a button Execute which in turn has its command bound to an ICommand derived class that the viewmodel exposes as a property.
<Button Grid.Row="0" Command="{Binding ExecuteCommand}">Execute</Button>
Now, I'd like to bind this to a keyboard shortcut (F5) as well. This needs to be bound in the context of the UserControl since, it's only applicable if this usercontrol is visible at the moment.
Another option is to bind it to the KeyDown of the textbox that actually contains the text to execute, but I'm really shaky when it comes to how to route the event from the control to the command in the viewmodel without really ugly hacks in the code-behind of the usercontrol.
Any pointers are appreciated!
There was another answer that disappeared for some reason. This worked fine:
<UserControl.InputBindings>
<KeyBinding Key="F5" Command="{Binding ExecuteCommand}" />
</UserControl.InputBindings>
I'd like to give credit to that guy if possible. Please appear again :)
Afaik, there isn't a way to directly bind to a keypress, but there are some work arounds. It looks like others have had this problem as well, have you seen this post? My other suggestion is to look into attached commands.
I am building a simple Calculator application. I'm also learning how to apply the MVVM pattern in my app.
I would like each one of the "Digit" buttons of my calculator to bind to the same Command, where they would only differ in the digit (Text) of the button that raised the command.
For example, When button "1" is clicked, i would like to receive the notification about it, extract "1" from the Sender's property, and continue the rest of the work needed.
This allows me to define a single method instead of 10 different handlers.
This is not possible as far as what i've seen in all MVVM tutorials till now, since the Command binding does not provide all this information to me when binding to the actual method that will handle the click.
Is there any way to easily do what i require?
Assuming I understand what you're trying to do, you can use the CommandParameter property to let different buttons supply values to the same command. For example:
...
<Button Content="1" Command="{Binding ButtonClickCommand}" CommandParameter="1"/>
<!-- Or, bind directly to the button's content using RelativeSource, like so: -->
<Button Content="2" Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}"/>
...
And in your command's delegate method:
private void ButtonClickCommandHandler(object parameter)
{
switch(int.Parse(parameter.ToString()))
{
case 1:
...
case 2:
...
}
}
Just provide the digit as a Button.CommandParameter. (Which is passed as a method parameter of ICommand.Execute (and CanExecute))
In Silverlight, both the Button and the RadioButton controls have a Click event, since they inherit from System.Windows.Controls.Primitives.ButtonBase.
If we want to simulate this Click event for a Button, we can use the ButtonAutomationPeer class, like so (given a button called myButton):
ButtonAutomationPeer peer = new ButtonAutomationPeer(myButton);
IInvokeProvider ip = (IInvokeProvider)peer;
ip.Invoke();
However, when we try to do the same thing for a RadioButton, we discover that the RadioButtonAutomationPeer class does not implement IInvokeProvider (so we can't call Invoke()). Is there some other way we can cause the Click event for a RadioButton to be fired?
Try this:
RadioButtonAutomationPeer peer = new RadioButtonAutomationPeer(myRadioButton);
IToggleProvider tp = (IToggleProvider) peer;
while (tp.ToggleState != targetToggleState)
{
tp.Toggle();
}
You'll need to know your desired toggle state (On, Off, or Indeterminate). Or if you just want to toggle to a different state, strike the while loop just call Toggle.
Obviously this isn't the exact same thing as a click. If you've bound the radio button to a command, you might be forced to be a little more explicit and do something like this to get your automation to work:
<RadioButton ... (don't set the Command property)>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
Using the Blend SDK (that's the "i:" xmlns), you're invoking a command when the RadioButton is checked. Personally I'd do that approach even without automation over just binding a command to a RadioButton: it's not clear when the command executes with a binding, but the triggers add some clarity and remove the need for me to think the next time I look at the code.