如何将FrameworkContentElement附加到MVVM Light EventToCommand

时间:2011-08-05 11:50:26

标签: wpf devexpress mvvm-light

我正在尝试将事件转换为devexpress wpf网格上下文菜单项上的命令,该菜单项派生自FrameworkContentElement而不是FrameworkElement。这会导致运行时错误:

{“无法附加type \”EventToCommand \“来键入\”BarButtonItem \“。类型为”“EventToCommand \”的实例只能附加到类型为“FrameworkElement \”的对象。“}

有解决方法吗?

<dxg:TableView.RowCellMenuCustomizations>
    <dxb:BarButtonItem Name="deleteRowItem" Content="Delete" >
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="ItemClick">
                <cmd:EventToCommand Command="{Binding FooChangeCommand}"
                PassEventArgsToCommand="True" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </dxb:BarButtonItem>

    <!--ItemClick="deleteRowItem_ItemClick"/>-->
</dxg:TableView.RowCellMenuCustomizations>

不幸的是,devexpress在将基类更改为有意进行更改的FrameworkElement时遇到了问题......

3 个答案:

答案 0 :(得分:1)

FrameworkConentElement是一个仅在WPF中可用而不在Silverlight中的类。由于MVVM Light旨在为所有 WPF方言(WPF 3.5,WPF 4,Silverlight 3,Silverlight 4,Sivlverlight 5,WP 7,WP 7.1)提供通用功能它不能包含仅在其中一个框架中工作的实现。

有关FrameworkElementFrameworkContentElement see here之间差异的讨论。

