【问题标题】:Listbox horizontal scrollbar not updating properly列表框水平滚动条未正确更新
【发布时间】:2012-04-10 16:18:19
【问题描述】:

我有一个表单,其中包含一个列表框控件以及许多其他确定列表框内容的控件。将项目添加到列表框会正确设置滚动条的范围,但更新列表框上的项目(通过 this.lstResources.Items[index] = myUri)会导致滚动条的范围缩小到超过最大项目的宽度,从而切断最后几个字符。滚动条仍然有效,但以这种方式更新列表框会导致列表中项目出现意外且不可接受的滚动范围。以下是我实现列表框的方式:

public System.Windows.Forms.ListBox lstResources;
this.lstResources = new System.Windows.Forms.ListBox();

this.lstResources.FormattingEnabled = true;
this.lstResources.HorizontalScrollbar = true;
this.lstResources.Location = new System.Drawing.Point(331, 122);
this.lstResources.Name = "lstResources";
this.lstResources.Size = new System.Drawing.Size(307, 316);
this.lstResources.TabIndex = 8;
this.lstResources.Click += new System.EventHandler(this.ResourcesPage.LstResources_Click);

我对这个列表框的修改操作如下:

this.parentClassReference.lstResources.Items.Add(myUri);
this.parentClassReference.lstResources.Items[index] = myUri;
this.parentClassReference.lstResources.Items.RemoveAt(index);

我在表单和列表框控件上都尝试过 Refresh() 和 Update(),但都没有任何效果。我查看了所有列表框滚动条问题,但似乎没有一个有这种特殊的重绘问题。

它应该是这样的:

这就是实际发生的事情:

我有一种感觉,我遗漏了一些明显的东西,或者可能是错误地更改了项目,但我对这个项目的想法很新鲜。我对 C# 和 UI 设计并不完全陌生,但我也不能说我知道所有的细节控制操作。任何帮助将不胜感激。

编辑 1:这是一个应该重现问题的示例表单。我开始怀疑 StringBuilder 可能与更新部分有关,但它也用于 btnAdd_Click 代码......

public partial class SampleListBoxForm : Form
{
    public Dictionary<string, string> CurrentUriQuery { get; set; }

    public SampleListBoxForm()
    {
        this.CurrentUriQuery = new Dictionary<string, string>();
        this.InitializeComponent();
    }

    private void btnAdd_Click(object sender, EventArgs e)
    {
        this.UpdateParams("sampleApp");
        this.listBox1.Items.Add(this.GenerateURI());
        this.listBox1.Refresh();
    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        int index = 0;
        this.UpdateParams("sampleApp2");
        for (; index < this.listBox1.Items.Count; index++)
        {
            this.listBox1.Items[index] = this.GenerateURI();
        }
    }

    private void UpdateParams(string filename)
    {
        this.CurrentUriQuery = new Dictionary<string, string>();
        this.CurrentUriQuery["filename"] = filename;
        this.CurrentUriQuery["url"] = @"C:\Users\me\Desktop\" + filename;
        this.CurrentUriQuery["type"] = "dynamicType";
        this.CurrentUriQuery["p1"] = "foo";
        this.CurrentUriQuery["p2"] = "bar";
        this.CurrentUriQuery["p3"] = "stuff";
        this.CurrentUriQuery["p4"] = "test";
    }

    private string GenerateURI()
    {
        StringBuilder currentUri = new StringBuilder();
        bool firstParam = true;
        currentUri.Append(this.CurrentUriQuery["filename"]);
        foreach (KeyValuePair<string, string> pair in this.CurrentUriQuery)
        {
            if (pair.Key != "url" && pair.Key != "filename" && pair.Value != string.Empty && pair.Value != "0")
            {
                if (firstParam)
                {
                    currentUri.Append("?");
                    firstParam = false;
                }
                else
                {
                    currentUri.Append("&");
                }

                currentUri.Append(pair.Key);
                currentUri.Append("=");
                currentUri.Append(pair.Value.Replace(" ", ""));
            }
        }

        return currentUri.ToString();
    }

