MVVM Relay Command triggers before Silverlight NumericUpDown changes value - silverlight

If you have a Silverlight Toolkit NumericUpDown control binded to a MVVM property and a RelayCommand trigger set (any event), the command is called before NumericUpDown changes MVVM property value. This means, you can not use the new (changed) value with you method/action/command...
XAML:
<inputToolkit:NumericUpDown x:Name="testNum" Value="{Binding RegisterForm, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding DoSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</inputToolkit:NumericUpDown>
MVVM (C#):
DoSomethingCommand = new RelayCommand(() =>
{
OtherRegisterForm = RegisterForm;
});
In this case, if you have v value 0 and you input a new value 123 in NumericUpDown control it triggers the "DoSomethingCommand" before "RaisePropertyChange" event on MVVM property.
"OtherRegisterForm" would be 0 and not 123.
Is there a way to make this work?

oh boy, wasn't easy but here u are :
xaml part :
<toolkit:NumericUpDown Value="{Binding SomeNumber}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding MyCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</toolkit:NumericUpDown>
and cs code :
public class MainViewModel : ViewModelBase
{
public double SomeNumber { get; set; }
public MainViewModel()
{
SomeNumber = 10;
MyCommand = new RelayCommand<RoutedPropertyChangedEventArgs<double>>(myActionMethod);
}
public RelayCommand<RoutedPropertyChangedEventArgs<double>> MyCommand { get; set; }
public void myActionMethod(RoutedPropertyChangedEventArgs<double> arg)
{
MessageBox.Show(arg.NewValue.ToString());
}
}
hope that helps, Arek

Related

How can I take all value in screen?

I have an employee table I filtered from Location with a combobox in WPF MVVM.
like that:
in xaml:
<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>
In ViewModel:
Locations = new ObservableCollection<string>()
{ "All","A","B"};
and in LocationFilterCommand
#region LocationFilterCommand
private DelegateCommand _locationFilterCommand;
public DelegateCommand LocationFilterCommand
{
get { return _locationFilterCommand ?? (_locationFilterCommand = new DelegateCommand(CanLocationFilter, LocationFilter)); }
}
private bool CanLocationFilter()
{
return true;
}
private void LocationFilter()
{
ParticularEntries = ParticularEntries.Where(p =>p.Region.Location==_selectedLocation);
}
I dont have problem with when I select A location or when I select B location they comes to screen.But I dont know how to take all values together with "All" item in combobox?
Hint:All is not a location type.All need all locations employee together come to screen.But I dont know how to this filter ?
thank you for all your helps.

RichTextBox two-way binding to a flag in wpf

How to bind RichTextBox to a flag (true/false value). For example i want the flag to be true if the text in the RTB is edited. And also the binding should be two-way.
You have to use two things to solve your problem.
You have to add System.Windows.Interactivity reference and use this link in your xaml:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Here is an xaml (view part) example:
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
...
<RichTextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding TextChangedCommand}"
x:Name="textChangedCommand" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RichTextBox>
...
After this, you have to use an ICommand implementation in your ViewModel:
Here is a simple example to use in "ViewModel" part:
public partial class MainWindow : Window
{
RelayCommand _textChangedCommand;
public RelayCommand TextChangedCommand
{
get
{
if (_textChangedCommand == null)
_textChangedCommand = new RelayCommand(() => IsEdited = true);
return _textChangedCommand;
}
}
private bool _isEdited;
public bool IsEdited
{
get
{
return _isEdited;
}
set
{
_isEdited = value;
}
}
public MainWindow()
{
InitializeComponent();
//Use the following code if you want to use xaml.cs as ViewModel but It is not recommended, it is not correct MVVM pattern only simple example.
DataContext = this;
}
}
There are several way to implement the ICommand interface.
Here you can find one with description.

use of selectedindex in datagrid wpf

I'm learning wpf with mvvm light and i'm face an issue.
I've a usercontrol with a datagrid that's loaded with a query.
Now i'd like to add an event on the selectedIndex to retrieve the selected line and after that i'll pass the value to an other user control.
the issue is that my event is never fired.
Here is what i did
<DataGrid HorizontalAlignment="Left" Name="dgUser" VerticalAlignment="Top" SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}" SelectedItem="{Binding selectedRow, Mode=TwoWay}" Width="800" Height="253" >
Here is the code of my viewmodel
public RelayCommand selectedRow { get; private set; }
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
RaisePropertyChanged("SelectedIndex");
}
}
/// <summary>
/// Initializes a new instance of the ListUserViewModel class.
/// </summary>
public ListUserViewModel()
{
selectedRow = new RelayCommand(() => SelectedRowChange());
SelectedIndex = 2;
}
private void SelectedRowChange()
{
System.Windows.MessageBox.Show("test");
}
My row is not selected with SelectedXIndex = 2
and nothing happen when i select an other row
You are trying to bind a Command on your ViewModel to the SelectedItem of your DataGrid and that's not quite how it works.
You want to hook into the DataGrid.SelectionChanged event and presumably fire your ViewModel's RelayCommand at that point.
Check this answer for a great explanation on how to do this in XAML.
EDIT:
To pass the SelectedItem to your RelayCommand as a command parameter, use the following in your XAML:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding MyCommand}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
and ensure that you change your RelayCommand to be of the type you wish.
NOTE: You have not provided what the type of the items you have in your DataGrid, so I'll use object here.
public RelayCommand<object> selectedRow { get; private set; }
You can then rely on the command's parameter being the selected item:
public ListUserViewModel()
{
selectedRow = new RelayCommand(i => SelectedRowChange(i));
SelectedIndex = 2;
}
private void SelectedRowChange(object selectedItem)
{
// selectedItem is the item that has just been selected
// do what you wish with it here
System.Windows.MessageBox.Show("test");
}

