2016-06-16 55 views
0

我有兩個ListBoxes,它們的ItemsSource必須分開ObservableCollection<ICustomObject>ICustomObject是爲不同類型的CustomObject定義一些基本屬性的接口。拖放時複製對象

我想要一個ListBox是靜態的,不斷變化的可能元素來源,用戶可以從其中多次拖動它們到其他ListBox。目標中的元素也應該是可重新排列的。

結果應該是一個工具箱,用戶可以在該工具箱中構建由多個CustomObject組成的文檔。

我爲此使用了GongSolutions.WPF.DragDrop庫,提交c680fcf

<ListBox ItemsSource="{Binding AvailableElements}" dd:DragDrop.IsDragSource="True" dd:DragDrop.DragDropCopyKeyState="ControlKey"> 
<ListBox.ItemTemplate> 
    <DataTemplate> 
    <TextBlock Text="{Binding Name}" /> 
    </DataTemplate> 
</ListBox.ItemTemplate> 
</ListBox> 

<ListBox ItemsSource="{Binding SelectedElements}" dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDragSource="True"> 
<ListBox.ItemTemplate> 
    <DataTemplate> 
    <TextBlock Text="{Binding Name}" /> 
    </DataTemplate> 
</ListBox.ItemTemplate> 
</ListBox> 

有了這個,我可以通過按住ControlKey將元素從源複製到目標。

但有兩個問題:

  • 有什麼辦法默認爲複製動作,使被無按鍵操作的需要?
  • 我該如何做一個真正的副本?目前,目標列表的所有列表元素都指向同一個對象,結果是不能更改單個元素的屬性。

我已經有一個自定義DropHandler嘗試過,但,這使得重新排序的元素不可能:

public void Drop(IDropInfo dropInfo) 
{ 
    IFormElement data = Activator.CreateInstance(dropInfo.Data.GetType()) as IFormElement; 
    if (data != null) 
    { 
     SelectedElements.Add(data); 
    } 
} 

任何幫助和提示讚賞。

回答

1

Followed Wills回答並根據默認的拖放處理程序創建了我自己的拖放處理程序。這樣我可以覆蓋默認行爲總是複製,但不在同一個列表中。

看着默認代碼,我還發現它試圖複製副本上的對象,如果他們實現了ICloneable。所以我讓他們可複製返回他們自己的新實例。

這裏是(主要是基於原有DefaultDropHandler.cs DropHandler代碼)代碼的相關部分:

View.xaml:

<ListBox ItemsSource="{Binding AvailableElements}" dd:DragDrop.IsDragSource="True"> 
<ListBox.ItemTemplate> 
    <DataTemplate> 
    <TextBlock Text="{Binding Name}" /> 
    </DataTemplate> 
</ListBox.ItemTemplate> 
</ListBox> 
<ListBox ItemsSource="{Binding SelectedElements}" dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDragSource="True" dd:DragDrop.DropHandler="{Binding}"> 
<ListBox.ItemTemplate> 
    <DataTemplate> 
    <TextBlock Text="{Binding Name}" /> 
    </DataTemplate> 
</ListBox.ItemTemplate> 
</ListBox> 

