Handling Mouse events in Viewmodel in WPF MVVM project - wpf

I am using System.Windows.Interactivity.dll and Microsoft.Expression.Interaction.dll to do event handling in Viewmodel in my MVVM WPF project.
below is the code inside my Xaml:
<ItemsControl ItemsSource="{Binding Path= HeaderList}" Grid.Row="0" Grid.Column="0" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Width="100" HorizontalAlignment="Left" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
for this I added namespaces in the same Xaml.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactions"
and in my viewmodel I have created a method having PrevMouseDownEventHandler name which is same as that of I mentioned as CallMethod inside EventTigger in the Xaml.
On running my application when I presses mouse button on TextBlock event is generated and look for PrevMouseDownEventHandler method and leave me into following exception:
Could not find method named 'PrevMouseDownEventHandler' on object of type 'string' that matches the expected signature.
this method is as below in my ViewModel.
public void PrevMouseMoveEventHandler(object sender, MouseButtonEventArgs e)
{
// Some implementation here;
}
I don't have any idea where I am going wrong.
Except this all the functionalities inside Viewmodel is working fine for me.
what would be possible solution for this?

CallMethodAction is a delegate with no parameters and no return value. So the "handler" (really an action trigger) would have to look like this:
public void PrevMouseMoveEventHandler()
{
// Some implementation here;
}
Also, you'll need to bind to the View Model (your current binding points to the current item in the ItemsControl). You could do this using RelativeSource binding:
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler"
TargetObject="{Binding Path=DataContext,RelativeSource={RelativeSource AncestorType=ItemsControl}" />

It is looking for the method on the String object you have bound your Text property to.
Basically your data context has changed from the view model to a property of the View Model.

Related

WPF Set Visibility on DataTemplate UI Element from MVVM View Model

I have a control that is set up as a DataTemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="KEYBOARD_EN">
<StackPanel>
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
</StackPanel>
</DataTemplate>
In this DataTemplate there is a control on which I wish to set the Visibility from various view models:
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource ...} > Register </Button>
I do routed events with my control, so I tried to set up something similar, but no matter what I try, the RegisterButtonVisible property does not get picked up:
public partial class MainKeyboard : UserControl
{
public static DependencyProperty RegisterButtonVisibleProperty;
public Visibility RegisterButtonVisible
{
get { return (Visibility)GetValue(RegisterButtonVisibleProperty); }
set { SetValue(RegisterButtonVisibleProperty, value); }
}
static MainKeyboard()
{
RegisterButtonVisibleProperty = DependencyProperty.Register("RegisterButtonVisible", typeof (Visibility),
typeof (MainKeyboard));
}
}
In my ViewModel I do this:
public Visibility RegisterButtonVisible // get, set, raisepropchange, etc
My DataTemplate with the button in it is wrapped in a userControl:
<UserControl x:Class="Bleh.Assets.MainKeyboard"
x:Name="TheControl"
Unloaded="UserControl_Unloaded">
<Viewbox>
<Grid>
<ContentControl Name="ctrlContent" Button.Click="Grid_Click" />
</Grid>
</Viewbox>
and is used in my views like this:
<assets:MainKeyboard
RegisterButtonVisible="Collapsed"
Loaded="MainKeyboard_Loaded">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Register">
<b:InvokeCommandAction Command="{Binding ConfirmEmailAddressCommand}"/>
</b:EventTrigger>
<b:EventTrigger EventName="Enter">
<b:InvokeCommandAction Command="{Binding EnterKeyCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</assets:MainKeyboard>
Please note this attribute:
RegisterButtonVisible="Collapsed"
This is my dependency property. It shows up in intelliesense, so the CLR has registered it correctly, but it does NOT pick up the property assignment (Collapsed is ignored).
This makes me feel like it is very close, but I do remember someone telling me I can not do this, thus the EventTriggers (this is a common issue with datatemplates and MVVM apparently).
So one option is to use something in the Interaction namespace, like I do my event triggers ( I just need to fire a "Visibility" trigger on this button somehow, at least I figure).
What is the right ANY way to do this in MVVM?
Fixing your code
In order to make your existing code work, you need to tell need to tell WPF what object RegisterButtonVisible should be read from. If it's a user control, give the UserControl a name and then reference that element via ElementName, like so:
<UserControl ... lots of stuff here
x:Name="TheControl"
>
In your button binding:
<Button Visibility="{Binding ElementName=TheControl, Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, if you can't do that because the button and the usercontrol are in different files, you can still use an ancestor binding:
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type assets:MainKeyboard}},
Path=RegisterButtonVisible}"
Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
which, for each button, will walk up to find the closest instance of assets:MainKeyboard and then bind to the RegisterButtonVisible property.
Using MVVM
If you want to achieve the same using MVVM (instead of on a control), you need to use a converter to convert a boolean to a visibility property, like so:
<Button Visibility="{Binding IsRegistrationAllowed, Converter={StaticResource BoolToVis}}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, that assumes that your DataContext is set up correctly and pointing at your ViewModel.

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.

