none
Move item up or down in listbox

    Question

  • I have code which was working but for some reason now fails and I don't understand why!

    I have 2 listboxes on a Form and 2 buttons (1xUp & 1xDown).  

    When the Form Loads ListBox 1 is populated with items from a DataTable, by either Drag n Drop or double click items can be moved from ListBox1 to ListBox2.  All that works fine...

    The user can select an item in ListBox2 (if >=3Items) and press either and Up or Down button... to re-order the list.  Now for some reason this all worked some time ago but now fails on the MoveSelectedItemUp or MoveSelectedItemDown on the same line... Box.Items.RemoveAt(Index) with the following error...

    "Items collection cannot be modified with the DataSource property is set."

    FormLoad code...

     // get the vessels ID & Name
            GetVesselNameID();
            // starting WayPoints
            DataTable myDTTP = PopulateFormControls.FillData("WayPoints", null, null, null);
            SetDefaultWayPoints(myDTTP);
            // assigning the same database structure to the listbox2
            DataTable dtTemp = myDTTP.Copy();
            pCC.PopulateLB(lbTripWayPoints, dtTemp, "Description", "ID");
            dtTemp.Rows.Clear();
    

    The UP/Down code...

        private void MoveSelectedItemUp(ListBox Box) 
        {
          int Index = Box.SelectedIndex;     //Selected Index
          object Swap = Box.SelectedItem;   //Selected Item
          if (Index != -1) 
            {        //If something is selected...
            Box.Items.RemoveAt(Index);         //Remove it
            Box.Items.Insert(Index - 1, Swap);    //Add it back in one spot up
            Box.SelectedItem = Swap;          //Keep this item selected
            }
        }
    
        private void MoveSelectedItemDown(ListBox Box)
        {
          int Index = Box.SelectedIndex;     //Selected Index
          object Swap = Box.SelectedItem;   //Selected Item
          if (Index != -1)
          {        //If something is selected...
            Box.Items.RemoveAt(Index);         //Remove it
            Box.Items.Insert(Index + 1, Swap);    //Add it back in one spot up
            Box.SelectedItem = Swap;          //Keep this item selected
          }
    
        }
    
    

    This code is all prety straight forward and is almost a direct copy of an MS (and several others) example.

    What did I change to mess this up?

     

     


    SquireDude
    Thursday, May 05, 2011 3:45 PM

Answers

  • Hey... I got the solution :)

    The code uses DataTable and its binding source, and all the code is done inside of it. And this is example code of items: a, b, c and d inside listBox.

    Here it is:

     public partial class Form1 : Form
      {
        DataTable table;
        public Form1()
        {
          InitializeComponent();
          table = new DataTable("myTable");
          table.Columns.Add(new DataColumn("items", typeof(string)));
          table.Rows.Add("a");
          table.Rows.Add("b");
          table.Rows.Add("c");
          table.Rows.Add("d");
          listBox1.DataSource = new BindingSource(table, null);
          listBox1.DisplayMember = "items";
          listBox1.ValueMember = "items";
        }
    
        private void buttonDown_Click(object sender, EventArgs e)
        {
          if (listBox1.SelectedIndex < listBox1.Items.Count - 1)
          {
            object item = listBox1.SelectedValue;
            DataTable tableClone = table.Clone();
    
            bool bInsert = false;
            int selectedIndex = 0;
            for (int i = 0; i < table.Rows.Count; i++)
            {
              if (table.Rows[i]["items"] == item)
                bInsert = true;
              else
              {
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i]["items"];
              }
              if (bInsert)
              {
                tableClone.Rows.Add();
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i + 1]["items"];
                tableClone.Rows[i + 1]["items"] = item;
                selectedIndex = i + 1;
                i++;
                bInsert = false;
              }
            }
            table.Clear();
            table = tableClone.Copy();
            listBox1.DataSource = new BindingSource(table, null);
            listBox1.DisplayMember = "items";
            listBox1.ValueMember = "items";
            listBox1.SelectedIndex = selectedIndex;
          }
        }
    
        private void buttonUp_Click(object sender, EventArgs e)
        {
          if (listBox1.SelectedIndex > 0)
          {
            object item = listBox1.SelectedValue;
            DataTable tableClone = table.Clone();
    
            bool bInsert = false;
            int selectedIndex = 0;
            for (int i = 0; i < table.Rows.Count; i++)
            {
              if (table.Rows[i]["items"] == item)
                bInsert = true;
              else
              {
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i]["items"];
              }
              if (bInsert)
              {            
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = tableClone.Rows[i -1]["items"];
                tableClone.Rows[i - 1]["items"] = item;
                selectedIndex = i - 1;
                bInsert = false;
              }
            }
            table.Clear();
            table = tableClone.Copy();
            listBox1.DataSource = new BindingSource(table, null);
            listBox1.DisplayMember = "items";
            listBox1.ValueMember = "items";
            listBox1.SelectedIndex = selectedIndex;
          }
        }
      }

    I hope you like it!


    Mitja

    • Marked as answer by SquireDude Friday, May 06, 2011 6:55 PM
    Thursday, May 05, 2011 5:26 PM

