Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Dll second problem: data binding

Status
Not open for further replies.

tzzdvd

Programmer
Aug 17, 2001
52
IT
Hi

I am trying to manage an external dll.
I would like to create an assembly (later exported in a dll) that encapsulates the management of such dll so that it is "easily" usable (I hope!)
I have two different problems so I will split them into different post:

second problem: data binding

I want to have a DataGridView in my form bound to a List into my class

Code:
public class Person
{
    private string name = "";
    public string Name
    {
        get { return name; }
        private set { name = value; }
    }
    /// and some other properties
    ...
}

public class DllAux
{
    List<Person> myList = new List<Person>();
    
    void AddPerson(string name)
    {
        Person p = new Person(name);
        myList.Add(p);
    }
}

As obvious, I want my DataGridView to show any changes in myList

First test
Code:
public class DllAux
{
    public List<Person> myList = new List<Person>();
}
class myForm : form
{
    ...
    DataGridView dgv = new DataGridView();
    DllAux DllUse = new DllAux();
    ...
    public frmMain()
    {
        InitializeComponent();
        dgv.AutoGenerateColumns = true;
        dgv.DataSource = DllUse.myList; // orrible I know!!
    }
}
[code]
failed
Question... If I had used an ArrayList instead of a List<>?

Second test
[code]
public class DllAux : IList<Person>
{
    private List<Person> myList = new List<Person>();
    ...
}

class myForm : form
{
    ...
    DataGridView dgv = new DataGridView();
    DllAux DllUse = new DllAux();
    ...
    public frmMain()
    {
        InitializeComponent();
        dgv.AutoGenerateColumns = true;
        dgv.DataSource = DllUse;
    }
}
better but failed again

third test
Code:
public class DllAux : IList<Person>
{
    private List<Person> myList = new List<Person>();
    ...
}

class myForm : form
{
    ...
    DataGridView dgv = new DataGridView();
    BindingSource bS;
    DllAux DllUse = new DllAux();
    ...
    public frmMain()
    {
        InitializeComponent();
        bS = new BindingSource(DllUse)
        dgv.AutoGenerateColumns = true;
        dgv.DataSource = bS;
    }
}

If I remeber well, in this solution I have the dgv filled with only the column tytle but nothing else
I have also tried to put the BindingSource member into the DllAux class.

I have read that I need to export the INotifyPropertyChanged but I don't know where to put the bs.ResetBinding or how to use it in an efficent way

Somewhere I have read that you can bind everything that exports the IBindingSource interface
No result

I have done so many test in a single afternoon reading so many instruction in internet that I have a lot of confusion in my mind.

Thank you
Davide

 
binding data to the grid is independent of where the data comes from or how the data is fetched. for example you should be able to swap implementations for fake adapter without the client code caring.

the easiest way to accomplish this is using an interface.
Code:
public interface IService
{
   IEnumerable<ObjectToReturn> GetData();
}

public class SimpleService : IService
{
   public IEnumerable<ObjectToReturn> GetData()
   {
      yield return new ObjectToReturn();
      yield return new ObjectToReturn();
      yield return new ObjectToReturn();
   }
}

public class ComplexService : IService
{
   //members, attributes, other objects required;

   public IEnumerable<ObjectToReturn> GetData()
   {
      //logic required to get the data;
   }
}
now the client doesn't need to be concerned with what object retrieves the data, only the a service is returning the data.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Dear Jason,

I am trying this code in order to understand your suggestion

Code:
namespace TestBinding
{
    interface IBindingService
    {
        IEnumerable<Person> GetData();
    }
    
    public class BindingClass : IBindingService
    {
        private List<Person> group = new List<Person>();
        public BindingClass()
        {
        }

        public void AddPerson(Person p)
        {
            group.Add(p);
        }

        public void RemovePerson(int i)
        {
            if ((i > 0) && (i < group.Count))
            {
                group.RemoveAt(i);
            }
        }

        public void RemovePerson()
        {
            if (group.Count > 0)
            {
                group.RemoveAt(0);
            }
        }

        #region IBindingService Membri di

        public IEnumerable<Person> GetData()
        {
            foreach (Person p in group)
                yield return p;
        }

        #endregion
    }

    public partial class frmBinding : Form
    {
        private System.Windows.Forms.DataGridView dgvBinding;
        private System.Windows.Forms.Button cmdAdd;
        private System.Windows.Forms.Button cmdRemove;
        
