3

WPFのコマンドパターンを使用して、ユーザーコントロールのドラッグアンドドロップイベントに応答するUIを作成するにはどうすればよいですか?

4

1 に答える 1

2

ユーザーコントロールについて

パラメータを持つコマンドを実装します。Josh Smiths RelayCommandでICommand を使用していますが、それを拡張してパラメーターを指定しています。(この回答の最後のコード)

  /// <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); }

これで、ビュー モデルをこのコマンドにバインドできます。

エンティティ ドラッグ タイプに別のプロパティを設定します (これをハード コードすることもできます) が、このユーザー コントロールを別のものに再利用し、ドロップ時に 1 つのコントロールが間違ったエンティティ タイプを受け入れないようにします。

  /// <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); }
  }

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);
   }

DragDrop.DoDragDrop を呼び出すと、以下のメソッドが適切なタイミングで呼び出されます

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); });

通常、1 つのユーザー コントロールから別のユーザー コントロールにドラッグするため、1 つのユーザー コントロールに対して 1 つのコマンドを設定し、別のユーザー コントロールに対して 1 つのコマンドを設定し、それぞれが受け入れる異なる DragEntityType を持ちます。ドラッグ元、ドロップ先、およびその逆の 2 つのユーザー コントロール。各ユーザー コントロールには異なる 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
        }
     }
  }

何かを見逃しているかもしれませんが、このテクニックを使用すると、アイテムをドラッグできるかどうかをビュー モデルに尋ねたり、(ビュー モデルがアイテムを受け入れる場合) バインド可能で、ビューを分離できるかどうかをビュー モデルに尋ねたりすることができます。 / モデルをきれいに表示します。ご不明な点がございましたら、お気軽にお問い合わせください。

拡張リレー コマンド、ありがとう 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
   }
于 2009-12-08T00:32:28.790 に答える