Implementing EventHandlers within ViewModel with MVVM WPF - 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>

Related

How can I disable combobox before button clicked in WPF MVVM

In my project I have a "Generate Reports" Button and a "Select Location" ComboBox. I want the user to be unable to choose a location before clicking
"Generate Report" Button.
the xaml containing the combobox and the button:
<Button Content="Generate Reports" Command="{Binding GenerateReportsCommand}" Height="36" Margin="4" />
<ComboBox Grid.Column="2" ItemsSource="{Binding Locations}" SelectedItem="{Binding SelectedLocation}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding LocationFilterCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
What can I do in the ViewModel or UI? Thank you for all your helps.
I'm going to assume you know how to use data binding (based on your post you already use it).
Create a new Boolean in your ViewModel and bind the ComboBox's IsEnabled to it. (Something like IsEnabled="{Binding YOURBOOLEAN}")
Update that Boolean from your GenerateReportsCommand after the user successfully generated the report. If you set that variable to true, the ComboBox's IsEnabled will also be set to true.
You should have an additional property inside the ViewModel.
private bool _areReportsGenerated = false;
public bool AreReportsGenerated
{
get => _areReportsGenerated;
set
{
_areReportsGenerated = value;
OnPropertyChanged(); // you method's implementation of INotifyPropertyChanged
}
}
If you don't have an implementation of INotifyPropertyChanged you can take a look at Microsoft one.
Inside GenerateReportsCommand you should set AreReportsGenerated to true.
In the xaml then you only have to bind the newly created property to the IsEnabled property of the ComboBox
<ComboBox Grid.Column="2" ItemsSource="{Binding Locations}" SelectedItem="{Binding SelectedLocation}" IsEnabled="{Binding AreReportsGenerated}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding LocationFilterCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
It should work.
I hope I was helpful.

Can a WPF DataTrigger Deactivate a KeyBinding?

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.

InputBinding CommandParameter Binding to a collection

For KeyBinding I am setting up the CommandParameter in Xaml using Binding. In the Binding has a Converter setup.
When I bind the parameter to a property (INPC) the binding system re-evaluates using the Converter.
However, when I the parameter to observable collection, on CollectionChanged the binding system does not re-evaluate. Hence I receive the initial converted value.
Is there way I trigger CommandParameter to re-evalaute on CollectionChanged.
<TextBox Grid.Row="0" Text="{Binding MyParameter}">
<TextBox.InputBindings>
<KeyBinding Gesture="CTRL+D"
Command="{Binding MyCommand}"
CommandParameter="{Binding MyParameter,
Converter={StaticResource converter}}">
</KeyBinding>
<KeyBinding Gesture="CTRL+T"
Command="{Binding MyCommand}"
CommandParameter="{Binding ChangedValuesCollection,
Converter={StaticResource CollectionConverter}}">
</KeyBinding>
</TextBox.InputBindings>
</TextBox>
In the above code, I have setup a viewmodel (datacontext). But I intend to bind the command parameter to Grid's SelectedRows collection.
If you just want the selected items of DataGrid as your command parameter you can directly bind to it. Assuming Datagrid and Textbox are in same visual tree
<KeyBinding Gesture="CTRL+T"
Command="{Binding MyCommand}"
CommandParameter="{Binding SelectedItems, ElementName="myDataGrid"}">

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.

Event trigger is not working inside ItemControl

I have a Item control which fills by a list, and list is collection of two parameters 'Time' and 'Description'. For it, i am using a HyperLinkButton for time and a Label for description.
What i want is that, i want to create click event using EventTrigger of hyperLink button in Main viewModel. My code is:
<ItemsControl
x:Name="transcriptionTextControl"
ItemsSource="{Binding MyCollectionOfTranscription, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<HyperlinkButton Content="{Binding Time}">
<ToolTipService.ToolTip>
<ToolTip Content="Time"/>
</ToolTipService.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
</i:EventTrigger>
</i:Interaction.Triggers>
</HyperlinkButton>
<sdk:Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When i build project, it doesn't give error but ICommand for hyperLink, shows warning as 'Cannot resolve symbol HyperLinkButtonCommand', while this event trigger is working fine outside this .
Not getting, what is actual problem behind it, plz give your valuable suggestion...
First off,
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
The Binding is trying to locate a property called HyperLinkButtonCommand on the instance of the type that is contained within MyCollectionOfTranscription (you don't need to bind to this two-way).
(Side note, sending an ItemsControl into your Command is not MVVM.)
The ItemsControl iterates through each element in this collection, creates a copy of the template defined in ItemsControl.ItemTemplate, and sets the BindingContext equal to this element (I assume its a Transcript). You can tell this from the warnings you get from the binding failing to find your HyperLinkButtonCommand if you crank up databinding debug settings.
Assuming
HyperLinkButtonCommand is a command defined in your ViewModel, and
The root of this xaml is a Window (could be a UserControl, but am assuming here)
Your ViewModel is the DataContext of the Window
you can change the binding to the following and it should work (or you should get a clue from it)
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
I prefer just to give my root an x:Name of "root" and use "ElementName=root" in cases like this.

Resources