При использовании паттерна MVVM часто возникает желание вызвать команду на событие, которое получает параметры необходимые в методе выполняемом командой. И если просто вызов команды сделать достаточно легко, я сегодня бы хотел продемонстрировать пример именно с передаче параметров. Собственно, как достаточно часто в последнее время, на написание этого топика меня подтолкнул вот этот вопрос на форумах MSDN.
Итак, я буду делать все в максимально простом исполнении, чтобы не перегружать оишними подробностями.
В пустой проект, я добавил вот такой класс для реализации команды:
private Action<DataGridCellInfo> _handler;
_handler = p_handler;
}
return true;
}
public void Execute(object parameter)
{
if (_handler != null)
{
_handler((DataGridCellInfo)parameter);
}
}
}
Вот такой ViewModel:
public int[] Items
{
get { return (int[])GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
get { return (ICommand)GetValue(SelectedCellChangedCommandProperty); }
set { SetValue(SelectedCellChangedCommandProperty, value); }
}
public static readonly DependencyProperty SelectedCellChangedCommandProperty =
DependencyProperty.Register("SelectedCellChangedCommand", typeof(ICommand), typeof(MyGridViewModel), new PropertyMetadata(null));
Items = new int[] { 1, 2, 3, 4, 5 };
SelectedCellChangedCommand = new MyCommand(SelectedCellChanged);
}
private void SelectedCellChanged(DataGridCellInfo cell)
{
MessageBox.Show(cell.Item.ToString());
}
}
Ну и главную форму размечаем вот так:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding }" Header="Числа" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Ну и для присвоения ViewModel п=воспользуемся конструктором окна:
InitializeComponent();
DataContext = new MyGridViewModel();
}
Вполне логично, что метод из ViewModel не вызывается, хотя числа мы уже видим. Итак, нам необходимо из XAML подписать команду из ViewModel на событие, причем так, чтобы в команду передавался параметр. Для этого добавим в проект вот такой класс:
static void dataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
ICommand command = GetSelectedCellsChangedCommand(sender as DataGrid) as ICommand;
if (command != null)
{
foreach (var item in e.AddedCells)
{
command.Execute(item);
}
}
}
return (ICommand)obj.GetValue(SelectedCellsChangedCommandProperty);
}
public static void SetSelectedCellsChangedCommand(DataGrid obj, ICommand value)
{
obj.SetValue(SelectedCellsChangedCommandProperty, value);
}
public static readonly DependencyProperty SelectedCellsChangedCommandProperty =
DependencyProperty.RegisterAttached("SelectedCellsChangedCommand", typeof(ICommand), typeof(DataGridBehavior), new UIPropertyMetadata(null, OnSelectedCellsChangedCommandChanged));
#endregion
if (e.OldValue != null)
{
DataGrid dg = d as DataGrid;
dg.SelectedCellsChanged -= dataGrid_SelectedCellsChanged;
}
if (e.NewValue != null)
{
DataGrid dg = d as DataGrid;
dg.SelectedCellsChanged += dataGrid_SelectedCellsChanged;
}
}
}
Ну и подключив локальное пространство имен, подключаем через присоединенное свойство команду:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behavior="clr-namespace:CellSelectionExample"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Items}" SelectionUnit="Cell" behavior:DataGridBehavior.SelectedCellsChangedCommand="{Binding SelectedCellChangedCommand}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding }" Header="Числа" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Все, теперь при клике на ячейке мы будем видеть MessageBox со значением из этой ячейки.
Итак, я буду делать все в максимально простом исполнении, чтобы не перегружать оишними подробностями.
В пустой проект, я добавил вот такой класс для реализации команды:
class MyCommand : ICommand
{private Action<DataGridCellInfo> _handler;
public MyCommand(Action<DataGridCellInfo> p_handler)
{_handler = p_handler;
}
public bool CanExecute(object parameter)
{return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (_handler != null)
{
_handler((DataGridCellInfo)parameter);
}
}
}
Вот такой ViewModel:
public class MyGridViewModel : DependencyObject
{public int[] Items
{
get { return (int[])GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(int[]), typeof(MyGridViewModel), new PropertyMetadata(null));
public ICommand SelectedCellChangedCommand
{get { return (ICommand)GetValue(SelectedCellChangedCommandProperty); }
set { SetValue(SelectedCellChangedCommandProperty, value); }
}
public static readonly DependencyProperty SelectedCellChangedCommandProperty =
DependencyProperty.Register("SelectedCellChangedCommand", typeof(ICommand), typeof(MyGridViewModel), new PropertyMetadata(null));
public
MyGridViewModel()
{Items = new int[] { 1, 2, 3, 4, 5 };
SelectedCellChangedCommand = new MyCommand(SelectedCellChanged);
}
private void SelectedCellChanged(DataGridCellInfo cell)
{
MessageBox.Show(cell.Item.ToString());
}
}
Ну и главную форму размечаем вот так:
<Grid>
<DataGrid
ItemsSource="{Binding Items}"
SelectionUnit="Cell"><DataGrid.Columns>
<DataGridTextColumn Binding="{Binding }" Header="Числа" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Ну и для присвоения ViewModel п=воспользуемся конструктором окна:
public
MainWindow()
{InitializeComponent();
DataContext = new MyGridViewModel();
}
Вполне логично, что метод из ViewModel не вызывается, хотя числа мы уже видим. Итак, нам необходимо из XAML подписать команду из ViewModel на событие, причем так, чтобы в команду передавался параметр. Для этого добавим в проект вот такой класс:
public sealed class DataGridBehavior
{static void dataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
ICommand command = GetSelectedCellsChangedCommand(sender as DataGrid) as ICommand;
if (command != null)
{
foreach (var item in e.AddedCells)
{
command.Execute(item);
}
}
}
#region Attached
Properties
public static ICommand GetSelectedCellsChangedCommand(DataGrid obj)
{return (ICommand)obj.GetValue(SelectedCellsChangedCommandProperty);
}
public static void SetSelectedCellsChangedCommand(DataGrid obj, ICommand value)
{
obj.SetValue(SelectedCellsChangedCommandProperty, value);
}
public static readonly DependencyProperty SelectedCellsChangedCommandProperty =
DependencyProperty.RegisterAttached("SelectedCellsChangedCommand", typeof(ICommand), typeof(DataGridBehavior), new UIPropertyMetadata(null, OnSelectedCellsChangedCommandChanged));
#endregion
private static void
OnSelectedCellsChangedCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{if (e.OldValue != null)
{
DataGrid dg = d as DataGrid;
dg.SelectedCellsChanged -= dataGrid_SelectedCellsChanged;
}
if (e.NewValue != null)
{
DataGrid dg = d as DataGrid;
dg.SelectedCellsChanged += dataGrid_SelectedCellsChanged;
}
}
}
Ну и подключив локальное пространство имен, подключаем через присоединенное свойство команду:
<Window x:Class="CellSelectionExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behavior="clr-namespace:CellSelectionExample"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Items}" SelectionUnit="Cell" behavior:DataGridBehavior.SelectedCellsChangedCommand="{Binding SelectedCellChangedCommand}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding }" Header="Числа" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Все, теперь при клике на ячейке мы будем видеть MessageBox со значением из этой ячейки.
Этот комментарий был удален автором.
ОтветитьУдалитьПочему вы не используете INotifyPropertyChanged?
ОтветитьУдалитьВообще использую. Но у каждого подхода есть свои плюсы и минусы. Применение INotifyPropertyChanged требует меньше памяти, но работает медленнее чем DependencyObject. Ну и другие есть особенности применения.
Удалить