Silverlight ListBox 第 II 部分(同一列表框中的拖放和滚动)






4.60/5 (2投票s)
本文主要讨论在 ListBox 中实现拖放和滚动等问题。
引言
在本文中,我将讨论我在 ListBox 中遇到的一些问题。我花了很多时间在 Google 上搜索解决方案,但没有找到太多。以下是本文中我将要讨论的问题列表:
- 在同一个 ListBox 中拖放。
- 滚动(将选定的项目带入可见区域)。
背景
本文是我的上一篇文章的延续,所以如果您还没有阅读过,请阅读,因为我将使用相同的示例来讨论这个问题。这是该文章的链接:
ListBox 中有三种类型的项目(软件开发人员、团队负责人、经理)。需求是在 ListBox 中提供拖放功能,以便用户可以拖动开发人员项目并将其放到团队负责人项目上,这将把开发人员包括在团队负责人的报告列表中,同样适用于团队负责人和经理。 我在 ListBox 上找到了很多关于拖放的文章,但大多数都实现了从一个 ListBox 到另一个 ListBox 的拖放。 我还有另一个问题:获取 ListBox 内的控件。 由于抽象,没有直接的方法可以实现这一点。 我们只能获取 listboxitems。
运行附加的示例后,如果您将开发人员项目拖到团队负责人项目,您会看到开发人员被添加到团队负责人的列表中。
使用代码
对于拖放功能,我们首先要做的是在表单中添加一个弹出控件以显示视觉效果。 这是鼠标按下事件的代码,我们在其中设置数据模板和弹出控件的内容,该内容将随着我们拖动 listbox 项目而移动
<Popup x:Name="DragPopupControl" IsOpen="True">
<ContentControl x:Name="DragPopupControlContent" Opacity="0.5"/>
</Popup>
代码隐藏部分
private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Employee employee = (sender as UserControl).DataContext as Employee;
if(employee is Developer || employee is TeamLeader)
{
this.DragPopupControlContent.Content = employee;
DataTemplate dataTemplate =
this.EmployeeList.ItemTemplateSelector.SelectTemplate(employee, null);
dataTemplate.LoadContent();
this.DragPopupControlContent.ContentTemplate = dataTemplate;
this.DragPopupControl.CaptureMouse();
this.IsDragging = true;
this.DragPopupControl.IsOpen = true;
this.DragPopupControl.HorizontalOffset = e.GetPosition(LayoutRoot).X;
this.DragPopupControl.VerticalOffset = e.GetPosition(LayoutRoot).Y;
}
}
这是鼠标移动的代码,我们更改弹出控件的坐标以移动
private void UserControl_MouseMove(object sender, MouseEventArgs e)
{
if (this.IsDragging)
{
double currentVerticalPosition = e.GetPosition(LayoutRoot).Y;
double currentHorizontalPosition = e.GetPosition(LayoutRoot).X;
this.DragPopupControl.VerticalOffset = currentVerticalPosition;
this.DragPopupControl.HorizontalOffset = currentHorizontalPosition;
}
}
到目前为止一切都很顺利,但主要问题出现了。 如何获取我们正在放置任何其他项目的 ListBox 中的控件? 没有直接的方法可以做到这一点。 感谢我的一个同事,他给了我扩展方法的代码,解决了我的问题。 这是扩展方法的代码,它返回给定控件中所有控件的列表。 您可以传递您想要查找其子控件的控件和鼠标位置
public static class VisualTreeHelperExtensions
{
public static IList<T> GetChildControls<T>(
this DependencyObject obj) where T : DependencyObject
{
var childControls = new List<T>();
DependencyObject child;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child != null && (child.GetType() == typeof(T) ||
child.GetType().IsSubclassOf(typeof(T))))
{
childControls.Add(child as T);
}
else if (child != null)
{
IList<T> subChilds = child.GetChildControls<T>();
if (subChilds.Count > 0)
{
childControls.AddRange(subChilds);
}
}
}
return childControls;
}
}
我们可以使用扩展方法在给定坐标处获取 ListBox 中的所有控件,然后我们可以遍历所有项目并找出是否有任何团队负责人视图(如果用户正在拖动开发人员项目),或者是否有经理视图(如果用户正在拖动团队负责人项目)。 这是实际接受鼠标抬起位置并将拖动的项目添加到放下项目(如果拖放有效)的代码。
private void EmployeeDropOnFolder(object sender, Employee employee, MouseEventArgs e)
{
if (this.IsDragging && this.DragPopupControlContent.Content != null && employee != null)
{
Point currentMousePosition = e.GetPosition(this);
List<UIElement> controlList =
VisualTreeHelper.FindElementsInHostCoordinates(currentMousePosition,
EmployeeList) as List<UIElement>;
if (controlList != null && controlList.Count > 0)
{
// Remove the sender as it can't be target.
controlList.Remove(sender as UIElement);
// Find out if there is FolderView in the list.
foreach (UIElement uiElement in controlList)
{
if(uiElement is TeamLeaderView && employee is Developer)
{
TeamLeader teamLeader =
(uiElement as TeamLeaderView).DataContext as TeamLeader;
if(!teamLeader.DirectReports.Contains(employee))
{
teamLeader.DirectReports.Add(employee);
}
}
else if (uiElement is ManagerView && employee is TeamLeader)
{
Manager manager = (uiElement as ManagerView).DataContext as Manager;
if (!manager.DirectReports.Contains(employee))
{
manager.DirectReports.Add(employee);
}
}
}
}
}
}
private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
EmployeeDropOnFolder(sender, this.DragPopupControlContent.Content as Employee, e);
this.IsDragging = true;
this.DragPopupControl.IsOpen = false;
this.DragPopupControlContent.Content = null;
this.DragPopupControlContent.ContentTemplate = null;
this.DragPopupControlContent.ReleaseMouseCapture();
}
ListBox 中存在一个直接的方法 ScrollIntoView
以将选定的控件带入可见区域,但它对我不起作用,这可能是因为我使用的是 WrapPanel
。 我添加了代码以维护 WrapPanel
中的行索引和项目高度。 当用户单击 UI 中的按钮时,它会获取 ListBox 中当前选定的项目,将其传递给 WrapPanel
公共方法,并从 WrapPanel 顶部获取该项目的位置。 现在,我们可以设置滚动条的位置。 您可以在附加的源代码中查看 WrapPanel
的代码。