You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
532 lines
14 KiB
532 lines
14 KiB
/************************************************************************
|
|
|
|
Extended WPF Toolkit
|
|
|
|
Copyright (C) 2010-2012 Xceed Software Inc.
|
|
|
|
This program is provided to you under the terms of the Microsoft Reciprocal
|
|
License (Ms-RL) as published at http://wpftoolkit.codeplex.com/license
|
|
|
|
This program can be provided to you by Xceed Software Inc. under a
|
|
proprietary commercial license agreement for use in non-Open Source
|
|
projects. The commercial version of Extended WPF Toolkit also includes
|
|
priority technical support, commercial updates, and many additional
|
|
useful WPF controls if you license Xceed Business Suite for WPF.
|
|
|
|
Visit http://xceed.com and follow @datagrid on Twitter.
|
|
|
|
**********************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Input;
|
|
using System.Windows.Markup.Primitives;
|
|
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
|
|
using Xceed.Wpf.Toolkit.PropertyGrid.Commands;
|
|
|
|
namespace Xceed.Wpf.Toolkit.PropertyGrid
|
|
{
|
|
public class PropertyItem : Control
|
|
{
|
|
#region Members
|
|
|
|
private DependencyPropertyDescriptor _dpDescriptor;
|
|
private MarkupObject _markupObject;
|
|
|
|
#endregion //Members
|
|
|
|
#region Properties
|
|
|
|
public string BindingPath
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
#region Category
|
|
|
|
public static readonly DependencyProperty CategoryProperty = DependencyProperty.Register( "Category", typeof( string ), typeof( PropertyItem ), new UIPropertyMetadata( string.Empty ) );
|
|
public string Category
|
|
{
|
|
get
|
|
{
|
|
return ( string )GetValue( CategoryProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( CategoryProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //Category
|
|
|
|
#region Description
|
|
|
|
public string Description
|
|
{
|
|
get
|
|
{
|
|
return PropertyDescriptor.Description;
|
|
}
|
|
}
|
|
|
|
#endregion //Description
|
|
|
|
#region DisplayName
|
|
|
|
public static readonly DependencyProperty DisplayNameProperty = DependencyProperty.Register( "DisplayName", typeof( string ), typeof( PropertyItem ), new UIPropertyMetadata( null ) );
|
|
public string DisplayName
|
|
{
|
|
get
|
|
{
|
|
return ( string )GetValue( DisplayNameProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( DisplayNameProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //DisplayName
|
|
|
|
#region Editor
|
|
|
|
public static readonly DependencyProperty EditorProperty = DependencyProperty.Register( "Editor", typeof( FrameworkElement ), typeof( PropertyItem ), new UIPropertyMetadata( null, OnEditorChanged ) );
|
|
public FrameworkElement Editor
|
|
{
|
|
get
|
|
{
|
|
return ( FrameworkElement )GetValue( EditorProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( EditorProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnEditorChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
PropertyItem propertyItem = o as PropertyItem;
|
|
if( propertyItem != null )
|
|
propertyItem.OnEditorChanged( ( FrameworkElement )e.OldValue, ( FrameworkElement )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnEditorChanged( FrameworkElement oldValue, FrameworkElement newValue )
|
|
{
|
|
if( oldValue != null )
|
|
oldValue.DataContext = null;
|
|
|
|
if( newValue != null )
|
|
newValue.DataContext = this;
|
|
}
|
|
|
|
#endregion //Editor
|
|
|
|
#region Instance
|
|
|
|
private object _instance;
|
|
public object Instance
|
|
{
|
|
get
|
|
{
|
|
return _instance;
|
|
}
|
|
private set
|
|
{
|
|
_instance = value;
|
|
_markupObject = MarkupWriter.GetMarkupObjectFor( _instance );
|
|
}
|
|
}
|
|
|
|
#endregion //Instance
|
|
|
|
#region IsDataBound
|
|
|
|
/// <summary>
|
|
/// Gets if the property is data bound
|
|
/// </summary>
|
|
public bool IsDataBound
|
|
{
|
|
get
|
|
{
|
|
var dependencyObject = Instance as DependencyObject;
|
|
if( dependencyObject != null && _dpDescriptor != null )
|
|
return BindingOperations.GetBindingExpressionBase( dependencyObject, _dpDescriptor.DependencyProperty ) != null;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion //IsDataBound
|
|
|
|
#region IsDynamicResource
|
|
|
|
public bool IsDynamicResource
|
|
{
|
|
get
|
|
{
|
|
var markupProperty = _markupObject.Properties.Where( p => p.Name == PropertyDescriptor.Name ).FirstOrDefault();
|
|
if( markupProperty != null )
|
|
return markupProperty.Value is DynamicResourceExtension;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion //IsDynamicResource
|
|
|
|
#region IsExpanded
|
|
|
|
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( "IsExpanded", typeof( bool ), typeof( PropertyItem ), new UIPropertyMetadata( false, OnIsExpandedChanged ) );
|
|
public bool IsExpanded
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( IsExpandedProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( IsExpandedProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnIsExpandedChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
PropertyItem propertyItem = o as PropertyItem;
|
|
if( propertyItem != null )
|
|
propertyItem.OnIsExpandedChanged( ( bool )e.OldValue, ( bool )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnIsExpandedChanged( bool oldValue, bool newValue )
|
|
{
|
|
if( newValue && ( Properties == null || Properties.Count == 0 ) )
|
|
{
|
|
GetChildProperties();
|
|
}
|
|
}
|
|
|
|
#endregion IsExpanded
|
|
|
|
#region HasChildProperties
|
|
|
|
public static readonly DependencyProperty HasChildPropertiesProperty = DependencyProperty.Register( "HasChildProperties", typeof( bool ), typeof( PropertyItem ), new UIPropertyMetadata( false ) );
|
|
public bool HasChildProperties
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( HasChildPropertiesProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( HasChildPropertiesProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion HasChildProperties
|
|
|
|
#region HasResourceApplied
|
|
|
|
public bool HasResourceApplied
|
|
{
|
|
//TODO: need to find a better way to determine if a StaticResource has been applied to any property not just a style
|
|
get
|
|
{
|
|
var markupProperty = _markupObject.Properties.Where( p => p.Name == PropertyDescriptor.Name ).FirstOrDefault();
|
|
if( markupProperty != null )
|
|
return markupProperty.Value is Style;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion //HasResourceApplied
|
|
|
|
public bool IsReadOnly
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
#region IsSelected
|
|
|
|
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register( "IsSelected", typeof( bool ), typeof( PropertyItem ), new UIPropertyMetadata( false, OnIsSelectedChanged ) );
|
|
public bool IsSelected
|
|
{
|
|
get
|
|
{
|
|
return ( bool )GetValue( IsSelectedProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( IsSelectedProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnIsSelectedChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
PropertyItem propertyItem = o as PropertyItem;
|
|
if( propertyItem != null )
|
|
propertyItem.OnIsSelectedChanged( ( bool )e.OldValue, ( bool )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnIsSelectedChanged( bool oldValue, bool newValue )
|
|
{
|
|
if( newValue )
|
|
PropertyGrid.SelectedProperty = this;
|
|
}
|
|
|
|
#endregion //IsSelected
|
|
|
|
#region Level
|
|
|
|
public static readonly DependencyProperty LevelProperty = DependencyProperty.Register( "Level", typeof( int ), typeof( PropertyItem ), new UIPropertyMetadata( 0 ) );
|
|
public int Level
|
|
{
|
|
get
|
|
{
|
|
return ( int )GetValue( LevelProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( LevelProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //Level
|
|
|
|
#region Properties
|
|
|
|
public static readonly DependencyProperty PropertiesProperty = DependencyProperty.Register( "Properties", typeof( PropertyItemCollection ), typeof( PropertyItem ), new UIPropertyMetadata( null ) );
|
|
public PropertyItemCollection Properties
|
|
{
|
|
get
|
|
{
|
|
return ( PropertyItemCollection )GetValue( PropertiesProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( PropertiesProperty, value );
|
|
}
|
|
}
|
|
|
|
#endregion //Properties
|
|
|
|
#region PropertyDescriptor
|
|
|
|
private PropertyDescriptor _propertyDescriptor;
|
|
public PropertyDescriptor PropertyDescriptor
|
|
{
|
|
get
|
|
{
|
|
return _propertyDescriptor;
|
|
}
|
|
private set
|
|
{
|
|
_propertyDescriptor = value;
|
|
_dpDescriptor = DependencyPropertyDescriptor.FromProperty( _propertyDescriptor );
|
|
}
|
|
}
|
|
|
|
#endregion //PropertyDescriptor
|
|
|
|
public PropertyGrid PropertyGrid
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
public int PropertyOrder
|
|
{
|
|
get;
|
|
set;
|
|
} //maybe make a DP
|
|
|
|
#region PropertyType
|
|
|
|
public Type PropertyType
|
|
{
|
|
get
|
|
{
|
|
return PropertyDescriptor.PropertyType;
|
|
}
|
|
}
|
|
|
|
#endregion //PropertyType
|
|
|
|
public ICommand ResetValueCommand
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
#region Value
|
|
|
|
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof( object ), typeof( PropertyItem ), new UIPropertyMetadata( null, OnValueChanged ) );
|
|
public object Value
|
|
{
|
|
get
|
|
{
|
|
return ( object )GetValue( ValueProperty );
|
|
}
|
|
set
|
|
{
|
|
SetValue( ValueProperty, value );
|
|
}
|
|
}
|
|
|
|
private static void OnValueChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
|
|
{
|
|
PropertyItem propertyItem = o as PropertyItem;
|
|
if( propertyItem != null )
|
|
propertyItem.OnValueChanged( ( object )e.OldValue, ( object )e.NewValue );
|
|
}
|
|
|
|
protected virtual void OnValueChanged( object oldValue, object newValue )
|
|
{
|
|
if( IsInitialized )
|
|
{
|
|
PropertyGrid.RaiseEvent( new PropertyValueChangedEventArgs( PropertyGrid.PropertyValueChangedEvent, this, oldValue, newValue ) );
|
|
}
|
|
}
|
|
|
|
#endregion //Value
|
|
|
|
#region ValueSource
|
|
|
|
/// <summary>
|
|
/// Gets the value source.
|
|
/// </summary>
|
|
public BaseValueSource ValueSource
|
|
{
|
|
get
|
|
{
|
|
var dependencyObject = Instance as DependencyObject;
|
|
if( _dpDescriptor != null && dependencyObject != null )
|
|
return DependencyPropertyHelper.GetValueSource( dependencyObject, _dpDescriptor.DependencyProperty ).BaseValueSource;
|
|
|
|
return BaseValueSource.Unknown;
|
|
}
|
|
}
|
|
|
|
#endregion //ValueSource
|
|
|
|
#endregion //Properties
|
|
|
|
#region Constructors
|
|
|
|
static PropertyItem()
|
|
{
|
|
DefaultStyleKeyProperty.OverrideMetadata( typeof( PropertyItem ), new FrameworkPropertyMetadata( typeof( PropertyItem ) ) );
|
|
}
|
|
|
|
public PropertyItem( object instance, PropertyDescriptor property, PropertyGrid propertyGrid, string bindingPath )
|
|
{
|
|
PropertyDescriptor = property;
|
|
PropertyGrid = propertyGrid;
|
|
Instance = instance;
|
|
BindingPath = bindingPath;
|
|
|
|
SetPropertyDescriptorProperties();
|
|
ResolveExpandableObject();
|
|
ResolvePropertyOrder();
|
|
|
|
CommandBindings.Add( new CommandBinding( PropertyItemCommands.ResetValue, ExecuteResetValueCommand, CanExecuteResetValueCommand ) );
|
|
AddHandler( Mouse.PreviewMouseDownEvent, new MouseButtonEventHandler( PropertyItem_PreviewMouseDown ), true );
|
|
}
|
|
|
|
#endregion //Constructors
|
|
|
|
#region Event Handlers
|
|
|
|
void PropertyItem_PreviewMouseDown( object sender, MouseButtonEventArgs e )
|
|
{
|
|
IsSelected = true;
|
|
}
|
|
|
|
#endregion //Event Handlers
|
|
|
|
#region Commands
|
|
|
|
private void ExecuteResetValueCommand( object sender, ExecutedRoutedEventArgs e )
|
|
{
|
|
if( PropertyDescriptor.CanResetValue( Instance ) )
|
|
PropertyDescriptor.ResetValue( Instance );
|
|
|
|
//TODO: notify UI that the ValueSource may have changed to update the icon
|
|
}
|
|
|
|
private void CanExecuteResetValueCommand( object sender, CanExecuteRoutedEventArgs e )
|
|
{
|
|
bool canExecute = false;
|
|
|
|
if( PropertyDescriptor.CanResetValue( Instance ) && !PropertyDescriptor.IsReadOnly )
|
|
{
|
|
canExecute = true;
|
|
}
|
|
|
|
e.CanExecute = canExecute;
|
|
}
|
|
|
|
#endregion //Commands
|
|
|
|
#region Methods
|
|
|
|
private void GetChildProperties()
|
|
{
|
|
if( Value == null )
|
|
return;
|
|
|
|
var propertyItems = new List<PropertyItem>();
|
|
|
|
try
|
|
{
|
|
PropertyDescriptorCollection descriptors = PropertyGridUtilities.GetPropertyDescriptors( Value );
|
|
|
|
foreach( PropertyDescriptor descriptor in descriptors )
|
|
{
|
|
if( descriptor.IsBrowsable )
|
|
propertyItems.Add( PropertyGridUtilities.CreatePropertyItem( descriptor, Instance, PropertyGrid, String.Format( "{0}.{1}", BindingPath, descriptor.Name ), Level + 1 ) );
|
|
}
|
|
}
|
|
catch( Exception )
|
|
{
|
|
//TODO: handle this some how
|
|
}
|
|
|
|
Properties = PropertyGridUtilities.GetAlphabetizedProperties( propertyItems );
|
|
}
|
|
|
|
private void ResolveExpandableObject()
|
|
{
|
|
var attribute = PropertyGridUtilities.GetAttribute<ExpandableObjectAttribute>( PropertyDescriptor );
|
|
if( attribute != null )
|
|
{
|
|
HasChildProperties = true;
|
|
IsReadOnly = true;
|
|
}
|
|
}
|
|
|
|
private void ResolvePropertyOrder()
|
|
{
|
|
var attrs = PropertyDescriptor.Attributes.OfType<PropertyOrderAttribute>();
|
|
if( attrs.Any() )
|
|
PropertyOrder = attrs.First().Order;
|
|
else
|
|
PropertyOrder = 0;
|
|
}
|
|
|
|
private void SetPropertyDescriptorProperties()
|
|
{
|
|
Name = PropertyDescriptor.Name;
|
|
DisplayName = PropertyDescriptor.DisplayName;
|
|
Category = PropertyDescriptor.Category;
|
|
IsReadOnly = PropertyDescriptor.IsReadOnly;
|
|
}
|
|
|
|
#endregion //Methods
|
|
}
|
|
}
|
|
|