我有两个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);
}
}
任何帮助和提示都表示赞赏。
答案 0 :(得分:1)
&#34; 但是这使得重新排序元素不可能&#34;为什么?它不应该,除非你做错了什么。要做你想做的事,一个自定义处理程序是要走的路,所以你应该详细说明它为什么不起作用。
要删除对象的集合应实现INotifyCollectionChanged,以便在向其中添加新项时更新UI。如果这不是您的问题,请编辑您的问题以添加详细信息。假设这不是问题所在,我建议一个替代的,更简单的,有时候更好的替代方案。
我最近使用此库添加了拖放功能到我的应用程序。我必须创建一个自定义放置处理程序,因为集合被放在包含关系上,而不是被拖动类的实例。
将其想象为关系数据库。你有两张桌子 - 人和宠物。宠物可以由多人拥有,人们可以拥有多只宠物。在数据库中,您将拥有一个多对多表。
人物 - &gt; PeoplePets&lt; - Pets
PeoplePets描述了人与宠物以及宠物与人们之间的关系。
在您的设计中,您实际上克隆宠物。这意味着你和你的女朋友现在有两只狗,它们都有相同的名字,并且都有强烈的愿望在邻居猫的大便中滚动。这很奇怪(克隆部分,而不是粪便部分),虽然我确信很多人会对这种安排感到满意。
让您的收藏品保留定义关系的对象,而不是收藏宠物克隆的收藏品。
cut -d: -f1,6 /etc/passwd | tr : ' ' | sort
所以,至于您的问题,在您的自定义放置处理程序中,当有人在您的列表框中放弃宠物时,只需创建一个新的PeoplePets实例,将宠物放入其中,然后将关系对象添加到集合中。您不必担心克隆任何内容,并且您不会添加相同内容的新实例(这可能非常有用,具体取决于您对未来数据的处理方式 - 检测和合并欺骗是PITA)。
答案 1 :(得分:1)
Followw Wills回答并基于默认的drop handler创建了我自己的drop handler。这样我可以覆盖默认行为,使其始终复制,但不在同一列表中。
查看默认代码我还发现它试图克隆副本上的对象,如果它们实现ICloneable
。所以我让它们可以克隆,返回一个新的自我实例。
以下是代码的相关部分(DropHandler代码主要基于原始DefaultDropHandler.cs):
<强> 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:谢谢你的回答,它指出了我正确的方向。为了帮助别人,我会回答我自己的问题,但我赞成你的回答。
答案 2 :(得分: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);
}
}
}