【问题标题】:Is there a way to auto scroll a listbox in C# WinForms有没有办法在 C# WinForms 中自动滚动列表框
【发布时间】:2015-02-02 19:20:19
【问题描述】:

我编写程序将在大显示器上的多个列表框中显示数字列表,我的问题是有没有办法自动滚动列表框以显示框中的所有数据?

【问题讨论】:

  • 您的目标是什么类型的应用程序? WinForm, ASP.Net, WPF ?
  • 如果您熟悉 EventHandlers 和静态方法创建,网上有很多工作示例@Evanark...这一切都可以很容易地完成..
  • 当您从列表框中选择一个条目时(通过listBox.SelectedIndex = number;listBox.SelectedItem = "string";),列表框会自动滚动到所选项目。

标签: c# winforms listboxitems


【解决方案1】:

通常,我会这样做:

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

不过你也可以试试

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

希望这会有所帮助:)

【讨论】:

  • 很抱歉,我的描述并不清楚我的意思是给列表框一个选取框效果:) 让数据自动向下滚动然后重复。
  • 如果绘制模式设置为UserDrawVariable,第二个选项将不起作用。在这种情况下,您需要从最后一个项目索引开始调用 GetItemHeight 并进行计算。
【解决方案2】:

要直接控制滚动而不选择项目,你需要使用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_HORIZSB_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,它会产生更好、更流畅的效果。然后,您可以轻松地包括加速和减速 - 可以选择在鼠标悬停时减速到停止,然后在鼠标移出时加速等。不过目前它只是不打球 - 我在某处的旧项目中有一个有效的滚动更新方法,所以如果我让它再次工作,我会更新它。

希望有帮助!

【讨论】:

  • 我想我想要的是选择第一个项目并运行代码以选择下一个项目,所以直到它到达列表的末尾然后让它重复。我还是一个新手,所以我不确定如何应用您发布的代码。
  • 而不是选择项目,此代码允许您模拟滚动控件时引发的 Windows 消息,例如使用鼠标滚轮。如果您仍然希望能够手动选择项目,则可能会更好。我将使用一些代码对其进行更新,以便稍后自动上下滚动控件。
  • 酷我试试看会发生什么。谢谢!我会让你知道结果如何。
  • 问题枚举去哪个类,我使用哪个类,marqueeListView 类还是可滚动列表视图类?我很好奇试试这个方法,而不是 timer 和 selectedindex 方法。
  • 枚举可以像类一样直接进入命名空间。如果你想启用自动滚动,你可以创建一个MarqueeListView
【解决方案3】:

或者只需使用以下方法将您的项目插入顶部: lbLog.Items.Insert(0,"LogItem");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-27
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多