【问题标题】:Attached Property binding in KeyBindingKeyBinding 中的附加属性绑定
【发布时间】:2015-02-05 10:03:04
【问题描述】:

我在 WPF 中的 KeyBinding 中的绑定有问题。我正在使用带有 MVVM 模式的 WPF 开发 .net 3.5 项目。每当输入一些字母时,我都必须发出命令。不幸的是,Command 和 CommandParameter 在这个 .net 版本中不是依赖属性,我无法绑定到它们。因此,我编写了附加属性以从我的视图模型中分配命令和命令参数。但是绑定到它们不起作用,当我将绑定更改为文本(在命令参数中)时,CommandBindingParameterChanged 会上升,但当绑定到参数时它不会上升。我厌倦了设置窗口的名称并将其传递给绑定,但它也不起作用。但是当我将相同的命令分配给按钮时,它工作正常。这是我的代码 sn-p:

附加属性:

public class Helper
{
    public static readonly DependencyProperty CommandBindingProperty = DependencyProperty.RegisterAttached("CommandBinding", typeof(ICommand), typeof(Helper), new FrameworkPropertyMetadata(default(ICommand), FrameworkPropertyMetadataOptions.None, CommandChanged));

    public static ICommand GetCommandBinding(DependencyObject o)
    {
        return (ICommand)o.GetValue(CommandBindingProperty);
    }
    public static void SetCommandBinding(DependencyObject o, ICommand value)
    {
        o.SetValue(CommandBindingProperty, value);
    }

    private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var input = d as InputBinding;

        input.Command = (ICommand)e.NewValue;
    }


    public static readonly DependencyProperty CommandBindingParameterProperty = DependencyProperty.RegisterAttached("CommandBindingParameter", typeof(object), typeof(Helper), new PropertyMetadata(CommandParameterChanged));

    private static void CommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var input = d as InputBinding;
        if (input != null)
            input.CommandParameter = e.NewValue;
    }
    public static object GetCommandBindingParameter(DependencyObject o)
    {
        return o.GetValue(CommandBindingParameterProperty);
    }
    public static void SetCommandBindingParameter(DependencyObject o, object value)
    {
        o.SetValue(CommandBindingParameterProperty, value);
    }
}

视图模型

public class MainWindowViewModel : ViewModelBase
{

    private string _text;

    public string Text
    {
        get { return _text; }
        set
        {
            _text = value;
            RaisePropertyChanged("Text");
        }
    }


    private bool _parameter;
    public bool Parameter
    {
        get { return _parameter; }
        set
        {
            _parameter = value;
            RaisePropertyChanged("Parameter");
        }
    }

    public MainWindowViewModel()
    {
        Parameter = true;
    }

    private RelayCommand<bool> _someCommand;

    public ICommand SomeCommand
    {
        get { return _someCommand ?? (_someCommand = new RelayCommand<bool>(Execute, CanExecute)); }
    }

    private bool CanExecute(bool arg)
    {
        return arg;
    }

    private void Execute(bool obj)
    {
        //do something
    }
}

XAML:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" 
    xmlns:local="clr-namespace:Test"
    Name="Window"
    DataContext="{Binding Main, Source={StaticResource Locator}}"
    >
<Grid>
    <StackPanel>
        <TextBox Text="{Binding Text}">
            <TextBox.InputBindings>
                <KeyBinding Key="A" local:Helper.CommandBinding="{Binding DataContext.SomeCommand, ElementName=Window}" local:Helper.CommandBindingParameter="{Binding DataContext.Parameter, ElementName=Window}"/>
            </TextBox.InputBindings>
        </TextBox>
        <Button Content="SomeButton" Command="{Binding SomeCommand}" CommandParameter="{Binding Parameter}"/>
    </StackPanel>
</Grid>

