Access to ValidationResult - wpf

Hi I've a datagrid with textbox for receive as input an ip address.
To validate the textbox I bound it with my custom validator.
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Margin="20,10,20,10" Height="20" Width="100" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="{Binding Path=IPSrcValidationStatus.Color}">
<TextBox.Text>
<Binding Path="IPSrc" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<validators:IPv4ValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
How I can access the ValidationResult from my code, or even better, bind it with my view model?

Validationrules happen purely in the UI. In a similar way to conversion failures, they set your control as having errors but no transfer of data from view to viewmodel takes place.
As is often the case with wpf, there are several ways of telling your viewmodel there are data errors in the view.
Your validationrule can get the binding expression and set some property on the viewmodel.
There's some code in Maxence's post here:
Passing state of WPF ValidationRule to View Model in MVVM
I've never used that approach ( but it looks like it'd work ).
I usually want to know about conversion failures as well as any validationrule failures. In scenarios where I use validationrule then I usually only care whether valid data made it to the viewmodel and IsDirty==true
The approach I often use is to get any errors as they bubble up the control tree in a parent grid.
This sample here has the code I use:
https://gallery.technet.microsoft.com/scriptcenter/WPF-Entity-Framework-MVVM-78cdc204
I set
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
On all bindings I'm interested in.
The errors will then bubble up.
They are trapped and passed to the viewmodel from a standard template in the resource dictionary. The ConversionErrorCommand will fire with conversion and validation result fails.
<Grid ... >
<i:Interaction.Triggers>
<local:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}">
<e2c:EventToCommand
Command="{Binding EditVM.TheEntity.ConversionErrorCommand, Mode=OneWay}"
EventArgsConverter="{StaticResource BindingErrorEventArgsConverter}"
PassEventArgsToCommand="True" />
</local:RoutedEventTrigger>
<local:RoutedEventTrigger RoutedEvent="{x:Static Binding.SourceUpdatedEvent}">
<e2c:EventToCommand
Command="{Binding EditVM.TheEntity.SourceUpdatedCommand, Mode=OneWay}"
EventArgsConverter="{StaticResource BindingSourcePropertyConverter}"
PassEventArgsToCommand="True" />
</local:RoutedEventTrigger>
</i:Interaction.Triggers>
You will also need RoutedEventTrigger:
public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
RoutedEvent routedEvent;
public RoutedEvent RoutedEvent
{
get
{
return routedEvent;
}
set
{
routedEvent = value;
}
}
public RoutedEventTrigger()
{
}
protected override void OnAttached()
{
Behavior behavior = base.AssociatedObject as Behavior;
FrameworkElement associatedElement = base.AssociatedObject as FrameworkElement;
if (behavior != null)
{
associatedElement = ((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
}
if (associatedElement == null)
{
throw new ArgumentException("This only works with framework elements");
}
if (RoutedEvent != null)
{
associatedElement.AddHandler(RoutedEvent, new RoutedEventHandler(this.OnRoutedEvent));
}
}
void OnRoutedEvent(object sender, RoutedEventArgs args)
{
base.OnEvent(args);
}
protected override string GetEventName()
{
return RoutedEvent.Name;
}
}
}

Related

Why isn't my user control with a combobox binding correctly?