All replies

  • You have a control that supports data binding (such as a DropDownList or a DataGrid) which is binded to a DataSource (such as a database table, an XML file or an array). If you try to add new items to it after you binded to a DataSource, you will get the errorItems collection cannot be modified when the DataSource property is set.
    This error is normal since controls that are binded to a DataSource cannot have their items changed or allow new items to be manually added, using a method for example. There is no way around this, thus if you want to add additional items to the control, you will need to add them to the DataSource. For example if the DataSource is an array, you will have to add a new value inside the array for each new item in the control, or if it's a database table, you will need a new row.

     

    Taken from: http://www.geekpedia.com/KB68_Items-collection-cannot-be-modified-when-the-DataSource-property-is-set.html

    So I'd say it's as designed and I cannot understand how it could work before.


    Don't forget to mark the correct answer Blog
    Thursday, May 05, 2011 4:16 PM
  • Maybe the code (Up/Down methods) get executed even when the selected item in on the top most or down  most position. You have to define this condition as well.

    for moving up:

     if (Index > 0)
    {}
    

    and for moving down:

     if (Index < (this.listBox1.Items.Count - 1))
    {}
    

     

    You see the difference? When moving up, the if loop must not be executed when the item at Index 0 is selected.

    When you moving down, the if loop must not be executed when the last item in listBox is selected - the last Item`s Index equals to listBox1.Item.Count -1 number.

    If this conditions are not met, nothing happens!!

    Hope it helps.


    Mitja
    Thursday, May 05, 2011 4:21 PM
  • Hi Oleksandr,

    Thanks for the reply.  Yeah I am not sure how it worked before as I see I have both Bound and UnBound code mixed together!!!

    I do know that the Load and Save methods are using Bound ListBox so now it is just a metter of how do I move an item Up or Down within ListBox2's DataSource (DataTable)?

    I have read several blog items that the DataSource has to be manipulated but none show how it can be done!  Can you offer an example?


    SquireDude
    Thursday, May 05, 2011 4:36 PM
  • sorry, I dont know it too. I never did something like it, and I cant imagine how it should be done correctly. But your code seems to be interesting.

    Maybe you should only move time row in the DataTable, while it is dataBound to listbox.

     


    Mitja
    Thursday, May 05, 2011 4:50 PM
  • Hi Mitja,

    I do see exactly what you are saying and I agree 100% and have adjusted my code to make teh correction.

    Unfortunatly I have set ListBox2 to be bound!!!

            // assigning the same database structure to the listbox2
            DataTable dtTemp = myDTTP.Copy();
            pCC.PopulateLB(lbTripWayPoints, dtTemp, "Description", "ID");
            dtTemp.Rows.Clear();
    
    

    and need to unbind it and add rows from ListBox1 to ListBox2...

    I have got Bound and Unbound code mixed up on the same controls/Form... I am not sure which way to go to correct this erron on my part and get it all working???

     


    SquireDude
    Thursday, May 05, 2011 4:54 PM
  • Hey... I got the solution :)

    The code uses DataTable and its binding source, and all the code is done inside of it. And this is example code of items: a, b, c and d inside listBox.

    Here it is:

     public partial class Form1 : Form
      {
        DataTable table;
        public Form1()
        {
          InitializeComponent();
          table = new DataTable("myTable");
          table.Columns.Add(new DataColumn("items", typeof(string)));
          table.Rows.Add("a");
          table.Rows.Add("b");
          table.Rows.Add("c");
          table.Rows.Add("d");
          listBox1.DataSource = new BindingSource(table, null);
          listBox1.DisplayMember = "items";
          listBox1.ValueMember = "items";
        }
    
        private void buttonDown_Click(object sender, EventArgs e)
        {
          if (listBox1.SelectedIndex < listBox1.Items.Count - 1)
          {
            object item = listBox1.SelectedValue;
            DataTable tableClone = table.Clone();
    
            bool bInsert = false;
            int selectedIndex = 0;
            for (int i = 0; i < table.Rows.Count; i++)
            {
              if (table.Rows[i]["items"] == item)
                bInsert = true;
              else
              {
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i]["items"];
              }
              if (bInsert)
              {
                tableClone.Rows.Add();
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i + 1]["items"];
                tableClone.Rows[i + 1]["items"] = item;
                selectedIndex = i + 1;
                i++;
                bInsert = false;
              }
            }
            table.Clear();
            table = tableClone.Copy();
            listBox1.DataSource = new BindingSource(table, null);
            listBox1.DisplayMember = "items";
            listBox1.ValueMember = "items";
            listBox1.SelectedIndex = selectedIndex;
          }
        }
    
        private void buttonUp_Click(object sender, EventArgs e)
        {
          if (listBox1.SelectedIndex > 0)
          {
            object item = listBox1.SelectedValue;
            DataTable tableClone = table.Clone();
    
            bool bInsert = false;
            int selectedIndex = 0;
            for (int i = 0; i < table.Rows.Count; i++)
            {
              if (table.Rows[i]["items"] == item)
                bInsert = true;
              else
              {
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = table.Rows[i]["items"];
              }
              if (bInsert)
              {            
                tableClone.Rows.Add();
                tableClone.Rows[i]["items"] = tableClone.Rows[i -1]["items"];
                tableClone.Rows[i - 1]["items"] = item;
                selectedIndex = i - 1;
                bInsert = false;
              }
            }
            table.Clear();
            table = tableClone.Copy();
            listBox1.DataSource = new BindingSource(table, null);
            listBox1.DisplayMember = "items";
            listBox1.ValueMember = "items";
            listBox1.SelectedIndex = selectedIndex;
          }
        }
      }

    I hope you like it!


    Mitja

    • Marked as answer by SquireDude Friday, May 06, 2011 6:55 PM
    Thursday, May 05, 2011 5:26 PM
  • Mitja,

    Thanks for the code... I am working on implimenting it now... BUT before I do that I have messed up all the older code that moved an item from ListBox1 to ListBox2!!!!  In ListBox2 instead of the text value I am getting "System.Data.DataRowView"....????  I have no idea what I changed to mess this up  but the code I am working with is...

     string item = null;
            if (listBox == this.lbWayPoints.Name)
            {
              item = this.lbWayPoints.SelectedItem.ToString();
              this.lbTripWayPoints.Items.Add(item);
              this.lbWayPoints.Items.Remove(item);
            }
            else
            {
              item = this.lbTripWayPoints.SelectedItem.ToString();
              this.lbWayPoints.Items.Add(item);
              this.lbTripWayPoints.Items.Remove(item);
            }
    

    The value for "item" is System.Data.DataRowView NOT the Text value at all???  I could change it to be this.lbWayPoints.Text;  BUT will that then move the Display Member & ValueMember values over to ListBox2?

     

    Why does that always seem to happen to me????   booo hooo :-(

     


    SquireDude
    Thursday, May 05, 2011 8:41 PM
  • "System.Data.DataRowView" :

    You want to get the Item from the dataBound value. This will not do. You have to know that one dataRow is consisted of Item and Value. So when you only use SelectedItem, or SelectedText, you will get this kind of data.

    Maybe using SelectedValue.ToString() will do.

     

    Also: When you set dataSource to comboBox control, you always have to define the DisplayMember and ValueMember for the comboBox. 

    For example if you have a custom class of Person: 

    class Person
    {
     public int ID {get;set;}
     public string Name {get;set;}
    }
    
    //then you have a List of persons:
    List<Person> list = new List<Person>();
    list.Add(new Person {ID = 1, Name = "Name 1"});
    //and on..
    
    

     

    You set the displayMemer for the "Name", and valueMember is "ID".

    Then when you want to get the the Name from the selected item you have to do: 

    string name = (comboBox1.SelectedItem as Person).Name;
    

    The same goes for id: 

    int id = (comboBox1.SelectedItem as Person).ID;
    

     

    You got the point when you are using dataSource on the comboBox control? Its not the same as when you populate it with Add() metod.

    Full example:

    public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
    
          List<Person> list = new List<Person>();
          list.Add(new Person { ID = 1, Name = "Name 1" });
          comboBox1.DataSource = list;
          comboBox1.DisplayMember = "Name";
          comboBox1.ValueMember = "ID";
        }
    
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
          string name = (comboBox1.SelectedItem as Person).Name;
          int id = (comboBox1.SelectedItem as Person).ID;
        }
      }
    
      class Person
      {
        public int ID { get; set; }
        public string Name { get; set; }
      }

     


    Mitja


    Thursday, May 05, 2011 8:46 PM
  • Thanks Mitja,

    I swear my day and code is going backwards... I am am now unable to move a row from ListBox1 to ListBox2 without it throwing an error... what was working 5 minutes ago now is broken....

    Drag n Drop does nothing and if I double click on ListBox1 I get and Object reference not set to an instance of an object when it executes the following code...

     private void lbWayPoints_MouseDoubleClick(object sender, MouseEventArgs e)
     {
      int mouseItemIndex = this.lbWayPoints.IndexFromPoint(e.X, e.Y);
      if (mouseItemIndex != -1)
      {
      // valid item selected
      CopyFromOneToAnother(this.lbWayPoints.Name);
      }
     }
    

     The error is on the 1st line - int mouseItemIndex...????

    I am starting to think I have changed so many bits and pieces I should delete ALL the code behind this form and start again?

    To go back to basics... this is something I am sure is done every day...

    I have a Form with 2 ListBoxes on it...
    ListBox1 is bound to a DataTable by "Description" & "ID"
    I need to give the user the abillity to Drag n Drop &/or Double Click items in ListBox1 to move to ListBox2

    There are 2 buttons to the Right of ListBox2... btnUp & btnDown so the user can Move an Item Up or Down in the order in ListBox2.

    There is a 3rd button next to ListBox2 - btnRemove to remove an Item from ListBox2...

    All items should remain in ListBox1 as an item could be used more than once in ListBox2.

    At one point I had all this working and now its all busted and broke up... sao tiring going over the same code again and again!!!


    SquireDude

    Forgot to mention that the data comes from SQL DB...
    Thursday, May 05, 2011 9:55 PM
  • Good morning Mitja,

    I hope your day turned out better than mine!

    I got the code so mixed up between what was working and new code trying to resolve my MoveUp, MoveDOwn issue that I screpped the form and restored a back-up copy from a couple of days ago.... S I now am back to ListBox 1 loading correctly... Drag n Drop and ListBox1.DoubleClick both working correctly in moving a item from ListBox1 to ListBox2....

    Back to the original issue... How to move and item in ListBox2 Up or Down a row...

    ListBox2 is Bound to a DataTable...

    I tried the code you suggested above and modified it a bit to fit my Form... but I could not get it to Move and Item.  All it would do is copy the existing data from the bound table in ListBox2 and put it back!  It did not move any items.

    I am not sure how to write the code yet but I believe that the code should go something like this....

    MoveListBox2Item_UP

    If (ListBox2.SelectedIndex < ListBox2.Items.Count - 1)
    {
        object vID = ListBox2.SelectedValue;   // Get the rows ValueMemeber = "ID"
        string vDescription = ListBox2.Text;     // Get the DisplayMember value  = "Description"
        int selectedIndex = ListBox2.SelectedIndex;  // Get the Index value

        for (int i = 0; i < dtTemp.Rows.Count; i++)   // loop through the DataTable to find the SelectedItem
        {
            if (dtTemp.Rows[i].Index == selectedIndex)  // selected item found
                {
                     // To move item UP use -1 / To move Item DOWN use +1
                     dtTemp.Rows[i].Index == dtTemp.Rows[i].Index -1             // decrease the index by 1 to move it up the ListBox
                 }
         }

    OR

    1. Find SelectedItem,
    2. copy the Index, ValueMember & DisplayMember values to vars
    3. Delete the seleted item from the DataTable by Index value
    4. Insert the Item at Index value +1 or -1 to move up or down
    5. Refresh the DataTable and ListBox2 so teh change displayes.

    Not sure if I am completely off base here but it seems that a method like these should work???

    Now I have to figure out if (a) it will work & (b) how to write the code to make it work????

    That is where I need help.


    SquireDude
    Friday, May 06, 2011 4:16 PM
  • Hi Mitja,

    I restored a backup of the Form to get the original sort-of-working code....

    I used your example above to get the MoveItemUp & MoveItemDown methods... below is the modified working solution for MOVEITEMUP...

        private void MoveSelectedItemUp(ListBox Box)
        { // moves selected item UP 1 row...
          if (this.lbTripWayPoints.SelectedIndex > 0)
          {
            // Get the selected items ValueMember
            string item = this.lbTripWayPoints.SelectedValue.ToString();
            // get the selected items Member Value
            string vDescription = this.lbTripWayPoints.Text;
            // clone the table
            DataTable tableClone = dtTemp.Clone();
    
            // created flag
            bool bInsert = false;
            // create a default Index value variable
            int selectedIndex = 0;
            // loop through the DataTable rows
            for (int i = 0; i < dtTemp.Rows.Count; i++)
            {
              // if the selected item is found in the DataTable
              if (dtTemp.Rows[i]["ID"].ToString() == item)
                // set the selected Item flag
                bInsert = true;
              else
              {
                // add a blank row
                tableClone.Rows.Add();
                // add the ValueMember and MemberValues to the new row
                tableClone.Rows[i]["ID"] = dtTemp.Rows[i]["ID"];
                tableClone.Rows[i]["Description"] = dtTemp.Rows[i]["Description"];
              }
              if (bInsert)  // record is flagged
              {
                // add a blank row
                tableClone.Rows.Add();
                // add the NEXT + 1 ValueMember and MemberValues to the table (moved DOWN 1 row)
                tableClone.Rows[i]["ID"] = tableClone.Rows[i - 1]["ID"];
                tableClone.Rows[i]["Description"] = tableClone.Rows[i - 1]["Description"];
                // add a blank row
                // add the NEXT ValueMember and MemberValues to the table but bumped (moved UP 1 row)
                tableClone.Rows[i - 1]["ID"] = item;
                tableClone.Rows[i - 1]["Description"] = vDescription;
                // reset the selected Index value (- 1)
                selectedIndex = i - 1;
                // reset the flag
                bInsert = false;
              }
            }
            // empty the DataTable
            dtTemp.Clear();
            // copy the data from the Temp Table to the Bindable one
            dtTemp = tableClone.Copy();
            // dispose of the temp table
            tableClone.Dispose();
            // bind the revised data table to the ListBox
            this.lbTripWayPoints.DataSource = new BindingSource(dtTemp, null);
            this.lbTripWayPoints.DisplayMember = "Description";
            this.lbTripWayPoints.ValueMember = "ID";
            this.lbTripWayPoints.SelectedIndex = selectedIndex + 1;
          }
    
        }
    
    

    Now for the MOVEITEMDOWN solution...

        private void MoveSelectedItemDown(ListBox Box)
        { // moves selected item down 1 row...
          if (this.lbTripWayPoints.SelectedIndex < this.lbTripWayPoints.Items.Count - 1)
          {
            // Get the selected items ValueMember
            string item = this.lbTripWayPoints.SelectedValue.ToString();
            // get the selected items Member Value
            string vDescription = this.lbTripWayPoints.Text;
            // clone the table
            DataTable tableClone = dtTemp.Clone();
    
            // created flag
            bool bInsert = false;
            // create a default Index value variable
            int selectedIndex = 0;
            // loop through the DataTable rows
            for (int i = 0; i < dtTemp.Rows.Count; i++)
            {
              // if the selected item is found in the DataTable
              if (dtTemp.Rows[i]["ID"].ToString() == item)
                // set the selected Item flag
                bInsert = true;
              else
              {
                // add a blank row
                tableClone.Rows.Add();
                // add the ValueMember and MemberValues to the new row
                tableClone.Rows[i]["ID"] = dtTemp.Rows[i]["ID"];
                tableClone.Rows[i]["Description"] = dtTemp.Rows[i]["Description"];
              }
              if (bInsert)  // record is flagged
              {
                // add a blank row
                tableClone.Rows.Add();
                // add the NEXT + 1 ValueMember and MemberValues to the table (moved UP 1 row)
                tableClone.Rows[i]["ID"] = dtTemp.Rows[i + 1]["ID"];
                tableClone.Rows[i]["Description"] = dtTemp.Rows[i + 1]["Description"];
                // add a blank row
                tableClone.Rows.Add();
                // add the NEXT ValueMember and MemberValues to the table but bumped (moved DOWN 1 row)
                tableClone.Rows[i + 1]["ID"] = item;
                tableClone.Rows[i + 1]["Description"] = vDescription;
                // reset the selected Index value (+ 1)
                selectedIndex = i + 1;
                i++;
                // reset the flag
                bInsert = false;
              }
            }
            // empty the DataTable
            dtTemp.Clear();
            // copy the data from the Temp Table to the Bindable one
            dtTemp = tableClone.Copy();
            // dispose of the temp table
            tableClone.Dispose();
            // bind the revised data table to the ListBox
            this.lbTripWayPoints.DataSource = new BindingSource(dtTemp, null);
            this.lbTripWayPoints.DisplayMember = "Description";
            this.lbTripWayPoints.ValueMember = "ID";
            this.lbTripWayPoints.SelectedIndex = selectedIndex + 1;
          }
        }
    
    

     

     


    SquireDude
    Friday, May 06, 2011 6:53 PM
  • Since I have it working and in mind I also worked out how to RemoveItem from ListBox2...

        private void button1_Click(object sender, EventArgs e)
        {
          // remove the seleted item
          // Get the selected items ValueMember
          string item = this.lbTripWayPoints.SelectedValue.ToString();
          // clone the table
          DataTable tableClone = dtTemp.Clone();
    
          // create a default Index value variable
          int selectedIndex = 0;
          // loop through the DataTable rows
          for (int i = 0; i < dtTemp.Rows.Count; i++)
          {
            // if the selected item is found in the DataTable
            if (dtTemp.Rows[i]["ID"].ToString() == item)
            {
              dtTemp.Rows[i].Delete();
            }
          }
          // bind the revised data table to the ListBox
          this.lbTripWayPoints.DataSource = new BindingSource(dtTemp, null);
          this.lbTripWayPoints.DisplayMember = "Description";
          this.lbTripWayPoints.ValueMember = "ID";
          this.lbTripWayPoints.SelectedIndex = selectedIndex - 1;
        }
    
    

     


    SquireDude
    Friday, May 06, 2011 6:55 PM