PropertyChanged event on viewmodel property - wpf

i'm trying to Raise a PropertyChanged event on a Property in my ViewModel
using interaction triggers .
CS :
public string MyContentProperty
{
get { return "I Was Raised From an outside Source !";}
}
XAML :
<Button Content="{Binding MyContentProperty}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Button.Click">
< .... what needs to be done ?>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
of course if there was any doubt for this question you have references to
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
at your disposal , thanks in advance .

You can use a normal command or Expression Blend's CallMethodAction, InvokeCommandAction or ChangePropertyAction.
Here are four ways to do what you want:
<Button Content="Button" Height="23" Width="100" Command="{Binding RaiseItCmd}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding RaiseItCmd}"/>
<ei:CallMethodAction MethodName="RaiseIt" TargetObject="{Binding}"/>
<ei:ChangePropertyAction Value=""
PropertyName="MyContentProperty" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Here I'm using MVVM Light's ViewModelBase:
using System.Windows.Input;
using GalaSoft.MvvmLight;
using Microsoft.Expression.Interactivity.Core;
public class ViewModel : ViewModelBase
{
public ViewModel()
{
RaiseItCmd = new ActionCommand(this.RaiseIt);
}
public string MyContentProperty
{
get
{
return "property";
}
set
{
this.RaiseIt();
}
}
public void RaiseIt()
{
RaisePropertyChanged("MyContentProperty");
}
public ICommand RaiseItCmd { get; private set; }
}

Related

Inner GroupBox does not swallow the Interactivity event

I have a GroupBox within a parent GroupBox. Both of them have their own
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ...}" />
</i:EventTrigger>
</i:Interaction.Triggers>
When I press the inner GroupBox it fires its own Command and then the parent Command is also triggered.
How do I prevent that? How do I make the inner GroupBox swallow the event?
You could use another implementation of TriggerAction that supports passing the event args as a command parameter to the command, for example the EventToCommand class in the MvvmLight library:
<GroupBox Header="Outer" xmlns:mvvm="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding OuterCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock>...</TextBlock>
<GroupBox Header="Inner" Grid.Row="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<mvvm:EventToCommand Command="{Binding InnerCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock>inner...</TextBlock>
</GroupBox>
</Grid>
</GroupBox>
public class ViewModel
{
public ViewModel()
{
OuterCommand = new RelayCommand(arg => true, (arg)=> { MessageBox.Show("outer"); });
InnerCommand = new RelayCommand(arg => true,
(arg) =>
{
MessageBox.Show("inner");
MouseButtonEventArgs mbea = arg as MouseButtonEventArgs;
if (mbea != null)
mbea.Handled = true;
});
}
public RelayCommand OuterCommand { get; }
public RelayCommand InnerCommand { get; }
}
The ugly thing with this solution is that the view model has a dependency upon the view related MouseButtonEventArgs type though. If you don't like this you can implement your own behaviour as suggested by #adabyron here:
MVVM Passing EventArgs As Command Parameter
Then you could set the Handled property of the MouseButtonEventArgs directly in the behaviour instead of passing it along to the view model.

WPF Relaycommand get passwordbox value

I have this Passwordbox with this trigger :
<PasswordBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PasswordChanged">
<i:InvokeCommandAction Command="{Binding PasswordChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</PasswordBox>
How can I, in my RelayCommand, get the content of my PasswordBox?
This is my relay command :
PasswordChanged = new RelayCommand<object>(param => this.GoPasswordChanged());
private void GoPasswordChanged()
{
}
I found the solution, I had just to change :
<i:InvokeCommandAction Command="{Binding PasswordChanged}" />
To :
<i:InvokeCommandAction Command="{Binding PasswordChanged}" CommandParameter="{Binding ElementName=PasswordBoxInput}" />
And :
PasswordChanged = new RelayCommand<object>(param => this.GoPasswordChanged(param));
private void GoPasswordChanged(param)
{
}
To :
private void GoPasswordChanged(object param)
{
var passwordBox = param as PasswordBox;
var password = passwordBox.Password;
}

Handle MainWindow events in ViewModel - WPF

I want to handle Windows event like Closing, SourceInitialized in my viewModel. I don't want to handle them in my code behind. How can I do that?
Thanks in advance.
Simply use EventToCommand.
ViewModel:
public ICommand WindowClosing
{
get
{
return new RelayCommand<CancelEventArgs>(
(args) =>{
});
}
}
and in XAML:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding WindowClosing}" />
</i:EventTrigger>
</i:Interaction.Triggers>

EventTrigger not firing for Loaded event of UserControl

