C#实现ListView的拖放自动滚动

5

如何在Winforms ListView中实现自动滚动(例如,当您接近顶部或底部时,ListView会滚动)?我已经在Google上搜索了一番,但没有什么收获。我无法相信这不是开箱即用的功能! 提前感谢您的帮助。 Dave

4个回答

9

感谢提供链接(http://www.knowdotnet.com/articles/listviewdragdropscroll.html),我将其转换为C#代码。

class ListViewBase:ListView
{
    private Timer tmrLVScroll;
    private System.ComponentModel.IContainer components;
    private int mintScrollDirection;
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
    const int WM_VSCROLL = 277; // Vertical scroll
    const int SB_LINEUP = 0; // Scrolls one line up
    const int SB_LINEDOWN = 1; // Scrolls one line down

    public ListViewBase()
    {
        InitializeComponent();
    }
    protected void InitializeComponent()
    {
        this.components = new System.ComponentModel.Container();
        this.tmrLVScroll = new System.Windows.Forms.Timer(this.components);
        this.SuspendLayout();
        // 
        // tmrLVScroll
        // 
        this.tmrLVScroll.Tick += new System.EventHandler(this.tmrLVScroll_Tick);
        // 
        // ListViewBase
        // 
        this.DragOver += new System.Windows.Forms.DragEventHandler(this.ListViewBase_DragOver);
        this.ResumeLayout(false);

    }

    protected void ListViewBase_DragOver(object sender, DragEventArgs e)
    {
        Point position = PointToClient(new Point(e.X, e.Y));

        if (position.Y <= (Font.Height / 2))
        {
            // getting close to top, ensure previous item is visible
            mintScrollDirection = SB_LINEUP;
            tmrLVScroll.Enabled = true;
        }else if (position.Y >= ClientSize.Height - Font.Height / 2)
        { 
            // getting close to bottom, ensure next item is visible
            mintScrollDirection = SB_LINEDOWN;
            tmrLVScroll.Enabled = true;
        }else{
            tmrLVScroll.Enabled = false;
        }
    }

    private void tmrLVScroll_Tick(object sender, EventArgs e)
    {
        SendMessage(Handle, WM_VSCROLL, (IntPtr)mintScrollDirection, IntPtr.Zero);
    }
}

George Polevoy的答案比这个简单得多。 - Carlos Quintero

4
滚动可以使用ListViewItem.EnsureVisible方法完成。 您需要确定当前拖动的项目是否在列表视图的可见边界上,且不是第一个/最后一个。
private static void RevealMoreItems(object sender, DragEventArgs e)
{
    var listView = (ListView)sender;

    var point = listView.PointToClient(new Point(e.X, e.Y));
    var item = listView.GetItemAt(point.X, point.Y);
    if (item == null)
        return;

    var index = item.Index;
    var maxIndex = listView.Items.Count;
    var scrollZoneHeight = listView.Font.Height;

    if (index > 0 && point.Y < scrollZoneHeight)
    {
        listView.Items[index - 1].EnsureVisible();
    }
    else if (index < maxIndex && point.Y > listView.Height - scrollZoneHeight)
    {
        listView.Items[index + 1].EnsureVisible();
    }
}

然后将此方法与DragOver事件连接起来。
targetListView.DragOver += RevealMoreItems;

targetListView.DragOver += (sender, e) =>
{
    e.Effect = DragDropEffects.Move;
};

一旦您获得一个非空项,就可以始终调用item.EnsureVisible()。如果需要滚动,则会执行滚动操作,否则将不执行任何操作。 - Carlos Quintero
我不懂。这里的想法是在你拖动的物品旁边呼叫一个InsureVisible函数。 - George Polevoy
在检查项不为null并避免获取索引、maxIndex、scrollZoneHeight等信息后,您可以使用item.EnsureVisible()函数。用户需要在顶部或底部边缘拖动一点距离,但它能够工作。如果您希望用户在悬停到第一个或最后一个项目时就能进行滚动,则您的代码是完美的。 - Carlos Quintero
两个更正。将 var maxIndex = listView.Items.Count - 1 以避免在向下滚动时崩溃到最后一个项目,将 var scrollZoneHeight = listView.Font.Height * 2 以扩大滚动开始的范围。然后点赞。 - Luzius

2

看看ObjectListView。它可以实现这些功能。如果你不想使用ObjectListView本身,你可以阅读代码并使用它。


0

给未来的谷歌搜索者一个提示:如果要在更复杂的控件(如DataGridView)上使其工作,请参见this thread


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接