How to use Commands to react to a ListBox - wpf

I'm trying to learn Commanding and have set up a simple wpf project to use a custom command. I have a ListBox and a Button on a Window. When the ListBox has the focus and an Item is selected, I want the Button to be enabled, otherwise it should be disabled.
I define a CustomCommand in a separate CustomCommands class:
Public Shared ReceivedFocus As New RoutedCommand
and in my Window I set it up as follows:
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
and use the command for the ListBox as follows:
<ListBox
x:Name="lstInactive">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="GotFocus">
<i:InvokeCommandAction
Command="{x:Static local:CustomCommands.ReceivedFocus}"
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
and, finally, the CanActivate routine is:
Private Sub CanActivate(sender As Object, e As CanExecuteRoutedEventArgs)
If lstInactive.SelectedIndex >= 0 Then
e.CanExecute = True
Else
e.CanExecute = False
End If
End Sub
This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button. Should I ignore the CanExecute value in the CanActivate routine and instead just set the Enabled property of the Button? If so, what is the value of the CanExecute paramter of the CanExecuteRoutedEventArgs?
A second problem is that the GotFocus event is not firing until I select an item in the ListBox a second time.
Or maybe I don't have a grasp on Commanding at all and this is not the right approach. This small project is not important in itself, it is intended to make sure I understand Commanding after reading numerous articles about it before I start to use Commands in "real" projects. Sadly, at this stage it is clear I don't.

This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button.
Bind its Command property to the same command:
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
The Button should then be enabled or disabled based on the value that you set the CanExecute property to in your CanActivate event handler.
You probably also want to listen to the SelectionChanged event. This works as expected:
<StackPanel>
<StackPanel.CommandBindings>
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
</StackPanel.CommandBindings>
<ListBox x:Name="lstInactive">
<ListBoxItem>first</ListBoxItem>
<ListBoxItem>second</ListBoxItem>
<ListBoxItem>third</ListBoxItem>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{x:Static local:CustomCommands.ReceivedFocus}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CanActivate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = lstInactive.SelectedIndex >= 0;
}
private void ChangeSelection(object sender, ExecutedRoutedEventArgs e)
{
}
}

Related

How to trigger ViewModel command for a specific button events

How can a command on a ViewModel be invoked by a specific event of a button, such as MouseDoubleClick?
You can use the EventTrigger in the System.Windows.Interactivity namespace, which is part of the so-called Prism framework. If you're just getting started with MVVM, don't care too much for Prism by now, but keep it in mind for later. Anyway, you can steel the EventTrigger
It works like this:
Reference the assembly System.Windows.Interactivity.dll
In XAML, reference the namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Then in your Button or any other control, add a EventTrigger like this:
<Button Content="Button">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding CommandToBindTo}"
CommandParameter="{Binding CommandParameterToBindTo}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
This way, you bind your event to a Command on your DataContext.
Remark
To clarify the usage, here's a kind of real life example including the ViewModel. The fictional requirement is to allow the user to select an item in a list and then perform a command which takes the selected item as a parameter:
<ListBox x:Name="ItemsList" ItemsSource="{Binding Items}" />
<Button Content="Do something with selected item">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoSomethingCommand}"
CommandParameter="{Binding SelectedItem,
ElementName=ItemsList}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
And that would be the ViewModel. Note how the parameter to the command is used, in the example with a generic version of a DelegateCommand object as you get it in every MVVM framework (sometimes RelayCommand). This class takes the type of the required parameter as a generic parameter (here ItemViewModel) and requires a method which takes an according parameter (here ExecuteDoSomethingWithItem(ItemViewModel ...)). The rest is WPF magic: The oject to which the CommandParameter property is bound in your XAML will be passed through as the parameter in your Execute(...) function.
public class ViewModel
{
ObservableCollection<ItemViewModel> Items { get; set; }
public ICommand DoSomethingCommand
{
get
{
return _doSomethingCommand ??
(_doSomethingCommand = new DelegateCommand<ItemViewModel>(ExecuteDoSomethingWithItem));
}
}
private DelegateCommand<ItemViewModel> _doSomethingCommand;
private void ExecuteDoSomethingWithItem(ItemViewModel itemToDoSomethingWith)
{
// Do something
}
public ViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
// Fill the collection
}
}
Have fun with learning MVVM, it's worth it.
you can use attached command behaviors
=> http://geekswithblogs.net/HouseOfBilz/archive/2009/08/21/adventures-in-mvvm-ndash-generalized-command-behavior-attachments.aspx
You need to do a lot of pluming yourself if you going to use Command and Event Binding from out of the box WPF. You can gain a lot of just using existing framework such as MVVM Light Toolkit, or Cliburn Micro that already provide command and even binding.

how to use EventToCommand in a ItemContainerStyle?

<ListBox Grid.Row="1" ItemsSource="{Binding Source}" SelectedItem="{Binding SelectedItem,Mode=TwoWay}" DisplayMemberPath="Name">
<ListBox.ItemContainerStyle>
<Style>
<EventSetter Event="ListBoxItem.MouseDoubleClick" Handler="DoubleClick" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
This is how it works now.
What should I do if I want to Bind every ListBoxItem's DoubleClick event to a RelayCommand?
This is the way I am using the MVVMLight EventToCommand feature.
If you have a doubleclick event hook to that. If that is not available take the (preview)mousedown and check the clickCount in the command args. A ClickCount of 2 corresponds to a double click.
Please note: I have my own RelayCommand Implementation. The one from the MVMMLight toolkit might look different.
XAML:
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="MouseDown">
<mvvmLight:EventToCommand PassEventArgsToCommand="True" Command="{Binding MouseDownCommand}"></mvvmLight:EventToCommand>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
ViewModel:
public ICommand MouseDownCommand
{
get
{
if (_mouseDownCommand == null)
{
_mouseDownCommand = new RelayCommand(x => MouseDown(x as MouseButtonEventArgs));
}
return _mouseDownCommand;
}
}
private void MouseDown(MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
// do stuff
}
}
The best way to do this is to just use a normal event handler written in code-behind. If needed this can relay to a method or command on your model or view-model.
Tricks like using an EventToCommand behavior just cost you in terms of more complex XAML and a pretty high risk that you will leak memory. (This happens because EventToCommand listens to the CanExecuteChanged event even when it shouldn't.)

Is there any way to alias commands in WPF?

Is there any way to effectively "alias" commands in WPF ? My situation is this : I've created an application that uses ApplicationCommands.Delete in the context of a graphical editor that has a number of customized canvases. Some of the controls that are on these canvases use TextBoxes, but here's the problem : TextBox doesn't respond to ApplicationCommands.Delete, it responds to EditorCommands.Delete. Is there any way to cleanly get TextBox to respond to ApplicationCommands.Delete without subclassing or manually setting bindings on every TextBox instance ?
To answer your specific question, I know of no way to cause two separate routed commands to be treated as the same command. But because ApplicationCommands.Delete is a routed command, after it is delivered to its target, the TextBox and there is no command binding, it will begin bubbling up. So the simplest solution that meets your requirements is to install a command binding for ApplicationCommands.Delete somewhere inbetween the TextBox all the way up to and possibly including the Window, that implements the behavior you desire.
Here's an example that installs a handler on a parent Grid that sends the "right" command the the focused element which in this case will be a TextBox:
<Grid>
<Grid.CommandBindings>
<CommandBinding Command="ApplicationCommands.Delete" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Grid.CommandBindings>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Edit">
<MenuItem Header="_Delete" Command="ApplicationCommands.Delete"/>
</MenuItem>
</Menu>
<StackPanel>
<TextBox Text="Some text"/>
</StackPanel>
</DockPanel>
</Grid>
and here's the code-behind:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
EditingCommands.Delete.Execute(null, Keyboard.FocusedElement);
}

WPF Command bindings - how can I get to event handlers

Hi
Is there any way to choose from where XAML should use command bindings event handlers?
I added copule of command binding to my cusotm control, however functions which are resonsible for execute and can_execute are not directly in code behind but in another class. This class is derived from Canvas and I create instance of this class in XAML.
<s:MyCanvas Focusable="true" Background="Transparent" x:Name="OwnCanvas" FocusVisualStyle="{x:Null}" ScrollViewer.CanContentScroll="True" >
I add command bindings this way
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Copy}" CanExecute="event handler from object OwnCanvas" />
</UserControl.CommandBindings>
Is there any way to do that ? Or I have to transfer event handler directly to codebehind ??
I think you're gonna have to transfer the handler in codebehind as I don't think that's possible. I could be wrong and would love to be corrected if it is possible though.
What I usually do is just define the CommandBinding in your MyCanvas class (code behind) and then reference that MyCanvas as the CommandTarget in the custom control. Like this:
public MyCanvas()
{
...
CommandBindings.Add(
new CommandBinding(ApplicationCommands.Copy,
(sender, e) => {
// Execute Stuff
},
(sender, e) => {
e.CanExecute = true;
e.Handled = true;
}));
...
}
And in your custom control (given it lies within the visual tree of MyCanvas)...
<Button Command="{x:Static ApplicationCommands.Copy}" CommandTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type s:MyCanvas}}}"/>
With your CommandTarget set up like that the Execute and CanExecute methods will be called on it.

Wpf: Drag And Drop To A Textbox

I've googled this problem, and people have answered similar questions, but for some reason I can't get anything to work. I must have missed something here... At any rate, when I run the following code, the TextBox_DragEnter handler is never called. However, if I change the TextBox element in the xaml to a TextBlock element, it is called. Is there any way to get the same behavior from a TextBox element? The following code completely isolates the problem...
MainWindow.xaml:
<Window x:Class="Wpf1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Name="myGrid">
<TextBox AllowDrop="True" PreviewDragEnter="TextBox_DragEnter" PreviewDrop="TextBox_Drop" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace Wpf1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_DragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
}
private void TextBox_Drop(object sender, DragEventArgs e)
{
}
}
}
Many thanks in advance!
Andrew
EDIT:
Just to clarify, I would like to allow dropping a custom object into a textbox. In the Drop handler for the textbox, I would then like to set the text of the textbox to a property in the object, and then set the IsReadOnly property of the TextBox to false. I'm just having some trouble enabling drag and drop for the TextBox...
If you add a handler for PreviewDragOver, then set e.Handled = true it should work.
Works for me in any case.
TextBox seems to have already some default handling for DragAndDrop. If your data object is a String, it simply works. Other types are not handled and you get the Forbidden mouse effect and your Drop handler is never called.
It seems like you can enable your own handling with e.Handled to true in a PreviewDragOver event handler.
I could not find any details about that at MSDN, but
found http://www.codeproject.com/Articles/42696/Textbox-Drag-Drop-in-WPF very helpfull.
You may also want to handle PreviewDragEnter the same way as PreviewDragOver or it will default to the Forbidden Mouse on the first pixel.
In the handler make sure the DragEventArgs.Data is the type you want to drop. If it is, set DragEventsArgs.Effects to DragDropEffects.Move or something else in AllowedEffects. If it isn't the type you want to drop, set to DragDropEffects.None which disables dropping.
XAML for MVVM Light:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventToCommand Command="{Binding DragDropCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewDragOver">
<cmd:EventToCommand Command="{Binding PreviewDragEnterCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewDragEnter">
<cmd:EventToCommand Command="{Binding PreviewDragEnterCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
Handler in ViewModel:
private void ExecutePreviewDragEnterCommand(DragEventArgs drgevent)
{
drgevent.Handled = true;
// Check that the data being dragged is a file
if (drgevent.Data.GetDataPresent(DataFormats.FileDrop))
{
// Get an array with the filenames of the files being dragged
string[] files = (string[])drgevent.Data.GetData(DataFormats.FileDrop);
if ((String.Compare(System.IO.Path.GetExtension(files[0]), ".xls", true) == 0)
&& files.Length == 1)
drgevent.Effects = DragDropEffects.Move;
else
drgevent.Effects = DragDropEffects.None;
}
else
drgevent.Effects = DragDropEffects.None;
}
Better create your own Textbox class that implements Textbox. Then override the OnDrag-Events and set e.handled to false or do whatever you want.
It's a little dirty to use events that are not made for the original wanted behavior. Preview is to check some stuff and have a good Undo option before committing the real DragDrop-Events.

Resources