WPFのコマンドパターンを使用して、ユーザーコントロールのドラッグアンドドロップイベントに応答するUIを作成するにはどうすればよいですか?
1 に答える
ユーザーコントロールについて
パラメータを持つコマンドを実装します。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
}