【问题讨论】:

    标签: wpf mvvm wpf-controls dependency-properties


    【解决方案1】:

    您可能想试试这个解决方案。

    使用 Blend 3 Interactions,即将 System.Windows.Interactivity 和 Microsoft.Expression.Interactions.dll 作为参考添加到您的项目中。我已经测试了以下更改。 Execute 方法(在 ViewModel 中定义)被称为移动文本框被键入。

    修改后的 XAML:

    <Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" 
        xmlns:local="clr-namespace:Test"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        Name="Window">
        <Window.DataContext>
            <local:MainWindowViewModel/>
        </Window.DataContext>
        <Grid>
            <StackPanel>
                <TextBox>
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="KeyUp">
                           <local:CommandAction Command="{Binding Path=SomeCommand}" CommandParameter="{Binding Path=Parameter}" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </TextBox>
            </StackPanel>
        </Grid>
    </Window>
    

    CommandAction.CS:使用 CommandAction 代替 Helper。 CommandAction 位于this location

    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Input;
    using Microsoft.Expression.Interactivity;
    using System.Windows.Interactivity;
    
    namespace Test
    {
        /// <summary>
        /// The CommandAction allows the user to route a FrameworkElement's routed event to a Command.
        /// For instance this makes it possible to specify--in Xaml--that right-clicking on a Border 
        /// element should execute the Application.Close command (this example may not make much sense, 
        /// but it does illustrate what's possible).
        /// 
        /// CommandParameter and CommandTarget properties are provided for consistency with the Wpf 
        /// Command pattern.
        /// 
        /// The action's IsEnabled property will be updated according to the Command's CanExecute value.
        /// 
        /// In addition a SyncOwnerIsEnabled property allows the user to specify that the owner element
        /// should be enabled/disabled whenever the action is enabled/disabled.
        /// </summary>
        public class CommandAction : TargetedTriggerAction<FrameworkElement>, ICommandSource
        {
            #region Properties to Expose
            [Category("Command Properties")]
            public ICommand Command
            {
                get { return (ICommand)GetValue(CommandProperty); }
                set { SetValue(CommandProperty, value); }
            }
    
            public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
                "Command", typeof(ICommand), typeof(CommandAction), new PropertyMetadata(
                    (ICommand)null, OnCommandChanged));
    
            [Category("Command Properties")]
            public object CommandParameter
            {
                get { return (object)GetValue(CommandParameterProperty); }
                set { SetValue(CommandParameterProperty, value); }
            }
    
            public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
                "CommandParameter", typeof(object), typeof(CommandAction), new PropertyMetadata());
    
            [Category("Command Properties")]
            public IInputElement CommandTarget
            {
                get { return (IInputElement)GetValue(CommandTargetProperty); }
                set { SetValue(CommandTargetProperty, value); }
            }
    
            public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
                "CommandTarget", typeof(IInputElement), typeof(CommandAction), new PropertyMetadata());
    
            [Category("Command Properties")]
            public bool SyncOwnerIsEnabled
            {
                get { return (bool)GetValue(SyncOwnerIsEnabledProperty); }
                set { SetValue(SyncOwnerIsEnabledProperty, value); }
            }
    
            /// <summary>
            /// When SyncOwnerIsEnabled is true then changing CommandAction.IsEnabled will automatically
            /// update the owner (Target) IsEnabled property.
            /// </summary>
            public static readonly DependencyProperty SyncOwnerIsEnabledProperty = DependencyProperty.Register(
                "SyncOwnerIsEnabled", typeof(bool), typeof(CommandAction), new PropertyMetadata());
    
            #endregion
    
            #region Command implementation
    
            /// <summary>
            /// This is a strong reference to the Command.CanExecuteChanged event handler. The commanding
            /// system uses a weak reference and if we don't enforce a strong reference then the event
            /// handler will be gc'ed.
            /// </summary>
            private EventHandler CanExecuteChangedHandler;
    
            private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var action = (CommandAction)d;
                action.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
            }
    
            private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
            {
                if (oldCommand != null)
                    UnhookCommand(oldCommand);
                if (newCommand != null)
                    HookCommand(newCommand);
            }
    
            private void UnhookCommand(ICommand command)
            {
                command.CanExecuteChanged -= CanExecuteChangedHandler;
                UpdateCanExecute();
            }
    
            private void HookCommand(ICommand command)
            {
                // Save a strong reference to the Command.CanExecuteChanged event handler. The commanding
                // system uses a weak reference and if we don't save a strong reference then the event
                // handler will be gc'ed.
                CanExecuteChangedHandler = new EventHandler(OnCanExecuteChanged);
                command.CanExecuteChanged += CanExecuteChangedHandler;
                UpdateCanExecute();
            }
    
            private void OnCanExecuteChanged(object sender, EventArgs e)
            {
                UpdateCanExecute();
            }
    
            private void UpdateCanExecute()
            {
                if (Command != null)
                {
                    RoutedCommand command = Command as RoutedCommand;
                    if (command != null)
                        IsEnabled = command.CanExecute(CommandParameter, CommandTarget);
                    else
                        IsEnabled = Command.CanExecute(CommandParameter);
                    if (Target != null && SyncOwnerIsEnabled)
                        Target.IsEnabled = IsEnabled;
                }
            }
    
            #endregion
    
            protected override void Invoke(object o)
            {
                if (Command != null)
                {
                    var command = Command as RoutedCommand;
                    if (command != null)
                        command.Execute(CommandParameter, CommandTarget);
                    else
                        Command.Execute(CommandParameter);
                }
            }
        }
    }
    

    屏幕截图:如果您的环境中缺少 System.Windows.Interactivity 和 Microsoft.Expression.Interactions.dll,请安装 blend。 Blend 很容易安装,安装不需要太多时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-02
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-25
      • 2011-11-01
      相关资源
      最近更新 更多