WPF Привязка событий пользовательского интерфейса к командам во ViewModel - PullRequest
50 голосов
/ 04 февраля 2011

Я делаю некоторый рефакторинг простого приложения для отслеживания MVVM, и мой вопрос заключается в том, как переместить событие SelectionChanged из моего кода в viewModel? Я рассмотрел несколько примеров привязки элементов к командам, но не совсем понял. Может ли кто-нибудь помочь с этим. Спасибо!

Может ли кто-нибудь предложить решение, используя приведенный ниже код? Большое спасибо!

public partial class MyAppView : Window 
{
    public MyAppView()
    {
        InitializeComponent();

        this.DataContext = new MyAppViewModel ();

        // Insert code required on object creation below this point.
    }

    private void contactsList_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
    {
        //TODO: Add event handler implementation here.           
        //for each selected contact get the labels and put in collection 

        ObservableCollection<AggregatedLabelModel> contactListLabels = new ObservableCollection<AggregatedLabelModel>();

        foreach (ContactListModel contactList in contactsList.SelectedItems)
        {
            foreach (AggregatedLabelModel aggLabel in contactList.AggLabels)
            {
                contactListLabels.Add(aggLabel);
            }
        }
        //aggregate the contactListLabels by name
        ListCollectionView selectedLabelsView = new ListCollectionView(contactListLabels);

        selectedLabelsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
        tagsList.ItemsSource = selectedLabelsView.Groups;
    }
}

Ответы [ 10 ]

96 голосов
/ 04 февраля 2011

Вы должны использовать EventTrigger в сочетании с InvokeCommandAction из пространства имен Windows.Interactivity. Вот пример:

<ListBox ...>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

Вы можете сослаться на System.Windows.Interactivity, перейдя Add reference > Assemblies > Extensions.

И полное пространство имен i: xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

10 голосов
/ 04 февраля 2011

В этом вопросе есть похожая проблема.

WPF MVVM: Команды просты.Как соединить View и ViewModel с RoutedEvent

Способ решения этой проблемы состоит в том, чтобы иметь свойство SelectedItem в ViewModel, а затем привязать SelectedItem вашего ListBox или что-то к этому свойству.

7 голосов
/ 04 февраля 2011

Чтобы изменить это, вам нужно изменить свое мышление. Вы больше не будете обрабатывать событие «выбор изменен», а будете хранить выбранный элемент в своей модели представления. Затем вы должны использовать двустороннюю привязку данных, чтобы при выборе пользователем элемент обновлялся, а при изменении выбранного элемента - обновлялся вид.

7 голосов
/ 04 февраля 2011

Ваша лучшая ставка - Windows.Interactivity.Используйте EventTriggers, чтобы прикрепить ICommand к любому RoutedEvent.

. Вот статья для начала: Поведения и триггеры Silverlight и WPF

2 голосов
/ 27 января 2019

Рассмотрим Microsoft.Xaml.Behaviors.Wpf , его владельцем является Microsoft, который вы можете увидеть на этой странице.

System.Windows.Interactivity.WPF владелец mthamil, кто-нибудь может сказать мне, надежно ли это?

Пример Microsoft.Xaml.Behaviors.Wpf:

<UserControl ...
             xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"
             ...>

<Button x:Name="button">
    <behaviors:Interaction.Triggers>
        <behaviors:EventTrigger EventName="Click" SourceObject="{Binding ElementName=button}">
            <behaviors:InvokeCommandAction Command="{Binding ClickCommand}" />
        </behaviors:EventTrigger>
    </behaviors:Interaction.Triggers>
</Button>

</UserControl>
2 голосов
/ 20 ноября 2014
<ListBox SelectionChanged="{eb:EventBinding Command=SelectedItemChangedCommand, CommandParameter=$e}">

</ListBox>

Command

{eb: EventBinding} (Простой шаблон именования для поиска команды)

{eb: EventBinding Command = CommandName}

CommandParameter

$ e (EventAgrs)

$ this или $ this. Свойство

строка

https://github.com/JonghoL/EventBindingMarkup

1 голос
/ 23 января 2015

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

Объявление поведения привязки, например:

public class PageChangedBehavior
{
    #region Attached property

    public static ICommand PageChangedCommand(DependencyObject obj)
    {
        return (ICommand)obj.GetValue(PageChangedCommandProperty);
    }
    public static void SetPageChangedCommand(DependencyObject obj, ICommand value)
    {
        obj.SetValue(PageChangedCommandProperty, value);
    }

