Click here to Skip to main content
12,304,884 members (64,504 online)
Click here to Skip to main content
Articles » Database » Database » General » Downloads

Stats

155.3K views
4K downloads
159 bookmarked
Posted

Implementing complex data binding in custom controls

, 29 Aug 2006 CPOL
An article on implementing complex data binding (DataSource and DataMember) on a custom control.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sascha Knopf
Germany Germany
No Biography provided

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160530.1 | Last Updated 29 Aug 2006
Article Copyright 2006 by Sascha Knopf
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid