65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (2投票s)

2009年3月8日

CPOL

3分钟阅读

viewsIcon

52428

downloadIcon

908

本文主要讨论在 ListBox 中实现拖放和滚动等问题。

ListBoxScreen1.png

引言

在本文中,我将讨论我在 ListBox 中遇到的一些问题。我花了很多时间在 Google 上搜索解决方案,但没有找到太多。以下是本文中我将要讨论的问题列表:

  1. 在同一个 ListBox 中拖放。
  2. 滚动(将选定的项目带入可见区域)。

背景

本文是我的上一篇文章的延续,所以如果您还没有阅读过,请阅读,因为我将使用相同的示例来讨论这个问题。这是该文章的链接:

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 的代码。

© . All rights reserved.