    public static readonly DependencyProperty PageChangedCommandProperty =
        DependencyProperty.RegisterAttached("PageChangedCommand", typeof(ICommand), typeof(PageChangedBehavior),
            new PropertyMetadata(null, OnPageChanged));

    #endregion

    #region Attached property handler

    private static void OnPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as PageControl;
        if (control != null)
        {
            if (e.NewValue != null)
            {
                control.PageChanged += PageControl_PageChanged;
            }
            else
            {
                control.PageChanged -= PageControl_PageChanged;
            }
        }
    }

    static void PageControl_PageChanged(object sender, int page)
    {
        ICommand command = PageChangedCommand(sender as DependencyObject);

        if (command != null)
        {
            command.Execute(page);
        }
    }

    #endregion

}

А затем привяжите его к команде в xaml:

        <controls:PageControl
            Grid.Row="2"
            CurrentPage="{Binding Path=UsersSearchModel.Page,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            PerPage="{Binding Path=UsersSearchModel.PageSize,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            Count="{Binding Path=UsersSearchModel.SearchResults.TotalItemCount}"
            behaviors:PageChangedBehavior.PageChangedCommand="{Binding PageChangedCommand}">
        </controls:PageControl>
1 голос
/ 10 февраля 2011

Я бы следовал за лучшим ответом в этом вопросе

В основном ваша модель представления будет содержать список всех ваших предметов и список выбранных предметов. Затем вы можете прикрепить поведение к вашему списку, который управляет вашим списком выбранных элементов.

Это означает, что у вас ничего нет в коде, а xaml довольно легко проследить, а также поведение можно использовать в другом месте вашего приложения.

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />
0 голосов
/ 20 марта 2019

Это реализация, использующая MarkupExtension.Несмотря на низкоуровневый характер (который требуется в этом сценарии), код XAML очень прост:

XAML

<SomeControl Click="{local:EventBinding EventToCommand}" CommandParameter="{local:Int32 12345}" />

Расширение Marup

public class EventBindingExtension : MarkupExtension
{
    private static readonly MethodInfo EventHandlerImplMethod = typeof(EventBindingExtension).GetMethod(nameof(EventHandlerImpl), new[] { typeof(object), typeof(string) });
    public string Command { get; set; }

    public EventBindingExtension()
    {
    }
    public EventBindingExtension(string command) : this()
    {
        Command = command;
    }

    // Do not use!!
    public static void EventHandlerImpl(object sender, string commandName)
    {
        if (sender is FrameworkElement frameworkElement)
        {
            object dataContext = frameworkElement.DataContext;

            if (dataContext?.GetType().GetProperty(commandName)?.GetValue(dataContext) is ICommand command)
            {
                object commandParameter = (frameworkElement as ICommandSource)?.CommandParameter;
                if (command.CanExecute(commandParameter)) command.Execute(commandParameter);
            }
        }
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget targetProvider &&
            targetProvider.TargetObject is FrameworkElement targetObject &&
            targetProvider.TargetProperty is MemberInfo memberInfo)
        {
            Type eventHandlerType;
            if (memberInfo is EventInfo eventInfo) eventHandlerType = eventInfo.EventHandlerType;
            else if (memberInfo is MethodInfo methodInfo) eventHandlerType = methodInfo.GetParameters()[1].ParameterType;
            else return null;

            MethodInfo handler = eventHandlerType.GetMethod("Invoke");
            DynamicMethod method = new DynamicMethod("", handler.ReturnType, new[] { typeof(object), typeof(object) });

            ILGenerator ilGenerator = method.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg, 0);
            ilGenerator.Emit(OpCodes.Ldstr, Command);
            ilGenerator.Emit(OpCodes.Call, EventHandlerImplMethod);
            ilGenerator.Emit(OpCodes.Ret);

            return method.CreateDelegate(eventHandlerType);
        }
        else
        {
            throw new InvalidOperationException("Could not create event binding.");
        }
    }
}
0 голосов
/ 04 февраля 2011

Как упоминает @Cameron MacFarland, я бы просто сделал двустороннюю привязку к свойству в viewModel.В установщике свойств вы можете использовать любую логику, например, добавление в список контактов, в зависимости от ваших требований.

Однако я не обязательно вызывал бы свойство SelectedItem, поскольку viewModel не должен знать о слое вида и о том, как он взаимодействует со своими свойствами.Я бы назвал это чем-то вроде CurrentContact или что-то в этом роде.

Очевидно, что если вы просто не хотите создавать команды в качестве упражнения для практики и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...