------ #### ryControls V2.1.2101.1201 - *.[更新]内置的ObjectListView从1.13更新到2.9.1版本,并对主要属性进行汉化。 - *.[修复]修复新版ObjectListView选中项有筛选结果时,筛选结果白色字体看不清的BUG。 - *.[改进]TextBoxEx2默认事件改为TextChanged2。
262 lines
9.6 KiB
C#
262 lines
9.6 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
|
|
namespace BrightIdeasSoftware
|
|
{
|
|
/// <summary>
|
|
/// A TreeDataSourceAdapter knows how to build a tree structure from a binding list.
|
|
/// </summary>
|
|
/// <remarks>To build a tree</remarks>
|
|
public class TreeDataSourceAdapter : DataSourceAdapter
|
|
{
|
|
#region Life and death
|
|
|
|
/// <summary>
|
|
/// Create a data source adaptor that knows how to build a tree structure
|
|
/// </summary>
|
|
/// <param name="tlv"></param>
|
|
public TreeDataSourceAdapter(DataTreeListView tlv)
|
|
: base(tlv) {
|
|
this.treeListView = tlv;
|
|
this.treeListView.CanExpandGetter = delegate(object model) { return this.CalculateHasChildren(model); };
|
|
this.treeListView.ChildrenGetter = delegate(object model) { return this.CalculateChildren(model); };
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the property/column that uniquely identifies each row.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The value contained by this column must be unique across all rows
|
|
/// in the data source. Odd and unpredictable things will happen if two
|
|
/// rows have the same id.
|
|
/// </para>
|
|
/// <para>Null cannot be a valid key value.</para>
|
|
/// </remarks>
|
|
public virtual string KeyAspectName {
|
|
get { return keyAspectName; }
|
|
set {
|
|
if (keyAspectName == value)
|
|
return;
|
|
keyAspectName = value;
|
|
this.keyMunger = new Munger(this.KeyAspectName);
|
|
this.InitializeDataSource();
|
|
}
|
|
}
|
|
private string keyAspectName;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the property/column that contains the key of
|
|
/// the parent of a row.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The test condition for deciding if one row is the parent of another is functionally
|
|
/// equivilent to this:
|
|
/// <code>
|
|
/// Object.Equals(candidateParentRow[this.KeyAspectName], row[this.ParentKeyAspectName])
|
|
/// </code>
|
|
/// </para>
|
|
/// <para>Unlike key value, parent keys can be null but a null parent key can only be used
|
|
/// to identify root objects.</para>
|
|
/// </remarks>
|
|
public virtual string ParentKeyAspectName {
|
|
get { return parentKeyAspectName; }
|
|
set {
|
|
if (parentKeyAspectName == value)
|
|
return;
|
|
parentKeyAspectName = value;
|
|
this.parentKeyMunger = new Munger(this.ParentKeyAspectName);
|
|
this.InitializeDataSource();
|
|
}
|
|
}
|
|
private string parentKeyAspectName;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value that identifies a row as a root object.
|
|
/// When the ParentKey of a row equals the RootKeyValue, that row will
|
|
/// be treated as root of the TreeListView.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The test condition for deciding a root object is functionally
|
|
/// equivilent to this:
|
|
/// <code>
|
|
/// Object.Equals(candidateRow[this.ParentKeyAspectName], this.RootKeyValue)
|
|
/// </code>
|
|
/// </para>
|
|
/// <para>The RootKeyValue can be null.</para>
|
|
/// </remarks>
|
|
public virtual object RootKeyValue {
|
|
get { return rootKeyValue; }
|
|
set {
|
|
if (Equals(rootKeyValue, value))
|
|
return;
|
|
rootKeyValue = value;
|
|
this.InitializeDataSource();
|
|
}
|
|
}
|
|
private object rootKeyValue;
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether or not the key columns (id and parent id) should
|
|
/// be shown to the user.
|
|
/// </summary>
|
|
/// <remarks>This must be set before the DataSource is set. It has no effect
|
|
/// afterwards.</remarks>
|
|
public virtual bool ShowKeyColumns {
|
|
get { return showKeyColumns; }
|
|
set { showKeyColumns = value; }
|
|
}
|
|
private bool showKeyColumns = true;
|
|
|
|
|
|
#endregion
|
|
|
|
#region Implementation properties
|
|
|
|
/// <summary>
|
|
/// Gets the DataTreeListView that is being managed
|
|
/// </summary>
|
|
protected DataTreeListView TreeListView {
|
|
get { return treeListView; }
|
|
}
|
|
private readonly DataTreeListView treeListView;
|
|
|
|
#endregion
|
|
|
|
#region Implementation
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected override void InitializeDataSource() {
|
|
base.InitializeDataSource();
|
|
this.TreeListView.RebuildAll(true);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
protected override void SetListContents() {
|
|
this.TreeListView.Roots = this.CalculateRoots();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="property"></param>
|
|
/// <returns></returns>
|
|
protected override bool ShouldCreateColumn(PropertyDescriptor property) {
|
|
// If the property is a key column, and we aren't supposed to show keys, don't show it
|
|
if (!this.ShowKeyColumns && (property.Name == this.KeyAspectName || property.Name == this.ParentKeyAspectName))
|
|
return false;
|
|
|
|
return base.ShouldCreateColumn(property);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
protected override void HandleListChangedItemChanged(System.ComponentModel.ListChangedEventArgs e) {
|
|
// If the id or the parent id of a row changes, we just rebuild everything.
|
|
// We can't do anything more specific. We don't know what the previous values, so we can't
|
|
// tell the previous parent to refresh itself. If the id itself has changed, things that used
|
|
// to be children will no longer be children. Just rebuild everything.
|
|
// It seems PropertyDescriptor is only filled in .NET 4 :(
|
|
if (e.PropertyDescriptor != null &&
|
|
(e.PropertyDescriptor.Name == this.KeyAspectName ||
|
|
e.PropertyDescriptor.Name == this.ParentKeyAspectName))
|
|
this.InitializeDataSource();
|
|
else
|
|
base.HandleListChangedItemChanged(e);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
protected override void ChangePosition(int index) {
|
|
// We can't use our base method directly, since the normal position management
|
|
// doesn't know about our tree structure. They treat our dataset as a flat list
|
|
// but we have a collapsable structure. This means that the 5'th row to them
|
|
// may not even be visible to us
|
|
|
|
// To display the n'th row, we have to make sure that all its ancestors
|
|
// are expanded. Then we will be able to select it.
|
|
object model = this.CurrencyManager.List[index];
|
|
object parent = this.CalculateParent(model);
|
|
while (parent != null && !this.TreeListView.IsExpanded(parent)) {
|
|
this.TreeListView.Expand(parent);
|
|
parent = this.CalculateParent(parent);
|
|
}
|
|
|
|
base.ChangePosition(index);
|
|
}
|
|
|
|
private IEnumerable CalculateRoots() {
|
|
foreach (object x in this.CurrencyManager.List) {
|
|
object parentKey = this.GetParentValue(x);
|
|
if (Object.Equals(this.RootKeyValue, parentKey))
|
|
yield return x;
|
|
}
|
|
}
|
|
|
|
private bool CalculateHasChildren(object model) {
|
|
object keyValue = this.GetKeyValue(model);
|
|
if (keyValue == null)
|
|
return false;
|
|
|
|
foreach (object x in this.CurrencyManager.List) {
|
|
object parentKey = this.GetParentValue(x);
|
|
if (Object.Equals(keyValue, parentKey))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private IEnumerable CalculateChildren(object model) {
|
|
object keyValue = this.GetKeyValue(model);
|
|
if (keyValue != null) {
|
|
foreach (object x in this.CurrencyManager.List) {
|
|
object parentKey = this.GetParentValue(x);
|
|
if (Object.Equals(keyValue, parentKey))
|
|
yield return x;
|
|
}
|
|
}
|
|
}
|
|
|
|
private object CalculateParent(object model) {
|
|
object parentValue = this.GetParentValue(model);
|
|
if (parentValue == null)
|
|
return null;
|
|
|
|
foreach (object x in this.CurrencyManager.List) {
|
|
object key = this.GetKeyValue(x);
|
|
if (Object.Equals(parentValue, key))
|
|
return x;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private object GetKeyValue(object model) {
|
|
return this.keyMunger == null ? null : this.keyMunger.GetValue(model);
|
|
}
|
|
|
|
private object GetParentValue(object model) {
|
|
return this.parentKeyMunger == null ? null : this.parentKeyMunger.GetValue(model);
|
|
}
|
|
|
|
#endregion
|
|
|
|
private Munger keyMunger;
|
|
private Munger parentKeyMunger;
|
|
}
|
|
} |