有没有一种方法可以在C# WinForms中自动滚动列表框?

4

我正在编写一个程序,它会在大屏幕上显示几个列表框中的数字列表。我的问题是是否有一种自动滚动列表框以显示框中所有数据的方法?


你的目标是什么类型的应用程序?WinForm、ASP.Net、WPF? - undefined
大量的工作示例可以在网上找到。如果你熟悉事件处理程序和静态方法的创建,这些都可以非常容易地完成。 - undefined
当您从列表框中选择一个项目(通过 listBox.SelectedIndex = number;listBox.SelectedItem = "string";),列表框会自动滚动到所选项目。 - undefined
【可能是重复的问题】(https://dev59.com/WkbRa4cB1Zd3GeqPzl1M) - undefined
4个回答

9

通常,我会这样做:

listBox.SelectedIndex = listBox.Items.Count - 1;
listBox.SelectedIndex = -1;

但是您也可以尝试使用

该方法


int nItems = (int)(listBox.Height / listBox.ItemHeight);
listBox.TopIndex = listBox.Items.Count - nItems;

希望这可以帮到您 :)

抱歉,我的描述不够清楚。我指的是给列表框添加跑马灯效果 :) 让数据自动向下滚动,然后重复。 - undefined
第二个选项将在绘制模式设置为“UserDrawVariable”时不起作用。在这种情况下,您需要从最后一个项目索引开始调用“GetItemHeight”并进行计算。 - undefined

3

如果您需要直接控制滚动而不选择项目,则需要使用来自User32.dll的Win32 SetScrollPos方法。以下是一个扩展类,它为您提供基本支持:

public class ScrollableListView : ListView
{
    private const int WM_VSCROLL = 0x115;

    private enum ScrollBar : int { Horizontal = 0x0, Vertical = 0x1 }

    public void SetScroll(int x, int y)
    {
        this.SetScroll(ScrollBar.Horizontal, x);
        this.SetScroll(ScrollBar.Vertical, y);
    }

    public void SetScrollX(int position)
    {
        this.SetScroll(ScrollBar.Horizontal, position);
    }

    public void SetScrollY(int position)
    {
        this.SetScroll(ScrollBar.Vertical, position);
    }

    [DllImport("User32.Dll", EntryPoint = "PostMessageA")]
    private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

    [DllImport("user32.dll")]
    private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

    private void SetScroll(ScrollBar bar, int position)
    {
        if (!this.IsDisposed)
        {
            ScrollableListView.SetScrollPos((IntPtr)this.Handle, (int)bar, position, true);
            ScrollableListView.PostMessage((IntPtr)this.Handle, ScrollableListView.WM_VSCROLL, 4 + 0x10000 * position, 0);
        }
    }
}

您可以快速轻松地设置X或Y滚动条。这对其他控件也适用。
如果您想使控件自动滚动,需要设置一个重复定时器,间隔大约为20毫秒。跟踪滚动位置和方向,并相应地增加或减少它,使用这些方法将位置发送到控件。
更新: 我之前发布的SetScrollPos方法存在一些问题,主要围绕滚动条移动而内容没有移动的问题。这可能只是一个小错误,但同时,这里有一个有点“非传统”的MarqueeListView解决方案。
首先,枚举表示要使用哪个滚动条。我使用了显示名称而不是Win32名称(SB_HORIZ和SB_VERT),只是为了让事情变得更清晰。
public enum ScrollBarDirection : int { Horizontal = 0x0, Vertical = 0x1 }

为了滚动命令代码本身,我们还需要另一个枚举 - 我们只保留了Up (SB_LINEUP), Down (SB_LINEDOWN)和EndScroll(SB_ENDSCROLL)。在滚动消息之后,需要EndScroll以通知控件进行更新。

public enum ScrollCommand : int { Up = 0x0, Down = 0x1, EndScroll = 0x8 }