Another implementation of WPF Event to Command (with problems)

I am looking at various implementations of hooking a ICommand up to a control's event. So for instance the GotFocus of a TextBox should call a GotFocusCommand in my View Model. I then got an idea to implement my own version (for my own learning) and it is working well, but I can only link one event to one command in the XAML.
( Basically I just use reflection to find the specified Event and then do a AddEventHandler that executes the command )
This works fine :
<Button
local:EventToCommand.Event="Click"
local:EventToCommand.Command="{Binding TestCommand}"
/>
But this does not :
<Button
local:EventToCommand.Event="Click"
local:EventToCommand.Command="{Binding ClickCommand}"
local:EventToCommand.Event="GotFocus"
local:EventToCommand.Command="{Binding GotFocusCommand}"
/>
as you it leads to a duplicate attribute name error.
Would it be possible to do something like :
<Button>
<Some Xaml Element>
<local:EventToCommand Event="Click" Command="{Binding ClickCommand}" />
<local:EventToCommand Event="GotFocus" Command="{Binding GotFocusCommand}" />
</Some Xaml Element>
</Button>
to "map" multiple events to commands ?
There are a couple of ways you could approach this, either using an Attached Property or inheriting from Button and adding your own DependencyProperty that contains a list of EventToCommand objects, and when you add to that collection you wire up the event to command. If this seems confusing, I can try to whip up some examples.
C#
public class EventedButton : Button
{
public static DependencyProperty EventCommandsProperty
= DependencyProperty.Register("EventCommands", typeof(EventToCommandCollection), typeof(EventedButton), new PropertyMetadata(null));
public EventToCommandCollection EventCommands
{
get
{
return this.GetValue(EventCommandsProperty) as EventToCommandCollection;
}
set
{
this.SetValue(EventCommandsProperty, value);
}
}
public EventedButton()
{
this.EventCommands = new EventToCommandCollection(this);
}
}
Xaml:
<local:EventedButton>
<local:EventedButton.EventCommands>
<local:EventToCommand />
</local:EventedButton.EventCommands>
</local:EventedButton>
Inside of EventToCommandCollection, you would attach/detach to the Event you wanted when items are added to the collection.
UPDATE: Attached Property
Here is some code to do the collection as an attached property:
C#
public static DependencyProperty CommandsProperty =
DependencyProperty.RegisterAttached(
"Commands",
typeof(ICollection<EventToCommand>),
typeof(DependencyObject),
new PropertyMetadata(null, OnCommandsChanged));
private static void OnCommandsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Attach/Detach event handlers
}
public static void SetCommands(DependencyObject element, ICollection<EventToCommand> value)
{
element.SetValue(CommandsProperty, value);
}
public static ICollection<EventToCommand> GetCommands(DependencyObject element)
{
return (ICollection<EventToCommand>)element.GetValue(CommandsProperty);
}
Xaml:
<local:EventedButton>
<local:EventToCommand.Commands>
<local:EventToCommandCollection>
<local:EventToCommand/>
</local:EventToCommandCollection>
</local:EventToCommand.Commands>
</local:EventedButton>
Using the Blend Event Triggers and an action negates the need to handle your own collections.
And it can be added to any control.
See MVVM Lights EventToCommand
Or my extension of it here.(source)
GalaSoft MVVM Light ToolKit - EventToCommand you can do this
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" >
<cmd:EventToCommand
PassEventArgsToCommand="True"
Command="{Binding ButtonClick}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus" >
<cmd:EventToCommand
PassEventArgsToCommand="True"
Command="{Binding ButtonGotFocus}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Where import this namespaces
i- xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
cmd-xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;
assembly=GalaSoft.MvvmLight.Extras.WPF4"

MVVM - WPF DataGrid - AutoGeneratingColumn Event