但是,您可以轻松实现自己的EventToCommand类支持ContentElementFrameworkContentElement继承)。该类是从MVVM Light源代码的BL0015复制并修改的:

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace GalaSoft.MvvmLight.Command
{
    /// <summary>
    /// This <see cref="System.Windows.Interactivity.TriggerAction" /> can be
    /// used to bind any event on any FrameworkElement to an <see cref="ICommand" />.
    /// Typically, this element is used in XAML to connect the attached element
    /// to a command located in a ViewModel. This trigger can only be attached
    /// to a FrameworkElement or a class deriving from FrameworkElement.
    /// <para>To access the EventArgs of the fired event, use a RelayCommand&lt;EventArgs&gt;
    /// and leave the CommandParameter and CommandParameterValue empty!</para>
    /// </summary>
    ////[ClassInfo(typeof(EventToCommand),
    ////  VersionString = "3.0.0.0",
    ////  DateString = "201003041420",
    ////  Description = "A Trigger used to bind any event to an ICommand.",
    ////  UrlContacts = "http://stackoverflow.com/q/6955785/266919",
    ////  Email = "")]
    public partial class EventToCommandWpf : TriggerAction<DependencyObject>
    {
        /// <summary>
        /// Gets or sets a value indicating whether the EventArgs passed to the
        /// event handler will be forwarded to the ICommand's Execute method
        /// when the event is fired (if the bound ICommand accepts an argument
        /// of type EventArgs).
        /// <para>For example, use a RelayCommand&lt;MouseEventArgs&gt; to get
        /// the arguments of a MouseMove event.</para>
        /// </summary>
        public bool PassEventArgsToCommand
        {
            get;
            set;
        }

        /// <summary>
        /// Provides a simple way to invoke this trigger programatically
        /// without any EventArgs.
        /// </summary>
        public void Invoke()
        {
            Invoke(null);
        }

        /// <summary>
        /// Executes the trigger.
        /// <para>To access the EventArgs of the fired event, use a RelayCommand&lt;EventArgs&gt;
        /// and leave the CommandParameter and CommandParameterValue empty!</para>
        /// </summary>
        /// <param name="parameter">The EventArgs of the fired event.</param>
        protected override void Invoke(object parameter)
        {
            if (AssociatedElementIsDisabled())
            {
                return;
            }

            var command = GetCommand();
            var commandParameter = CommandParameterValue;

            if (commandParameter == null
                 && PassEventArgsToCommand)
            {
                commandParameter = parameter;
            }

            if (command != null
                 && command.CanExecute(commandParameter))
            {
                command.Execute(commandParameter);
            }
        }

        private static void OnCommandChanged(
             EventToCommandWpf element,
             DependencyPropertyChangedEventArgs e)
        {
            if (element == null)
            {
                return;
            }

            if (e.OldValue != null)
            {
                ((ICommand)e.OldValue).CanExecuteChanged -= element.OnCommandCanExecuteChanged;
            }

            var command = (ICommand)e.NewValue;

            if (command != null)
            {
                command.CanExecuteChanged += element.OnCommandCanExecuteChanged;
            }

            element.EnableDisableElement();
        }

        private bool AssociatedElementIsDisabled()
        {
            var element = GetAssociatedObject();
            return AssociatedObject == null
                 || (element != null
                     && !element.IsEnabled);
        }

        private void EnableDisableElement()
        {
            var element = GetAssociatedObject();

            if (element == null)
            {
                return;
            }

            var command = this.GetCommand();

            if (this.MustToggleIsEnabledValue
                 && command != null)
            {
                SetIsEnabled(element, command.CanExecute(this.CommandParameterValue));  
            }
        }

        private void OnCommandCanExecuteChanged(object sender, EventArgs e)
        {
            EnableDisableElement();
        }

        /// <summary>
        /// Identifies the <see cref="CommandParameter" /> dependency property
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
             "CommandParameter",
             typeof(object),
             typeof(EventToCommandWpf),
             new PropertyMetadata(
                  null,
                  (s, e) => {
                      var sender = s as EventToCommandWpf;
                      if (sender == null)
                      {
                          return;
                      }

                      if (sender.AssociatedObject == null)
                      {
                          return;
                      }

                      sender.EnableDisableElement();
                  }));

        /// <summary>
        /// Identifies the <see cref="Command" /> dependency property
        /// </summary>
        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
             "Command",
             typeof(ICommand),
             typeof(EventToCommandWpf),
             new PropertyMetadata(
                  null,
                  (s, e) => OnCommandChanged(s as EventToCommandWpf, e)));

        /// <summary>
        /// Identifies the <see cref="MustToggleIsEnabled" /> dependency property
        /// </summary>
        public static readonly DependencyProperty MustToggleIsEnabledProperty = DependencyProperty.Register(
             "MustToggleIsEnabled",
             typeof(bool),
             typeof(EventToCommandWpf),
             new PropertyMetadata(
                  false,
                  (s, e) => {
                      var sender = s as EventToCommandWpf;
                      if (sender == null)
                      {
                          return;
                      }

                      if (sender.AssociatedObject == null)
                      {
                          return;
                      }

                      sender.EnableDisableElement();
                  }));

        private object _commandParameterValue;

        private bool? _mustToggleValue;

        /// <summary>
        /// Gets or sets the ICommand that this trigger is bound to. This
        /// is a DependencyProperty.
        /// </summary>
        public ICommand Command
        {
            get
            {
                return (ICommand)GetValue(CommandProperty);
            }

            set
            {
                SetValue(CommandProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets an object that will be passed to the <see cref="Command" />
        /// attached to this trigger. This is a DependencyProperty.
        /// </summary>
        public object CommandParameter
        {
            get
            {
                return this.GetValue(CommandParameterProperty);
            }

            set
            {
                SetValue(CommandParameterProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets an object that will be passed to the <see cref="Command" />
        /// attached to this trigger. This property is here for compatibility
        /// with the Silverlight version. This is NOT a DependencyProperty.
        /// For databinding, use the <see cref="CommandParameter" /> property.
        /// </summary>
        public object CommandParameterValue
        {
            get
            {
                return this._commandParameterValue ?? this.CommandParameter;
            }

            set
            {
                _commandParameterValue = value;
                EnableDisableElement();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the attached element must be
        /// disabled when the <see cref="Command" /> property's CanExecuteChanged
        /// event fires. If this property is true, and the command's CanExecute 
        /// method returns false, the element will be disabled. If this property
        /// is false, the element will not be disabled when the command's
        /// CanExecute method changes. This is a DependencyProperty.
        /// </summary>
        public bool MustToggleIsEnabled
        {
            get
            {
                return (bool)this.GetValue(MustToggleIsEnabledProperty);
            }

            set
            {
                SetValue(MustToggleIsEnabledProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the attached element must be
        /// disabled when the <see cref="Command" /> property's CanExecuteChanged
        /// event fires. If this property is true, and the command's CanExecute 
        /// method returns false, the element will be disabled. This property is here for
        /// compatibility with the Silverlight version. This is NOT a DependencyProperty.
        /// For databinding, use the <see cref="MustToggleIsEnabled" /> property.
        /// </summary>
        public bool MustToggleIsEnabledValue
        {
            get
            {
                return this._mustToggleValue == null
                              ? this.MustToggleIsEnabled
                              : this._mustToggleValue.Value;
            }

            set
            {
                _mustToggleValue = value;
                EnableDisableElement();
            }
        }

        /// <summary>
        /// Called when this trigger is attached to a DependencyObject.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            EnableDisableElement();
        }

        /// <summary>
        /// This method is here for compatibility
        /// with the Silverlight version.
        /// </summary>
        /// <returns>The object to which this trigger
        /// is attached casted as a FrameworkElement.</returns>
        private IInputElement GetAssociatedObject()
        {
            return AssociatedObject as IInputElement;
        }

        private void SetIsEnabled(IInputElement element, bool value)
        {
            if (element is UIElement)
            {
                ((UIElement)element).IsEnabled = value;
            }
            else if (element is ContentElement)
            {
                ((ContentElement)element).IsEnabled = value;
            }
            else
            {
                throw new InvalidOperationException("Cannot set IsEnabled. Element is neither ContentElemen, nor UIElement.");
            }
        }

        /// <summary>
        /// This method is here for compatibility
        /// with the Silverlight version.
        /// </summary>
        /// <returns>The command that must be executed when
        /// this trigger is invoked.</returns>
        private ICommand GetCommand()
        {
            return Command;
        }
    }
}

要将它包含到您的代码中,您必须定义指向正确dll的xml命名空间,然后像普通EventToCommand类一样使用它。

注意:此类在Silverlight中不起作用!

答案 1 :(得分:1)

对于那些尝试使用dev express来解决这个特定问题的人来说,这样就可以了!

<dxg:TableView.RowCellMenuCustomizations>
   <dxb:BarButtonItem Name="deleteRowItem" Content="Delete" Command="{Binding View.DataContext.DeleteSelectionCommand}" />
</dxg:TableView.RowCellMenuCustomizations>

我跟着他们的例子,按钮上有一个事件,很少意识到我还可以使用一个命令。然后挑战是解决绑定,因为菜单项不在主视觉树上。但是上面解决了这个问题。

答案 2 :(得分:0)

我发现这与DEV express一起使用

 <dxb:BarButtonItem Content="123" Name="item1">
                <dxmvvm:Interaction.Triggers>
                    <dxmvvm:EventToCommand EventName="ItemClick" Command="{Binding SomeCommand}" CommandParameter="{Binding ElementName=item1, Path=Content}"/>
                </dxmvvm:Interaction.Triggers>
            </dxb:BarButtonItem>