WP7 XAML Bind to property in parent

I'm writting an app in WP7 (Silverlight 3). I have a view model that looks like this
public class MainViewModel
{
public List<ActivityTypes> ActivityTypes{get;set;}
public RelayCommand LoadActivity{get;set;}
}
My pages datacontext is set to the view model and I have a listbox with it's item source set to the ActivityTypes collection. In the listbox I'm trying to render a list of buttons who's command is bound to the LoadActivity property on the viewmodel. RelayCommand is part of the MVVM Light toolkit in case you are wondering what it is.
The problem I am having is I can't find a way to bind my button command to the LoadActivity property as my listbox has it's itemsource set to the Activitytypes collection and this property is in the parent. I've read about FindAncester but it doesn't look like this is supported in Silverlight 3.
My XAML looks like this
<UserControl.Resources>
<DataTemplate x:Key="ActivityTypeListTemplate">
<StackPanel>
<Button Command="{Binding LoadActivity}"> <!--binding not found as we're in the collection-->
<TextBlock Text="{Binding Name}" FontSize="50"/>
</Button>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ListBox Margin="0" ItemsSource="{Binding ActivityTypes}" ItemTemplate="{StaticResource ActivityTypeListTemplate}"/>
</Grid>
What's the best way to code something like this?
There is no "direct" way to do this. You can set the Buttons DataContext to your MainViewModel (preferably as a StaticResource link) and the it would work.
Take a look at Bind to parent object in xaml

Show calender on TextBox.GotFocus() event in MVVM

I have an application which is having TextBox. Upon getting the focus, I need to show the Calendar as Popup.
My question is how to show subscribe the GotFocus event and show the calendar through view model?
It is fairly acceptable to write code-behind for view-specific tasks like this one, however if you insist to have clean code-behind files , do the following
you will need MvvmLight.Extras.WPF4.dll and System.Windows.Interactivity.dll, the second DLL comes mainly with blend , google the first and at any case you can find them both on the MVVMLight package.
reference them as follows:
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
see your textBox
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<cmd:EventToCommand Command="{Binding showCalendar, Mode=OneWay}" MustToggleIsEnabledValue="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
on your view model you should have a property that is bound to your Calendar Visibility property , change it to Visible inside the method invoked by the command.
You really don't need to go to the ViewModel for this - it can be done very simply in XAML. Use a BooleanToVisibilityConverter on a binding that is attached to the TextBox's IsFocused property.
<TextBox x:Name="_textBox" Text="{Binding Text}" />
<myNameSpace:Calendar Visibility="{Binding ElementName=_textBox, Path=IsFocused, Converter={x:Static _boolToVisibilityConverter}, Mode=OneWay}" />

Getting the SelectedItem from a combobox in a DataTemplate

Lets say I've got a DataTemplate like so
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"></ComboBox>
<Button Click="Button_Click">Hello</Button>
</StackPanel>
</DataTemplate>
Which shows a list of ComboBoxes followed by a button.
Now, on clicking the button I need to discover the value in the combo next to the button pressed. I can get the data context as below but can't work out how to get the combos SelectedItem
private void Button_Click(object sender, RoutedEventArgs e)
{
// Can get the data context
var p = ((Button)sender).DataContext as Person;
// How to get the value in the combo ...?
}
you can also reference the combobox in your codebehind if you give it a name. However its cleaner to use a separate class to do your logic than the code behind. Such as a viewmodel.
then you could also do something like this...
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"
SelectedItem="{Binding Path=SelectedChild}"
IsSynchronizedWithCurrentItem="True"/>
<Button Command="{Binding Path=ButtonCommand}">Hello</Button>
</StackPanel>
</DataTemplate>
Instead of using the Click event handler, use a Command and bind the CommandParameter property to the ComboBox.SelectedItem. Then in your command's executed logic, you can use the parameter.

Resources