I'm currently taking a good look at the excellent toolkit from Laurent and I have the following question.
From Blend 4, I have added an EventTrigger for the Loaded event, in my ViewModel I have the following:
public RelayCommand rcAutoGeneratingColumn { get; private set; }
In the constructor I have:
rcAutoGeneratingColumn =
new RelayCommand(o => DataGridAutoGeneratingColumn(o));
Also in the ViewModel, I have the method which I wish to be invoked by the RelayCommand:
private void DataGridAutoGeneratingColumn(Object o)
{
DataGrid grid = (DataGrid)o;
foreach (DataGridTextColumn col in grid.Columns)
{
if (col.Header.ToString().ToLower() == "id")
{
col.Visibility = System.Windows.Visibility.Hidden;
}
}
}
My XAML contains the following (for the DataGrid):
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
There is NO PROBLEM here the code works just fine, but obviously the event used to hide certain columns should be the AutoGeneratingColumn event and not Loaded.
I have used to Loaded event as a getaround.
I was hoping that I could relay any event offered by the control so that, in this case, the following would work instead:
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding rcAutoGeneratingColumn, Mode=OneWay}"
CommandParameter="{Binding ElementName=dataGrid1, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I am unable to get the AutoGeneratingColumn event to trigger, and I'm hoping that I've overlooked something and appreciate any advice given!
This behaviour is the same with the GridControl from DevExpress, in that the Loaded event is triggered whereas the ColumnsPopulated event (this being the equivalent of the AutoGeneratingColumn event) is not.
DevExpress offered the following information with regard to my question:
"We have reviewed this question, and come to an interesting conclusion. It looks like the visual tree is not being built at the moment when the Interaction.Triggers are being processed"
If this is true, and there is no other way in which to invoke the events within the ViewModel, then one would have to go ahead and - by using trial and error - note which of the DataGrid events (of which there are over 100) can be invoked in this way and which cannot!
One would like to think that every event which is available in the code-behind, can also be reached when applying the MVVM pattern.
I have searched for an answer but I cannot rule out that I have overlooked something, so if this is to be the case, then please accept my apologies!
You don't have to use evil code behind ;-) You can do this using an attached behaviour...
public class AutoGeneratingColumnEventToCommandBehaviour
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(AutoGeneratingColumnEventToCommandBehaviour),
new PropertyMetadata(
null,
CommandPropertyChanged));
public static void SetCommand(DependencyObject o, ICommand value)
{
o.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject o)
{
return o.GetValue(CommandProperty) as ICommand;
}
private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dataGrid = d as DataGrid;
if (dataGrid != null)
{
if (e.OldValue != null)
{
dataGrid.AutoGeneratingColumn -= OnAutoGeneratingColumn;
}
if (e.NewValue != null)
{
dataGrid.AutoGeneratingColumn += OnAutoGeneratingColumn;
}
}
}
private static void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dependencyObject = sender as DependencyObject;
if (dependencyObject != null)
{
var command = dependencyObject.GetValue(CommandProperty) as ICommand;
if (command != null && command.CanExecute(e))
{
command.Execute(e);
}
}
}
}
Then use it in XAML like this...
<DataGrid ItemsSource="{Binding MyGridSource}"
AttachedCommand:AutoGeneratingColumnEventToCommandBehaviour.Command="{Binding CreateColumnsCommand}">
</DataGrid>
Just set EventTrigger.SourceObject property.
<DataGrid
x:Name="DataGrid"
AutoGenerateColumns="True"
IsReadOnly="True"
ItemsSource="{Binding Data}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
<local:EventToCommand
Command="{Binding ColumnGeneratingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
As MVVMLight from Galasoft is deprecated now, we can use CommunityToolkit.Mvvm package and use it like this:
<DataGrid AutoGenerateColumns="True"
Name="DataGrid"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn" SourceObject="{Binding ElementName=DataGrid}">
<i:InvokeCommandAction Command="{Binding AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
Note that Items property is a simple List, It could be an ObservableCollection or whatever.
The trick to get the fired event is to load your data after the window is loaded, or raise OnpropertyChanged on Items property after loaded.
<Window ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Window>
In your View Model:
private RelayCommand<DataGridAutoGeneratingColumnEventArgs> myAutoGeneratingColumnCommand;
public RelayCommand<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumnCommand
{
get
{
if (myAutoGeneratingColumnCommand == null)
myAutoGeneratingColumnCommand = new RelayCommand<DataGridAutoGeneratingColumnEventArgs>(AutoGeneratingColumnCommandAction);
return myAutoGeneratingColumnCommand;
}
}
private void AutoGeneratingColumnCommandAction(DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyName == "Id")
{
e.Column.Width = 60;
}
else if (e.PropertyName == "Name")
{
e.Column.Header = "myName";
e.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
else
e.Cancel = true; // ignore all other properties and remove their column
}
RelayCommand myLoadedCommand;
public RelayCommand LoadedCommand
{
get
{
if (myLoadedCommand == null)
myLoadedCommand = new RelayCommand(LoadedCommandAction);
return myLoadedCommand;
}
}
private void LoadedCommandAction()
{
Load(); // Populate the Items List
}
During the course of developing a project with MVVM you're going to have circumstances where you must handle events in your view's code-behind and EventToCommand just plain doesn't work. You especially find this with Silverlight, but I assume from your question that you're using WPF. It's okay to do some event handling in your view's code-behind, just don't put any business logic there. You can even leave the command in your view model, just call it directly from your event handler.
((YourViewModel)this.DataContext).rcAutoGeneratingColumn.Execute(sender);

Resources