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 Microsoft.Windows.Controls.PropertyGrid.Commands; using Microsoft.Windows.Controls.PropertyGrid.Attributes; namespace Microsoft.Windows.Controls.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 /// /// Gets if the property is data bound /// 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; } #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) { // TODO: Add your property changed side-effects. Descendants can override as well. } #endregion //Value #region ValueSource /// /// Gets the value source. /// 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(); 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(); 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 ex) { //TODO: handle this some how } Properties = PropertyGridUtilities.GetAlphabetizedProperties(propertyItems); } private void ResolveExpandableObject() { var attribute = PropertyGridUtilities.GetAttribute(PropertyDescriptor); if (attribute != null) { HasChildProperties = true; IsReadOnly = true; } } private void SetPropertyDescriptorProperties() { Name = PropertyDescriptor.Name; DisplayName = PropertyDescriptor.DisplayName; Category = PropertyDescriptor.Category; IsReadOnly = PropertyDescriptor.IsReadOnly; } #endregion //Methods } }