Reputation: 13057
I'm curious if there is a simple way to bind properties of a class to individual items of a CheckedListBox. There are several components out there (Telerik/DevExpress) that provide PropertyEditor grids, but I'm looking to do it in a CheckedListBox.
IE:
public class MyItem
{
public bool Property1
{
get;
set;
}
public bool Property2
{
get;
set;
}
public bool Property3
{
get;
set;
}
}
And when adding items to the CheckedListBox, I'd like to have some sort of method that lets me do:
this.AddCheckListBoxItem("Property A", this.myItem.Property1);
this.AddCheckListBoxItem("Property B", this.myItem.Property2);
this.AddCheckListBoxItem("Property C", this.myItem.Property3);
the first parameter being the display name within the CheckedListBox.
Then throughout any changes to the checkstate, the bool values would automatically be updated without further code.
Upvotes: 1
Views: 3038
Reputation: 13057
I've created a solution using reflection as suggested by @Jethro. I used generics in the class definition, but I didn't implement anything that uses them yet. To summarize, basically using reflection, I've bound a single object and it's boolean properties to individual items within a standard CheckedListBox. It's nice as it doesn't require having to code to set the property values and get them when saving data, as the reflection takes care of this.
I created a Form, and added a CheckedListBox to it, and a button. Here's the code side.
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
/// <summary>
/// The main form.
/// </summary>
public partial class Form1 : Form
{
/// <summary>
/// The checked list box property helper.
/// </summary>
private CheckedListBoxPropertyHelper<MyItem> helper;
/// <summary>
/// My object to bind to the checked list box.
/// </summary>
private MyItem myObjectDataSource;
/// <summary>
/// Initializes a new instance of the <see cref="Form1"/> class.
/// </summary>
public Form1()
{
this.InitializeComponent();
this.myObjectDataSource = new MyItem();
this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, true);
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property One", "Property1"));
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property Two", "Property2"));
this.helper.AddCheckListBoxItem(new CheckedPropertyItem("Property Three", "Property3"));
}
/// <summary>
/// Handles the Click event of the Button1 control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void Button1_Click(object sender, EventArgs e)
{
// In the constructor
// if we instantiated: this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, true);
// as: this.helper = new CheckedListBoxPropertyHelper<MyItem>(this.checkedListBox1, this.myObjectDataSource, false);
// changing the last bindImmediate property to false, the changes to the checkboxes wouldn't take effect immediately
// on the underlying object, you need to call this.helper.CommitChanges() at which point the changes
// will be made to the datasource object.
// this.helper.CommitChanges();
Console.WriteLine(this.myObjectDataSource.Property1.ToString());
Console.WriteLine(this.myObjectDataSource.Property2.ToString());
Console.WriteLine(this.myObjectDataSource.Property3.ToString());
}
}
/// <summary>
/// My item.
/// </summary>
public class MyItem
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property1.
/// </summary>
/// <value><c>true</c> if property1; otherwise, <c>false</c>.</value>
public bool Property1
{
get;
set;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property2.
/// </summary>
/// <value><c>true</c> if property2; otherwise, <c>false</c>.</value>
public bool Property2
{
get;
set;
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MyItem"/> is property3.
/// </summary>
/// <value><c>true</c> if property3; otherwise, <c>false</c>.</value>
public bool Property3
{
get;
set;
}
}
/// <summary>
/// The checked list box property helper. This binds datasource properties to checkedlistbox items.
/// </summary>
public class CheckedListBoxPropertyHelper<T> where T : class
{
/// <summary>
/// The checked list box.
/// </summary>
private CheckedListBox checkedListBox;
/// <summary>
/// The property info.
/// </summary>
private PropertyInfo[] PropertyInfo;
/// <summary>
/// Initializes a new instance of the <see cref="CheckedListBoxPropertyHelper"/> class.
/// </summary>
/// <param name="checkedListBox">The checked list box.</param>
/// <param name="dataSource">The data source.</param>
/// <param name="bindImmediate">if set to <c>true</c> [bind immediate].</param>
public CheckedListBoxPropertyHelper(CheckedListBox checkedListBox, T dataSource, bool bindImmediate)
{
this.checkedListBox = checkedListBox;
this.DataSource = dataSource;
this.PropertyInfo = this.DataSource.GetType().GetProperties();
this.BindImmediate = bindImmediate;
this.checkedListBox.ItemCheck += new ItemCheckEventHandler(CheckedListBox_ItemCheck);
}
/// <summary>
/// The data source.
/// </summary>
public T DataSource
{
get;
private set;
}
/// <summary>
/// Gets or sets a value indicating whether to [bind immediately] to the datasource object.
/// </summary>
/// <value><c>true</c> if [bind immediately]; otherwise, <c>false</c>.</value>
public bool BindImmediate
{
get;
set;
}
/// <summary>
/// Commits the changes.
/// </summary>
public void CommitChanges()
{
if (!this.BindImmediate)
{
for (int i = 0; i < this.checkedListBox.Items.Count; i++)
{
CheckedPropertyItem checkedItem = this.checkedListBox.Items[i] as CheckedPropertyItem;
this.SetChecked(this.checkedListBox.GetItemChecked(i), checkedItem);
}
}
else
{
throw new InvalidOperationException("You cannot commit changes when bind immediate is on.");
}
}
/// <summary>
/// Adds the check list box item.
/// </summary>
/// <param name="item">The item.</param>
public void AddCheckListBoxItem(CheckedPropertyItem item)
{
PropertyInfo info = this.GetPropertyInfo(item.PropertyName);
bool isChecked = false;
if (info != null)
{
isChecked = (bool)info.GetValue(this.DataSource, null);
}
this.checkedListBox.Items.Add(item, isChecked);
}
/// <summary>
/// Handles the ItemCheck event of the CheckedListBox control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.Windows.Forms.ItemCheckEventArgs"/> instance containing the event data.</param>
private void CheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (BindImmediate)
{
CheckedListBox clb = sender as CheckedListBox;
CheckedPropertyItem checkedItem = clb.Items[e.Index] as CheckedPropertyItem;
this.SetChecked(
e.NewValue == CheckState.Checked ? true : false,
checkedItem);
}
}
/// <summary>
/// Sets the checked.
/// </summary>
/// <param name="isChecked">if set to <c>true</c> [is checked].</param>
/// <param name="clb">The CLB.</param>
private void SetChecked(bool isChecked, CheckedPropertyItem item)
{
PropertyInfo info = this.GetPropertyInfo(item.PropertyName);
if (info.CanWrite)
{
info.SetValue(this.DataSource, isChecked, null);
}
}
/// <summary>
/// Gets the property info.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
/// <returns></returns>
private PropertyInfo GetPropertyInfo(string propertyName)
{
return this.PropertyInfo
.Where(c => c.Name == propertyName)
.SingleOrDefault();
}
}
/// <summary>
/// Checked Property Item binding.
/// </summary>
public class CheckedPropertyItem
{
/// <summary>
/// Initializes a new instance of the <see cref="CheckedPropertyItem"/> class.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="propertyName">Name of the property.</param>
public CheckedPropertyItem(string title, string propertyName)
{
this.Title = title;
this.PropertyName = propertyName;
}
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <value>The title.</value>
public string Title
{
get;
private set;
}
/// <summary>
/// Gets the name of the property.
/// </summary>
public string PropertyName
{
get;
private set;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>A <see cref="System.String"/> that represents this instance.</returns>
public override string ToString()
{
return this.Title;
}
}
Upvotes: 0
Reputation: 5916
Currently there isn't any easy/simple way to get the functionallity you are looking for. As in the comments the nearest solution I can think of would be to use reflection.
If you manage to build a helper class that has this functionallity, please post here as I would also find that usefull.
Upvotes: 1