WPF 拖放式可重排包装面板项目
WPF drag-drop rearrangeable wrappanel items
我需要一些 arranges/sorts 项目的东西,比如包装面板,但允许您通过拖放重新排列项目。我正在尝试尽快制作原型,所以我现在只是在寻找一些非常简单或 hacky 的东西。我到处寻找,但我能找到的唯一答案需要相对大量的工作才能实现。
如果可能的话,我宁愿不使用现有的库。
编辑
澄清一下:我需要拖放以及控件内的自动重新排列机制,像包装面板一样排列项目。
自从 post 提出这个问题后,我发现两个 posts/articles 似乎很合适,但前提是结合起来。
包裹面板:http://www.codeproject.com/Articles/18561/Custom-ListBox-Layout-in-WPF
拖放和排列:WPF C#: Rearrange items in listbox via drag and drop
我会 post 当我的代码全部正常工作时,我会在答案中给出我的代码。
花了一些时间,但在Micky的建议下我终于弄明白了。我在这里发布一个答案以供将来参考,如果其他人正在寻找这个机制。以下代码片段只需将它们粘贴到相应的文件中即可工作。
这是有效的方法:
通过 custom/default 样式使 ListBox 像 WrapPanel 一样排列项目(我的叫做 Default.xaml)。
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<!--'WrapPanel' can be replaced with other controls if you want it to display differently-->
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<!--Controls in each item-->
<local:DocPage />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
设置 ListBox 控件(即 MainWindow.xaml):
<ListBox x:Name="lbx_Pages" AllowDrop="True" DragEnter="lbx_Pages_DragEnter" PreviewMouseLeftButtonDown="lbx_Pages_PreviewMouseLeftButtonDown" PreviewMouseMove="lbx_Pages_PreviewMouseMove" Drop="lbx_Pages_PagesDrop"/>
在 .cs 文件中实现控件(即 MainWindow.xaml.cs):
private Point dragStartPoint;
// Bindable list of pages (binding logic omitted-out of scope of this post)
private static ObservableCollection<DocPage> pages = new ObservableCollection<DocPage>();
// Find parent of 'child' of type 'T'
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
do
{
if (child is T)
return (T)child;
child = VisualTreeHelper.GetParent(child);
} while (child != null);
return null;
}
private void lbx_Pages_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(null);
}
private void lbx_Pages_PreviewMouseMove(object sender, MouseEventArgs e)
{
ListBoxItem item = null;
DataObject dragData;
ListBox listBox;
DocPage page;
// Is LMB down and did the mouse move far enough to register a drag?
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(dragStartPoint.X - e.GetPosition(null).X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(dragStartPoint.Y - e.GetPosition(null).Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the ListBoxItem object from the object being dragged
item = FindParent<ListBoxItem>((DependencyObject)e.OriginalSource);
if (null != item)
{
listBox = sender as ListBox;
page = (DocPage)listBox.ItemContainerGenerator.ItemFromContainer(item);
dragData = new DataObject("pages", page);
DragDrop.DoDragDrop(item, dragData, DragDropEffects.Move);
}
}
}
private void lbx_Pages_PagesDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("pages"))
return;
DocPage draggedItem = e.Data.GetData("pages") as DocPage;
// Hit-test needed for rearranging items in the same ListBox
HitTestResult hit = VisualTreeHelper.HitTest((ListBox)sender, e.GetPosition((ListBox)sender));
DocPage target = (DocPage)FindParent<ListBoxItem>(hit.VisualHit).DataContext;
int removeIdx = lbx_Pages.Items.IndexOf(draggedItem);
int targetIdx = lbx_Pages.Items.IndexOf(target);
if(removeIdx < targetIdx)
{
pages.Insert(targetIdx + 1, draggedItem);
pages.RemoveAt(removeIdx);
}
else
{
removeIdx++;
if(pages.Count+1 > removeIdx)
{
pages.Insert(targetIdx, draggedItem);
pages.RemoveAt(removeIdx);
}
}
}
private void lbx_Pages_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("pages") || sender == e.Source)
e.Effects = DragDropEffects.None;
}
我需要一些 arranges/sorts 项目的东西,比如包装面板,但允许您通过拖放重新排列项目。我正在尝试尽快制作原型,所以我现在只是在寻找一些非常简单或 hacky 的东西。我到处寻找,但我能找到的唯一答案需要相对大量的工作才能实现。
如果可能的话,我宁愿不使用现有的库。
编辑
澄清一下:我需要拖放以及控件内的自动重新排列机制,像包装面板一样排列项目。
自从 post 提出这个问题后,我发现两个 posts/articles 似乎很合适,但前提是结合起来。
包裹面板:http://www.codeproject.com/Articles/18561/Custom-ListBox-Layout-in-WPF 拖放和排列:WPF C#: Rearrange items in listbox via drag and drop
我会 post 当我的代码全部正常工作时,我会在答案中给出我的代码。
花了一些时间,但在Micky的建议下我终于弄明白了。我在这里发布一个答案以供将来参考,如果其他人正在寻找这个机制。以下代码片段只需将它们粘贴到相应的文件中即可工作。
这是有效的方法:
通过 custom/default 样式使 ListBox 像 WrapPanel 一样排列项目(我的叫做 Default.xaml)。
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<!--'WrapPanel' can be replaced with other controls if you want it to display differently-->
<WrapPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<!--Controls in each item-->
<local:DocPage />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
设置 ListBox 控件(即 MainWindow.xaml):
<ListBox x:Name="lbx_Pages" AllowDrop="True" DragEnter="lbx_Pages_DragEnter" PreviewMouseLeftButtonDown="lbx_Pages_PreviewMouseLeftButtonDown" PreviewMouseMove="lbx_Pages_PreviewMouseMove" Drop="lbx_Pages_PagesDrop"/>
在 .cs 文件中实现控件(即 MainWindow.xaml.cs):
private Point dragStartPoint;
// Bindable list of pages (binding logic omitted-out of scope of this post)
private static ObservableCollection<DocPage> pages = new ObservableCollection<DocPage>();
// Find parent of 'child' of type 'T'
public static T FindParent<T>(DependencyObject child) where T : DependencyObject
{
do
{
if (child is T)
return (T)child;
child = VisualTreeHelper.GetParent(child);
} while (child != null);
return null;
}
private void lbx_Pages_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStartPoint = e.GetPosition(null);
}
private void lbx_Pages_PreviewMouseMove(object sender, MouseEventArgs e)
{
ListBoxItem item = null;
DataObject dragData;
ListBox listBox;
DocPage page;
// Is LMB down and did the mouse move far enough to register a drag?
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(dragStartPoint.X - e.GetPosition(null).X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(dragStartPoint.Y - e.GetPosition(null).Y) > SystemParameters.MinimumVerticalDragDistance))
{
// Get the ListBoxItem object from the object being dragged
item = FindParent<ListBoxItem>((DependencyObject)e.OriginalSource);
if (null != item)
{
listBox = sender as ListBox;
page = (DocPage)listBox.ItemContainerGenerator.ItemFromContainer(item);
dragData = new DataObject("pages", page);
DragDrop.DoDragDrop(item, dragData, DragDropEffects.Move);
}
}
}
private void lbx_Pages_PagesDrop(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("pages"))
return;
DocPage draggedItem = e.Data.GetData("pages") as DocPage;
// Hit-test needed for rearranging items in the same ListBox
HitTestResult hit = VisualTreeHelper.HitTest((ListBox)sender, e.GetPosition((ListBox)sender));
DocPage target = (DocPage)FindParent<ListBoxItem>(hit.VisualHit).DataContext;
int removeIdx = lbx_Pages.Items.IndexOf(draggedItem);
int targetIdx = lbx_Pages.Items.IndexOf(target);
if(removeIdx < targetIdx)
{
pages.Insert(targetIdx + 1, draggedItem);
pages.RemoveAt(removeIdx);
}
else
{
removeIdx++;
if(pages.Count+1 > removeIdx)
{
pages.Insert(targetIdx, draggedItem);
pages.RemoveAt(removeIdx);
}
}
}
private void lbx_Pages_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("pages") || sender == e.Source)
e.Effects = DragDropEffects.None;
}