        ...     // designer
           
        BindingClass myBindingClass = new BindingClass();

        Random rnd = new Random();

        BindingSource bs;

        public frmBinding()
        {
            InitializeComponent();
            cmdAdd_Click(null, null); // first data entered
            cmdAdd_Click(null, null); // second data entered
            bs = new BindingSource();
            bs.DataSource = myBindingClass.GetData();
            dgvBinding.AutoGenerateColumns = true;
            dgvBinding.DataSource = bs;
        }

        private Person CreateRandomPerson()
        {
            StringBuilder str = new StringBuilder();
            int len = rnd.Next(1, 10);
            for (int i = 0; i < len; i++)
                str.Append((char)(((byte)'a') + rnd.Next(0, 25)));
            return new Person(str.ToString(), rnd.Next(0, 100));
        }

        private void cmdAdd_Click(object sender, EventArgs e)
        {
            myBindingClass.AddPerson(CreateRandomPerson());
        }

        private void cmdRemove_Click(object sender, EventArgs e)
        {
            myBindingClass.RemovePerson();
        }
    }

    public class Person : INotifyPropertyChanged
    {
        private string name = "";
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Name"));
            }
        }


        private int age = 0;
        public int Age
        {
            get { return age; }
            set
            {
                age = value; 
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Age"));
            }
        }

        public Person(string n, int a)
        {
            Name = n;
            Age = a;
        }

        #region INotifyPropertyChanged Membri di

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

The result is that my datagrid display correctly the column, the first two "Person" added but there is non reaction to Add or Remove actions.
As you see, I have also added the support to INotifyPropertyChanged because somewhere I have read that this is the only way to make the datagridview sensible to my list changes.
Unfortunatly with no result.
The reason in this piece of code
Code:
if (PropertyChanged != null)
    PropertyChanged(this, new PropertyChangedEventArgs("Age"));

PropertyChanged == null of course!!!!!

Do you know where to fill this event delegate?

I have done another improvement.
I have changed the List<> into BindingList<> but nothing happens till I make it public and I link it to the DataSource
Code:
bs = new BindingSource();
bs.DataSource = myBindingClass.group; // BindingList<Person>
dgvBinding.AutoGenerateColumns = true;
dgvBinding.DataSource = bs;
or
Code:
dgvBinding.AutoGenerateColumns = true;
dgvBinding.DataSource = myBindingClass.group;  // BindingList<Person>
with the same result: the datagridview is bounded correctly and every time I add or remove a Person these changes are displayed into the control

At this point I remove the IBindingService and the INotifyPropertyChanged support and the result doesn't change.
It seems that the only important thing is the BindingList<>

This is my final solution
Code:
public class BindingClass
{
    private BindingList<Person> group = new BindingList<Person>();

    private BindingSource bs = new BindingSource();

    public object DataSource
    {
        get { return bs.DataSource; }              // in order not to let people to modify this parameter!
    }

    public BindingClass()
    {
        bs.DataSource = group;
    }
    
    public void AddPerson(Person p)
    {
        group.Add(p);
    }
    
    ...
}

public partial class frmBinding : Form
{
    BindingClass myBindingClass = new BindingClass();
    public frmBinding()
    {
        InitializeComponent();
        dgvBinding.DataSource = myBindingClass.DataSource;
    }
    ...
}
and it seems to work as I wish.

Now I am in front of three different problems:
1) how to cusomize my column
2) Can I create a runtime context menu row by row according to some parameters internal to the class Person?
For example (very stupid example) I would like to have a menu "Buy a car" only if the person age is above the legal age to drive it.
The same as for "Drink a beer" but the "Drink some water" always present. Of course if such parameters changes (birthday) the menu must appear according to these changes
This is a problem of Visible/NotVisible of course in a contextmenu always present in the person class.
I don't know how to bind it to the row!
3) If the AddPerson function is called for a different thread I receive the cross-thread exception on the form




 
I'm not fimilar with rich client GUI development. however I do know that I would put as much abstraction between the GUI and the "real working" code so I could test the code independent of the GUI.

1. I don't have experience with that.
2. I would build the menu on the fly instead of hiding/showing items. with a series of command, flyweight and specifications this can be a very dynamic menu with only a handful of objects to build out the menu.
3. yes, you need to return to the main thread.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top