KeyBinding中的附加属性绑定

时间:2015-02-05 10:03:05

标签: wpf mvvm wpf-controls dependency-properties

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

附属物:

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>

1 个答案:

答案 0 :(得分:0)

你可能想尝试这个解决方案。

使用Blend 3 Interactions,即Add System.Windows.Interactivity&amp; Microsoft.Expression.Interactions.dll作为项目的参考。我测试了下面的更改。执行方法(在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);
            }
        }
    }
}

屏幕截图:if System.Windows.Interactivity&amp;您的环境中缺少Microsoft.Expression.Interactions.dll,请安装blend。混合非常容易,并且安装不会花费太多时间。

Screenshot