How to DataBind a UserControl with ComboBox?

S

Steve K.

** I just posted another, long thread related to UserControls and
DataBinding, but this is separate and smaller in scope so I mad a new
thread. **

OK, simple question (I hope): I have a UserControl with a ComboBox inside
and I would like to be able to DataBind to the UserControl and hook up to
it's ComboBox DataBinding properties.

For example:
<psuedo code>
class Address
{
// usual address fields
}

class AddressService
{
// application wide service for address related tasks
static List<string> GetStateList();
}

class AddressUserControl
{
// textboxes for Street, City, etc
// combobox for state
}


// From inside my Form that is hosting the AddressUserControl
// Create a new address to bind to the UserControl
Address address = new Address();

// Setup the Address User Control with the list of states (somehow?)
UserControl addressUserControl = new UserControl();
addressUserControl.States[?] = AddressService.GetStateList();

// Bind my Address instance to the User Control
// ? How?
</psuedo code>

A quick test of dropping a combo box in a UserControl, then dropping the
User Control on a Form shows that the properties of the ComboBox are hidden
from the editors Formatting and Advanced Binding tools (expected)

The ComboBox seems to have the following BindableProperties:
SelectedItem
SelectedValue
Text

Do I need to expose these properties to my UserControl's interface so that a
consuming object can get to them?

What do you all do in this circumstance?

-Steve
 
M

Marc Gravell

Do I need to expose these properties to my UserControl's interface so that a
consuming object can get to them?

Yes; making some public properties for what you need (that are simply
proxies to the inner control) is the way to go. You can cheat and
change the protection modifier of the control itself to "internal",
but this doens't make for a maintainable system, as you can't change
the internals of the control without changing everything that *uses*
the control.

Marc
 
S

Steve K.

Marc Gravell said:
Yes; making some public properties for what you need (that are simply
proxies to the inner control) is the way to go. You can cheat and
change the protection modifier of the control itself to "internal",
but this doens't make for a maintainable system, as you can't change
the internals of the control without changing everything that *uses*
the control.

Marc

I don't know how good of an imagination you have.. but try to picture this:

I've been sitting here pounding coffee trying to recover from my whopping
2.5 hours of sleep hitting F5 on Outlook Express waiting for someone to
respond.

Now I'm off!! I have my direction!!

:0)

(thanks)
 
N

Nicholas Paldino [.NET/C# MVP]

Steve,

It should be noted that you aren't going to get designer support for
setting the data source by just exposing the property.

In order to get designer support for the DataSource, you will have to
add the AttributeProviderAttribute to the property, specifying the type of
IListProvider for the constructor. This give your property designer
support.

When exposing properties that are really just wrappers for properties on
contained controls, you should look at the attributes that are attached to
the properties in Reflector. These attributes will help with providing the
designer support when the simple property declaration isn't enough.
 
S

Steve K.

More Direction!!
I love it!
;0)

Thanks Nicholas, I had been using
[EditorBrowsable(EditorBrowsableState.Always)] to get them to show up, but
taking just a *peek* via reflector should give some more good ideas, thanks
for the suggestion.


Nicholas Paldino said:
Steve,

It should be noted that you aren't going to get designer support for
setting the data source by just exposing the property.

In order to get designer support for the DataSource, you will have to
add the AttributeProviderAttribute to the property, specifying the type of
IListProvider for the constructor. This give your property designer
support.

When exposing properties that are really just wrappers for properties
on contained controls, you should look at the attributes that are attached
to the properties in Reflector. These attributes will help with providing
the designer support when the simple property declaration isn't enough.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Steve K. said:
I don't know how good of an imagination you have.. but try to picture
this:

I've been sitting here pounding coffee trying to recover from my whopping
2.5 hours of sleep hitting F5 on Outlook Express waiting for someone to
respond.

Now I'm off!! I have my direction!!

:0)

(thanks)
 
M

Marc Gravell

For info, EditorBrowsable mainly controls intellisense, not the
designer. As Nicholas says - looking at the properties for similar
controls is a good route - either by reflection, ILDASM, or reflector
[the easiest route].

Marc
 
S

Steve K.