I've got a really simple UserControl I'm trying to create that contains a list of US states. I am trying to expose the selected state via a "SelectedState" property. However, I'm having trouble trying to get this binding to fire once it's hooked up in another UserControl / form.
The XAML for the user control looks like this:
<UserControl x:Class="Sample.Desktop.UserControls.StateDropdown"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Sample.Desktop.UserControls"
mc:Ignorable="d"
Width="170" Height="28"
d:DesignHeight="28" d:DesignWidth="170">
<ComboBox x:Name="cboState"
ItemsSource="{Binding StateList, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Abbreviation}"></Label>
<Label> - </Label>
<Label Content="{Binding Name}"></Label>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the code-behind, I have this code:
public static readonly DependencyProperty SelectedStateProperty = DependencyProperty.Register("SelectedState",
typeof(USState),
typeof(StateDropdown),
new UIPropertyMetadata(null,
new PropertyChangedCallback(OnSelectedStateChanged),
new CoerceValueCallback(OnCoerceSelectedState)));
private static object OnCoerceSelectedState(DependencyObject o, object value)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
return stateDropdown.OnCoerceSelectedState((USState)value);
else
return value;
}
private static void OnSelectedStateChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
StateDropdown stateDropdown = o as StateDropdown;
if (stateDropdown != null)
stateDropdown.OnSelectedStateChanged((USState)e.OldValue, (USState)e.NewValue);
}
protected virtual USState OnCoerceSelectedState(USState value)
{
// TODO: Keep the proposed value within the desired range.
return value;
}
protected virtual void OnSelectedStateChanged(USState oldValue, USState newValue)
{
// TODO: Add your property changed side-effects. Descendants can override as well.
}
public USState SelectedState
{
// IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
get
{
return (USState)GetValue(SelectedStateProperty);
}
set
{
SetValue(SelectedStateProperty, value);
}
}
I wasn't able to get the SelectedValue bound property of SelectedState to fire, so I ended up hooking up the SelectionChanged event.
private void cboState_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems?.Count > 0)
{
SelectedState = (USState)e.AddedItems[0];
}
}
In my other user control, I have this in the XAML:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState}" ></uc:StateDropdown>
And the ViewModel (I'm using Caliburn Micro), I have this property:
protected USState _selectedState;
public USState SelectedState
{
get { return _selectedState; }
set
{
_selectedState = value;
NotifyOfPropertyChange(() => SelectedState);
}
}
The combo is populated as expected. However, SelectedState is never fired/updated when I change the selection.
I had also previously tried using SelectedItem instead of SelectedValue, with the same results.
I'm sure I'm missing something obvious, but I'm having trouble seeing where I went wrong.
EDIT: Here's what fixed the binding.
I removed the SelectionChanged event. Then I modified my "hosting page" usercontrol to set TwoWay binding:
<uc:StateDropdown Margin="10,0,0,0" SelectedState="{Binding SelectedState, Mode=TwoWay}" ></uc:StateDropdown>
As soon as I added that, SelectedState started being updated when I changed the ComboBox value.
The only things I see, is this line :
SelectedValue="{Binding SelectedState, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}"
You don't need it, because of the SelectionChanged event. And it can cause the problem.
Also I would bind the SelectedState of the UserControl using a TwoWay binding.
Hope that will help you.

Viewmodel object does not receive database source changes

New to WPF, MVVM, data binding, and Entity Framework, so apologies in advance.
I'm attempting to bind WPF controls to my database-generated model objects through a viewmodel. I'm able to change database values by typing in the textbox, but any direct changes to the database rows do not seem to fire off the PropertyChanged event. At least they are not reflected in my viewmodel objects. I've implemented iNotifyPropertyChanged on my Entity Framework generated classes thus:
public partial class GenGround : INotifyPropertyChanged
{
public double ClMax
{
get { return (double)this.clMax; }
set
{
this.clMax = (float)value;
MainWindow.db.SaveChanges();
OnPropertyChanged("ClMax");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Property));
}
}
}
I have a listbox:
<DataTemplate x:Key="missionLegTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<ListBox x:Name="missionList" SelectionChanged="missionList_SelectionChanged" Grid.ColumnSpan="2" ItemsSource="{Binding Mode=OneWay}" ItemTemplate="{DynamicResource missionLegTemplate}" />
private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{
this.missionList.ItemsSource = _CurrentMissionProfile.MissionLegs;
}
"MissionLegs" is an ObservableCollection of MissionLeg objects, attached to the database. The selected item of this listbox should tell the textboxes what properties to get and set. Textbox:
<TextBox x:Name="velocityBox" Text="{Binding Mode=TwoWay,Path=SelectedItem.Velocity, ElementName=missionList}" IsEnabled="False" />
As I said, this seems to write to the database, but when I make changes to the corresponding row in SSMS, nothing seems to happen. Ideas?

How to make the binding happen from the ViewModel

I'm trying to capture the Enter key being pressed on a text box, so that I can kick off an update to the server. It's not working, and so I've reduced the problem to it's simplist elemetns.
In this example, it seems that the binding is not happening per keystroke, but at sometime later. I need to the binding to be completed by the time the enter key is pressed. Consider the following XAML and function from the VM.
Here's the XAML of the textbox
<TextBox Text="{Binding TextValue, Mode=TwoWay}" Height="23" Width="300">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{Binding KeyDownCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
The KeyDownCommand fire as expected, howerver the value is not yet in the TextValue property. If I hit enter a second time then the value is in the property? Here's the KeyDownCommand. The constructor of the ViewModel sets the keyDownCommand correctly.
public RelayCommand<RoutedEventArgs> KeyDownCommand { get; private set; }
private void KeyDownAction(RoutedEventArgs eventArg)
{
var source = eventArg.OriginalSource as FrameworkElement;
var e = eventArg as KeyEventArgs;
if (source != null && e != null && e.Key== Key.Enter)
{
e.Handled = true;
MessageBox.Show(TextValue);
}
}
It seems that what I need is a way to "post" the Text of the TextBox back to the TextValue property of the VM when the Enter key is pressed. Or is there something else I'm missing.
Try setting UpdateSourceTrigger to PropertyChanged on binding, like this:
<TextBox Text="{Binding TextValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" Width="300">
Now the view model property will be update every time the text is changed.
Update:
For Silverlight, as an alternative to UpdateSourceTrigger, you can use the following simple behavior that updates binding source whenever text changes:
public class TextChangedUpdateSourceBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += OnTextChanged;
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
var bindingExpression = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
}
}
Use it like this:
<TextBox Text="{Binding TextValue, Mode=TwoWay}" Height="23" Width="300">
<i:Interaction.Behaviors>
<b:TextChangedUpdateSourceBehavior />
</i:Interaction.Behaviors>
</TextBox>
No sooner did I post the question, than I hit upon the answer.
Here's the corrected KeyDownAction
private void KeyDownAction(RoutedEventArgs eventArg)
{
var source = eventArg.OriginalSource as FrameworkElement;
source.GetBindingExpression(TextBox.TextProperty).UpdateSource();
var e = eventArg as KeyEventArgs;
if (source != null && e != null && e.Key== Key.Enter)
{
e.Handled = true;
MessageBox.Show(TextValue);
}
}
Of now as I type this I realize that I'm "breaking" the pattern, in as much as now my ViewModel knows more about the View that it should.