最后是类本身。它基本上通过向下滚动开始,每20毫秒一次(默认情况下 - 请注意,可以通过MarqueeSpeed属性进行更改)。然后获取滚动位置,并将其与上次进行比较。一旦滚动条停止移动,它就会反转方向。这是为了解决我使用GetScrollInfo方法时遇到的问题。

public class MarqueeListView : ListView
{
    protected const int WM_VSCROLL = 0x115;

    private ScrollCommand scrollCommand;
    private int scrollPositionOld;
    private Timer timer;

    public MarqueeListView()
        : base()
    {
        this.MarqueeSpeed = 20;

        this.scrollPositionOld = int.MinValue;
        this.scrollCommand = ScrollCommand.Down;

        this.timer = new Timer() { Interval = this.MarqueeSpeed };
        this.timer.Tick += (sender, e) =>
        {
            int scrollPosition = MarqueeListView.GetScrollPos((IntPtr)this.Handle, (int)ScrollBarDirection.Vertical);
            if (scrollPosition == this.scrollPositionOld)
            {
                if (this.scrollCommand == ScrollCommand.Down)
                {
                    this.scrollCommand = ScrollCommand.Up;
                }
                else
                {
                    this.scrollCommand = ScrollCommand.Down;
                }
            }
            this.scrollPositionOld = scrollPosition;

            MarqueeListView.SendMessage((IntPtr)this.Handle, MarqueeListView.WM_VSCROLL, (IntPtr)this.scrollCommand, IntPtr.Zero);
            MarqueeListView.SendMessage((IntPtr)this.Handle, MarqueeListView.WM_VSCROLL, (IntPtr)ScrollCommand.EndScroll, IntPtr.Zero);
        };
        this.timer.Start();
    }

    public int MarqueeSpeed
    {
        get
        {
            return this.timer.Interval;
        }
        set
        {
            this.timer.Interval = value;
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetScrollPos(IntPtr hWnd, int nBar);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    protected static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
}

最后,这里有一个快速的Main方法来测试它:
    private static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Form form = new Form() { StartPosition = FormStartPosition.CenterScreen, Width = 1280, Height = 720 };
        MarqueeListView list = new MarqueeListView() { View = View.Tile, Dock = DockStyle.Fill };
        for (int i = 0; i < 1000; i++) { list.Items.Add(Guid.NewGuid().ToString()); }
        form.Controls.Add(list);

        Application.Run(form);
    }

请记住,这并不一定是“正确”或最好的方法,但我认为不同的方法可能会给您一些想法!
我希望使用SetScrollPos,这将产生更好、更平滑的效果。您可以轻松地包括加速和减速 - 可能选择在鼠标悬停时缓慢停止,然后在鼠标移出时加速等等。目前它只是无法正常工作 - 我在旧项目中有一个可用的滚动更新方法,所以如果我再次使其工作,我会更新此内容。
希望对您有所帮助!

问题是枚举类应该放在哪个类中,我应该使用哪个类,是MarqueeListView类还是ScrollableListView类?我很好奇尝试这种方法,而不是使用计时器和选定索引的方法。 - undefined
枚举可以直接放在命名空间中,就像类一样。如果你想启用自动滚动,那么你可以创建一个MarqueeListView - undefined
您还可以查看此方法,而不是使用SelectedIndex。但是它确实还有另一个问题 - 如果ListView正在使用其中一种图标或磁贴视图,那么很难控制速度,因为它会在项目之间循环滚动,所以采用Win32滚动消息的方法更适合。 - undefined
这个能用于一个listBox吗? - undefined
确实 - 几乎任何控件都可以 :) - undefined
显示剩余3条评论

1
即使是很久以前,其他人肯定也会遇到同样的问题。由于选择最后一项并没有自动滚动,这里有一个对我有效的解决方案:
ListBox1.ScrollIntoView(ListBox1.Items[ListBox1.Items.Count - 1]);

-1

或者您可以使用以下代码在顶部插入您的项目: lbLog.Items.Insert(0,"日志项");


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