XAML 如何在WPF中同时使用命令和绑定?

5us2dqdw  于 2023-01-28  发布在  其他
关注(0)|答案(2)|浏览(212)

我一直在练习MVVM模式,遇到了一个我不知道如何解决的问题。这个问题很简单,我也希望能找到解决方案。问题的关键是,当我设置一个元素的样式时,我试图使用一个命令和绑定,但我不能同时做这两件事。
我有以下样式的ListBoxItem:

<Style x:Key="OptionDieStyle" TargetType="ListBoxItem">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Border Width="Auto"
                                BorderThickness="1.5"
                                CornerRadius="10"
                                Height="30"
                                Background="Transparent"
                                Margin="5">
                            <TextBlock Margin="5"
                                       Text="{Binding}"
                                       Foreground="White"
                                       VerticalAlignment="Center"/>
                            <Border.InputBindings>
                                <MouseBinding MouseAction="LeftClick" Command="#Omitted"
                            </Border.InputBindings>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

这个ListBox填充了字符串,由于样式的原因,字符串会以特定的方式显示,这意味着当我想用command处理用户点击那个元素时,需要为这个项目设置DataContext,DataContext包含command所在的ViewModel,但是如果我这样做了,ListBox Items中就不会显示任何内容,当然,我可以为这个边界设置事件,如“鼠标按下”,但这将是错误的使用MVVM的方式。
如果您对如何使用命令解决这个问题有什么想法,请与我们分享。

0vvn1miw

0vvn1miw1#

为了简化这些场景,我从CommandBindin派生了一个类。在这个类中,他添加了绑定到ViewModel命令的功能。您可以将绑定设置为Execute和PreviewExecute。

using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace CommonCore.AttachedProperties
{
    public class CommandBindingHelper : CommandBinding
    {
        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        protected static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(CommandBindingHelper),
                new PropertyMetadata(null));

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        protected static readonly DependencyProperty PreviewCommandProperty =
            DependencyProperty.RegisterAttached(
                "PreviewCommand",
                typeof(ICommand),
                typeof(CommandBindingHelper),
                new PropertyMetadata(null));

        public BindingBase Binding { get; set; }
        public BindingBase PreviewBinding { get; set; }

        public CommandBindingHelper()
        {
            Executed += (s, e) => PrivateExecuted(CheckSender(s), e.Parameter, CommandProperty, Binding);
            CanExecute += (s, e) => e.CanExecute = PrivateCanExecute(CheckSender(s), e.Parameter, CommandProperty, Binding);
            PreviewExecuted += (s, e) => PrivateExecuted(CheckSender(s), e.Parameter, PreviewCommandProperty, PreviewBinding);
            PreviewCanExecute += (s, e) => e.CanExecute = PrivateCanExecute(CheckSender(s), e.Parameter, PreviewCommandProperty, PreviewBinding);
        }
        private static void PrivateExecuted(UIElement sender, object parameter, DependencyProperty commandProp, BindingBase commandBinding)
        {
            ICommand command = GetCommand(sender, commandProp, commandBinding);
            if (command is not null && command.CanExecute(parameter))
            {
                command.Execute(parameter);
            }
        }
        private static bool PrivateCanExecute(UIElement sender, object parameter, DependencyProperty commandProp, BindingBase commandBinding)
        {
            ICommand command = GetCommand(sender, commandProp, commandBinding);
            return command?.CanExecute(parameter) ?? true;
        }

        private static UIElement CheckSender(object sender)
        {
            if (sender is not UIElement element)
                throw new NotImplementedException("Implemented only for UIElement.");
            return element;
        }
        private static ICommand GetCommand(UIElement sender, DependencyProperty commandProp, BindingBase commandBinding)
        {
            BindingBase binding = BindingOperations.GetBindingBase(sender, commandProp);
            if (binding != commandBinding)
            {
                if (commandBinding is null)
                {
                    BindingOperations.ClearBinding(sender, commandProp);
                }
                else
                {
                    BindingOperations.SetBinding(sender, commandProp, commandBinding);
                }
            }
            return (ICommand)sender.GetValue(CommandProperty);
        }
    }
}

其使用示例:
x一个一个一个一个x一个一个二个x
如果我想设置不同的名称,我该怎么做?
1.最简单的方法是在Windows或(更好的)应用程序资源中创建命令。
一个三个三个一个
1.创建一个包含命令的静态属性。但它应该在视图级别创建,而不是ViewModel。

public static class MyCommands
    {
        public static RoutedUICommand Remove { get; }
            = new RoutedUICommand("Delete Item", "Remove", typeof(MyCommands));
        public static RoutedUICommand Add { get; }
            = new RoutedUICommand("Add Item", "Add", typeof(MyCommands));
    }
<Button Content="Remove"
           Command="{x:Static local:MyCommands.Remove}"
           CommandParameter="{Binding}"/>

1.将标记扩展添加到早期版本中,使其更易于在XAML中使用。

public class MyCommands : MarkupExtension
    {
        public static RoutedUICommand Remove { get; } = new RoutedUICommand("Delete Item", "Remove", typeof(MyCommands));
        public static RoutedUICommand Add { get; } = new RoutedUICommand("Add Item", "Add", typeof(MyCommands));

        public string? CommandName { get; set; }

        public MyCommands() { }

        public MyCommands(string commandName) => CommandName = commandName;

        public override object ProvideValue(IServiceProvider serviceProvider)
            => CommandName switch
        {
            nameof(Remove) => Remove,
            nameof(Add) => Add,
            _ => throw new NotImplementedException()
        };
    }
<Button Content="Remove"
           Command="{local:MyCommands Remove}"
           CommandParameter="{Binding}"/>
des4xlb0

des4xlb02#

上面的方法很好用,但前提是我们要使用具有默认ApplicationCommands名称的命令,并且不给予它们单独的名称。我绞尽脑汁,最终找到了合适的方法。我所要做的就是在ViewModel中使我的命令静态化,并在XAML中更改命令的定义,如下所示:
Command="{x:Static viewModels:MyViewModel.MyCommand}

相关问题