views:

106

answers:

1

Hello. I'm trying to bind a ListBox's SelectedItem data to a property. The following code is an example:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace BindingFailure
{
 static class Program
 {
  class OuterObject
  {
   public string selected { get; set; }
   public List<string> strings { get; set; }
  }

  public static void Main()
  {
   List<OuterObject> objs = new List<OuterObject>()
   {
    new OuterObject(), new OuterObject()
   };
   objs[0].strings = new List<string> { "one", "two", "three" };
   objs[1].strings = new List<string> { "four", "five", "six" };

   Form form = new Form();

   BindingSource obs = new BindingSource(objs, null),
    ibs = new BindingSource(obs, "strings");
   BindingNavigator nav = new BindingNavigator(obs);
   ListBox lbox = new ListBox();
   lbox.DataSource = ibs;
   lbox.DataBindings.Add(new Binding("SelectedItem", obs, "selected"));

   form.Controls.Add(nav);
   form.Controls.Add(lbox);
   lbox.Location = new System.Drawing.Point(30, 30);

   Application.Run(form);
  }
 }
}

If you just select an item, move forward, select an item and then exit, it works as expected. But if you switch back and forth between the two outer objects with the navigator, the selected item seems to be overwritten with an incorrect value. It appears that every time the BindingNavigator moves to an element, the ListBox is told to move to the first item in its collection and thus overwrites whatever value used to be in the variable bound to its SelectedItem.

Ideas on how to fix this? Thanks in advance.

EDIT: Here is an archive of the example project, including a debug binary.

http://www.mediafire.com/?dzmqmz0mynj

EDIT: Here is the helper function based on the accepted answer:

        public static void Bind(ListControl list, BindingSource outersource, string dataMember)
        {
            Binding bindSel = new Binding("SelectedItem", outersource, dataMember);
            list.DataBindings.Add(bindSel);

            outersource.CurrentChanged += delegate
            {
                list.BeginInvoke(new MethodInvoker(bindSel.ReadValue));
            };
        }
+1  A: 

It happens because SelectedItem update happens before listbox is updated. So on the first stage listbox cannot "accept" value from different form record since it contains no such record and then it cannot restore selection after listbox items are repopulated.

The solution (one of) is to force SelectedItem binding to reapply after current form record is changed. First we give the binding a name:

//lbox.DataBindings.Add(new Binding("SelectedIndex", obs, "Index"));
var selItemBinding = new Binding("SelectedItem", obs, "selected");
lbox.DataBindings.Add(selItemBinding);

Than we have to reapply binding (notice BeginInvoke is essential to apply binding after listbox is repopulated):

obs.CurrentChanged += delegate
{
    form.BeginInvoke(new MethodInvoker(selItemBinding.ReadValue));
};

You could also create simple helper method that will do all this tricks in one call.

olegz
Works perfectly. THANK YOU.
Reinderien