Init
This commit is contained in:
@@ -0,0 +1,540 @@
|
||||
/*************************************************************************************
|
||||
|
||||
Extended WPF Toolkit
|
||||
|
||||
Copyright (C) 2007-2013 Xceed Software Inc.
|
||||
|
||||
This program is provided to you under the terms of the Microsoft Public
|
||||
License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
|
||||
|
||||
For more features, controls, and fast professional support,
|
||||
pick up the Plus Edition at http://xceed.com/wpf_toolkit
|
||||
|
||||
Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
|
||||
|
||||
***********************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
using Xceed.Wpf.Toolkit.Core.Utilities;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
|
||||
|
||||
namespace Xceed.Wpf.Toolkit.PropertyGrid
|
||||
{
|
||||
internal abstract class ObjectContainerHelperBase : ContainerHelperBase
|
||||
{
|
||||
// This is needed to work around the ItemsControl behavior.
|
||||
// When ItemsControl is preparing its containers, it appears
|
||||
// that calling Refresh() on the CollectionView bound to
|
||||
// the ItemsSource prevents the items from being displayed.
|
||||
// This patch is to avoid such a behavior.
|
||||
private bool _isPreparingItemFlag = false;
|
||||
private PropertyItemCollection _propertyItemCollection;
|
||||
|
||||
public ObjectContainerHelperBase( IPropertyContainer propertyContainer)
|
||||
: base( propertyContainer )
|
||||
{
|
||||
_propertyItemCollection = new PropertyItemCollection( new ObservableCollection<PropertyItem>() );
|
||||
UpdateFilter();
|
||||
UpdateCategorization( false );
|
||||
|
||||
}
|
||||
|
||||
public override IList Properties
|
||||
{
|
||||
get { return _propertyItemCollection; }
|
||||
}
|
||||
|
||||
private PropertyItem DefaultProperty
|
||||
{
|
||||
get
|
||||
{
|
||||
PropertyItem defaultProperty = null;
|
||||
var defaultName = this.GetDefaultPropertyName();
|
||||
if( defaultName != null )
|
||||
{
|
||||
defaultProperty = _propertyItemCollection
|
||||
.FirstOrDefault( ( prop ) => object.Equals( defaultName, prop.PropertyDescriptor.Name ) );
|
||||
}
|
||||
|
||||
return defaultProperty;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected PropertyItemCollection PropertyItems
|
||||
{
|
||||
get
|
||||
{
|
||||
return _propertyItemCollection;
|
||||
}
|
||||
}
|
||||
|
||||
public override PropertyItemBase ContainerFromItem( object item )
|
||||
{
|
||||
if( item == null )
|
||||
return null;
|
||||
// Exception case for ObjectContainerHelperBase. The "Item" may sometimes
|
||||
// be identified as a string representing the property name or
|
||||
// the PropertyItem itself.
|
||||
Debug.Assert( item is PropertyItem || item is string );
|
||||
|
||||
var propertyItem = item as PropertyItem;
|
||||
if( propertyItem != null )
|
||||
return propertyItem;
|
||||
|
||||
|
||||
var propertyStr = item as string;
|
||||
if( propertyStr != null )
|
||||
return PropertyItems.FirstOrDefault( ( prop ) => propertyStr == prop.PropertyDescriptor.Name );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override object ItemFromContainer( PropertyItemBase container )
|
||||
{
|
||||
// Since this call is only used to update the PropertyGrid.SelectedProperty property,
|
||||
// return the PropertyName.
|
||||
var propertyItem = container as PropertyItem;
|
||||
if( propertyItem == null )
|
||||
return null;
|
||||
|
||||
return propertyItem.PropertyDescriptor.Name;
|
||||
}
|
||||
|
||||
public override void UpdateValuesFromSource()
|
||||
{
|
||||
foreach( PropertyItem item in PropertyItems )
|
||||
{
|
||||
item.DescriptorDefinition.UpdateValueFromSource();
|
||||
item.ContainerHelper.UpdateValuesFromSource();
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateProperties()
|
||||
{
|
||||
if( (PropertyItems.Count == 0)
|
||||
)
|
||||
{
|
||||
this.RegenerateProperties();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnFilterChanged()
|
||||
{
|
||||
this.UpdateFilter();
|
||||
}
|
||||
|
||||
protected override void OnCategorizationChanged()
|
||||
{
|
||||
UpdateCategorization( true );
|
||||
}
|
||||
|
||||
protected override void OnAutoGeneratePropertiesChanged()
|
||||
{
|
||||
this.RegenerateProperties();
|
||||
}
|
||||
|
||||
protected override void OnHideInheritedPropertiesChanged()
|
||||
{
|
||||
this.RegenerateProperties();
|
||||
}
|
||||
|
||||
protected override void OnEditorDefinitionsChanged()
|
||||
{
|
||||
this.RegenerateProperties();
|
||||
}
|
||||
|
||||
protected override void OnPropertyDefinitionsChanged()
|
||||
{
|
||||
this.RegenerateProperties();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
protected internal override void SetPropertiesExpansion( bool isExpanded )
|
||||
{
|
||||
if( this.Properties.Count == 0 )
|
||||
{
|
||||
this.GenerateProperties();
|
||||
}
|
||||
|
||||
base.SetPropertiesExpansion( isExpanded );
|
||||
}
|
||||
|
||||
protected internal override void SetPropertiesExpansion( string propertyName, bool isExpanded )
|
||||
{
|
||||
if( this.Properties.Count == 0 )
|
||||
{
|
||||
this.GenerateProperties();
|
||||
}
|
||||
|
||||
base.SetPropertiesExpansion( propertyName, isExpanded );
|
||||
}
|
||||
|
||||
private void UpdateFilter()
|
||||
{
|
||||
FilterInfo filterInfo = PropertyContainer.FilterInfo;
|
||||
|
||||
PropertyItems.FilterPredicate = filterInfo.Predicate
|
||||
?? PropertyItemCollection.CreateFilter( filterInfo.InputString );
|
||||
}
|
||||
|
||||
private void UpdateCategorization( bool updateSubPropertiesCategorization )
|
||||
{
|
||||
_propertyItemCollection.UpdateCategorization( this.ComputeCategoryGroupDescription(), this.PropertyContainer.IsCategorized, this.PropertyContainer.IsSortedAlphabetically );
|
||||
if( updateSubPropertiesCategorization && (_propertyItemCollection.Count > 0) )
|
||||
{
|
||||
foreach( PropertyItem propertyItem in _propertyItemCollection )
|
||||
{
|
||||
PropertyItemCollection subPropertyItemsCollection = propertyItem.Properties as PropertyItemCollection;
|
||||
if( subPropertyItemsCollection != null )
|
||||
{
|
||||
subPropertyItemsCollection.UpdateCategorization( this.ComputeCategoryGroupDescription(), this.PropertyContainer.IsCategorized, this.PropertyContainer.IsSortedAlphabetically );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GroupDescription ComputeCategoryGroupDescription()
|
||||
{
|
||||
if( !PropertyContainer.IsCategorized )
|
||||
return null;
|
||||
return new PropertyGroupDescription( PropertyItemCollection.CategoryPropertyName );
|
||||
}
|
||||
|
||||
private string GetCategoryGroupingPropertyName()
|
||||
{
|
||||
var propGroup = this.ComputeCategoryGroupDescription() as PropertyGroupDescription;
|
||||
return ( propGroup != null ) ? propGroup.PropertyName : null;
|
||||
}
|
||||
|
||||
private void OnChildrenPropertyChanged( object sender, PropertyChangedEventArgs e )
|
||||
{
|
||||
if( ObjectContainerHelperBase.IsItemOrderingProperty( e.PropertyName )
|
||||
|| this.GetCategoryGroupingPropertyName() == e.PropertyName )
|
||||
{
|
||||
// Refreshing the view while Containers are generated will throw an exception
|
||||
if( this.ChildrenItemsControl.ItemContainerGenerator.Status != GeneratorStatus.GeneratingContainers
|
||||
&& !_isPreparingItemFlag )
|
||||
{
|
||||
PropertyItems.RefreshView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract string GetDefaultPropertyName();
|
||||
|
||||
protected abstract IEnumerable<PropertyItem> GenerateSubPropertiesCore();
|
||||
|
||||
private void RegenerateProperties()
|
||||
{
|
||||
IEnumerable<PropertyItem> subProperties = this.GenerateSubPropertiesCore();
|
||||
|
||||
foreach( var propertyItem in subProperties )
|
||||
{
|
||||
this.InitializePropertyItem( propertyItem );
|
||||
}
|
||||
|
||||
//Remove the event callback from the previous children (if any)
|
||||
foreach( var propertyItem in PropertyItems )
|
||||
{
|
||||
propertyItem.PropertyChanged -= OnChildrenPropertyChanged;
|
||||
}
|
||||
|
||||
PropertyItems.UpdateItems( subProperties );
|
||||
|
||||
//Add the event callback to the new childrens
|
||||
foreach( var propertyItem in PropertyItems )
|
||||
{
|
||||
propertyItem.PropertyChanged += OnChildrenPropertyChanged;
|
||||
}
|
||||
|
||||
// Update the selected property on the property grid only.
|
||||
PropertyGrid propertyGrid = PropertyContainer as PropertyGrid;
|
||||
if( propertyGrid != null )
|
||||
{
|
||||
propertyGrid.SelectedPropertyItem = this.DefaultProperty;
|
||||
}
|
||||
}
|
||||
|
||||
protected static List<PropertyDescriptor> GetPropertyDescriptors( object instance, bool hideInheritedProperties )
|
||||
{
|
||||
PropertyDescriptorCollection descriptors;
|
||||
|
||||
TypeConverter tc = TypeDescriptor.GetConverter( instance );
|
||||
if( tc == null || !tc.GetPropertiesSupported() )
|
||||
{
|
||||
if( instance is ICustomTypeDescriptor )
|
||||
{
|
||||
descriptors = ((ICustomTypeDescriptor)instance).GetProperties();
|
||||
}
|
||||
//ICustomTypeProvider is only available in .net 4.5 and over. Use reflection so the .net 4.0 and .net 3.5 still works.
|
||||
else if( instance.GetType().GetInterface( "ICustomTypeProvider", true ) != null )
|
||||
{
|
||||
var methodInfo = instance.GetType().GetMethod( "GetCustomType" );
|
||||
var result = methodInfo.Invoke( instance, null ) as Type;
|
||||
descriptors = TypeDescriptor.GetProperties( result );
|
||||
}
|
||||
else
|
||||
{
|
||||
descriptors = TypeDescriptor.GetProperties( instance.GetType() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
descriptors = tc.GetProperties( instance );
|
||||
}
|
||||
|
||||
if( ( descriptors != null ) )
|
||||
{
|
||||
var descriptorsProperties = descriptors.Cast<PropertyDescriptor>();
|
||||
if( hideInheritedProperties )
|
||||
{
|
||||
var properties = from p in descriptorsProperties
|
||||
where p.ComponentType == instance.GetType()
|
||||
select p;
|
||||
return properties.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
return descriptorsProperties.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected bool GetWillRefreshPropertyGrid( PropertyDescriptor propertyDescriptor )
|
||||
{
|
||||
if( propertyDescriptor == null )
|
||||
return false;
|
||||
|
||||
var attribute = PropertyGridUtilities.GetAttribute<RefreshPropertiesAttribute>( propertyDescriptor );
|
||||
if( attribute != null )
|
||||
return attribute.RefreshProperties != RefreshProperties.None;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void InitializeDescriptorDefinition(
|
||||
DescriptorPropertyDefinitionBase descriptorDef,
|
||||
PropertyDefinition propertyDefinition )
|
||||
{
|
||||
if( descriptorDef == null )
|
||||
throw new ArgumentNullException( "descriptorDef" );
|
||||
|
||||
if( propertyDefinition == null )
|
||||
return;
|
||||
|
||||
// Values defined on PropertyDefinition have priority on the attributes
|
||||
if( propertyDefinition != null )
|
||||
{
|
||||
if( propertyDefinition.Category != null )
|
||||
{
|
||||
descriptorDef.Category = propertyDefinition.Category;
|
||||
descriptorDef.CategoryValue = propertyDefinition.Category;
|
||||
}
|
||||
|
||||
if( propertyDefinition.Description != null )
|
||||
{
|
||||
descriptorDef.Description = propertyDefinition.Description;
|
||||
}
|
||||
|
||||
if( propertyDefinition.DisplayName != null )
|
||||
{
|
||||
descriptorDef.DisplayName = propertyDefinition.DisplayName;
|
||||
}
|
||||
|
||||
if( propertyDefinition.DisplayOrder != null )
|
||||
{
|
||||
descriptorDef.DisplayOrder = propertyDefinition.DisplayOrder.Value;
|
||||
}
|
||||
|
||||
if( propertyDefinition.IsExpandable != null )
|
||||
{
|
||||
descriptorDef.ExpandableAttribute = propertyDefinition.IsExpandable.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePropertyItem( PropertyItem propertyItem )
|
||||
{
|
||||
DescriptorPropertyDefinitionBase pd = propertyItem.DescriptorDefinition;
|
||||
propertyItem.PropertyDescriptor = pd.PropertyDescriptor;
|
||||
|
||||
propertyItem.IsReadOnly = pd.IsReadOnly;
|
||||
propertyItem.DisplayName = pd.DisplayName;
|
||||
propertyItem.Description = pd.Description;
|
||||
|
||||
propertyItem.Category = pd.Category;
|
||||
propertyItem.PropertyOrder = pd.DisplayOrder;
|
||||
|
||||
//These properties can vary with the value. They need to be bound.
|
||||
if( pd.PropertyDescriptor.Converter is ExpandableObjectConverter )
|
||||
{
|
||||
propertyItem.IsExpandable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetupDefinitionBinding( propertyItem, PropertyItemBase.IsExpandableProperty, pd, () => pd.IsExpandable, BindingMode.OneWay );
|
||||
}
|
||||
SetupDefinitionBinding( propertyItem, PropertyItemBase.AdvancedOptionsIconProperty, pd, () => pd.AdvancedOptionsIcon, BindingMode.OneWay );
|
||||
SetupDefinitionBinding( propertyItem, PropertyItemBase.AdvancedOptionsTooltipProperty, pd, () => pd.AdvancedOptionsTooltip, BindingMode.OneWay );
|
||||
SetupDefinitionBinding( propertyItem, PropertyItem.ValueProperty, pd, () => pd.Value, BindingMode.TwoWay );
|
||||
|
||||
if( pd.CommandBindings != null )
|
||||
{
|
||||
foreach( CommandBinding commandBinding in pd.CommandBindings )
|
||||
{
|
||||
propertyItem.CommandBindings.Add( commandBinding );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupDefinitionBinding<T>(
|
||||
PropertyItem propertyItem,
|
||||
DependencyProperty itemProperty,
|
||||
DescriptorPropertyDefinitionBase pd,
|
||||
Expression<Func<T>> definitionProperty,
|
||||
BindingMode bindingMode )
|
||||
{
|
||||
string sourceProperty = ReflectionHelper.GetPropertyOrFieldName( definitionProperty );
|
||||
Binding binding = new Binding( sourceProperty )
|
||||
{
|
||||
Source = pd,
|
||||
Mode = bindingMode
|
||||
};
|
||||
|
||||
propertyItem.SetBinding( itemProperty, binding );
|
||||
}
|
||||
|
||||
internal FrameworkElement GenerateChildrenEditorElement( PropertyItem propertyItem )
|
||||
{
|
||||
FrameworkElement editorElement = null;
|
||||
DescriptorPropertyDefinitionBase pd = propertyItem.DescriptorDefinition;
|
||||
object definitionKey = null;
|
||||
Type definitionKeyAsType = definitionKey as Type;
|
||||
ITypeEditor editor = null;
|
||||
|
||||
if( editor == null )
|
||||
editor = pd.CreateAttributeEditor();
|
||||
|
||||
if( editor != null )
|
||||
editorElement = editor.ResolveEditor( propertyItem );
|
||||
|
||||
|
||||
if( editorElement == null && definitionKey == null )
|
||||
editorElement = this.GenerateCustomEditingElement( propertyItem.PropertyDescriptor.Name, propertyItem );
|
||||
|
||||
if( editorElement == null && definitionKeyAsType == null )
|
||||
editorElement = this.GenerateCustomEditingElement( propertyItem.PropertyType, propertyItem );
|
||||
|
||||
if( editorElement == null )
|
||||
{
|
||||
if( pd.IsReadOnly )
|
||||
editor = new TextBlockEditor();
|
||||
|
||||
// Fallback: Use a default type editor.
|
||||
if( editor == null )
|
||||
{
|
||||
editor = ( definitionKeyAsType != null )
|
||||
? PropertyGridUtilities.CreateDefaultEditor( definitionKeyAsType, null, propertyItem )
|
||||
: pd.CreateDefaultEditor( propertyItem );
|
||||
}
|
||||
|
||||
Debug.Assert( editor != null );
|
||||
|
||||
editorElement = editor.ResolveEditor( propertyItem );
|
||||
}
|
||||
|
||||
return editorElement;
|
||||
}
|
||||
|
||||
internal PropertyDefinition GetPropertyDefinition( PropertyDescriptor descriptor )
|
||||
{
|
||||
PropertyDefinition def = null;
|
||||
|
||||
var propertyDefs = this.PropertyContainer.PropertyDefinitions;
|
||||
if( propertyDefs != null )
|
||||
{
|
||||
def = propertyDefs[ descriptor.Name ];
|
||||
if( def == null )
|
||||
{
|
||||
def = propertyDefs.GetRecursiveBaseTypes( descriptor.PropertyType );
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
public override void PrepareChildrenPropertyItem( PropertyItemBase propertyItem, object item )
|
||||
{
|
||||
_isPreparingItemFlag = true;
|
||||
base.PrepareChildrenPropertyItem( propertyItem, item );
|
||||
|
||||
if( propertyItem.Editor == null )
|
||||
{
|
||||
FrameworkElement editor = this.GenerateChildrenEditorElement( ( PropertyItem )propertyItem );
|
||||
if( editor != null )
|
||||
{
|
||||
// Tag the editor as generated to know if we should clear it.
|
||||
ContainerHelperBase.SetIsGenerated( editor, true );
|
||||
propertyItem.Editor = editor;
|
||||
}
|
||||
}
|
||||
_isPreparingItemFlag = false;
|
||||
}
|
||||
|
||||
public override void ClearChildrenPropertyItem( PropertyItemBase propertyItem, object item )
|
||||
{
|
||||
if( propertyItem.Editor != null
|
||||
&& ContainerHelperBase.GetIsGenerated( propertyItem.Editor ) )
|
||||
{
|
||||
propertyItem.Editor = null;
|
||||
}
|
||||
|
||||
base.ClearChildrenPropertyItem( propertyItem, item );
|
||||
}
|
||||
|
||||
public override Binding CreateChildrenDefaultBinding( PropertyItemBase propertyItem )
|
||||
{
|
||||
Binding binding = new Binding( "Value" );
|
||||
binding.Mode = ( ( ( PropertyItem )propertyItem ).IsReadOnly ) ? BindingMode.OneWay : BindingMode.TwoWay;
|
||||
return binding;
|
||||
}
|
||||
|
||||
protected static string GetDefaultPropertyName( object instance )
|
||||
{
|
||||
AttributeCollection attributes = TypeDescriptor.GetAttributes( instance );
|
||||
DefaultPropertyAttribute defaultPropertyAttribute = ( DefaultPropertyAttribute )attributes[ typeof( DefaultPropertyAttribute ) ];
|
||||
return defaultPropertyAttribute != null ? defaultPropertyAttribute.Name : null;
|
||||
}
|
||||
|
||||
private static bool IsItemOrderingProperty( string propertyName )
|
||||
{
|
||||
return string.Equals( propertyName, PropertyItemCollection.DisplayNamePropertyName )
|
||||
|| string.Equals( propertyName, PropertyItemCollection.CategoryOrderPropertyName )
|
||||
|| string.Equals( propertyName, PropertyItemCollection.PropertyOrderPropertyName );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user