All the controls missing in WPF. Over 1 million downloads.
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.

638 lines
16 KiB

/************************************************************************
Extended WPF Toolkit
Copyright (C) 2010-2012 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
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;
using System.Collections.ObjectModel;
using Xceed.Wpf.Toolkit.Core.Utilities;
namespace Xceed.Wpf.Toolkit.PropertyGrid
{
public class PropertyItem : Control, INotifyPropertyChanged, IPropertyParent
{
#region Members
private readonly DependencyPropertyDescriptor _dpDescriptor;
private readonly MarkupObject _markupObject;
private readonly NotifyPropertyChangedHelper _propertyChangedHelper;
private string _displayName;
private string _description;
private string _category;
private int _categoryOrder;
private int _propertyOrder;
#endregion //Members
#region Properties
#region Category
public string Category
{
get
{
return _category;
}
set
{
_propertyChangedHelper.HandleEqualityChanged( () => Category, ref _category, value );
}
}
#endregion //Category
#region CategoryOrder
public int CategoryOrder
{
get
{
return _categoryOrder;
}
set
{
_propertyChangedHelper.HandleEqualityChanged( () => CategoryOrder, ref _categoryOrder, value );
}
}
#endregion // CategoryOrder
#region Description
public string Description
{
get
{
return _description;
}
set
{
_propertyChangedHelper.HandleEqualityChanged( () => Description, ref _description, value );
}
}
#endregion //Description
#region DisplayName
public string DisplayName
{
get
{
return _displayName;
}
set
{
_propertyChangedHelper.HandleEqualityChanged( () => DisplayName, ref _displayName, 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
public object Instance
{
get;
private set;
}
#endregion //Instance
#region IsDataBound
/// <summary>
/// Gets if the property is data bound
/// </summary>
public bool IsDataBound
{
get
{
var dependencyObject = PropertyParent.ValueInstance 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.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
{
return PropertyDescriptor.IsReadOnly;
}
}
#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 )
{
this.RaiseItemSelectionChangedEvent();
}
#endregion //IsSelected
#region Level
public int Level
{
get;
private set;
}
#endregion //Level
#region Properties
public PropertyItemCollection Properties
{
get;
private set;
}
#endregion //Properties
#region PropertyDescriptor
public PropertyDescriptor PropertyDescriptor
{
get;
private set;
}
#endregion //PropertyDescriptor
#region PropertyParent
internal IPropertyParent PropertyParent
{
get;
private set;
}
#endregion
#region PropertyOrder
public int PropertyOrder
{
get
{
return _propertyOrder;
}
set
{
_propertyChangedHelper.HandleEqualityChanged( () => PropertyOrder, ref _propertyOrder, value );
}
}
#endregion
#region PropertyType
public Type PropertyType
{
get
{
return PropertyDescriptor.PropertyType;
}
}
#endregion //PropertyType
#region ResetValueCommand
public ICommand ResetValueCommand
{
get;
private set;
}
#endregion
#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 )
{
RaiseEvent( new PropertyValueChangedEventArgs( PropertyGrid.PropertyValueChangedEvent, this, oldValue, newValue ) );
}
}
#endregion //Value
#region ValueSource
/// <summary>
/// Gets the value source.
/// </summary>
public BaseValueSource ValueSource
{
get
{
var dependencyObject = PropertyParent.ValueInstance as DependencyObject;
if( _dpDescriptor != null && dependencyObject != null )
return DependencyPropertyHelper.GetValueSource( dependencyObject, _dpDescriptor.DependencyProperty ).BaseValueSource;
return BaseValueSource.Unknown;
}
}
#endregion //ValueSource
#endregion //Properties
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#region ItemSelectionChanged
internal static readonly RoutedEvent ItemSelectionChangedEvent = EventManager.RegisterRoutedEvent(
"ItemSelectedEvent", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( PropertyItem ) );
// This method raises the Tap event
private void RaiseItemSelectionChangedEvent()
{
RaiseEvent( new RoutedEventArgs( PropertyItem.ItemSelectionChangedEvent ) );
}
#endregion
#endregion
#region Constructors
static PropertyItem()
{
DefaultStyleKeyProperty.OverrideMetadata( typeof( PropertyItem ), new FrameworkPropertyMetadata( typeof( PropertyItem ) ) );
}
internal PropertyItem( PropertyDescriptor property, IPropertyParent propertyParent, int level )
{
_propertyChangedHelper = new NotifyPropertyChangedHelper( this, RaisePropertyChanged );
Properties = new PropertyItemCollection( new ObservableCollection<PropertyItem>() );
PropertyParent = propertyParent;
PropertyDescriptor = property;
Level = level;
Name = PropertyDescriptor.Name;
Category = PropertyDescriptor.Category;
CategoryOrder = 0;
Description = ResolveDescription();
DisplayName = ResolveDisplayName();
HasChildProperties = ResolveExpandableObject();
PropertyOrder = ResolvePropertyOrder();
_dpDescriptor = DependencyPropertyDescriptor.FromProperty( PropertyDescriptor );
_markupObject = MarkupWriter.GetMarkupObjectFor( PropertyParent.ValueInstance );
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( PropertyParent.ValueInstance ) )
PropertyDescriptor.ResetValue( PropertyParent.ValueInstance );
//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( PropertyParent.ValueInstance ) && !PropertyDescriptor.IsReadOnly )
{
canExecute = true;
}
e.CanExecute = canExecute;
}
#endregion //Commands
#region Methods
private void RaisePropertyChanged( string propertyName )
{
if( this.PropertyChanged != null )
{
this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
}
}
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 )
{
PropertyItem childPropertyItem = PropertyGridUtilities.CreatePropertyItem( descriptor, this, Level + 1 );
propertyItems.Add( childPropertyItem );
}
}
}
catch( Exception )
{
//TODO: handle this some how
}
Properties.Update( propertyItems, false, null );
}
private string ResolveDescription()
{
//We do not simply rely on the "Description" property of PropertyDescriptor
//since this value is cached bye PropertyDescriptor and the localized version
//(eg. LocalizedDescriptionAttribute) value can dynamicaly change
DescriptionAttribute descriptionAtt = GetAttribute<DescriptionAttribute>();
return ( descriptionAtt != null )
? descriptionAtt.Description
: PropertyDescriptor.Description;
}
private string ResolveDisplayName()
{
string displayName = PropertyDescriptor.DisplayName;
var attribute = GetAttribute<ParenthesizePropertyNameAttribute>();
if( (attribute != null) && attribute.NeedParenthesis )
{
displayName = "(" + displayName + ")";
}
return displayName;
}
private bool ResolveExpandableObject()
{
bool isExpandable = false;
var attribute = GetAttribute<ExpandableObjectAttribute>();
if( attribute != null )
{
isExpandable = true;
}
return isExpandable;
}
private int ResolvePropertyOrder()
{
var attribute = GetAttribute<PropertyOrderAttribute>();
// Max Value. Properties with no order will be displayed last.
return ( attribute != null )
? attribute.Order
: int.MaxValue;
}
private T GetAttribute<T>() where T : Attribute
{
return PropertyGridUtilities.GetAttribute<T>( PropertyDescriptor );
}
#endregion //Methods
#region Interfaces
#region IPropertyParent Members
bool IPropertyParent.IsReadOnly
{
get { return PropertyParent.IsReadOnly; }
}
object IPropertyParent.ValueInstance
{
get { return Value; }
}
EditorDefinitionCollection IPropertyParent.EditorDefinitions
{
get { return PropertyParent.EditorDefinitions; }
}
#endregion
#endregion
}
}