Click here to Skip to main content
Click here to Skip to main content
Articles » Database » Database » General » Downloads
 
Add your own
alternative version
Go to top

Implementing complex data binding in custom controls

, 29 Aug 2006
An article on implementing complex data binding (DataSource and DataMember) on a custom control.
complexdatabinding_sample.zip
ComplexDataBindingSample.exe
complexdatabinding_source.zip
ComplexDataBindingSample
ComplexDataBindingSample.csproj.user
NwindDataSet.xsc
NwindDataSet.xss
Properties
Settings.settings
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

namespace ComplexDataBindingSample
{
	/// <summary>
	/// Extends the <see cref="ListView"/> with DataBinding-ability.
	/// </summary>
	public class BoundListView : ListView
	{
		private ListChangedEventHandler listChangedHandler;
		private EventHandler positionChangedHandler;
		private object dataSource;
		private string dataMember;
		private CurrencyManager dataManager;

		#region Properties
		#region DataSource
		/// <summary>
		/// Ruft die Datenquelle ab, f�r die Daten angezeigt werden sollen, oder legt diese fest.
		/// </summary>
		[TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
		[Category("Data")]
		[Description("Gibt die Datenquelle f�r das Control an.")]
		[DefaultValue(null)]
		public object DataSource
		{
			get
			{
				return this.dataSource;
			}
			set
			{
				if (this.dataSource != value)
				{
					this.dataSource = value;
					tryDataBinding();
				}
			}
		}
		#endregion

		#region DataMember
		/// <summary>
		/// Ruft die bestimmte Liste in DataSource ab, f�r die das Control 
		/// ein Datenblatt anzeigt, oder legt diese fest.
		/// </summary>
		[Category("Data")]
		[Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design",
			 "System.Drawing.Design.UITypeEditor, System.Drawing")]
		[Description("Gibt eine untergeordnete Liste von DataSource an, um sie anzuzeigen.")]
		[DefaultValue(null)]
		public string DataMember
		{
			get
			{
				return this.dataMember;
			}
			set
			{
				if (this.dataMember != value)
				{
					this.dataMember = value;
					tryDataBinding();
				}
			}
		}
		#endregion

		#region CurrencyManager
		/// <summary>
		/// Ruft den CurrencyManager der gebundenen Liste ab.
		/// </summary>
		protected CurrencyManager DataManager
		{
			get
			{
				return this.dataManager;
			}
		}
		#endregion
		#endregion
		
		/// <summary>
		/// Renew the Databinding. BindingContext is changed, if you change the Parent.
		/// </summary>
		protected override void OnBindingContextChanged(EventArgs e)
		{
			this.tryDataBinding();
			base.OnBindingContextChanged(e);
		}

		public BoundListView()
		{
			listChangedHandler = new ListChangedEventHandler(dataManager_ListChanged);
			positionChangedHandler = new EventHandler(dataManager_PositionChanged);

			this.View = System.Windows.Forms.View.Details;
			this.FullRowSelect = true;
			this.HideSelection = false;
			this.MultiSelect = false;
			this.LabelEdit = true;

			this.SelectedIndexChanged += new EventHandler(ListViewDataBinding_SelectedIndexChanged);
		}

		#region tryDataBinding
		/// <summary>
		/// Tries to get a new CurrencyManager for new DataBinding
		/// </summary>
		private void tryDataBinding()
		{
			if (this.DataSource == null ||
				base.BindingContext == null)
				return;

			CurrencyManager cm;
			try
			{
				cm = (CurrencyManager)base.BindingContext[this.DataSource, this.DataMember];
			}
			catch (System.ArgumentException)
			{
				// If no CurrencyManager was found
				return;
			}
			if (this.dataManager != cm)
			{
				// Unwire the old CurrencyManager
				if (this.dataManager != null)
				{
					this.dataManager.ListChanged -= listChangedHandler;
					this.dataManager.PositionChanged -= positionChangedHandler;
				}
				this.dataManager = cm;
				// Wire the new CurrencyManager
				if (this.dataManager != null)
				{
					this.dataManager.ListChanged += listChangedHandler;
					this.dataManager.PositionChanged += positionChangedHandler;
				}

				// Update metadata and data
				calculateColumns();
				updateAllData();
			}
		}
		#endregion

		#region Item Methods
		/// <summary>
		/// Updates all Items.
		/// </summary>
		private void updateAllData()
		{
			this.Items.Clear();
			for (int i = 0; i < DataManager.Count; i++ )
			{
				addItem(i);
			}
		}
		
