MVVM подход к обработке события Click в ItemsControl - CodeHelper

MVVM подход к обработке события Click в ItemsControl

1

Как можно применить MVVM подход к обработке клика на один из элементов списка? Есть одно "большое волосатое НО" - в метод-обработчик клика у презентера нужно передавать параметр (допустим, сам элемент списка). Да, при помощи стандартного обработчика клика в code behind это легко сделать - вешай обработчик на Click, а в нем вызывай метод презентера. Но вопрос в том, возможно ли/как это сделать только при помощи xaml-разметки?

Новые ответы


0

Вот еще неплохой вариант. Создать оболочку над элементами списка. Сделать так, чтобы каждый элемент списка с данными удовлетворял интерфейсу

public interface ICommandElement
{
    ICommand ClickCommand { get; }
}

Например:

public delegate void ItemClickHandler(object item);

public class CommandedItem: ICommandElement
{
    private ItemClickHandler _handler;
    private ICommand _command;

    private void DoClick()
    {
        if (_handler != null) _handler(this);
    }

    public CommandedItem(object item, ItemClickHandler handler)
    {
        ...
        _handler = handler;
        _command = new RelayCommand(x => DoClick(), x => true);
    }

    public ICommand ClickCommand { get { return _command; } }
}

Идея RelayCommand взята отсюда. В таком случае код разметки преобразится в следующее:

<ItemsControl ItemsSource="{Binding Path=Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Ellipse Width="3" Height="3" Fill="Black"/>
                <TextBlock Margin="4">
                    <Hyperlink Command="{Binding ClickCommand}">
                        <TextBlock Text="Тут мог бы быть Ваш байндинг на объект данных!"/>
                    </Hyperlink>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
0

Проиллюстрирую проблему кодом:

<ItemsControl ItemsSource="{Binding Path=MostRecentFiles.Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="15 0 0 0">
                <Ellipse Width="3" Height="3" Fill="Black"/>
                <Button Background="Transparent" 
                        Content="{Binding Mode=OneWay}" 
                        Command="{Binding OpenRecentProjectCommand}" 
                        CommandParameter="{Binding}"/>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

В данном примере я пытаюсь вызвать команду OpenRecentProjectCommand у презентера/ViewModel. Но тут проблема - контекстом для элемента списка будет не презентер, а элемент данных, на основе которого и строится этот элемент.

0

Можно использовать action из библиотеки caliburn, чтобы вызывать методы ViewModel по событию контрола:

<ListView x:Name="listView" Message.Attach="[Event MouseClick] = [Action ItemActivated(listView.SelectedItem)]" >
0

Чистый MVVM подход обычно подразумевает использование команд вместо событий и генерации code behind. Но проблема WPF в том, что не все события могут вызывать команду. Обычно с командой связано только одно основное событие некоторых контролов, например, клик на кнопке.

Для решения этой проблемы используется паттерн Attached Behavior и его модификация для команд. Суть паттерна Attached Behavior заключается в инкапсуляции поведения внутри некоторой сущности для последующего применения к любым визуальным элементам. В WPF такая инкапсуляция организуется при помощи стилей. Существует реализация этого паттерна, позволяющая мепить произвольные события на команды, задавая привязку для параметров. Одна из реализаций описана в статье AttachedCommandBehavior V2 aka ACB. Автор разработал библиотеку, позволяющую вызывать команду по событию:

<Border Background="Yellow" Width="350" Margin="0,0,10,0" Height="35" CornerRadius="2" x:Name="test">
    <local:CommandBehaviorCollection.Behaviors>
    <local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
    <local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
    </local:CommandBehaviorCollection.Behaviors>
    <TextBlock Text="MouseDown on this border to execute the command"/>
</Border>

v1.7.123.556
© 2009—2010 CodeHelper FAQ | О сайте | Обратная связь | История изменений | Статьи
Creative Commons LicenseМатериалы сайта распространяются под лицензией Creative Commons Attribution-Share Alike 3.0 Unported.