2009-06-23 37 views
6

Tôi đang cố gắng triển khai chức năng kéo và thả trong Ứng dụng bề mặt được tạo bằng mẫu MVVM. Tôi đang đấu tranh để đưa ra một phương tiện để thực hiện điều này trong khi tôn trọng mẫu MVVM. Mặc dù tôi đang cố gắng để làm điều này trong một ứng dụng bề mặt Tôi nghĩ rằng giải pháp là đủ chung để áp dụng cho WPF là tốt.Kéo và thả trong MVVM với ScatterView

Tôi đang cố gắng để tạo ra các chức năng sau:

  • danh bạ tài khoản một FrameworkElement trong một ScatterViewItem để bắt đầu một hoạt động kéo (một phần cụ thể của ScatterViewItem khởi kéo/thả chức năng)
  • Khi hoạt động kéo bắt đầu một bản sao của ScatterViewItem đó được tạo ra và được áp đặt trên ScatterViewItem ban đầu, bản sao là những gì người dùng sẽ kéo và cuối cùng thả
  • Người dùng có thể thả mục này vào ScatterViewItem khác (được đặt trong một ScatterView riêng biệt)

Tương tác tổng thể khá giống với ứng dụng ShoppingCart được cung cấp trong SDK bề mặt, ngoại trừ các đối tượng nguồn được chứa trong một ScatterView chứ không phải là ListBox.

Tôi không chắc chắn cách tiếp tục để cho phép liên lạc thích hợp giữa Mô hình chế độ xem của mình để cung cấp chức năng này. Vấn đề chính tôi gặp phải là sao chép ScatterViewItem khi người dùng liên lạc với FrameworkElement.

+0

Có cơ hội nhìn thấy một số mã không? Làm thế nào để ScatterViewItems giữ điều khiển trẻ em? Làm thế nào bạn ràng buộc điều này với một viewmodel? –

+0

Tôi sẽ cố gắng cung cấp câu trả lời 'thực' sau, nhưng về cơ bản, thao tác kéo và thả xảy ra chủ yếu trong các khung nhìn.Thực tế là việc kéo đang diễn ra có thể không yêu cầu bất kỳ liên lạc nào với chế độ xem của chế độ xem ban đầu của bạn cho đến khi thả xảy ra. Khi thả được phát hiện, bạn sẽ hoặc là gọi một phương thức hoặc thực thi một lệnh trong viewmodel của bạn và truyền vào thông tin về những gì đã bị loại bỏ. VM sau đó sẽ thêm nó vào một danh sách được gắn kết với scatterview đích của bạn. Chế độ xem gốc cũng sẽ xử lý cuộc gọi đã hoàn thành thả và chuyển cho chế độ xem của nó. –

Trả lời

4

Bạn có thể sử dụng thuộc tính được đính kèm. Tạo thuộc tính được đính kèm và trong phương thức setproperty liên kết với sự kiện thả xuống:


public static void SetDropCommand(ListView source, ICommand command) 
     { 
      source.Drop += (sender, args) => 
           { 
            var data = args.Data.GetData("FileDrop"); 
            command.Execute(data); 
           }; 
     } 

Sau đó, bạn có thể liên kết lệnh trong mô hình chế độ xem với điều khiển có liên quan trên chế độ xem. Rõ ràng bạn có thể muốn làm cho thuộc tính đính kèm của bạn áp dụng cho loại điều khiển cụ thể của bạn chứ không phải là một listview.

Hy vọng điều đó sẽ hữu ích.

2

Tôi đã có ý định làm cho ý tưởng của Steve Psaltis hoạt động. Phải mất một thời gian - thuộc tính phụ thuộc tùy chỉnh dễ bị sai. Dường như với tôi giống như SetXXX là nơi sai lầm để đặt các tác dụng phụ của bạn - WPF không phải đi qua đó, nó có thể đi trực tiếp đến DependencyObject.SetValue, nhưng PropertyChangedCallback sẽ luôn được gọi.

Vì vậy, đây là đầy đủ và làm việc mã cho các tùy chỉnh kèm theo tài sản:

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

namespace WpfApplication1 
{ 
    public static class PropertyHelper 
    { 
     public static readonly DependencyProperty DropCommandProperty = DependencyProperty.RegisterAttached(
      "DropCommand", 
      typeof(ICommand), 
      typeof(PropertyHelper), 
      new PropertyMetadata(null, OnDropCommandChange)); 

     public static void SetDropCommand(DependencyObject source, ICommand value) 
     { 
      source.SetValue(DropCommandProperty, value); 
     } 

     public static ICommand GetDropCommand(DependencyObject source) 
     { 
      return (ICommand)source.GetValue(DropCommandProperty); 
     } 

     private static void OnDropCommandChange(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      ICommand command = e.NewValue as ICommand; 
      UIElement uiElement = d as UIElement; 
      if (command != null && uiElement != null) 
      { 
       uiElement.Drop += (sender, args) => command.Execute(args.Data); 
      } 

      // todo: if e.OldValue is not null, detatch the handler that references it 
     } 
    } 
} 

Trong markup XAML nơi bạn muốn sử dụng này, bạn có thể làm ví dụ

xmlns:local="clr-namespace:WpfApplication1" 
... 
<Button Content="Drop here" Padding="12" AllowDrop="True" 
    local:PropertyHelper.DropCommand="{Binding DropCommand}" /> 

.. Và phần còn lại chỉ đảm bảo rằng ViewModel, liên kết và lệnh của bạn là đúng.

Phiên bản này chuyển số IDataObject đến lệnh có vẻ tốt hơn với tôi - bạn có thể truy vấn nó cho tệp hoặc bất kỳ điều gì trong lệnh. Nhưng đó chỉ là một sở thích hiện tại, không phải là một tính năng cần thiết của câu trả lời.