		/// <summary>
		/// Adds a new item.
		/// </summary>
		/// <param name="index">The index of the item.</param>
		private void addItem(int index)
		{
			ListViewItem item = getListViewItem(index);
			this.Items.Insert(index, item);
		}
		
		/// <summary>
		/// Updates the data of the item with the DataSource.
		/// </summary>
		/// <param name="index">The index of the item.</param>
		private void updateItem(int index)
		{
			if (index >= 0 &&
			    index < this.Items.Count)
			{
				ListViewItem item = getListViewItem(index);
				this.Items[index] = item;
			}
		}

		/// <summary>
		/// Returns a <see cref="ListViewItem"/> wich contains the row-data at given index.
		/// </summary>
		/// <param name="index">The index of the row.</param>
		/// <returns>A item wich contains the data.</returns>
		private ListViewItem getListViewItem(int index)
		{
			object row = DataManager.List[index];
			PropertyDescriptorCollection propColl = DataManager.GetItemProperties();
			ArrayList items = new ArrayList();
			
			// Fill value for each column
			foreach(ColumnHeader column in this.Columns)
			{
				PropertyDescriptor prop = null;
				prop = propColl.Find(column.Text, false);
				if (prop != null)
				{
					items.Add(prop.GetValue(row).ToString());
				}
			}
			return new ListViewItem((string[])items.ToArray(typeof(string)));
		}

		/// <summary>
		/// Delete the item at the given index.
		/// </summary>
		/// <param name="index">The index of the item.</param>
		private void deleteItem(int index)
		{
			if (index >= 0 &&
			    index < this.Items.Count)
				this.Items.RemoveAt(index);
		}
		
		/// <summary>
		/// Calculates the Colums of the <see cref="BoundListView"/>.
		/// </summary>
		private void calculateColumns()
		{
			this.Columns.Clear();
			if (dataManager == null)
				return;
			foreach (PropertyDescriptor prop in DataManager.GetItemProperties())
			{
				ColumnHeader column = new ColumnHeader();
				column.Text = prop.Name;
				this.Columns.Add(column);
			}
		}
		#endregion
		
		#region Position changed from DataSource
		private void dataManager_PositionChanged(object sender, EventArgs e)
		{
			if (this.Items.Count > DataManager.Position)
			{
				this.Items[DataManager.Position].Selected = true;
				this.EnsureVisible(DataManager.Position);
			}
		}
		#endregion

		#region Item(s) changed from DataSource
		private void dataManager_ListChanged(object sender, ListChangedEventArgs e)
		{
			if (e.ListChangedType == ListChangedType.Reset ||
				e.ListChangedType == ListChangedType.ItemMoved)
			{
				// Update all data
				updateAllData();
			}
			else if (e.ListChangedType == ListChangedType.ItemAdded)
			{
				// Add new Item
				addItem(e.NewIndex);
			}
			else if (e.ListChangedType == ListChangedType.ItemChanged)
			{
				// Change Item
				updateItem(e.NewIndex);
			}
			else if (e.ListChangedType == ListChangedType.ItemDeleted)
			{
				// Delete Item
				deleteItem(e.NewIndex);
			}
			else
			{
				// Update metadata and all data
				calculateColumns();
				updateAllData();
			}
		}
		#endregion

		#region Position changed from ListView
		private void ListViewDataBinding_SelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				if (this.SelectedIndices.Count > 0 &&
					DataManager.Position != this.SelectedIndices[0])
					DataManager.Position = this.SelectedIndices[0];
			}
			catch
			{
				// Could appear, if you change the position while someone edits a row with invalid data.
			}
		}
		#endregion

		#region Item changed from ListView
		protected override void OnAfterLabelEdit(LabelEditEventArgs e)
		{
			base.OnAfterLabelEdit(e);
			if (e.Label == null)
			{
				// If you press ESC while editing.
				e.CancelEdit = true;
				return;
			}
			
			if (DataManager.List.Count > e.Item)
			{
				object row = DataManager.List[e.Item];
				// In a ListView you are only able to edit the first Column.
				PropertyDescriptor col = DataManager.GetItemProperties().Find(this.Columns[0].Text, false);
				try
				{
					if (row != null &&
						col != null)
						col.SetValue(row, e.Label);
					DataManager.EndCurrentEdit();
				}
				catch(Exception ex)
				{
					// If you try to enter strings in number-columns, too long strings or something
					// else wich is not allowed by the DataSource.
					MessageBox.Show("Edit failed:\r\n" + ex.Message, "Edit failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
					dataManager.CancelCurrentEdit();
					e.CancelEdit = true;
				}
			}
		}
		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Sascha Knopf

Germany Germany
No Biography provided

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 29 Aug 2006
Article Copyright 2006 by Sascha Knopf
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid