Can a WPF DataTrigger Deactivate a KeyBinding? - wpf

I've got a ListView with some KeyBindings that let the user move and delete entries with keyboard shortcuts. However, I don't want the bindings to be accessible all the time.
The button controls to add, remove, and move entries have their visibility tied to the selection of a ComboBox (only certain users can edit). I want the keyboard shortcuts to deactivate based on the box selection as well.
I haven't been able to find any info on whether or not this is possible yet. What do you guys think?
<ComboBox x:Name="TesterIdentityBox" ItemsSource="{Binding Path=TesterIdentityList, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Name" SelectedItem="{Binding Path=TesterIdentitySelection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="{Binding TesterIdentityIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ListView ItemsSource="{Binding TestViewList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="{Binding SelectedTestIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SelectedTest}">
<ListView.InputBindings>
<KeyBinding Key="Up" Command="{Binding Path=MoveTestUpCommand}" CommandParameter="{Binding Path=SelectedTest.Description}" />
<KeyBinding Key="Down" Command="{Binding Path=MoveTestDownCommand}" CommandParameter="{Binding Path=SelectedTest.Description}" />
<KeyBinding Key="Delete" Command="{Binding Path=RemoveTestCommand}" />
</ListView.InputBindings>
I used Style Setters with DataTriggers to alter the command buttons' visibility, but I don't know what (if anything) is the equivalent for a non-visual element like a KeyBinding.

The simplest way in this case would be to implement the CanExecute() methods in your MoveTestUpCommand, MoveTestDownCommand and RemoveTestCommand. This methods should return false when you don't want the user to be able to do those things. So, your KeyBindings will have no effect since the commands will not be executed.
If your buttons' Command properties are also bound to these commands, these buttons will then update their availability (IsEnabled property) automagically according to the CanExecute() return values. To update the view state from the viewmodel, simply call the RaiseCanExecuteChanged() methods on corresponding commands (this depends on your ICommand implementation however).
To set the button's visibility according to its availability, you could use something like:
<Button
Command = "{Binding SampleCommand}"
Visibility = "{Binding IsEnabled, RelativeSource = {RelativeSource Self}, Converter = {StaticResource BooleanToVisibilityConverter}}"/>
There is an implementation of the BooleanToVisibilityConverter in System.Windows.Controls.

Related

Binding a button nested in DataTemplate

I need to bind a button's command inside a datatemplate like below:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="-" Cursor="Hand" Width="50"
Background="Red" x:Name="removeButton"
Command="{Binding Remove}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
Unfortunately it does not work. How can I bind a command in a button insade a datatemplate?
I found that thread in the forum:
Bindings in nested WPF DataTemplates
but the method given by person, who answered this question, does not work as well. I think, that something has changed in WPF since this time, I would you grateful for your help.
If Remove is defined in the view model of the parent ListView, you could bind to it using a RelativeSource:
Command="{Binding DataContext.Remove,
RelativeSource={RelativeSource AncestorType=ListView}}"
You could also set the AncestorType to Window or UserControl depending on where the command property is defined and where the DataTemplate is applied.

Send current Item and checkbox value in command parameters

I have a TreeView setup with a HierarchialDataTemplate. It's ItemsSource is bound to a collection of Overlay objects in my viewmodel, where each Overlay has a collection of Layer objects (thus the HierarchialDataTemplate). For each Overlay, I'm displaying a CheckBox and a Label which is simply bound to the Overlay's Name property.
What I'm trying to do is, each time one of the checkboxes is checked/unchecked, the current Overlay and the IsChecked property of the CheckBox will be sent as command parameters to my viewmodel.
If I'm not using the MultiValueConverter, I can send one of the properties fine. But I need to send both as parameters.
Below is the related .xaml for the treeview. I'm only showing the necessary parts and just the Checked trigger because the Unchecked is exactly the same:
<TreeView ItemsSource="{Binding OverlaysViewSource}" Name="LayersTreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Layers}" >
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding DataContext.SetVisibilityCmd, RelativeSource={RelativeSource AncestorType=UserControl}}" >
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource multiValueConverter}">
<Binding Path="IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}" />
<Binding/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<Label Content="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
So in the MultiBinding, the first one: <Binding Path="IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}" /> to try and send the checkbox's IsChecked property. However, the value I'm getting in the command is DependencyProperty.UnsetValue.
The second one is just for the current Overlay item, but the whole TreeView is being sent as a parameter.
Update:
The Overlay class is a third party control and is used in a lot of places that I can't modify. So I can't just add a property to it.
Update2: I've managed to get the Overlay to send properly. Just need the IsChecked property now.
The binding for IsChecked should use {RelativeSource Self}, since the binding is being applied to the CheckBox via the Style.
Your update to your question shows you've already solved the other one.

Hook up the key down in TextBox (MVVM)

Have a little problem with hooking up the keydown event without codebehind!
So, we have the combobox
<ComboBox Height="20" Width="auto"
ItemsSource="{Binding AlignComboItems}"
SelectedValue="{Binding SelectedComboItem, Mode=TwoWay}"
SelectedValuePath="Key" DisplayMemberPath="Value"
SelectedItem="{Binding SelectedItem}"
x:Name="cmbBoxAlign">
</ComboBox>
and some TextBox.
<TextBox Text={Binding SomeSource}></TextBox>
How to catch the keydown event on the TextBox for selecting the (for example) the last element in ComboBox? I cant use the TextBox DataSource property changing, because need hook the user input up.
If you don't mind installing the Expression Blend SDK you should be able to do this in your textbox
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding Path=TheCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
After adding a reference to System.Windows.Interactivity and the following namespace in your xaml
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Link to the Expression SDK for 4.0
http://www.microsoft.com/en-us/download/details.aspx?id=10801
If you want to have code fire in your view model every time there is a key down in the text box you need to change the binding slightly:
<TextBox Text="{Binding SomeSource, UpdateSourceTrigger=PropertyChanged}"
Then in the view model the setter will be called:
private string _someSource;
public string SomeSource{
get { return _someSource; }
set {
//this will fire on key down
_someSource= value;
//based off the value you can set SelectedComboItem accordingly
OnPropertyChanged( "SomeSource" );
}
}
Also, make sure you have INotifyPropertyChanged set up on your view model.

Implementing EventHandlers within ViewModel with MVVM WPF

I have a ComboBox:
<ComboBox x:Name="cbConnection"
ItemsSource="{Binding Source={StaticResource XmlConnectionList}, XPath=//ComboItem}"
DisplayMemberPath="Key"
SelectedValuePath="Value"
SelectedValue="{Binding Path=ConnectionString,ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}"
Margin="{StaticResource ConsistentMargins}"
Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}" Width="120"
LostFocus="{Binding Path=cbConnection_LostFocus}"/>
I am trying to get the LostFocus event handler moved to the ViewModel because I do some error handling within the setter for the SelectedValue binding "ConnectionString" found in the ViewModel. I want this to happen if a user reselects the same ComboBoxItem, which does fire OnPropertyChanged unless a different list item is selected.
The above binding results in error
A 'Binding' cannot be set on the 'AddLostFocusHandler' property of
type 'ComboBox'. A 'Binding' can only be set on a DependencyProperty
of a DependencyObject.
How can I fire repeatable code within the ViewModel on selection of any item within a ComboBox, regardless of the user's selection?
You'll need to include a reference to the System.Windows.Interactivity dll, but it will look something like this:
xmlns:b="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<ComboBox>
<b:Interaction.Triggers>
<b:EventTrigger EventName="LostFocus">
<b:InvokeCommandAction Command="{Binding cbConnection_LostFocus}" CommandParameter="{Binding}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</ComboBox>
Josh's answer worked for me with a different namespace:
xmlns:b="http://schemas.microsoft.com/expression/2010/interactivity"
<ComboBox>
<b:Interaction.Triggers>
<b:EventTrigger EventName="LostFocus">
<b:InvokeCommandAction Command="{Binding cbConnection_LostFocus}" CommandParameter="{Binding}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</ComboBox>

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

Resources