命令绑定到用户控件拖/放

发布于 2024-08-13 07:21:56 字数 41 浏览 5 评论 0原文

如何使用 WPF 中的命令模式创建响应用户控件的拖/放事件的 UI?

How can I create a UI that responds to Drag/Drop events of a usercontrol by usinng the Command pattern in WPF?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

木落 2024-08-20 07:21:56

在用户控件上

实现具有参数的命令。我将 ICommand 与 Josh Smiths RelayCommand 一起使用,但我对其进行了扩展以给它一个参数。 (代码在此答案末尾)

  /// <summary>
  /// Gets and Sets the ICommand that manages dragging and dropping.
  /// </summary>
  /// <remarks>The CanExecute will be called to determin if a drop can take place, the Executed is called when a drop takes place</remarks>
  public ICommand DragDropCommand {
     get { return (ICommand)GetValue(DragDropCommandProperty); }
     set { SetValue(DragDropCommandProperty, value); }

现在您可以将视图模型绑定到此命令。

为我们的实体拖动类型设置另一个属性(您可以对其进行硬编码),但我将此用户控件重用于不同的事物,并且我不希望一个控件接受拖放时的错误实体类型。

  /// <summary>
  /// Gets and Sets the Name of the items we are dragging
  /// </summary>
  public String DragEntityType {
     get { return (String)GetValue(DragEntityTypeProperty); }
     set { SetValue(DragEntityTypeProperty, value); }
  }

当你调用DragDrop.DoDragDrop时,重写OnPreviewLeftMouseButtonDown

   protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
      //find the item the mouse is over, i.e. the one you want to drag.
      var itemToDrag = FindItem(e);

      //move the selected items, using the drag entity type
      DataObject data = new DataObject(this.DragEntityType, itemToDrag);
      //use the helper class to initiate the drag
      DragDropEffects de = DragDrop.DoDragDrop(this, data, DragDropEffects.Move);

      //call the base
      base.OnPreviewMouseLeftButtonDown(e);
   }

,下面的方法会在适当的时候被调用

重写OnDragOver和OnDragDrop方法,并使用一个命令来询问我们是否可以拖动,我们可以

protected override void OnDragOver(DragEventArgs e) {

     //if we can accept the drop
     if (this.DragDropCommand != null && this.DragDropCommand.CanExecute(e.Data)) {

        // Console.WriteLine(true);
     }
     //otherwise
     else {
        e.Effects = DragDropEffects.None;
        e.Handled = true;
     }
     base.OnDragOver(e);
  }

  protected override void OnDrop(DragEventArgs e) {

     if (this.DragDropCommand == null) { }
     //if we dont allow dropping on ourselves and we are trying to do it
     //else if (this.AllowSelfDrop == false && e.Source == this) { }
     else {
        this.DragDropCommand.Execute(e.Data);
     }
     base.OnDrop(e);
  }

在视图模型中放置 通常您会从一个用户控件拖动到另一个用户控件

那么当您在视图模型中设置命令时,请使用类似这样的内容,然后将命令绑定到您的用户控件,

     this.MyDropCommand = new ExtendedRelayCommand((Object o) => AddItem(o), (Object o) => { return ItemCanBeDragged(o); });

,这样您就可以为一个用户控件设置一个命令,并为一个用户控件设置一个命令对于另一个,每个都有您可以接受的不同 DragEntityType。两个用户控件,一个用于拖动,一个用于放置,反之亦然。每个用户控件都有不同的 DragEntityType,因此您可以辨别拖动源自哪一个。

  private Boolean ItemCanBeDragged(object o) {
     Boolean returnValue = false;

     //do they have permissions to dragt
     if (this.HasPermissionToDrag) {

        IDataObject data = o as IDataObject;

        if (data == null) { }
        //this line looks up the DragEntityType
        else if (data.GetDataPresent("ItemDragEntityTypeForItemWeAreDragging")) {
           returnValue = true;
        }
     }
     return returnValue;
  }

当我们放下时,

  private void AddItem(object o) {
     IDataObject data = o as IDataObject;

     if (data == null) { }
     else {
        MyDataObject myData = data.GetData("ItemDragEntityTypeForItemWeAreDroppingHere") as MyDataObject ;

        if (myData == null) { }
        else {
            //do something with the dropped data
        }
     }
  }

我可能会错过一些东西,但是这种技术可以让我询问视图模型是否可以拖动项目,并让我询问视图模型是否可以放下(如果视图模型将接受该项目)其可绑定的,它很好地分离了视图/视图模型。如果您有任何疑问,请随时提问。

扩展中继命令,谢谢乔什史密斯......

   /// <summary>
   /// A command whose sole purpose is to 
   /// relay its ExtendedFunctionality to other
   /// objects by invoking delegates. The
   /// default return value for the CanExecute
   /// method is 'true'.
   /// </summary>
   public class ExtendedRelayCommand : ICommand {
      #region Constructors

      /// <summary>
      /// Creates a new command that can always execute.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      public ExtendedRelayCommand(Action<Object> execute)
         : this(execute, null) {
      }

      /// <summary>
      /// Creates a new command.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      /// <param name="canExecute">The execution status logic.</param>
      public ExtendedRelayCommand(Action<Object> execute, Func<Object, bool> canExecute) {
         if (execute == null)
            throw new ArgumentNullException("execute");

         _execute = execute;
         _canExecute = canExecute;
      }

      #endregion // Constructors

      #region ICommand Members

      [DebuggerStepThrough]
      public bool CanExecute(object parameter) {
         return _canExecute == null ? true : _canExecute(parameter);
      }

      public event EventHandler CanExecuteChanged {
         add {
            if (_canExecute != null)
               CommandManager.RequerySuggested += value;
         }
         remove {
            if (_canExecute != null)
               CommandManager.RequerySuggested -= value;
         }
      }

      public void Execute(object parameter) {
         _execute(parameter);
      }

      #endregion // ICommand Members

      #region Fields

      readonly Action<Object> _execute;
      readonly Func<Object, bool> _canExecute;

      #endregion // Fields
   }