WPF ValidationRule and Validation.Error does not raise event

What's wrong with these code, the Validation.Error is never fired whereas I setthe and the NotifyOnValidationError property to True. So, the method "Grid_Error(object sender, ValidationErrorEventArgs e)" is never executed, but I don't know why :(
<Window xmlns:my="clr-namespace:WpfDigitalClock;assembly=WpfDigitalClock" x:Class="WpfApplication1.MainWindow"
xmlns:local="clr-namespace:WpfApplication1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Names x:Key="MyNames" />
</Window.Resources>
<Grid Validation.Error="Grid_Error">
<TextBox Height="21" Margin="12,62,0,0" Name="TextBox1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="120">
<TextBox.Text>
<Binding Source="{StaticResource MyNames}" Path="FirstName" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local:StringValidator />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Height="21" HorizontalAlignment="Right" Margin="0,62,12,0" Name="TextBox2" VerticalAlignment="Top" Width="120" >
<TextBox.Text>
<Binding Source="{StaticResource MyNames}" Path="LastName" NotifyOnValidationError="True">
<Binding.ValidationRules>
<local:StringValidator />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Button HorizontalAlignment="Left" Margin="35,122,0,116" Name="Button1" Width="75" Click="Button1_Click">Back</Button>
<Button HorizontalAlignment="Right" Margin="0,122,34,117" Name="Button2" Width="75" Click="Button2_Click">Forward</Button>
<Button Height="22" Margin="101,0,101,56" Name="Button3" VerticalAlignment="Bottom" Click="Button3_Click">Add</Button>
</Grid>
in the Window1.xaml.cs file :
public class StringValidator : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureinfo)
{
string aString = value.ToString();
if (aString == "")
return new ValidationResult(false, "String cannot be null");
return new ValidationResult(true, null);
}
}
private void Grid_Error(object sender, ValidationErrorEventArgs e)
{
if(e.Action == ValidationErrorEventAction.Added)
MessageBox.Show(e.Error.ErrorContent.ToString());
}
Thank you for your help !
EDIT :
Here my Names Class :
class Names : ObservableCollection<Name>
{
public Names ()
{
Name aName = new Name("FirstName " + (this.Count +1).ToString(),
"LastName " + (this.Count + 1).ToString());
this.Add(aName);
}
}
Here my Name class :
class Name : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
private string _lastName;
public Name(string fName, string lName)
{
_firstName = fName;
_lastName = lName;
}
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
}
}
}
The application cannot modify the content of this collection. See the Example section for an example of how to use this attached property.
The WPF data binding model enables you to associate ValidationRules with your Binding object. Validation occurs during binding target-to-binding source value transfer before the converter is called. The following describes the validation process:
1.When a value is being transferred from the target property to the source property, the data binding engine first removes any ValidationError that may have been added to the Validation.Errors attached property of the bound element. It then checks if there are any custom ValidationRules defined for that Binding, in which case it calls the Validate method on each of the ValidationRules until one of them runs into an error or until all of them pass.
2.Once there is a custom rule that does not pass, the binding engine creates a ValidationError object and adds it to the Validation.Errors collection of the bound element. When Validation.Errors is not empty, the Validation.HasError attached property of the element is set to true. Also, if the NotifyOnValidationError property of the Binding is set to true, then the binding engine raises the Validation.Error attached event on the element.
3.If all of the rules pass, the binding engine then calls the converter, if one exists.
4.If the converter passes, the binding engine calls the setter of the source property.
5.If the binding has an ExceptionValidationRule associated with it and an exception is thrown during step
4, the binding engine checks to see if there is a UpdateSourceExceptionFilter. You have the option to use the UpdateSourceExceptionFilter callback to provide a custom handler for handling exceptions. If an UpdateSourceExceptionFilter is not specified on the Binding, the binding engine creates a ValidationError with the exception and adds it to the Validation.Errors collection of the bound element.
Also note that a valid value transfer in either direction (target-to-source or source-to-target) clears the Validation.Errors attached property.
For information about the behavior of this property in MultiBinding scenarios, see ValidationError.
From your comment i would conclude that the ValidationRule does not return an error, hence the error event is not fired. Try stepping through the Validation-method with the debugger.
Also, validation is only performed upon a source-update, in TextBoxes that normally happens on LostFocus.
Edit: MyNames is a collection, it not have the properties you try to bind to, there should be binding errors in the Output window.
If you want to bind to the first element you need to change the path to something like [0].LastName for the last-name-binding.
Does your Names class implement INotifyPropertyChanged?
In the code-behind file of the file, set your datacontext to this. Expose your Names object as a property there and see if that works. I'm not comfortable with binding to the static resource in the window.

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