Another day on this and I still can't get things working. I've taken all
your advice (as best I've understood it) and created the most simple example
I can think of:
User Control with two TextBox controls.
Here is the code for the UserControl:
<code>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace PMD.Library.WinFormControls
{
public partial class ReallySimpleAddress : UserControl
{
public ReallySimpleAddress()
{
InitializeComponent();
}

[Editor("System.ComponentModel.Design.MultilineStringEditor,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)),
SettingsBindable(true)]
[Bindable(true)]
public string Address1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}

[Editor("System.ComponentModel.Design.MultilineStringEditor,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)),
SettingsBindable(true)]
[Bindable(true)]
public string Address2
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
}
}


// Designer.cs code (in case it helps)

namespace PMD.Library.WinFormControls
{
partial class ReallySimpleAddress
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Component Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Anchor =
((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(3, 3);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;
//
// textBox2
//
this.textBox2.Anchor =
((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox2.Location = new System.Drawing.Point(3, 29);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(100, 20);
this.textBox2.TabIndex = 1;
//
// ReallySimpleAddress
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Name = "ReallySimpleAddress";
this.Size = new System.Drawing.Size(109, 54);
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
}
}
</code>

I then have a simple Windows application with a single form. I've created
once instance of my "Address" object (dumb object, only fields and
properties, doesn't inherit from anything (except object of course)

I've added the UserControl to my form, then from the property editor created
a BindingSource from my Address object.
I've bound the Address1 property of the UserControl to the Address1 property
of the business object and done the same for Address2.

When I run my application and type in the Address1 field of the user
control, then leave the control (lose focus) the new value sticks. Good.
When I type in the Address2 field and leave focus it reverts back to the
value it was initialized to!
Ahh... I don't get it.

I've looked at the properties of a TextBox in reflector. The text property
has no special attributes, so I don't know how it shows up in the binding
area of the properties editor.
I added the [Binding(true)] attribute which seems to be the attribute that
makes the property appear in the bindable properties list.

Here is some more relevant code:
<code>
public Form1()
{
_address.Address1 = "350 N. Maplewood St.";
_address.Address2 = "Suite 101";
_address.City = "Orange";
_address.State = "CA";
_address.Zipcode = "92866";

InitializeComponent();

// Address testing
addressBindingSource.DataSource = _address;
}


namespace PMD.BusinessEntities.NetsuiteEntities
{
public class Address
{
private string _address1;
private string _address2;
private string _city;
private string _state;
private string _zipcode;

public string Address1
{
get { return _address1; }
set { _address1 = value; }
}

public string Address2
{
get { return _address2; }
set { _address2 = value; }
}

public string City
{
get { return _city; }
set { _city = value; }
}

public string State
{
get { return _state; }
set { _state = value; }
}

public string Zipcode
{
get { return _zipcode; }
set { _zipcode = value; }
}
}
}


//
// reallySimpleAddress1
//
this.reallySimpleAddress1.Address1 = "";
this.reallySimpleAddress1.Address2 = "";
this.reallySimpleAddress1.DataBindings.Add(new
System.Windows.Forms.Binding("Address1", this.addressBindingSource,
"Address1", true));
this.reallySimpleAddress1.DataBindings.Add(new
System.Windows.Forms.Binding("Address2", this.addressBindingSource,
"Address2", true));
this.reallySimpleAddress1.Location = new System.Drawing.Point(361, 137);
this.reallySimpleAddress1.Name = "reallySimpleAddress1";
this.reallySimpleAddress1.Size = new System.Drawing.Size(109, 54);
this.reallySimpleAddress1.TabIndex = 5;

</code>

I REALLY hope someone sees the problem or has some idea what in the world
I'm doing wrong. This is much more complex and difficult that I had
expected.

If a simple and complete (as Mr. Skeet usually requests) would be in order
let me know and I will zip and post online.

Thanks for any help,
Steve



Marc Gravell said:
For info, EditorBrowsable mainly controls intellisense, not the
designer. As Nicholas says - looking at the properties for similar
controls is a good route - either by reflection, ILDASM, or reflector
[the easiest route].

Marc
 
S

Steve K.

This sounds like my problem:

"There is a bug in databinding which appears when you are at the first row
in the datasource (row 0) and you modify more than one property of the
control. This bug causes the second modification to be lost."
http://www.freeweb.hu/noiseehc/SimpleBind.html

Now I'm not dealing with "rows" per se, but the second part of the quote
seems right on.


Steve K. said:
Another day on this and I still can't get things working. I've taken all
your advice (as best I've understood it) and created the most simple
example I can think of:
User Control with two TextBox controls.
Here is the code for the UserControl:
<code>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace PMD.Library.WinFormControls
{
public partial class ReallySimpleAddress : UserControl
{
public ReallySimpleAddress()
{
InitializeComponent();
}

[Editor("System.ComponentModel.Design.MultilineStringEditor,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)),
SettingsBindable(true)]
[Bindable(true)]
public string Address1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}

[Editor("System.ComponentModel.Design.MultilineStringEditor,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)),
SettingsBindable(true)]
[Bindable(true)]
public string Address2
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
}
}


// Designer.cs code (in case it helps)

namespace PMD.Library.WinFormControls
{
partial class ReallySimpleAddress
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Component Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox2 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Anchor =
((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox1.Location = new System.Drawing.Point(3, 3);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;
//
// textBox2
//
this.textBox2.Anchor =
((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBox2.Location = new System.Drawing.Point(3, 29);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(100, 20);
this.textBox2.TabIndex = 1;
//
// ReallySimpleAddress
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.textBox2);
this.Controls.Add(this.textBox1);
this.Name = "ReallySimpleAddress";
this.Size = new System.Drawing.Size(109, 54);
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox textBox2;
}
}
</code>

I then have a simple Windows application with a single form. I've created
once instance of my "Address" object (dumb object, only fields and
properties, doesn't inherit from anything (except object of course)

I've added the UserControl to my form, then from the property editor
created a BindingSource from my Address object.
I've bound the Address1 property of the UserControl to the Address1
property of the business object and done the same for Address2.

When I run my application and type in the Address1 field of the user
control, then leave the control (lose focus) the new value sticks. Good.
When I type in the Address2 field and leave focus it reverts back to the
value it was initialized to!
Ahh... I don't get it.

I've looked at the properties of a TextBox in reflector. The text
property has no special attributes, so I don't know how it shows up in the
binding area of the properties editor.
I added the [Binding(true)] attribute which seems to be the attribute that
makes the property appear in the bindable properties list.

Here is some more relevant code:
<code>
public Form1()
{
_address.Address1 = "350 N. Maplewood St.";
_address.Address2 = "Suite 101";
_address.City = "Orange";
_address.State = "CA";
_address.Zipcode = "92866";

InitializeComponent();

// Address testing
addressBindingSource.DataSource = _address;
}


namespace PMD.BusinessEntities.NetsuiteEntities
{
public class Address
{
private string _address1;
private string _address2;
private string _city;
private string _state;
private string _zipcode;

public string Address1
{
get { return _address1; }
set { _address1 = value; }
}

public string Address2
{
get { return _address2; }
set { _address2 = value; }
}

public string City
{
get { return _city; }
set { _city = value; }
}

public string State
{
get { return _state; }
set { _state = value; }
}

public string Zipcode
{
get { return _zipcode; }
set { _zipcode = value; }
}
}
}


//
// reallySimpleAddress1
//
this.reallySimpleAddress1.Address1 = "";
this.reallySimpleAddress1.Address2 = "";
this.reallySimpleAddress1.DataBindings.Add(new
System.Windows.Forms.Binding("Address1", this.addressBindingSource,
"Address1", true));
this.reallySimpleAddress1.DataBindings.Add(new
System.Windows.Forms.Binding("Address2", this.addressBindingSource,
"Address2", true));
this.reallySimpleAddress1.Location = new System.Drawing.Point(361, 137);
this.reallySimpleAddress1.Name = "reallySimpleAddress1";
this.reallySimpleAddress1.Size = new System.Drawing.Size(109, 54);
this.reallySimpleAddress1.TabIndex = 5;

</code>

I REALLY hope someone sees the problem or has some idea what in the world
I'm doing wrong. This is much more complex and difficult that I had
expected.

If a simple and complete (as Mr. Skeet usually requests) would be in order
let me know and I will zip and post online.

Thanks for any help,
Steve



Marc Gravell said:
For info, EditorBrowsable mainly controls intellisense, not the
designer. As Nicholas says - looking at the properties for similar
controls is a good route - either by reflection, ILDASM, or reflector
[the easiest route].

Marc
 
M

Marc Gravell

Generally you would encapsulate the binding details to inside the
user-control, just providing access to perhaps a DataSource and
DataMember pair to obtain the record. If you know that you only need
to support a single entity, then another option is to simply expose
that entity on the user-control, as below with the Address property on
the AddressControl.

I've also implemented change-notification on the Address entity so
that you can verify (by the second Address control) that the change
has committed itself. Let me know if I've missed the point of what you
want, or if you want an explanation of any of it (I didn't have time
for line-by-line comments).

Marc

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

public class Address : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected bool UpdateField<T>(ref T field, T value, string
propertyName) {
if(EqualityComparer<T>.Default.Equals(field, value)) return
false;
// update field and fire event
field = value;
if(!string.IsNullOrEmpty(propertyName) && PropertyChanged !=
null) {
PropertyChanged(this, new
PropertyChangedEventArgs(propertyName));
Trace.WriteLine(string.Format("{0}={1}",
propertyName,field));
}
return true;
}
private string _addressLine1, _zip;
public string AddressLine1 {
get { return _addressLine1; }
set { UpdateField(ref _addressLine1, value, "AddressLine1");}
}
public string Zip {
get { return _zip; }
set { UpdateField(ref _zip, value, "Zip"); }
}
}

public class AddressControl : UserControl {
public AddressControl() {
tb1 = new TextBox();
tb2 = new TextBox();
tb1.Dock = tb2.Dock = DockStyle.Top;
Controls.Add(tb1);
Controls.Add(tb2);
BorderStyle = BorderStyle.FixedSingle;
Height = tb1.Height + tb2.Height;
}
private Address _address;
TextBox tb1, tb2;
public Address Address {
get { return _address; }
set {
if (ReferenceEquals(Address, value)) return;
tb1.DataBindings.Clear();
tb2.DataBindings.Clear();
_address = value;
if (_address != null) {
tb1.DataBindings.Add("Text", _address,
"AddressLine1");
tb2.DataBindings.Add("Text", _address, "Zip");
}
}
}
}

static class Program {
static void Main() {
Address a = new Address();
a.AddressLine1 = "Somewhere";
a.Zip = "ZONE 1";
Application.EnableVisualStyles();
using (Form f = new Form())
using (AddressControl ac1 = new AddressControl())
using (AddressControl ac2 = new AddressControl()) {
ac1.Address = ac2.Address = a;
ac1.Dock = DockStyle.Top;
ac2.Dock = DockStyle.Bottom;
f.Controls.Add(ac1);
f.Controls.Add(ac2);
Application.Run(f);
}
}
}
 
S

Steve K.

Hi Marc,

Thanks for the real quick reply, I'm eager to try out what you've supplied
here.

First I just wanted to ask a few questions:
1) My original question on this thread (and apologies for letting the topic
drift) was how to deal with a ComboBox and you suggested that I expose the
required properties to the UserControl's interface. I liked your suggestion
for the sole reason that it didn't tightly couple my UserControl to a
specific entity. I could, in theory, bind my UserControl to ANY object that
made sense. Now the situation has changed a bit and it looks like I've
encountered a VERY OLD bug (I'm not one to scream bug, but after reading
that web page a couple more times I think it's explaining exactly what I'm
experiencing) with databinding more than one property in a UserControl. If
my original request had been "How can I bind to 2 or more TextBox controls
in a UserControl would you have still suggested I expose the properties to
the UserControl interface? I ask because I"m trying to understand if what
I'm trying to do is a "Heh, yeah.. of course that won't work, Kid. What did
you expect?" or if my attempt to bind to multiple properties was sound? Did
I misunderstand your earlier post when you suggested ( I thought ) that I
exposes the properties for binding directly?

2) If I do handle the binding in the UserControl and expose a DataSource
property (and other properties for my ComboBox 'member fields (God help me
if I want two ComboBox controls on the UserControl or I think I will be in
trouble again) to set the BindingSource.DataSource... is it possible to use
an interface to create the BindingSource? I'm just thinking of ways to keep
things loosely coupled and an interface-driven BindingSource seems to be the
only way.

3) By any chance did you try to do things the way I currently have them
setup (with the inner Control's Text properties exposed via proxies on the
UserControl interface and if so did you experience the same behavior (
changes not sticking on any control other than the first)? I'd like to know
if this is indeed a bug or if I've done something wrong. Not knowing is the
worst!

Thanks again for the super fast reply, gonna snoop your code now and learn
from it. :0)

(If I have found a bug and it's this old... it just might be enough to turn
me into one of the Anti-Microsoft people that I've never understood very
well... I hope not)

-Steve
 
S

Steve K.

Hi Marc,

I spent some time with your example code, it does indeed work just as I
would expect. Nice sample, thank you. I also answered my question #2 about
interfaces and yes, they work fine so that is good.

I still prefer the design of binding directly to a series of properties on
the UserControl. It could be that I've spent so much time on it that I
don't want to abandon it, I'm sure after a night's sleep I will have a
different perspective.

Can you see any advantage.disadvantage to creating a BindingSource object in
the UserControl and assigning the value of the Address property to the
DataSource property of the BindingSource? I haven't read up on
BindingSource much to know just what all it does, I have seen that you add
Bindings to it the same way you do for a ControlBindingCollection... I
don't need all the sorting/navigation stuff.

Just trying to think of any other options before I run with your solution
(which I think I will).

Anyway, I need sleep. ;0|

Thanks again for the GREAT help, I really do appreciate it and will let you
know how I make out with everything.

-Steve
 
S

Steve K.

Sorry for all the messages! :0)

Marc, I've completed my analysis of my options and determined that the
decision comes down to one thing: Coupling.

Your proposed solution is elegant and a month ago, before I started my
"loose coupling" obsession is EXACTLY what I would have wanted. Clean
interface, takes my business object directly and hides the implementation
from the consuming control. Very nice.

The alternative (exposing all inner control properties through properties on
the UserControl) is ugly, could have some code duplication and because of a
bug in DataBinding will result in much more code (2 TextChanged) handlers
for each control to bind to. However, it is loosely coupled.

I think I need to get over myself and not take this 'coupling' issue too
far. I'm not developing a control library but rather an inhouse application
that will likely never benefit from being loosely coupled. I'm stuck on the
bandwagon... help! ;0)

Anyway, just wanted to update you.
Thanks again,
Steve
 
M

Marc Gravell

is it possible to use an interface to create the BindingSource?
(but may answer some of the other questions)
Kind of - you could quite happily provide an object DataSource and a
pair of FooMember / BarMember strings (for the property-names -
i.e."Zip" etc). I recommend "object DataSource" since this would map
to what the IDE expects, and would allow for use with IList,
IListSource, BindingSource, etc. Crude but it works.

All you'd do to change the current code is something like below; if
you are binding to other controls, then you do it differently - for
example, with a ComboBox you'd set DataSource and DisplayMember etc,
rather than doing a DataBindings.Add; this is precisely why you only
want to expose "source" and "member" properties on the API; so that if
you change the internals the callers don't need to know. Theoretically
you can have multiple {Named}DataSource properties, but the IDE may
get even more confused than it does normally ;-p

// address unchanged

public class AddressControl : UserControl {
public AddressControl() {
tb1 = new TextBox();
tb2 = new TextBox();
tb1.Dock = tb2.Dock = DockStyle.Top;
Controls.Add(tb1);
Controls.Add(tb2);
BorderStyle = BorderStyle.FixedSingle;
Height = tb1.Height + tb2.Height;
}
private TextBox tb1, tb2;
private object _dataSource;
private string _fooMember, _barMember;

// add lots of attributes from typical DataSource
public object DataSource {
get { return _dataSource; }
set {
if (!ReferenceEquals(DataSource, value)) {
_dataSource = value;
Rebind();
}
}
}

// add lots of attributes from typical ValueMember
public string FooMember {
get { return _fooMember; }
set {
if (FooMember != value) {
_fooMember = value;
Rebind();
}
}
}
// add lots of attributes from typical ValueMember
public string BarMember {
get { return _barMember; }
set {
if (BarMember != value) {
_barMember = value;
Rebind();
}
}
}

private void Rebind() {
tb1.DataBindings.Clear();
tb2.DataBindings.Clear();
if (DataSource != null) {
if (!string.IsNullOrEmpty(FooMember)) {
tb1.DataBindings.Add("Text", DataSource, FooMember);
}
if (!string.IsNullOrEmpty(BarMember)) {
tb2.DataBindings.Add("Text", DataSource, BarMember);
}
}
}
}

static class Program {
static void Main() {
Address a = new Address();
a.AddressLine1 = "Somewhere";
a.Zip = "ZONE 1";
Application.EnableVisualStyles();
using (Form f = new Form())
using (AddressControl ac1 = new AddressControl())
using (AddressControl ac2 = new AddressControl()) {
ac1.DataSource = ac2.DataSource = a;
ac1.FooMember = ac2.BarMember = "AddressLine1";
ac1.BarMember = ac2.FooMember = "Zip";
ac1.Dock = DockStyle.Top;
ac2.Dock = DockStyle.Bottom;
f.Controls.Add(ac1);
f.Controls.Add(ac2);
Application.Run(f);
}
}
}
 
M

Marc Gravell

Note: you could also use the member to *obtain* a concrete address
from the source (just passing in property names on the container, like
"PrimaryAddress", "BackupAddress"; but this is more involved and
requires knowledge of the currency manager and data context. Let me
know if this is what you mean... i.e. so you can say:

BindingList<Person> people = ...
addressCtrl.DataSource = people;
addressCtrl.DataMember = "PrimaryAddress";

(to edit the PrimaryAddress of the currently selected Person in the
list)
marc
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top