On the User Control

Implement a command that has a parameter. I use ICommand with Josh Smiths RelayCommand, but i extend it to give it a parameter. (code at the end of this answer)

  /// <summary>
  /// Gets and Sets the ICommand that manages dragging and dropping.
  /// </summary>
  /// <remarks>The CanExecute will be called to determin if a drop can take place, the Executed is called when a drop takes place</remarks>
  public ICommand DragDropCommand {
     get { return (ICommand)GetValue(DragDropCommandProperty); }
     set { SetValue(DragDropCommandProperty, value); }

now you can bind your view model to this command.

set another property for our entity drag type (you could hard code this) but i reuse this user control for different things and i dont want one control to accept the wrong entity type on a drop.

  /// <summary>
  /// Gets and Sets the Name of the items we are dragging
  /// </summary>
  public String DragEntityType {
     get { return (String)GetValue(DragEntityTypeProperty); }
     set { SetValue(DragEntityTypeProperty, value); }
  }

Override the OnPreviewLeftMouseButtonDown

   protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
      //find the item the mouse is over, i.e. the one you want to drag.
      var itemToDrag = FindItem(e);

      //move the selected items, using the drag entity type
      DataObject data = new DataObject(this.DragEntityType, itemToDrag);
      //use the helper class to initiate the drag
      DragDropEffects de = DragDrop.DoDragDrop(this, data, DragDropEffects.Move);

      //call the base
      base.OnPreviewMouseLeftButtonDown(e);
   }

when you call DragDrop.DoDragDrop, the below methods will be called at the approriate time

Override the OnDragOver and OnDragDrop methods, and use a command to ask if we can drag and we can drop

protected override void OnDragOver(DragEventArgs e) {

     //if we can accept the drop
     if (this.DragDropCommand != null && this.DragDropCommand.CanExecute(e.Data)) {

        // Console.WriteLine(true);
     }
     //otherwise
     else {
        e.Effects = DragDropEffects.None;
        e.Handled = true;
     }
     base.OnDragOver(e);
  }

  protected override void OnDrop(DragEventArgs e) {

     if (this.DragDropCommand == null) { }
     //if we dont allow dropping on ourselves and we are trying to do it
     //else if (this.AllowSelfDrop == false && e.Source == this) { }
     else {
        this.DragDropCommand.Execute(e.Data);
     }
     base.OnDrop(e);
  }

In the View Model

then when you are setting up your command in the view model use something like this, then bind the command to your user control

     this.MyDropCommand = new ExtendedRelayCommand((Object o) => AddItem(o), (Object o) => { return ItemCanBeDragged(o); });

usually you are dragging from one user control to another so you would set up one command for one user control and one for the other, each having a different DragEntityType that you would accept. Two user controls one to drag from, one to drop on, and vica versa. each user control has a different DragEntityType so you can tell which one the drag originated from.

  private Boolean ItemCanBeDragged(object o) {
     Boolean returnValue = false;

     //do they have permissions to dragt
     if (this.HasPermissionToDrag) {

        IDataObject data = o as IDataObject;

        if (data == null) { }
        //this line looks up the DragEntityType
        else if (data.GetDataPresent("ItemDragEntityTypeForItemWeAreDragging")) {
           returnValue = true;
        }
     }
     return returnValue;
  }

and when we drop

  private void AddItem(object o) {
     IDataObject data = o as IDataObject;

     if (data == null) { }
     else {
        MyDataObject myData = data.GetData("ItemDragEntityTypeForItemWeAreDroppingHere") as MyDataObject ;

        if (myData == null) { }
        else {
            //do something with the dropped data
        }
     }
  }

I might have missed something, but this technique lets me ask the view model if i can drag an item, and lets me ask the view model if i can drop (if the view model will accept the item) its bindable, and it seperates view/ view model nicely. If you have any questions feel free to ask.

Extended Relay Command, thanks Josh Smith...

   /// <summary>
   /// A command whose sole purpose is to 
   /// relay its ExtendedFunctionality to other
   /// objects by invoking delegates. The
   /// default return value for the CanExecute
   /// method is 'true'.
   /// </summary>
   public class ExtendedRelayCommand : ICommand {
      #region Constructors

      /// <summary>
      /// Creates a new command that can always execute.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      public ExtendedRelayCommand(Action<Object> execute)
         : this(execute, null) {
      }

      /// <summary>
      /// Creates a new command.
      /// </summary>
      /// <param name="execute">The execution logic.</param>
      /// <param name="canExecute">The execution status logic.</param>
      public ExtendedRelayCommand(Action<Object> execute, Func<Object, bool> canExecute) {
         if (execute == null)
            throw new ArgumentNullException("execute");

         _execute = execute;
         _canExecute = canExecute;
      }

      #endregion // Constructors

      #region ICommand Members

      [DebuggerStepThrough]
      public bool CanExecute(object parameter) {
         return _canExecute == null ? true : _canExecute(parameter);
      }

      public event EventHandler CanExecuteChanged {
         add {
            if (_canExecute != null)
               CommandManager.RequerySuggested += value;
         }
         remove {
            if (_canExecute != null)
               CommandManager.RequerySuggested -= value;
         }
      }

      public void Execute(object parameter) {
         _execute(parameter);
      }

      #endregion // ICommand Members

      #region Fields

      readonly Action<Object> _execute;
      readonly Func<Object, bool> _canExecute;

      #endregion // Fields
   }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文