    public string[] ExtractPathAndQueryFromUriString(string uriString)
    {
        string[] pathAndQuery = new string[2];
        if (uriString.Contains('?'))
        {
            pathAndQuery[0] = uriString.Split('?')[0];
            pathAndQuery[1] = uriString.Split('?')[1];
        }
        else if (uriString.Contains("%3F"))
        {
            string[] stringSeparator = new string[] { "%3F" };
            pathAndQuery[0] = uriString.Split(stringSeparator, StringSplitOptions.None)[0];
            pathAndQuery[1] = uriString.Split(stringSeparator, StringSplitOptions.None)[1];
        }
        else
        {
            pathAndQuery[0] = uriString;
            pathAndQuery[1] = string.Empty;
        }

        return pathAndQuery;
    }
}

【问题讨论】:

  • 无法重现。更改 ListBox 项会更改滚动条范围。
  • 有什么方法可以跟踪滚动条的范围,以便我自己调试吗?也许是非公共财产?我会提供更多代码,但我认为其余的不相关。我正在遍历每个列表项以更新它们,但这与相关外部代码的范围有关。编辑:知道这些操作没有在与列表框相同的对象中执行可能会有所帮助,这就是它是公共的原因。
  • 反过来做。创建一个新项目并用最少的代码重现问题。发布该代码。您正在查看其他人无法看到的代码,这很难提供帮助。
  • 我会试一试,谢谢。
  • 在一个示例类中添加了应该演示该错误的示例。经历这个剥离过程很有启发性,但仍然没有完全揭示潜在的问题。

标签: c# winforms listbox scrollbar


【解决方案1】:

问题似乎与 & 字符有关。当您添加该项目时,它似乎可以正确测量。当您更新项目时,它不会。

这个丑陋的 hack 似乎是:记录索引,删除项目,将项目重新插入到记录的索引中。

或切换到DrawMode = OwnerDrawFixed 并计算您添加或更改的每个项目的HorizontalExtent 值。

private void ResizeListBox() {
  int maxWidth = 0;

  for (int i = 0; i < listBox1.Items.Count; i++) {
    int testWidth = TextRenderer.MeasureText(listBox1.Items[i].ToString(), 
                                             listBox1.Font, listBox1.ClientSize,
                                             TextFormatFlags.NoPrefix).Width;
    if (testWidth > maxWidth)
      maxWidth = testWidth;
  }

  listBox1.HorizontalExtent = maxWidth;
}

不幸的是,每次更改都必须调用它:

private void btnAdd_Click(object sender, EventArgs e) {
  this.UpdateParams("sampleApp");
  this.listBox1.Items.Add(this.GenerateURI());
  ResizeListBox();
}

private void btnUpdate_Click(object sender, EventArgs e) {
  this.UpdateParams("sampleApp");
  for (int index = 0; index < this.listBox1.Items.Count; index++) {
    this.listBox1.Items[index] = this.GenerateURI();
  }
  ResizeListBox();
}

这是一个又快又脏的 DrawItem 版本:

private void listBox1_DrawItem(object sender, DrawItemEventArgs e) {
  e.DrawBackground();
  if (e.Index > -1) {
    Color textColor = SystemColors.WindowText;
    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected) {
      textColor = SystemColors.HighlightText;
    }
    TextRenderer.DrawText(e.Graphics, listBox1.Items[e.Index].ToString(), 
                          listBox1.Font, e.Bounds, 
                          textColor, Color.Empty, TextFormatFlags.NoPrefix);
  }
}

【讨论】:

  • 哇,太棒了!我会解决这个问题,让你知道它是怎么回事。我可以接受“丑陋”的代码,只要它是 c# 的错误,它看起来就是这样。
  • 像魅力一样工作(ResizeListBox() 至少可以),非常感谢 LarsTech 付出的时间和努力。
  • 我需要使用 TextFormatFlags.SingleLine | TextRenderer.MeasureText() 中的 TextFormatFlags.NoPrefix 使其工作。
【解决方案2】:
public static void ResizeListBox()
{
    YourListView.HorizontalExtent = TextRenderer.MeasureText(YourListView.Items.ToString(),
                                                    YourListView.Font, YourListView.ClientSize,
                                                    TextFormatFlags.NoPrefix).Width;           
}

在表单加载时调用此函数...这可能会解决您的问题。 这里只检查第一个列表框元素的宽度,或者您可以使用循环找到最大宽度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-21
    • 1970-01-01
    • 2011-04-28
    • 2021-11-02
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多