From 4cb3045791b98f12104b28027284256b3d4857d8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 2 Jan 2007 12:16:17 +0000 Subject: [PATCH] WPF PropertyEditor git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2254 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../DependencyPropertyDotButton.cs | 96 ++++++++++++--- .../Controls/PropertyEditor/PropertyEditor.cs | 20 +++- .../PropertyEditor/PropertyGridView.cs | 30 +++-- .../PropertyEditor/PropertyNameTextBlock.cs | 65 ++++++++++ .../Controls/TypeEditors/BrushEditor.cs | 35 ++++++ .../Project/Controls/WindowClone.cs | 4 +- .../Project/WpfDesign.Designer.csproj | 3 + .../Project/Xaml/XamlDesignContext.cs | 5 + .../Project/Xaml/XamlDesignItem.cs | 19 ++- .../Project/Xaml/XamlModelProperty.cs | 73 ++++++++++-- .../WpfDesign.XamlDom/Project/XamlParser.cs | 6 +- .../WpfDesign.XamlDom/Project/XamlProperty.cs | 53 ++++++++- .../Project/XamlPropertyInfo.cs | 51 +++++--- .../WpfDesign/Project/DesignItemProperty.cs | 31 ++++- .../Project/PropertyEditor/BooleanEditor.cs | 30 +++++ .../PropertyEditor/DesignItemDataProperty.cs | 42 +++++++ .../PropertyEditor/DesignItemDataSource.cs | 6 + .../Project/PropertyEditor/EditorManager.cs | 111 ++++++++++++++++++ .../Project/PropertyEditor/EnumEditor.cs | 31 +++++ .../Project/PropertyEditor/FallbackEditor.cs | 50 ++++++++ .../IPropertyEditorDataSource.cs | 37 ++++++ .../PropertyEditor/PropertyEditorAttribute.cs | 50 ++++++++ .../PropertyEditorBindingHelper.cs | 79 +++++++++++++ .../Project/PropertyEditor/TextBoxEditor.cs | 54 +++++++++ .../PropertyEditor/TypeEditorAttribute.cs | 39 ++++++ .../WpfDesign/Project/WpfDesign.csproj | 8 ++ 26 files changed, 962 insertions(+), 66 deletions(-) create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyNameTextBlock.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/TypeEditors/BrushEditor.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/BooleanEditor.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EditorManager.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EnumEditor.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/FallbackEditor.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorAttribute.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorBindingHelper.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs create mode 100644 src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TypeEditorAttribute.cs diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/DependencyPropertyDotButton.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/DependencyPropertyDotButton.cs index 0ae05116ee..3d2bb659fc 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/DependencyPropertyDotButton.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/DependencyPropertyDotButton.cs @@ -6,6 +6,8 @@ // using System; +using System.ComponentModel; +using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; @@ -18,14 +20,6 @@ namespace ICSharpCode.WpfDesign.Designer.Controls /// public class DependencyPropertyDotButton : ButtonBase { - /* - /// - /// Dependency property for . - /// - public static readonly DependencyProperty DataPropertyProperty - = DependencyProperty.Register("DataProperty", typeof(IPropertyEditorDataProperty), typeof(DependencyPropertyDotButton)); - */ - /// /// Dependency property for . /// @@ -33,21 +27,56 @@ namespace ICSharpCode.WpfDesign.Designer.Controls = DependencyProperty.Register("Checked", typeof(bool), typeof(DependencyPropertyDotButton), new FrameworkPropertyMetadata(false)); - static DependencyPropertyDotButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DependencyPropertyDotButton), new FrameworkPropertyMetadata(typeof(DependencyPropertyDotButton))); } - /* /// - /// Gets/Sets the property the button is used for. + /// Creates a new DependencyPropertyDotButton instance. + /// + public DependencyPropertyDotButton() + { + } + + IPropertyEditorDataProperty property; + + bool isIsSetChangedEventHandlerAttached; + + /// + /// Creates a new DependencyPropertyDotButton instance that binds its Checked property to the + /// data properties IsSet property. + /// + public DependencyPropertyDotButton(IPropertyEditorDataProperty property) + { + if (property == null) + throw new ArgumentNullException("property"); + this.property = property; + + this.Loaded += delegate { + if (!isIsSetChangedEventHandlerAttached) { + isIsSetChangedEventHandlerAttached = true; + this.property.IsSetChanged += OnIsSetChanged; + OnIsSetChanged(null, null); + } + }; + this.Unloaded += delegate { + if (isIsSetChangedEventHandlerAttached) { + isIsSetChangedEventHandlerAttached = false; + this.property.IsSetChanged -= OnIsSetChanged; + } + }; + OnIsSetChanged(null, null); + } + + /// + /// Creates the context menu on-demand. /// - public IPropertyEditorDataProperty DataProperty { - get { return (IPropertyEditorDataProperty)GetValue(DataPropertyProperty); } - set { SetValue(DataPropertyProperty, value); } + protected override void OnContextMenuOpening(ContextMenuEventArgs e) + { + ContextMenu = CreateContextMenu(); + base.OnContextMenuOpening(e); } - */ /// /// Gets/Sets if the button looks checked. @@ -57,15 +86,48 @@ namespace ICSharpCode.WpfDesign.Designer.Controls set { SetValue(CheckedProperty, value); } } + void OnIsSetChanged(object sender, EventArgs e) + { + this.Checked = property.IsSet; + } + /// /// Fires the Click event and opens the context menu. /// protected override void OnClick() { base.OnClick(); - if (ContextMenu != null) { - ContextMenu.IsOpen = true; + ContextMenu = CreateContextMenu(); + ContextMenu.IsOpen = true; + } + + internal ContextMenu CreateContextMenu() + { + ContextMenu contextMenu = new ContextMenu(); + if (property.IsSet) { + contextMenu.Items.Add(CreateMenuItem("_Reset", OnResetClick)); + } else { + contextMenu.Items.Add(CreateMenuItem("_Copy to local", OnCopyToLocalClick)); } + return contextMenu; + } + + MenuItem CreateMenuItem(string title, RoutedEventHandler handler) + { + MenuItem item = new MenuItem(); + item.Header = title; + item.Click += handler; + return item; + } + + void OnResetClick(object sender, RoutedEventArgs e) + { + property.IsSet = false; + } + + void OnCopyToLocalClick(object sender, RoutedEventArgs e) + { + property.IsSet = true; } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyEditor.cs index 40a34399cd..4aa400da03 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyEditor.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyEditor.cs @@ -51,10 +51,6 @@ namespace ICSharpCode.WpfDesign.Designer Debug.WriteLine(ex.ToString()); throw; } - PropertyEditorCategoryView v = new PropertyEditorCategoryView(); - v.Header = "Titel"; - v.Content = "Inhalt"; - contentStackPanel.Children.Add(v); } /// @@ -66,7 +62,7 @@ namespace ICSharpCode.WpfDesign.Designer } /// - /// Is raised when the object being edited changes. + /// Is raised when the value of the property changes. /// public event EventHandler EditedObjectChanged; @@ -101,6 +97,9 @@ namespace ICSharpCode.WpfDesign.Designer List categories = new List(); foreach (IPropertyEditorDataProperty p in Linq.Sort(dataSource.Properties, ComparePropertyNames)) { + if (p.Name == "Name") { + continue; + } PropertyEditorCategoryView cv = GetOrCreateCategory(categories, p.Category); PropertyGridView grid = (PropertyGridView)cv.Content; grid.AddProperty(p); @@ -120,7 +119,9 @@ namespace ICSharpCode.WpfDesign.Designer return p1.Name.CompareTo(p2.Name); } - static PropertyEditorCategoryView GetOrCreateCategory(List categories, string category) + HashSet expandedCategories = new HashSet(); + + PropertyEditorCategoryView GetOrCreateCategory(List categories, string category) { foreach (PropertyEditorCategoryView c in categories) { if (c.Header.ToString() == category) @@ -129,6 +130,13 @@ namespace ICSharpCode.WpfDesign.Designer PropertyEditorCategoryView newCategory = new PropertyEditorCategoryView(); newCategory.Header = category; newCategory.Content = new PropertyGridView(); + newCategory.IsExpanded = expandedCategories.Contains(category); + newCategory.Expanded += delegate { + expandedCategories.Add(category); + }; + newCategory.Collapsed += delegate { + expandedCategories.Remove(category); + }; categories.Add(newCategory); return newCategory; } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyGridView.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyGridView.cs index 74054656e8..e97b852f02 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyGridView.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyGridView.cs @@ -7,6 +7,7 @@ using System; using System.Windows; +using System.Windows.Documents; using System.Windows.Data; using System.Windows.Controls; using ICSharpCode.WpfDesign.PropertyEditor; @@ -31,9 +32,9 @@ namespace ICSharpCode.WpfDesign.Designer.Controls this.ColumnDefinitions.Add(new ColumnDefinition()); this.ColumnDefinitions.Add(new ColumnDefinition()); this.ColumnDefinitions.Add(new ColumnDefinition()); - this.ColumnDefinitions[0].Width = new GridLength(0.45, GridUnitType.Star); + this.ColumnDefinitions[0].Width = new GridLength(0.48, GridUnitType.Star); this.ColumnDefinitions[0].MinWidth = 40; - this.ColumnDefinitions[1].Width = new GridLength(0.55, GridUnitType.Star); + this.ColumnDefinitions[1].Width = new GridLength(0.52, GridUnitType.Star); this.ColumnDefinitions[2].Width = new GridLength(16); } @@ -44,22 +45,27 @@ namespace ICSharpCode.WpfDesign.Designer.Controls { this.RowDefinitions.Add(new RowDefinition()); - Label propertyNameLabel = new Label(); - propertyNameLabel.Content = property.Name; - propertyNameLabel.HorizontalContentAlignment = HorizontalAlignment.Right; - SetRow(propertyNameLabel, this.RowDefinitions.Count - 1); - SetColumn(propertyNameLabel, 0); - this.Children.Add(propertyNameLabel); + // Column 0: name of the property + PropertyNameTextBlock propertyNameText = new PropertyNameTextBlock(property); + propertyNameText.Margin = new Thickness(0, 0, 2, 0); + SetRow(propertyNameText, this.RowDefinitions.Count - 1); + SetColumn(propertyNameText, 0); + this.Children.Add(propertyNameText); - DependencyPropertyDotButton dotButton = new DependencyPropertyDotButton(); + // Column 1: the actual property editor + UIElement editor = property.CreateEditor(); + SetRow(editor, this.RowDefinitions.Count - 1); + SetColumn(editor, 1); + this.Children.Add(editor); + + // Column 2: a "dot" button + DependencyPropertyDotButton dotButton = new DependencyPropertyDotButton(property); dotButton.VerticalAlignment = VerticalAlignment.Center; dotButton.HorizontalAlignment = HorizontalAlignment.Center; - Binding binding = new Binding("IsSet"); - binding.Source = property; - dotButton.SetBinding(DependencyPropertyDotButton.CheckedProperty, binding); SetRow(dotButton, this.RowDefinitions.Count - 1); SetColumn(dotButton, 2); this.Children.Add(dotButton); + propertyNameText.ContextMenuProvider = dotButton; } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyNameTextBlock.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyNameTextBlock.cs new file mode 100644 index 0000000000..a6b36523cb --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/PropertyEditor/PropertyNameTextBlock.cs @@ -0,0 +1,65 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using ICSharpCode.WpfDesign.PropertyEditor; + +namespace ICSharpCode.WpfDesign.Designer.Controls +{ + // Text block used in the first column of the PropertyGridView. + // Creates ToolTip and ContextMenu objects on-demand. + sealed class PropertyNameTextBlock : TextBlock + { + readonly IPropertyEditorDataProperty property; + readonly TextBlock toolTipTextBlock; + bool toolTipTextBlockInitialized; + internal DependencyPropertyDotButton ContextMenuProvider; + + public PropertyNameTextBlock(IPropertyEditorDataProperty property) + : base(new Run(property.Name)) + { + this.property = property; + this.TextAlignment = TextAlignment.Right; + this.TextTrimming = TextTrimming.CharacterEllipsis; + + this.ToolTip = toolTipTextBlock = new TextBlock(); + } + + protected override void OnToolTipOpening(ToolTipEventArgs e) + { + CreateToolTip(); + base.OnToolTipOpening(e); + } + + protected override void OnContextMenuOpening(ContextMenuEventArgs e) + { + if (ContextMenuProvider != null) { + this.ContextMenu = ContextMenuProvider.CreateContextMenu(); + } + base.OnContextMenuOpening(e); + } + + void CreateToolTip() + { + if (toolTipTextBlockInitialized) + return; + toolTipTextBlockInitialized = true; + toolTipTextBlock.TextAlignment = TextAlignment.Left; + toolTipTextBlock.Inlines.Add(new Bold(new Run(property.Name))); + if (property.ReturnType != null) { + toolTipTextBlock.Inlines.Add(" (" + property.ReturnType.Name + ")"); + } + if (!string.IsNullOrEmpty(property.Description)) { + toolTipTextBlock.Inlines.Add(new LineBreak()); + toolTipTextBlock.Inlines.Add(property.Description); + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/TypeEditors/BrushEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/TypeEditors/BrushEditor.cs new file mode 100644 index 0000000000..0ff5e90265 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/TypeEditors/BrushEditor.cs @@ -0,0 +1,35 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Data; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Media; +using ICSharpCode.WpfDesign.PropertyEditor; + +namespace ICSharpCode.WpfDesign.Designer.Controls.TypeEditors +{ + /// + /// Type editor used to edit Brush properties. + /// + [TypeEditor(typeof(Brush))] + public sealed class BrushEditor : Border + { + /// + /// Creates a new BooleanEditor instance. + /// + public BrushEditor(IPropertyEditorDataProperty property) + { + this.BorderBrush = Brushes.Black; + this.BorderThickness = new Thickness(1); + SetBinding(BackgroundProperty, PropertyEditorBindingHelper.CreateBinding(this, property)); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/WindowClone.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/WindowClone.cs index aee96b4f70..690710080e 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/WindowClone.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Controls/WindowClone.cs @@ -119,7 +119,8 @@ namespace ICSharpCode.WpfDesign.Designer.Controls } /// - /// A for . + /// A for + /// (and derived classes, unless they specify their own ). /// [ExtensionFor(typeof(Window))] public class WindowCloneExtension : CustomInstanceFactory @@ -130,7 +131,6 @@ namespace ICSharpCode.WpfDesign.Designer.Controls public override object CreateInstance(Type type, params object[] arguments) { Debug.Assert(arguments.Length == 0); - Debug.Assert(type == typeof(Window)); return new WindowClone(); } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj index b2e2069ac6..f010cc2f57 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/WpfDesign.Designer.csproj @@ -62,8 +62,10 @@ + + @@ -86,6 +88,7 @@ + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs index fb8fe68835..c4f1151bdd 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignContext.cs @@ -10,6 +10,7 @@ using System.Xml; using ICSharpCode.WpfDesign.XamlDom; using ICSharpCode.WpfDesign.Designer.Services; using ICSharpCode.WpfDesign.Extensions; +using ICSharpCode.WpfDesign.PropertyEditor; namespace ICSharpCode.WpfDesign.Designer.Xaml { @@ -42,8 +43,12 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml _componentService = new XamlComponentService(this); this.Services.AddService(typeof(IComponentService), _componentService); + EditorManager propertyGridEditorManager = new EditorManager(); + this.Services.AddService(typeof(EditorManager), propertyGridEditorManager); + // register extensions from this assembly: this.Services.ExtensionManager.RegisterAssembly(typeof(XamlDesignContext).Assembly); + propertyGridEditorManager.RegisterAssembly(typeof(XamlDesignContext).Assembly); XamlParserSettings xamlParseSettings = new XamlParserSettings(); xamlParseSettings.CreateInstanceCallback = OnXamlParserCreateInstance; diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs index 4890a7e73d..28f17f218f 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlDesignItem.cs @@ -5,6 +5,9 @@ // $Revision$ // +// enable this define to test that event handlers are removed correctly +//#define EventHandlerDebugging + using System; using System.Diagnostics; using System.Windows; @@ -48,12 +51,24 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml set { throw new NotImplementedException(); } } + #if EventHandlerDebugging + static int totalEventHandlerCount; + #endif + /// /// Is raised when the name of the design item changes. /// public override event EventHandler NameChanged { - add { Debug.WriteLine("Add event handler to " + this.Component.GetType().Name); } - remove { Debug.WriteLine("Remove event handler from " + this.Component.GetType().Name); } + add { + #if EventHandlerDebugging + Debug.WriteLine("Add event handler to " + this.Component.GetType().Name + " (handler count=" + (++totalEventHandlerCount) + ")"); + #endif + } + remove { + #if EventHandlerDebugging + Debug.WriteLine("Remove event handler from " + this.Component.GetType().Name + " (handler count=" + (--totalEventHandlerCount) + ")"); + #endif + } } public override DesignItem Parent { diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs index 639be84160..7b5ff8baf9 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.Designer/Project/Xaml/XamlModelProperty.cs @@ -5,6 +5,9 @@ // $Revision$ // +// Turn this on to ensure event handlers on model properties are removed correctly: +#define EventHandlerDebugging + using System; using System.Diagnostics; using ICSharpCode.WpfDesign.XamlDom; @@ -48,6 +51,18 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml get { return _property.IsCollection; } } + public override Type ReturnType { + get { return _property.ReturnType; } + } + + public override Type DeclaringType { + get { return _property.PropertyTargetType; } + } + + public override System.ComponentModel.TypeConverter TypeConverter { + get { return _property.TypeConverter; } + } + public override System.Collections.Generic.IList CollectionElements { get { throw new NotImplementedException(); @@ -69,23 +84,65 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml } } - public override object ValueOnInstance { - get { - return _property.ValueOnInstance; + public override event EventHandler ValueChanged { + add { + #if EventHandlerDebugging + if (ValueChangedEventHandlers == 0) { + Debug.WriteLine("ValueChangedEventHandlers is now > 0"); + } + ValueChangedEventHandlers++; + #endif + _property.ValueChanged += value; } - set { - _property.ValueOnInstance = value; + remove { + #if EventHandlerDebugging + ValueChangedEventHandlers--; + if (ValueChangedEventHandlers == 0) { + Debug.WriteLine("ValueChangedEventHandlers reached 0"); + } + #endif + _property.ValueChanged -= value; } } + public override object ValueOnInstance { + get { return _property.ValueOnInstance; } + set { _property.ValueOnInstance = value; } + } + public override bool IsSet { - get { - return _property.IsSet; + get { return _property.IsSet; } + } + + #if EventHandlerDebugging + static int IsSetChangedEventHandlers, ValueChangedEventHandlers; + #endif + + public override event EventHandler IsSetChanged { + add { + #if EventHandlerDebugging + if (IsSetChangedEventHandlers == 0) { + Debug.WriteLine("IsSetChangedEventHandlers is now > 0"); + } + IsSetChangedEventHandlers++; + #endif + _property.IsSetChanged += value; + } + remove { + #if EventHandlerDebugging + IsSetChangedEventHandlers--; + if (IsSetChangedEventHandlers == 0) { + Debug.WriteLine("IsSetChangedEventHandlers reached 0"); + } + #endif + _property.IsSetChanged -= value; } } public override void SetValue(object value) { + _property.ValueOnInstance = value; + XamlComponentService componentService = _designItem.ComponentService; XamlDesignItem designItem = (XamlDesignItem)componentService.GetDesignItem(value); @@ -98,8 +155,6 @@ namespace ICSharpCode.WpfDesign.Designer.Xaml designItem = componentService.RegisterXamlComponentRecursive(val as XamlObject); _property.PropertyValue = val; } - - _property.ValueOnInstance = value; } public override void Reset() diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs index bd4f8df977..303f78024b 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlParser.cs @@ -10,6 +10,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; +using System.Windows; using System.Windows.Markup; using System.Xml; @@ -280,7 +281,10 @@ namespace ICSharpCode.WpfDesign.XamlDom MethodInfo getMethod = elementType.GetMethod("Get" + propertyName, BindingFlags.Public | BindingFlags.Static); MethodInfo setMethod = elementType.GetMethod("Set" + propertyName, BindingFlags.Public | BindingFlags.Static); if (getMethod != null && setMethod != null) { - return new XamlAttachedPropertyInfo(getMethod, setMethod, propertyName); + FieldInfo field = elementType.GetField(propertyName + "Property", BindingFlags.Public | BindingFlags.Static); + if (field != null && field.FieldType == typeof(DependencyProperty)) { + return new XamlDependencyPropertyInfo((DependencyProperty)field.GetValue(null), true); + } } return null; } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs index 51bbf6fbbe..840346dab5 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlProperty.cs @@ -73,6 +73,20 @@ namespace ICSharpCode.WpfDesign.XamlDom get { return propertyInfo.TargetType; } } + /// + /// Gets the return type of the property. + /// + public Type ReturnType { + get { return propertyInfo.ReturnType; } + } + + /// + /// Gets the type converter used to convert property values to/from string. + /// + public TypeConverter TypeConverter { + get { return propertyInfo.TypeConverter; } + } + /// /// Gets the value of the property. Can be null if the property is a collection property. /// @@ -82,10 +96,21 @@ namespace ICSharpCode.WpfDesign.XamlDom if (IsCollection) throw new InvalidOperationException(); - Reset(); + bool wasSet = this.IsSet; + + ResetInternal(); propertyValue = value; propertyValue.AddNodeTo(this); propertyValue.ParentProperty = this; + + if (!wasSet) { + if (IsSetChanged != null) { + IsSetChanged(this, EventArgs.Empty); + } + } + if (ValueChanged != null) { + ValueChanged(this, EventArgs.Empty); + } } } @@ -129,10 +154,36 @@ namespace ICSharpCode.WpfDesign.XamlDom get { return propertyValue != null || collectionElements != null; } } + /// + /// Occurs when the value of the IsSet property has changed. + /// + public event EventHandler IsSetChanged; + + /// + /// Occurs when the value of the property has changed. + /// + public event EventHandler ValueChanged; + /// /// Resets the properties value. /// public void Reset() + { + if (IsSet) { + propertyInfo.ResetValue(parentObject.Instance); + + ResetInternal(); + + if (IsSetChanged != null) { + IsSetChanged(this, EventArgs.Empty); + } + if (ValueChanged != null) { + ValueChanged(this, EventArgs.Empty); + } + } + } + + void ResetInternal() { if (propertyValue != null) { propertyValue.RemoveNodeFromParent(); diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs index be53553c26..b2957b95bf 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign.XamlDom/Project/XamlPropertyInfo.cs @@ -6,10 +6,12 @@ // using System; +using System.Diagnostics; using System.Collections; using System.ComponentModel; using System.Globalization; using System.Reflection; +using System.Windows; using System.Windows.Markup; namespace ICSharpCode.WpfDesign.XamlDom @@ -22,8 +24,10 @@ namespace ICSharpCode.WpfDesign.XamlDom { public abstract object GetValue(object instance); public abstract void SetValue(object instance, object value); + public abstract void ResetValue(object instance); public abstract TypeConverter TypeConverter { get; } public abstract Type TargetType { get; } + public abstract Type ReturnType { get; } public abstract string Name { get; } public abstract string FullyQualifiedName { get; } public abstract bool IsAttached { get; } @@ -31,41 +35,44 @@ namespace ICSharpCode.WpfDesign.XamlDom internal abstract void AddValue(object collectionInstance, XamlPropertyValue newElement); } - internal sealed class XamlAttachedPropertyInfo : XamlPropertyInfo + internal class XamlDependencyPropertyInfo : XamlPropertyInfo { - MethodInfo _getMethod; - MethodInfo _setMethod; - string _name; + readonly DependencyProperty property; + readonly bool isAttached; - public XamlAttachedPropertyInfo(MethodInfo getMethod, MethodInfo setMethod, string name) + public XamlDependencyPropertyInfo(DependencyProperty property, bool isAttached) { - this._getMethod = getMethod; - this._setMethod = setMethod; - this._name = name; + Debug.Assert(property != null); + this.property = property; + this.isAttached = isAttached; } public override TypeConverter TypeConverter { get { - return TypeDescriptor.GetConverter(_getMethod.ReturnType); + return TypeDescriptor.GetConverter(this.ReturnType); } } public override string FullyQualifiedName { get { - return _getMethod.DeclaringType.FullName + "." + _name; + return this.TargetType.FullName + "." + this.Name; } } public override Type TargetType { - get { return _getMethod.DeclaringType; } + get { return property.OwnerType; } + } + + public override Type ReturnType { + get { return property.PropertyType; } } public override string Name { - get { return _name; } + get { return property.Name; } } public override bool IsAttached { - get { return true; } + get { return isAttached; } } public override bool IsCollection { @@ -74,12 +81,17 @@ namespace ICSharpCode.WpfDesign.XamlDom public override object GetValue(object instance) { - return _getMethod.Invoke(null, new object[] { instance }); + return ((DependencyObject)instance).GetValue(property); } public override void SetValue(object instance, object value) { - _setMethod.Invoke(null, new object[] { instance, value }); + ((DependencyObject)instance).SetValue(property, value); + } + + public override void ResetValue(object instance) + { + ((DependencyObject)instance).ClearValue(property); } internal override void AddValue(object collectionInstance, XamlPropertyValue newElement) @@ -107,6 +119,15 @@ namespace ICSharpCode.WpfDesign.XamlDom _propertyDescriptor.SetValue(instance, value); } + public override void ResetValue(object instance) + { + _propertyDescriptor.ResetValue(instance); + } + + public override Type ReturnType { + get { return _propertyDescriptor.PropertyType; } + } + public override Type TargetType { get { return _propertyDescriptor.ComponentType; } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs index b6f741bf9e..ad2f06bb70 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/DesignItemProperty.cs @@ -8,6 +8,7 @@ using System; using System.Collections.ObjectModel; using System.Collections.Generic; +using System.ComponentModel; using System.Windows; namespace ICSharpCode.WpfDesign @@ -25,6 +26,23 @@ namespace ICSharpCode.WpfDesign /// public abstract string Name { get; } + /// + /// Gets the return type of the property. + /// + public abstract Type ReturnType { get; } + + /// + /// Gets the type that declares the property. + /// + public abstract Type DeclaringType { get; } + + /// + /// Gets the type converter used to convert property values to/from string. + /// + public virtual TypeConverter TypeConverter { + get { return TypeDescriptor.GetConverter(this.ReturnType); } + } + /// /// Gets if the property represents a collection. /// @@ -36,10 +54,16 @@ namespace ICSharpCode.WpfDesign public abstract IList CollectionElements { get; } /// - /// Gets the value of the property. This property returns null if the value is not set. + /// Gets the value of the property. This property returns null if the value is not set, + /// or if the value is set to a primitive value. /// public abstract DesignItem Value { get; } + /// + /// Is raised when the value of the property changes (by calling or ). + /// + public abstract event EventHandler ValueChanged; + /// /// Gets/Sets the value of the property on the designed instance. /// If the property is not set, this returns the default value. @@ -57,6 +81,11 @@ namespace ICSharpCode.WpfDesign /// public abstract bool IsSet { get; } + /// + /// Occurs when the value of the IsSet property changes. + /// + public abstract event EventHandler IsSetChanged; + /// /// Resets the property value to the default, possibly removing it from the list of properties. /// diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/BooleanEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/BooleanEditor.cs new file mode 100644 index 0000000000..93b6f1c75e --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/BooleanEditor.cs @@ -0,0 +1,30 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Data; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Type editor used to edit bool properties. + /// + sealed class BooleanEditor : CheckBox + { + /// + /// Creates a new BooleanEditor instance. + /// + public BooleanEditor(IPropertyEditorDataProperty property) + { + SetBinding(IsCheckedProperty, PropertyEditorBindingHelper.CreateBinding(this, property)); + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataProperty.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataProperty.cs index 62d8f17ef7..015401c4f2 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataProperty.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataProperty.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; +using System.Windows; namespace ICSharpCode.WpfDesign.PropertyEditor { @@ -43,6 +44,10 @@ namespace ICSharpCode.WpfDesign.PropertyEditor get { return "Description for " + property.Name; } } + public System.ComponentModel.TypeConverter TypeConverter { + get { return property.TypeConverter; } + } + public bool IsSet { get { return property.IsSet; @@ -59,6 +64,11 @@ namespace ICSharpCode.WpfDesign.PropertyEditor } } + public event EventHandler IsSetChanged { + add { property.IsSetChanged += value; } + remove { property.IsSetChanged -= value; } + } + public object Value { get { return property.ValueOnInstance; @@ -68,6 +78,25 @@ namespace ICSharpCode.WpfDesign.PropertyEditor } } + public event EventHandler ValueChanged { + add { property.ValueChanged += value; } + remove { property.ValueChanged -= value; } + } + + /// + /// Gets the type of the property value. + /// + public Type ReturnType { + get { return property.ReturnType; } + } + + /// + /// Gets the type that declares the property. + /// + public Type DeclaringType { + get { return property.DeclaringType; } + } + public bool CanUseCustomExpression { get { return true; @@ -78,5 +107,18 @@ namespace ICSharpCode.WpfDesign.PropertyEditor { throw new NotImplementedException(); } + + /// + /// Creates a UIElement that can edit the property value. + /// + public UIElement CreateEditor() + { + EditorManager manager = ownerDataSource.DesignItem.Services.GetService(); + if (manager != null) { + return manager.CreateEditor(this); + } else { + return new FallbackEditor(this); + } + } } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataSource.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataSource.cs index 791dbba2b9..a3e198e1f2 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataSource.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/DesignItemDataSource.cs @@ -54,6 +54,12 @@ namespace ICSharpCode.WpfDesign.PropertyEditor return item.GetBehavior() ?? new DesignItemDataSource(item); } + /// + /// Gets the design item for which this DesignItemDataSource was created. + /// + public DesignItem DesignItem { + get { return item; } + } /// See public string Name { diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EditorManager.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EditorManager.cs new file mode 100644 index 0000000000..a686c2ce67 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EditorManager.cs @@ -0,0 +1,111 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.Collections.Generic; +using System.Reflection; +using System.Windows; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Manages registered type and property editors. + /// + public sealed class EditorManager + { + // property return type => editor type + Dictionary _typeEditors = new Dictionary(); + // property full name => editor type + Dictionary _propertyEditors = new Dictionary(); + + /// + /// Creates an editor for the specified property. + /// + public UIElement CreateEditor(IPropertyEditorDataProperty property) + { + return (UIElement)Activator.CreateInstance(GetEditorType(property), property); + } + + /// + /// Creates the fallback editor for the specified property. + /// + public static UIElement CreateFallbackEditor(IPropertyEditorDataProperty property) + { + return (UIElement)Activator.CreateInstance(GetFallbackEditorType(property), property); + } + + /// + /// Gets the type of the editor that can edit the specified property. + /// + public Type GetEditorType(IPropertyEditorDataProperty property) + { + if (property == null) + throw new ArgumentNullException("property"); + + Type editorType; + if (_propertyEditors.TryGetValue(property.DeclaringType.FullName + "." + property.Name, out editorType)) + return editorType; + else if (_typeEditors.TryGetValue(property.ReturnType, out editorType)) + return editorType; + else + return GetFallbackEditorType(property); + } + + /// + /// Gets the type of the fallback editor used for the specified property. + /// + public static Type GetFallbackEditorType(IPropertyEditorDataProperty property) + { + Type returnType = property.ReturnType; + if (returnType.IsEnum) { + return typeof(EnumEditor); + } else if (returnType == typeof(bool)) { + return typeof(BooleanEditor); + } else { + TypeConverter c = property.TypeConverter; + if (c != null && c.CanConvertFrom(typeof(string)) && c.CanConvertTo(typeof(string))) + return typeof(TextBoxEditor); + else + return typeof(FallbackEditor); + } + } + + /// + /// Registers property editors defined in the specified assembly. + /// + public void RegisterAssembly(Assembly assembly) + { + if (assembly == null) + throw new ArgumentNullException("assembly"); + + foreach (Type type in assembly.GetExportedTypes()) { + foreach (TypeEditorAttribute editorAttribute in type.GetCustomAttributes(typeof(TypeEditorAttribute), false)) { + CheckValidEditor(type); + _typeEditors[editorAttribute.SupportedPropertyType] = type; + } + foreach (PropertyEditorAttribute editorAttribute in type.GetCustomAttributes(typeof(PropertyEditorAttribute), false)) { + CheckValidEditor(type); + string propertyName = editorAttribute.PropertyDeclaringType.FullName + "." + editorAttribute.PropertyName; + _propertyEditors[propertyName] = type; + } + } + } + + static readonly Type[] typeArrayWithPropertyEditorDataProperty = { typeof(IPropertyEditorDataProperty) }; + + static void CheckValidEditor(Type type) + { + if (!typeof(UIElement).IsAssignableFrom(type)) { + throw new DesignerException("Editor types must derive from UIElement!"); + } + if (type.GetConstructor(typeArrayWithPropertyEditorDataProperty) == null) { + throw new DesignerException("Editor types must have a constructor that takes a IPropertyEditorDataProperty as argument!"); + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EnumEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EnumEditor.cs new file mode 100644 index 0000000000..d974084722 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/EnumEditor.cs @@ -0,0 +1,31 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Data; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Media; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Type editor used to edit enum properties. + /// + sealed class EnumEditor : ComboBox + { + /// + /// Creates a new EnumEditor instance. + /// + public EnumEditor(IPropertyEditorDataProperty property) + { + + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/FallbackEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/FallbackEditor.cs new file mode 100644 index 0000000000..5dfa34d6e9 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/FallbackEditor.cs @@ -0,0 +1,50 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// The type editor used when no other type editor could be found. + /// + [TypeEditor(typeof(object))] + public sealed class FallbackEditor : TextBlock + { + /// + /// Creates a new FallbackEditor instance for the specified property. + /// + public FallbackEditor(IPropertyEditorDataProperty property) + { + if (property == null) + throw new ArgumentNullException("property"); + + this.TextTrimming = TextTrimming.CharacterEllipsis; + if (property.IsSet) { + this.FontWeight = FontWeights.Bold; + } + object val = property.Value; + if (val == null) { + this.Text = "null"; + this.FontStyle = FontStyles.Italic; + } else { + try { + this.Text = val.ToString(); + } catch (Exception ex) { + this.FontWeight = FontWeights.Regular; + Inlines.Add(new Italic(new Run(ex.GetType().Name))); + Inlines.Add(" "); + Inlines.Add(ex.Message); + } + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/IPropertyEditorDataSource.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/IPropertyEditorDataSource.cs index 240e316fc9..d9d0d3c6d9 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/IPropertyEditorDataSource.cs +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/IPropertyEditorDataSource.cs @@ -9,6 +9,8 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Media; +using System.Windows; + namespace ICSharpCode.WpfDesign.PropertyEditor { /// @@ -21,6 +23,11 @@ namespace ICSharpCode.WpfDesign.PropertyEditor /// string Name { get; set; } + /// + /// Is raised whenever the Name property changes value. + /// + event EventHandler NameChanged; + /// /// Gets the type of the item (for display only). /// @@ -62,6 +69,21 @@ namespace ICSharpCode.WpfDesign.PropertyEditor /// string Name { get; } + /// + /// Gets the type of the property value. + /// + Type ReturnType { get; } + + /// + /// Gets the type that declares the property. + /// + Type DeclaringType { get; } + + /// + /// Gets the type converter used to convert property values to/from string. + /// + TypeConverter TypeConverter { get; } + /// /// Gets the description of the property. /// @@ -74,11 +96,21 @@ namespace ICSharpCode.WpfDesign.PropertyEditor /// bool IsSet { get; set; } + /// + /// Is raised when the IsSet property has changed. + /// + event EventHandler IsSetChanged; + /// /// Gets/Sets the value of the property. /// object Value { get; set; } + /// + /// Is raised when the Value property has changed. + /// + event EventHandler ValueChanged; + /// /// Gets if using a custom expression is supported. /// @@ -88,5 +120,10 @@ namespace ICSharpCode.WpfDesign.PropertyEditor /// Sets a custom expression. /// void SetCustomExpression(string expression); + + /// + /// Creates a UIElement that can edit the property value. + /// + UIElement CreateEditor(); } } diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorAttribute.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorAttribute.cs new file mode 100644 index 0000000000..22de895fae --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorAttribute.cs @@ -0,0 +1,50 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Attribute to specify that the decorated class is a editor for the specified property. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=false)] + public sealed class PropertyEditorAttribute : Attribute + { + readonly Type propertyDeclaringType; + readonly string propertyName; + + /// + /// Creates a new PropertyEditorAttribute that specifies that the decorated class is a editor + /// for the ".". + /// + public PropertyEditorAttribute(Type propertyDeclaringType, string propertyName) + { + if (propertyDeclaringType == null) + throw new ArgumentNullException("propertyDeclaringType"); + if (propertyName == null) + throw new ArgumentNullException("propertyName"); + this.propertyDeclaringType = propertyDeclaringType; + this.propertyName = propertyName; + } + + /// + /// Gets the type that declares the property that the decorated editor supports. + /// + public Type PropertyDeclaringType { + get { return propertyDeclaringType; } + } + + /// + /// Gets the name of the property that the decorated editor supports. + /// + public string PropertyName { + get { return propertyName; } + } + } +} + diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorBindingHelper.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorBindingHelper.cs new file mode 100644 index 0000000000..a8274b6b56 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/PropertyEditorBindingHelper.cs @@ -0,0 +1,79 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; +using System.ComponentModel; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Provides a static method to create a binding between a dependency property + /// and a data property. + /// + public static class PropertyEditorBindingHelper + { + /// + /// Binds editor.property to dataProperty.Value. + /// + public static Binding CreateBinding(FrameworkElement editor, IPropertyEditorDataProperty dataProperty) + { + if (editor == null) + throw new ArgumentNullException("editor"); + if (dataProperty == null) + throw new ArgumentNullException("dataProperty"); + + CustomBinding customBinding = new CustomBinding(dataProperty); + editor.Loaded += customBinding.OnLoaded; + editor.Unloaded += customBinding.OnUnloaded; + if (editor.IsLoaded) { + customBinding.OnLoaded(editor, null); + } + + Binding b = new Binding("BoundValue"); + b.Source = customBinding; + b.ConverterCulture = CultureInfo.InvariantCulture; + return b; + } + + sealed class CustomBinding : INotifyPropertyChanged + { + readonly IPropertyEditorDataProperty dataProperty; + + public CustomBinding(IPropertyEditorDataProperty dataProperty) + { + this.dataProperty = dataProperty; + } + + internal void OnLoaded(object sender, RoutedEventArgs e) + { + dataProperty.ValueChanged += OnValueChanged; + } + + internal void OnUnloaded(object sender, RoutedEventArgs e) + { + dataProperty.ValueChanged -= OnValueChanged; + } + + public object BoundValue { + get { return dataProperty.Value; } + set { dataProperty.Value = value; } + } + + public event PropertyChangedEventHandler PropertyChanged; + + void OnValueChanged(object sender, EventArgs e) + { + if (PropertyChanged != null) { + PropertyChanged(this, new PropertyChangedEventArgs("BoundValue")); + } + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs new file mode 100644 index 0000000000..48193170d1 --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TextBoxEditor.cs @@ -0,0 +1,54 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Windows; +using System.Windows.Data; +using System.Windows.Media; +using System.Windows.Controls; +using System.Windows.Documents; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Type editor used to edit properties using a text box and the type's default type converter. + /// + sealed class TextBoxEditor : TextBox + { + /// + /// Creates a new TextBoxEditor instance. + /// + public TextBoxEditor(IPropertyEditorDataProperty property) + { + Binding b = PropertyEditorBindingHelper.CreateBinding(this, property); + b.Converter = new ToStringConverter(property.TypeConverter); + SetBinding(TextProperty, b); + } + + sealed class ToStringConverter : IValueConverter + { + readonly TypeConverter converter; + + public ToStringConverter(TypeConverter converter) + { + this.converter = converter; + } + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return converter.ConvertToString(null, culture, value); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return converter.ConvertFromString(null, culture, (string)value); + } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TypeEditorAttribute.cs b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TypeEditorAttribute.cs new file mode 100644 index 0000000000..34de52b96b --- /dev/null +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/PropertyEditor/TypeEditorAttribute.cs @@ -0,0 +1,39 @@ +// +// +// +// +// $Revision$ +// + +using System; + +namespace ICSharpCode.WpfDesign.PropertyEditor +{ + /// + /// Attribute to specify that the decorated class is a editor for properties with the specified + /// return type. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=false)] + public sealed class TypeEditorAttribute : Attribute + { + readonly Type supportedPropertyType; + + /// + /// Creates a new TypeEditorAttribute that specifies that the decorated class is a editor + /// for properties with the return type "". + /// + public TypeEditorAttribute(Type supportedPropertyType) + { + if (supportedPropertyType == null) + throw new ArgumentNullException("supportedPropertyType"); + this.supportedPropertyType = supportedPropertyType; + } + + /// + /// Gets the supported property type. + /// + public Type SupportedPropertyType { + get { return supportedPropertyType; } + } + } +} diff --git a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj index 352b057e1c..a6ac387aae 100644 --- a/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj +++ b/src/AddIns/DisplayBindings/WpfDesign/WpfDesign/Project/WpfDesign.csproj @@ -79,10 +79,18 @@ + + + + + + + +