ViewModel.cs(必須實現IDropTarget

public void DragOver(IDropInfo dropInfo) 
{ 
    DragDrop.DefaultDropHandler.DragOver(dropInfo); 
} 

public void Drop(IDropInfo dropInfo) 
{ 
    if (dropInfo == null || dropInfo.DragInfo == null) 
    { 
     return; 
    } 

    var insertIndex = dropInfo.InsertIndex != dropInfo.UnfilteredInsertIndex ? dropInfo.UnfilteredInsertIndex : dropInfo.InsertIndex; 
    var destinationList = dropInfo.TargetCollection.TryGetList(); 
    var data = ExtractData(dropInfo.Data); 

    // default to copy but not if source equals target 
    var copyData = (!Equals(dropInfo.DragInfo.SourceCollection, dropInfo.TargetCollection)) 
        && !(dropInfo.DragInfo.SourceItem is HeaderedContentControl) 
        && !(dropInfo.DragInfo.SourceItem is HeaderedItemsControl) 
        && !(dropInfo.DragInfo.SourceItem is ListBoxItem); 
    if (!copyData) 
    { 
     var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList(); 

     foreach (var o in data) 
     { 
      var index = sourceList.IndexOf(o); 

      if (index != -1) 
      { 
       sourceList.RemoveAt(index); 
       if (Equals(sourceList, destinationList) && index < insertIndex) 
       { 
        --insertIndex; 
       } 
      } 
     } 
    } 

    var tabControl = dropInfo.VisualTarget as TabControl; 

    // clone data but not if source equals target 
    var cloneData = !Equals(dropInfo.DragInfo.SourceCollection, dropInfo.TargetCollection); 
    foreach (var o in data) 
    { 
     var obj2Insert = o; 
     if (cloneData) 
     { 
      var cloneable = o as ICloneable; 
      if (cloneable != null) 
      { 
       obj2Insert = cloneable.Clone(); 
      } 
     } 

     destinationList.Insert(insertIndex++, obj2Insert); 

     if (tabControl != null) 
     { 
      var container = tabControl.ItemContainerGenerator.ContainerFromItem(obj2Insert) as TabItem; 
      if (container != null) 
      { 
       container.ApplyTemplate(); 
      } 

      tabControl.SetSelectedItem(obj2Insert); 
     } 
    } 
} 

public static IEnumerable ExtractData(object data) 
{ 
    if (data is IEnumerable && !(data is string)) 
    { 
     return (IEnumerable)data; 
    } 
    else 
    { 
     return Enumerable.Repeat(data, 1); 
    } 
} 

@ will:thanks for your answe r,它指出我正確的方向。爲了幫助別人,我會回答我自己的問題,但我贊成你的答案。

0

我可以回答你的第二個問題。做一個真正的副本,你可以使用下面的類或只是在代碼中使用克隆部分:

public class GenericCloner<T> where T : class 
{ 
    public T Clone(T obj) 
    { 
     using (var ms = new MemoryStream()) 
     { 
      var formatter = new BinaryFormatter(); 
      formatter.Serialize(ms, obj); 
      ms.Position = 0; 
      return (T)formatter.Deserialize(ms); 
     } 
    } 
} 
1

但是這使得重新排序不可能元素」爲什麼?它不應該,除非你做錯了什麼。要做你想做的事情,一個自定義處理程序是要走的路,所以你應該詳細說明爲什麼它不起作用。

要將對象添加到其中的對象,您要將對象放入其中的集合應實現INotifyCollectionChanged以便UI更新。如果這不是您的問題,請編輯您的問題以添加詳細信息。假設這不是問題,我會建議一個替代,簡單,有時更好的替代方案。

我最近使用這個庫添加拖放到我的應用程序。我不得不創建一個自定義拖放處理程序,因爲集合被拖放到包含關係到,而不是被拖動的類的實例。

把它想象成一個關係數據庫。你有兩張桌子 - 人和寵物。寵物可以由多人擁有,人們可以擁有多個寵物。在數據庫中,您將擁有一個多對多表。

人物 - > PeoplePets < - 寵物

PeoplePets介紹了他們的人民人民和他們的寵物,和寵物之間的關係。

在你的設計中,你的字面意思是克隆一個寵物。這意味着你和你的女朋友現在有兩條狗,都是同名的,並且都渴望捲入鄰居貓的糞便。這很奇怪(克隆部分,而不是便便部分),儘管我相信很多人會對這種安排感到滿意。

而不是你的收藏品持有寵物的克隆,讓你的收藏品舉行定義關係的對象

public ObservableCollection<PeoplePets> Pets {get;} = 
    new ObservableCollection<PeoplePets>(); 

所以,你的問題點,在自定義下拉處理程序,當有人滴在你的列表框的寵物,只需創建PeoplePets的新實例,在它下降的寵物,並添加關係對象到集合。您不必擔心會克隆任何東西,也不會添加同一事物的新實例(這可能非常有幫助,具體取決於您對數據的處理方式 - 檢測和合並投影是一個PITA)。