none
ListBox.Refresh Scrolling RRS feed

回答

  • Yes, using Clear() defeats the point, it will reset the scrollbar back.  That's why binding doesn't work.  And why I recommended you change the item if one is already added to the list.  Something like this:

      void UpdateList(List<SomeThing> items) {
          int count = 0;
          listBox1.BeginUpdate();
          foreach (Something item in items) {
            if (listBox1.Items.Count > count) listBox1.Items[count] = item;
            else listBox1.Items.Add(item);
            count++;
          }
          while (listBox1.Items.Count > count)
             listBox1.Items.Remove(count);
          listBox1.EndUpdate();
      }

    Hans Passant.
    2009年10月8日 3:55
    モデレータ

すべての返信

  • Hm.  I don't see any issues with refresh unless you have a dataset and it has been updated.  Instead of binding to a dataset you can convert your dataset to something such as a string array and use AddItems().  Since a dataset is out of your control you won't know if items have been re-organized or removed entirely which would cause issues with things like SelectedIndex. If you simplify things a bit and just use AddItems() you simply append the new ones to the end...no harm no foul. 

                string[] items = { "1", "2", "3", "4" };//pretend 1,2,3,4 came from a dataset
                listBox1.Items.AddRange(items);
                listBox1.Refresh();
    If you actually desire to remove and add items then you should  write an algorithm that will ensure the safety of your indexes.  Then you can go into the refresh event and before doing the refresh save the selected index and perform the refresh. At this point it should "scroll to the top", then refresh again and set the selectedIndex (it is set-able I checked) only this time it should know that the dataset hasn't changed and just go to the selected index.  Haven't tried it, but sounds good in my head.

    Good coding involves knowing one's logical limits and expanding them as necessary.
    2009年10月8日 2:04
  • Doesn't work, if the list updates when I'm scrolling down it will reset the scroll bar to the top, it's extremely irritating since my list updates quite often so I am unable to even go through the list because of this, there must be a way to override it?
    2009年10月8日 2:16
  • What doesn't work?  What do you want to override, is it the refreshing or updating the datasets?  Is there a behavior that I missed or an explanation that is wrong? 
    Good coding involves knowing one's logical limits and expanding them as necessary.
    2009年10月8日 2:29
  • Thought I explained it in my original post, I have a list box which I update with .Refresh() (on a timer if that helps), when it calls .Refresh() the scroll bar positions itself back to the top of the list, regardless of whether I was currently scrolling down the list manually, obviously that's not what I want as I'm unable to see what's in the list as it keeps scrolling back up every refresh, not sure how else to explain it.
    2009年10月8日 2:35
  • Ok, it is my fault for skipping past the fundamental problem and going straight for a possible solution.  There is something going on in your code that is more complex than calling Refresh().  I created a test project with a ListBox.  I then added some items and created a button click event which calls Refresh().  It does not display any of the behavior that you currently experience:

            private void button1_Click(object sender, EventArgs e)
            {
                string[] items = { "1", "2", "3" };
                //listBox1.DataSource = items;
                listBox1.Items.AddRange(items);
                listBox1.Refresh();
            }

    So, then I bound a dataset and tried it again:

            private void button1_Click(object sender, EventArgs e)
            {
                string[] items = { "1", "2", "3", "1", "2", "3", "4", "1", "2", "3", "4", "1", "2", "3", "4", "1", "2", "3", "4", "1", "2", "3", "4" };
                listBox1.DataSource = items;
                //listBox1.Items.AddRange(items);
                listBox1.Refresh();
            }

    And sure enough,it scrolled up everytime.  I also noticed that it doesn't matter whether I call Refresh() or not, it scrolls up automatically when I rebind the dataset.  Which made the original post regarding "Refresh()" causing issues invalid in the simple case.  Therefore, you need to provide more information so we can re-produce the issue OR verify that the behavior I have been able to reproduce matches your implementation.


    Good coding involves knowing one's logical limits and expanding them as necessary.
    2009年10月8日 2:49
  • I just checked and yeah it's databinding that's making the scroll bar reset not refresh, I assumed it was refresh so my mistake, the list is updated quite often and I need the scroll bar to stop repositioning itself every time I update the list with databinding, or some other way to update the list without this happening.
    2009年10月8日 2:57
  • Don't bind it.  Don't clear the Items property when you update it, just change the items.  You'll need to add or remove items only when necessary.

    A completely different approach is suppressing the binding source update when you see the user interacting with the form.  This thread shows the code you'd need.

    Hans Passant.
    2009年10月8日 3:08
    モデレータ
  • Ok, now that we are on the same page hopefully I can explain myself clearly as I am confident as to why this behavior would occur, but as for a solution...that is more complex.  Maybe NoBugz or one of the other masters have a genious solution, but for now your stuck with me. 

    Bear with me for a second while I explain the logic I see.  A dataSource has any # of items in any order, if you change the datasource then the new datasource also has any # of items in any order.  Now, if your viewing a databound listbox and you change the datasource then that means the new datasource cannot be guaranteed to have the same # of items or the same order.  This makes the currently selectedItem possibly non-existent.  That's all theory, let's try something concrete.  Pretend this is a real listbox:

    ListBox
    Item1
    Item2   <----Currently Selected
    Item3

    now the timer does:  listBox1.Datasource = changedDatasource;

    ListBox
    Item9
    Item11   <----Currently Selected

    This new datasource doesn't have Item2, but since listbox maintained the previous state it has the wrong index selected.  Worse yet, what if you were on Index3 which doesn't exist anymore.  That would  cause an index out of bounds exception because your selectedIndex is no longer a valid index in the array.

    So, the listbox developers were smart to reset the listbox when the datasource is pulled out from under it.  But, that still leaves one question unanswered.  How is it that using ListBox.Items.Add("item1"); doesn't reset the listbox?  Well, when you call Add() you append the object to the end of the array, which guarantees order and Items + 1.  The important thing is that its ok to save state in this situation because the new items are in a place where they won't hurt anything.

    This is where I come back to my original suggestion for creating an algorithm to make your items bind in such a way where they will not hurt anything.  I don't see it being easy unless you convert your dataset to another object type and use AddRange() instead of re-databinding.  This puts the problem of maintaining order and the # of items in YOUR hands.

    So, the first thing you should ask yourself is whether it is possible for you to programatically predict the # of items in your list and predict their order.  If you can, then you can go back up to the first post I made and try that stuff out.


    Good coding involves knowing one's logical limits and expanding them as necessary.
    2009年10月8日 3:13
  • Ok tried your AddRange suggestion with .Refresh, the array list is brand new each update but it just keeps adding duplicates to the list, do I have to use listbox.Clear? Doesn't this defeat the point or am I missing something?
    2009年10月8日 3:44
  • Yes, using Clear() defeats the point, it will reset the scrollbar back.  That's why binding doesn't work.  And why I recommended you change the item if one is already added to the list.  Something like this:

      void UpdateList(List<SomeThing> items) {
          int count = 0;
          listBox1.BeginUpdate();
          foreach (Something item in items) {
            if (listBox1.Items.Count > count) listBox1.Items[count] = item;
            else listBox1.Items.Add(item);
            count++;
          }
          while (listBox1.Items.Count > count)
             listBox1.Items.Remove(count);
          listBox1.EndUpdate();
      }

    Hans Passant.
    2009年10月8日 3:55
    モデレータ
  • Edit:
    Ok I fixed it with:

    listBox1.Items[count] = item;

    Works perfect thanks mate :)
    • 編集済み w1z8yte 2009年10月8日 4:16
    2009年10月8日 4:02
  • Well, something like that.  I corrected the code.

    Hans Passant.
    2009年10月8日 4:12
    モデレータ
  • If you store the TopIndex in a variable before updating the listbox and restore the TopIndex after update then the ListBox will not scoll.

    private void UpdateListBox( List<string> newItems)
    {
       int topIndex = this.listBox.TopIndex;
       bool addItems = this.listBox.Items.Count != newItems.Count;
       if (addItems)
       {
           this.listBox.Items.Clear();
           foreach (var item in newItems)
           {
               this.listBox.Items.Add(item);
           }
       }
       else
       {
          int index = 0;
          foreach (var newItem in newItems)
          {   
             if (this.listBox.Items[index].ToString() != newItem )
             {
                this.listBox.Items[index] = newItem ;
             }
             index++;
          }
        }
        this.listBox.TopIndex = topIndex;
     }
    

    2019年8月22日 7:28