I have a UserControl with the following event trigger:
<UserControl x:Class="TestApp.Views.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding OnLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
It is being set via the constructor of the UserControl (please ignore the ServiceLocator ..this is just a quick prototype):
public MyUserControl()
{
InitializeComponent();
DataContext = ServiceLocator.Current.GetInstance<DirectorySearchViewModel>();
}
In my view-model I have the following:
public ICommand OnLoadedCommand { get; private set; }
public MyUserControl()
{
OnLoadedCommand = new DelegateCommand(OnLoaded);
}
public void OnLoaded()
{
}
OnLoaded never gets called. If I change the EventName to say ..MouseDown, then it works but it just won't work for Loaded
Pretty sure it's a stupid mistake (swear I've done this a million times before in the past) but can't seem to figure it out right now
public MyUserControl()
{
DataContext = ServiceLocator.Current.GetInstance<DirectorySearchViewModel>();
InitializeComponent();
}
The Aristocrats.
For anyone else that stumbles across this you can do it without code behind like this:
<UserControl x:Class="TestApp.Views.MyUserControl"
... etc...
x:Name="theControl"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction
Command="{Binding ElementName=theControl, Path=OnLoadedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Command Parameter is null in a Command of CustomDataGrid

This code works fine :
<sdk:DataGrid ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem,Mode=TwoWay}" x:Name="dataGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CommandName}" CommandParameter="{Binding ElementName=MainTabControl,Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
In my CustomDataGrid the Command executes but the CommandParameter is null :
<customControl:CustomDataGrid ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem,Mode=TwoWay}" x:Name="dataGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CommandName}" CommandParameter="{Binding ElementName=MainTabControl,Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
EDIT:
My CustomDataGrid is driven from standard DataGrid and add an Event to it. it overrides two event: OnLoadingRow and OnUnLoadingRow.
Note that a simple CommandParameter e.g. a simple string was sent fine.
And MainTabControl is:
<sdk:TabControl Name="MainTabControl"> ...
What is the problem?
OK, I have built a complete mockup of your situation and provided all code below. Everything is bare-bones, so everyone please ease up on the number of bad practices I put in it :)
The bad new is, that my derived class works exactly the same as the normal DataGrid! When the command breakpoint is hit with the derived grid the parameter is exactly the same TabControl type.
To take this further I would need to see what else you may be doing (in addition what you have already mentioned). Feel free to contact me through my website if you do not want to make your code public.
View:
<UserControl xmlns:my="clr-namespace:SilverlightApplication4" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Class="SilverlightApplication4.Page"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="59*" />
<RowDefinition Height="241*" />
</Grid.RowDefinitions>
<sdk:TabControl x:Name="MainTabControl">
<sdk:TabItem Header="Tab1">Tab 1 content</sdk:TabItem>
<sdk:TabItem Header="Tab2">Tab 2 content</sdk:TabItem>
<sdk:TabItem Header="Tab3">Tab 3 content</sdk:TabItem>
</sdk:TabControl>
<!--data:DataGrid Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" AutoGenerateColumns="True" ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem, Mode=TwoWay}" x:Name="dataGrid" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CommandName}" CommandParameter="{Binding ElementName=MainTabControl,Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</data:DataGrid-->
<my:CustomDataGrid Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" AutoGenerateColumns="True" ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem, Mode=TwoWay}" x:Name="dataGrid2" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CommandName}" CommandParameter="{Binding ElementName=MainTabControl,Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</my:CustomDataGrid>
</Grid>
</UserControl>
Code-behind:
using System.Windows.Controls;
namespace SilverlightApplication4
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
}
ViewModel:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
namespace SilverlightApplication4
{
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand CommandName { get; set; }
public ObservableCollection<Item> MyItems { get; set; }
public Item MySelectedItem { get; set; }
public ViewModel()
{
MyItems = new ObservableCollection<Item>();
MyItems.Add(new Item() { FirstName = "Joe", LastName = "Blogs" });
MyItems.Add(new Item() { FirstName = "Another", LastName = "One" });
MyItems.Add(new Item() { FirstName = "Jane", LastName = "Blogs" });
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("MyItems"));
}
CommandName = new DelegateCommand<TabControl>(OnCommandAction);
}
public void OnCommandAction(TabControl param)
{
// Breakpoint here
}
}
public class Item
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
Custom datagrid:
using System.Windows.Controls;
namespace SilverlightApplication4
{
public class CustomDataGrid : DataGrid
{
protected override void OnLoadingRow(DataGridRowEventArgs e)
{
base.OnLoadingRow(e);
}
protected override void OnUnloadingRow(DataGridRowEventArgs e)
{
base.OnUnloadingRow(e);
}
}
}

Resources