Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / PropertyPanel.cs / 1305376 / PropertyPanel.cs
//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing { using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; //// PropertyPanel is a simplified version of a horizontal StackPanel that we use for PropertyContainer // visuals in lieu of Grid, which was too heavy-weight and bogging down perf. It exposes a property, // LastChildWidth, that specifies the forced width of the last child in the panel. All other // children are stacked on the left and eat up the remainder of the space left on the panel. // // The panel also deals with drawing compartments for itself and the last child and it deals with // visually nesting sub-properties based on their depth (Level). // internal class PropertyPanel : Panel { // LastChildWidth DP public static readonly DependencyProperty OutlineBrushProperty = DependencyProperty.Register("OutlineBrush", typeof(Brush), typeof(PropertyPanel), new FrameworkPropertyMetadata((Brush)null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); public static readonly DependencyProperty SelectionBrushProperty = DependencyProperty.Register("SelectionBrush", typeof(Brush), typeof(PropertyPanel), new FrameworkPropertyMetadata((Brush)null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); public static readonly DependencyProperty OutlineThicknessProperty = DependencyProperty.Register("OutlineThickness", typeof(double), typeof(PropertyPanel), new FrameworkPropertyMetadata((double)1, FrameworkPropertyMetadataOptions.AffectsRender)); public static readonly DependencyProperty IgnoreFirstChildBackgroundProperty = DependencyProperty.Register("IgnoreFirstChildBackground", typeof(bool), typeof(PropertyPanel), new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.AffectsRender)); public static DependencyProperty LastChildWidthProperty = DependencyProperty.Register( "LastChildWidth", typeof(double), typeof(PropertyPanel), new FrameworkPropertyMetadata( (double)0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender)); public static DependencyProperty LevelProperty = DependencyProperty.Register( "Level", typeof(int), typeof(PropertyPanel), new FrameworkPropertyMetadata( (int)0, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); public static DependencyProperty LevelIndentProperty = DependencyProperty.Register( "LevelIndent", typeof(double), typeof(PropertyPanel), new FrameworkPropertyMetadata( (double)13, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)); //// Gets or sets the pixel width of the last child added into this panel. // public double LastChildWidth { get { return (double)this.GetValue(LastChildWidthProperty); } set { this.SetValue(LastChildWidthProperty, value); } } // Level DP //// Gets or sets the indentation level for the first child in this panel. Levels are // measured in ints, with 0 = no indentation, 1 = 1st sub-property, ... // The actual amount of space taken up by each level is controled by LevelIndent property // public int Level { get { return (int)this.GetValue(LevelProperty); } set { this.SetValue(LevelProperty, value); } } // LevelIndent DP //// Gets or sets the pixel width that the first child is indented for each level that // it belongs to // public double LevelIndent { get { return (double)this.GetValue(LevelIndentProperty); } set { this.SetValue(LevelIndentProperty, value); } } // OutlineBrush DP //// Gets or sets the line brush to use for the panel compartments // public Brush OutlineBrush { get { return (Brush)GetValue(OutlineBrushProperty); } set { SetValue(OutlineBrushProperty, value); } } // SelectionBrush DP //// Gets or sets the brush to be used as the background for everything but the last // element in the panel // public Brush SelectionBrush { get { return (Brush)GetValue(SelectionBrushProperty); } set { SetValue(SelectionBrushProperty, value); } } // OutlineThickness DP //// Gets or sets the line thickness for the panel compartments (not as Thickness, but // instead as a simple double) // public double OutlineThickness { get { return (double)GetValue(OutlineThicknessProperty); } set { SetValue(OutlineThicknessProperty, value); } } // IgnoreFirstChildBackground DP //// Gets or sets a flag indicating whether the SelectionBrush background should // or should not be applied to the first child of the panel // public bool IgnoreFirstChildBackground { get { return (bool)GetValue(IgnoreFirstChildBackgroundProperty); } set { SetValue(IgnoreFirstChildBackgroundProperty, value); } } // Stacks the children to the left, leaving LastChildWidth amount of space for the last child protected override Size MeasureOverride(Size availableSize) { double lastChildWidth = Math.Max(0, this.LastChildWidth); double indent = this.LevelIndent * this.Level; double availableWidth = Math.Max(0, availableSize.Width - lastChildWidth - indent); int childrenCount = InternalChildren.Count; int lastIndex = childrenCount - 1; Size actualSize = new Size(); for (int i = 0; i < childrenCount; i++) { UIElement child = InternalChildren[i]; if (i == lastIndex) { InternalChildren[i].Measure(new Size(lastChildWidth, availableSize.Height)); } else { InternalChildren[i].Measure(new Size(availableWidth, availableSize.Height)); } availableWidth -= child.DesiredSize.Width; //Compute the actual size for the propertypanel actualSize.Height = Math.Max(actualSize.Height, child.DesiredSize.Height); actualSize.Width += child.DesiredSize.Width; } return actualSize; } // Stacks the children to the left, leaving LastChildWidth amount of space for the last child protected override Size ArrangeOverride(Size finalSize) { double lastChildWidth = Math.Max(0, this.LastChildWidth); double indent = this.LevelIndent * this.Level; double availableWidth = Math.Max(0, finalSize.Width - lastChildWidth - indent); double left = indent; int childrenCount = InternalChildren.Count; int lastIndex = childrenCount - 1; for (int i = 0; i < childrenCount; i++) { UIElement child = InternalChildren[i]; double desiredWidth = child.DesiredSize.Width; if (i == lastIndex) { child.Arrange(new Rect(Math.Max(0, finalSize.Width - lastChildWidth), 0, lastChildWidth, finalSize.Height)); } else { child.Arrange(new Rect(left, 0, Math.Min(desiredWidth, availableWidth), finalSize.Height)); } left += desiredWidth; availableWidth -= desiredWidth; availableWidth = Math.Max(0, availableWidth); } return finalSize; } // Custom renders compartments and dividers protected override void OnRender(DrawingContext dc) { Size renderSize = this.RenderSize; Brush outlineBrush = this.OutlineBrush; double outlineThickness = this.OutlineThickness; double halfThickness = outlineThickness / 2.0; double dividerRight = Math.Max(0, this.LastChildWidth); double dividerLeft = renderSize.Width - dividerRight - outlineThickness; Brush selectionBrush = this.SelectionBrush; if (selectionBrush != null) { bool ignoreFirstChildBackground = this.IgnoreFirstChildBackground; double firstChildWidth = 0; if (ignoreFirstChildBackground && this.Children.Count > 0) { firstChildWidth = this.Children[0].RenderSize.Width; } dc.DrawRectangle(selectionBrush, null, new Rect( firstChildWidth, 0, Math.Max(dividerLeft - firstChildWidth, 0), renderSize.Height)); } base.OnRender(dc); // Use Guidelines to avoid anti-aliasing (fuzzy border lines) dc.PushGuidelineSet(new GuidelineSet( // X coordinates for guidelines (vertical lines) new double[] { 0, dividerLeft, dividerLeft + outlineThickness, renderSize.Width - outlineThickness, renderSize.Width }, // Y coordinates for guidelines (horizontal lines) new double[] { 0, renderSize.Height - outlineThickness, renderSize.Height })); Pen outlinePen = new Pen(outlineBrush, outlineThickness); // Bottom edge dc.DrawLine( outlinePen, new Point(0, renderSize.Height - halfThickness), new Point(renderSize.Width, renderSize.Height - halfThickness)); // Top edge dc.DrawLine( outlinePen, new Point(0, 0 - halfThickness), new Point(renderSize.Width, 0 - halfThickness)); // Right edge dc.DrawLine( outlinePen, new Point(renderSize.Width - halfThickness, 0), new Point(renderSize.Width - halfThickness, renderSize.Height)); // Divider dc.DrawLine( outlinePen, new Point(dividerLeft + halfThickness, 0), new Point(dividerLeft + halfThickness, renderSize.Height)); dc.Pop(); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10808,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/PropertyInspector.xaml.cs/1305376/PropertyInspector.xaml.cs,4/26/2010 8:54:39 AM,39389 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing { using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Windows.Threading; using System.Activities.Presentation; using System.Activities.Presentation.Model; using View = System.Activities.Presentation.View; using System.Activities.Presentation.PropertyEditing; using System.Runtime; using System.Activities.Presentation.Internal.PropertyEditing.Automation; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.ValueEditors; using System.Activities.Presentation.Internal.PropertyEditing.Model; using ModelUtilities=System.Activities.Presentation.Internal.PropertyEditing.Model.ModelUtilities; using System.Activities.Presentation.Internal.PropertyEditing.Resources; using System.Activities.Presentation.Internal.PropertyEditing.Selection; using System.Activities.Presentation.Internal.PropertyEditing.State; using System.Text; //// The main control that acts as the PropertyInspector // [SuppressMessage(FxCop.Category.Naming, "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "Code imported from Cider; keeping changes to a minimum as it impacts xaml files as well")] partial class PropertyInspector : INotifyPropertyChanged { private static readonly Size DesiredIconSize = new Size(40, 40); private View.Selection _displayedSelection; private View.Selection _lastNotifiedSelection; private ModelItem _lastParent; private bool _ignoreSelectionNameChanges; private List_pendingTransactions = new List (); private PropertyValueEditorCommandHandler _defaultCommandHandler; private IStateContainer _sessionStateContainer; private SelectionPath _lastSelectionPath; private bool _objectSelectionInitialized; private bool _disposed; private bool _isReadOnly; private ContextItemManager designerContextItemManager; // Map between currently displayed category editors and the names of the categories they belong to private Dictionary _activeCategoryEditors = new Dictionary (); // // Basic ctor // // FxCop complains this.DataContext, which is somewhat bogus [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public PropertyInspector() { this.DataContext = this; HookIntoCommands(); this.InitializeComponent(); //Handle the commit and cancel keys within the property inspector ValueEditorUtils.SetHandlesCommitKeys(this, true); _propertyToolBar.CurrentViewManagerChanged += new EventHandler(OnCurrentViewManagerChanged); } //// Event fired when the IsInAlphaView changes as a result of some // user or internal interaction. When IsInAlphaView is set by the // external host, this event will not and should not be fired. // public event EventHandler RootViewModified; public event PropertyChangedEventHandler PropertyChanged; [SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly", Justification = "No need for a Setter")] public ContextItemManager DesignerContextItemManager { set { this.designerContextItemManager = value; this.designerContextItemManager.Subscribe(this.OnSelectionChanged); } } // // Gets a value indicating whether the selected object Name should be read-only // public bool IsInfoBarNameReadOnly { get { return _displayedSelection == null || _displayedSelection.SelectionCount != 1; } } //// Gets the selection name to display // [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")] [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")] public string SelectionName { get { if (_displayedSelection == null || _displayedSelection.SelectionCount == 0) { return null; } if (_displayedSelection.SelectionCount == 1) { return _displayedSelection.PrimarySelection.Name; } return System.Activities.Presentation.Internal.Properties.Resources.PropertyEditing_MultipleObjectsSelected; } set { if (_disposed) { return; } if (CanSetSelectionName(_displayedSelection)) { ModelItem selection = _displayedSelection.PrimarySelection; Fx.Assert(selection != null, "PrimarySelection should not be null"); try { _ignoreSelectionNameChanges = true; using (ModelEditingScope change = selection.BeginEdit(System.Activities.Presentation.Internal.Properties.Resources.PropertyEditing_NameChangeUndoText)) { if (string.IsNullOrEmpty(value)) { // Null with cause ClearValue to be called in the base implementation on the NameProperty selection.Name = null; } else { selection.Name = value; } if (change != null) change.Complete(); } } catch (Exception e) { Debug.WriteLine(e.ToString()); ErrorReporting.ShowErrorMessage(e.Message); } finally { _ignoreSelectionNameChanges = false; } OnPropertyChanged("SelectionName"); } else { Debug.Fail("Shouldn't be able to set a selection name if no or more than one object is selected."); } } } //// Gets the icon for the selection // public object SelectionIcon { get { if (_displayedSelection == null || _displayedSelection.SelectionCount == 0) { return null; } if (_displayedSelection.SelectionCount == 1 || AreHomogenous(_displayedSelection.SelectedObjects)) { if (_displayedSelection.SelectionCount == 1) { Visual selectedVisual = _displayedSelection.PrimarySelection.View as Visual; // We dont want to show tooltips for elements that derive from "Window" class. // But we do want to show it for DesignTimeWindow, hence we check the View, so that modelItem returns the correct value // for designtimewindow. if (selectedVisual != null && !typeof(Window).IsAssignableFrom(_displayedSelection.PrimarySelection.View.GetType())) { // Show a small preview of the selected single object VisualBrush controlBrush = new VisualBrush(selectedVisual); controlBrush.Stretch = Stretch.Uniform; Rectangle rect = new Rectangle(); rect.Width = DesiredIconSize.Width; rect.Height = DesiredIconSize.Height; rect.DataContext = string.Empty; // If the control's parent is RTLed, then the VisualBrush "mirrors" the text. // so apply "mirror" transform to "negate" the mirroring. FrameworkElement curElement = selectedVisual as FrameworkElement; FrameworkElement parentElement = curElement.Parent as FrameworkElement; if (parentElement != null && parentElement.FlowDirection == FlowDirection.RightToLeft) { ScaleTransform mirrorTransform = new ScaleTransform(-1, 1); mirrorTransform.CenterX = rect.Width / 2; mirrorTransform.CenterY = rect.Height / 2; controlBrush.Transform = mirrorTransform; } rect.Fill = controlBrush; return rect; } else { // The selected object is not a visual, so show a non-designable object icon return GetEmbeddedImage("NonDesignableSelection.png"); } } // Show mutliple-selection of the same type icon return GetEmbeddedImage("MultiSelectionSameType.png"); } // Show multiple-selection of different types icon return GetEmbeddedImage("MultiSelectionDifferentType.png"); } } //// Gets the Type name for the current selection // public string SelectionTypeName { get { if (_displayedSelection == null || _displayedSelection.SelectionCount == 0) { return null; } if (_displayedSelection.SelectionCount == 1 || AreHomogenous(_displayedSelection.SelectedObjects)) { return GetStringRepresentation(_displayedSelection.PrimarySelection.ItemType); } return System.Activities.Presentation.Internal.Properties.Resources.PropertyEditing_MultipleTypesSelected; } } static string GetStringRepresentation(Type type) { return TypeNameHelper.GetDisplayName(type, true); } // Property View //// Gets the state that should be persisted while the host is // running, but discarded when the host shuts down. // public object SessionState { get { // Don't instantiate the SessionStateContainer until // CategoryList has been instantiated. Otherwise, we would // get an invalid container. if (_categoryList == null) { return null; } return SessionStateContainer.RetrieveState(); } set { // Don't instantiate the SessionStateContainer until // CategoryList has been instantiated. Otherwise, we would // get an invalid container. if (_categoryList == null || value == null) { return; } SessionStateContainer.RestoreState(value); _objectSelectionInitialized = false; } } public bool IsReadOnly { get { return this._isReadOnly; } internal set { this._isReadOnly = value; this._categoryList.Opacity = this._isReadOnly ? 0.8 : 1.0; this._categoryList.ToolTip = this._isReadOnly ? this.FindResource("editingDisabledHint") : null; this.OnPropertyChanged("IsReadOnly"); } } //// Gets or sets a flag indicating whether the root PropertyInspector // control is in alpha-view. We isolate this state from any other // to make VS integration easier. // public bool IsInAlphaView { get { return _propertyToolBar.IsAlphaViewSelected; } set { _propertyToolBar.IsAlphaViewSelected = value; } } internal TextBlock SelectionTypeLabel { get { return _typeLabel; } } //internal TextBlock SelectionNameLabel //{ get { return _nameLabel; } } //internal StringEditor SelectionNameEditor //{ get { return _nameEditor; } } internal PropertyToolBar PropertyToolBar { get { return _propertyToolBar; } } internal TextBlock NoSearchResultsLabel { get { return _noSearchResultsLabel; } } internal TextBlock UninitializedLabel { get { return _uninitializedLabel; } } internal CategoryList CategoryList { get { return _categoryList; } } private SelectionPath LastSelectionPath { get { return _lastSelectionPath; } set { _lastSelectionPath = value; } } private IStateContainer SessionStateContainer { get { if (_categoryList == null) { return null; } if (_sessionStateContainer == null) { _sessionStateContainer = new AggregateStateContainer( PropertyStateContainer.Instance, _categoryList, new SelectionPathStateContainer(this), PropertyActiveEditModeStateContainer.Instance, PropertyViewManagerStateContainer.Instance); } return _sessionStateContainer; } } // IPropertyInspectorState internal void Dispose() { _disposed = true; DisassociateAllProperties(); UpdateSelectionPropertyChangedEventHooks(_displayedSelection, null); _displayedSelection = null; _defaultCommandHandler.Dispose(); _defaultCommandHandler = null; } private void HookIntoCommands() { // Use a helper classes to handle all the standard PI commands _defaultCommandHandler = new PropertyValueEditorCommandHandler(this); } //// Marks all shown properties as disassociated which disables all modifications // done to them through the PI model objects. // private void DisassociateAllProperties() { if (_categoryList != null && _categoryList.IsLoaded) { foreach (ModelCategoryEntry category in _categoryList) { category.MarkAllPropertiesDisassociated(); } } } // Properties private void OnCurrentViewManagerChanged(object sender, EventArgs e) { this.RefreshPropertyList(false); // Isolate the current view of the root PropertyInspector into // its own separate flag and event to appease the VS ----s // if (this.RootViewModified != null) { RootViewModified(null, EventArgs.Empty); } } private void RefreshPropertyList(bool attachedOnly) { UpdateCategories(_lastNotifiedSelection, attachedOnly); UpdateCategoryEditors(_lastNotifiedSelection); // // The first time SelectionChanges, there is nothing selected, so don't store the // current property selected. It would just overwrite the selection path that we // received from SelectionPathStateContainer, which is not what we want. // if (_objectSelectionInitialized) { LastSelectionPath = _categoryList.SelectionPath; } _objectSelectionInitialized = true; // // Call UpdateSelectedProperty() _after_ the UI renders. We need to set PropertySelection.IsSelected // property on a templated visual objects (CategoryContainer, PropertyContainer) and those may not exist yet. // Dispatcher.BeginInvoke(DispatcherPriority.Render, new UpdateSelectedPropertyInvoker(UpdateSelectedProperty), _lastNotifiedSelection); } // Selection Logic // SelectionPathStateContainer //// Called externally whenever selection changes // // New selection public void OnSelectionChanged(View.Selection selection) { _lastNotifiedSelection = selection; RefreshSelection(); } //// Called when visibility of the PropertyBrowserPane changes and the // PropertyInspector may be showing a stale selection. This method is identical // to OnSelectionChanged() but with no new selection instance introduced. // public void RefreshSelection() { Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new MethodInvoker(OnSelectionChangedIdle)); } // Updates PI when the application becomes Idle (perf optimization) private void OnSelectionChangedIdle() { if (AreSelectionsEquivalent(_lastNotifiedSelection, _displayedSelection)) { return; } if (!VisualTreeUtils.IsVisible(this)) { return; } // Change the SelectedControlFlowDirectionRTL resource property // This will allow the 3rd party editors to look at this property // and change to RTL for controls that support RTL. // We set the resource to the primary selections RTL property. FlowDirection commmonFD = FlowDirection.LeftToRight; if (_lastNotifiedSelection != null && _lastNotifiedSelection.PrimarySelection != null) { FrameworkElement selectedElement = _lastNotifiedSelection.PrimarySelection.View as FrameworkElement; if (selectedElement != null) { commmonFD = selectedElement.FlowDirection; } // In case of mulitislection, // if the FlowDirection is different then always set it to LTR. // else set it to common FD. if (_lastNotifiedSelection.SelectionCount > 1) { foreach (ModelItem item in _lastNotifiedSelection.SelectedObjects) { FrameworkElement curElm = item.View as FrameworkElement; if (curElm != null && curElm.FlowDirection != commmonFD) { //reset to LTR (since the FD's are different within multiselect) commmonFD = FlowDirection.LeftToRight; break; } } } } PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"] = commmonFD; RefreshPropertyList(false); UpdateSelectionPropertyChangedEventHooks(_displayedSelection, _lastNotifiedSelection); _displayedSelection = _lastNotifiedSelection; _lastParent = GetCommonParent(_lastNotifiedSelection); // Handle dangling transactions _defaultCommandHandler.CommitOpenTransactions(); OnPropertyChanged("IsInfoBarNameReadOnly"); OnPropertyChanged("SelectionName"); OnPropertyChanged("SelectionIcon"); OnPropertyChanged("SelectionTypeName"); } // Removes / adds a PropertyChanged listener from / to the previous / current selection private void UpdateSelectionPropertyChangedEventHooks(View.Selection previousSelection, View.Selection currentSelection) { if (previousSelection != null && previousSelection.PrimarySelection != null) { previousSelection.PrimarySelection.PropertyChanged -= OnSelectedItemPropertyChanged; } if (currentSelection != null && currentSelection.PrimarySelection != null) { currentSelection.PrimarySelection.PropertyChanged += OnSelectedItemPropertyChanged; } } private void OnSelectedItemPropertyChanged(object sender, PropertyChangedEventArgs e) { if (_ignoreSelectionNameChanges) { return; } // PS 40699 - Name is not a special property for WF //if ("Name".Equals(e.PropertyName)) //{ // OnSelectedItemNameChanged(); //} if ("Parent".Equals(e.PropertyName)) { OnParentChanged(); } } // Called when the name changes private void OnSelectedItemNameChanged() { OnPropertyChanged("SelectionName"); } // Called when the parent of the current selection changes private void OnParentChanged() { Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new MethodInvoker(OnParentChangedIdle)); } private void OnParentChangedIdle() { if (_displayedSelection == null || _displayedSelection.SelectionCount < 1) { return; } ModelItem newParent = GetCommonParent(_displayedSelection); if (_lastParent != newParent) { RefreshPropertyList(true); _lastParent = newParent; } } // Looks for common parent ModelItem among all the items in the selection private static ModelItem GetCommonParent(View.Selection selection) { if (selection == null || selection.SelectionCount < 1) { return null; } ModelItem parent = null; foreach (ModelItem item in selection.SelectedObjects) { if (parent == null) { parent = item.Parent; } else if (parent != item.Parent) { return null; } } return parent; } // The user can only specify the name for the selected objects iff exactly one // object is selected. private static bool CanSetSelectionName(View.Selection selection) { return selection != null && selection.SelectionCount == 1; } private static bool AreSelectionsEquivalent(View.Selection a, View.Selection b) { if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } if (a.SelectionCount != b.SelectionCount) { return false; } // POSSIBLE OPTIMIZATION: be smarter about same selection in a different order IEnumeratorea = a.SelectedObjects.GetEnumerator(); IEnumerator eb = b.SelectedObjects.GetEnumerator(); while (ea.MoveNext() && eb.MoveNext()) { if (!object.Equals(ea.Current, eb.Current)) { return false; } } return true; } // This is the work-horse that refreshes the list of properties and categories within a PropertyInspector // window, including refreshing of CategoryEditors, based on the specified selection private void UpdateCategories(View.Selection selection, bool attachedOnly) { // Optimization stolen from Sparkle: // re-rendering the categories is the number one perf issue. Clearing // the databound collection results in massive Avalon code execution, and // then re-adding everything causes another huge shuffle. What is more, // even when changing the selection between different objects, most properties // do not change. Therefore we are going to take the new list of properties // and we are going to merge them into the existing stuff, using an // approach I call Mark, Match, and Cull. // // First we mark all the properties in the current collection. Those which // are still marked at the end will be culled out foreach (ModelCategoryEntry category in _categoryList) { if (attachedOnly) { category.MarkAttachedPropertiesDisassociated(); } else { category.MarkAllPropertiesDisassociated(); } } // Second we try to match each property in the list of properties for the newly selected objects // against something that we already have. If we have a match, then we reset the existing // ModelPropertyEntry and clear the mark // foreach (IEnumerable propertySet in ModelPropertyMerger.GetMergedProperties( selection == null ? null : selection.SelectedObjects, selection == null ? 0 : selection.SelectionCount)) { string propertyName = GetPropertyName(propertySet); // Specifically filter out the Name property // PS 40699 - Name is not a special property for WF //if ("Name".Equals(propertyName)) //{ // continue; //} if (attachedOnly && propertyName.IndexOf('.') < 0) { continue; } ModelPropertyEntry wrappedProperty = _propertyToolBar.CurrentViewManager.AddProperty(propertySet, propertyName, _categoryList); // Make sure no valid properties get culled out wrappedProperty.Disassociated = false; } // Third, we walk the properties and categories, and we cull out all of the // marked properties. Empty categories are removed. // for (int i = _categoryList.Count - 1; i >= 0; i--) { ModelCategoryEntry category = (ModelCategoryEntry)_categoryList[i]; category.CullDisassociatedProperties(); if (category.IsEmpty) { _categoryList.RemoveAt(i); } } _categoryList.RefreshFilter(); } // Helper method that adjusts the visible set of CategoryEditors based on the specified selection private void UpdateCategoryEditors(View.Selection selection) { // Figure out which category editors to show Dictionary newCategoryEditorTypes = _propertyToolBar.CurrentViewManager.GetCategoryEditors( FindCommonType(selection == null ? null : selection.SelectedObjects), _categoryList); // Figure out which CategoryEditors are no longer needed and remove them List editorTypesToRemove = null; foreach (KeyValuePair item in _activeCategoryEditors) { if (!newCategoryEditorTypes.ContainsKey(item.Key) || !IsCategoryShown(item.Key)) { // New selection does not include this existing category editor // or the category that contains this editor // so remove the editor. if (editorTypesToRemove == null) { editorTypesToRemove = new List (); } editorTypesToRemove.Add(item.Key); } else { // This category editor already exists, so don't re-add it newCategoryEditorTypes.Remove(item.Key); } } if (editorTypesToRemove != null) { foreach (Type editorTypeToRemove in editorTypesToRemove) { ModelCategoryEntry affectedCategory = _categoryList.FindCategory(_activeCategoryEditors[editorTypeToRemove]) as ModelCategoryEntry; if (affectedCategory != null) { affectedCategory.RemoveCategoryEditor(editorTypeToRemove); } _activeCategoryEditors.Remove(editorTypeToRemove); } } // Figure out which CategoryEditors are now required and add them foreach (Type editorTypeToAdd in newCategoryEditorTypes.Keys) { CategoryEditor editor = (CategoryEditor)ExtensibilityAccessor.SafeCreateInstance(editorTypeToAdd); if (editor == null) { continue; } ModelCategoryEntry affectedCategory = _categoryList.FindCategory(editor.TargetCategory) as ModelCategoryEntry; if (affectedCategory == null) { continue; } affectedCategory.AddCategoryEditor(editor); _activeCategoryEditors[editorTypeToAdd] = editor.TargetCategory; } } // Check if the category is shown for the current category editor type private bool IsCategoryShown(Type categoryEditorType) { bool ret = true; CategoryEditor editorToRemove = (CategoryEditor)ExtensibilityAccessor.SafeCreateInstance(categoryEditorType); if (editorToRemove != null) { ModelCategoryEntry affectedCategory = _categoryList.FindCategory(editorToRemove.TargetCategory) as ModelCategoryEntry; if (affectedCategory == null) { ret = false; } } else { ret = false; } return ret; } // Tries to figure out what property to select and selects is private void UpdateSelectedProperty(View.Selection selection) { // If we are not loaded, skip any and all selection magic if (!this.IsLoaded) { return; } if (selection != null) { // See what the view would like us to select if we run out of things // we can think of selecting // SelectionPath fallbackSelection = null; if (_propertyToolBar.CurrentViewManager != null) { fallbackSelection = _propertyToolBar.CurrentViewManager.GetDefaultSelectionPath(_categoryList); } // Select the first thing we request that exists, using the following // precedence order: // // * LastSelectionPath // * DefaultProperty // * Whatever the view wants to show (first category, first property, ...) // _categoryList.UpdateSelectedProperty( this.LastSelectionPath, ModelPropertyMerger.GetMergedDefaultProperty(selection.SelectedObjects), fallbackSelection); } } private static Type FindCommonType(IEnumerable modelItems) { Type commonType = null; if (modelItems != null) { foreach (ModelItem selectedItem in modelItems) { if (commonType == null) { commonType = selectedItem.ItemType; } else { commonType = ModelUtilities.GetCommonAncestor(commonType, selectedItem.ItemType); } } } return commonType; } private static bool AreHomogenous(IEnumerable items) { Fx.Assert(items != null, "items parameter is null"); Type type = null; foreach (ModelItem item in items) { if (type == null) { type = item.ItemType; } else if (type != item.ItemType) { return false; } } return true; } // Static Helpers private static string GetPropertyName(IEnumerable propertySet) { if (propertySet == null) { return null; } foreach (ModelProperty property in propertySet) { return property.Name; } return null; } private static Image GetEmbeddedImage(string imageName) { Image image = new Image(); image.Source = new BitmapImage(new Uri( string.Concat( "/System.Activities.Presentation;component/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Resources/", imageName), UriKind.RelativeOrAbsolute)); return image; } // AutomationPeer Stuff protected override AutomationPeer OnCreateAutomationPeer() { return new PropertyInspectorAutomationPeer(this); } // Cross-domain State Storage // // Clears the FilterString // public void ClearFilterString() { _categoryList.FilterString = null; } // INotifyPropertyChanged Members private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private delegate void MethodInvoker(); private delegate void UpdateSelectedPropertyInvoker(View.Selection selection); // Container for property-selection state represented by SelectionPath. // Since we receive a stored SelectionPath on the reload of this control, // at which point the visuals themselves have not been rendered yet, we // store the supplied SelectionPath instance and use it to select the // correct property only after the UI has been rendered. // private class SelectionPathStateContainer : IStateContainer { private PropertyInspector _parent; public SelectionPathStateContainer(PropertyInspector parent) { if (parent == null) { throw FxTrace.Exception.ArgumentNull("parent"); } _parent = parent; } // // Pulls the SelectionPath from the CategoryList, but only if it was Sticky, // meaning we should preserve it // public object RetrieveState() { if (_parent.CategoryList != null) { SelectionPath path = _parent._objectSelectionInitialized ? _parent.CategoryList.SelectionPath : _parent.LastSelectionPath; return path == null ? null : path.State; } return null; } // // Pulls the SelectionPath from the CategoryList, but only if it was Sticky, // meaning we should preserve it // public void RestoreState(object state) { if (state != null) { SelectionPath restoredPath = SelectionPath.FromState(state); _parent.LastSelectionPath = restoredPath; } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10809,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelUtilities.cs/1305376/ModelUtilities.cs,4/26/2010 8:54:39 AM,9327 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Windows.Markup; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; //// Static class full of useful helpers // internal static class ModelUtilities { //// Compares the name and Type of the specified ModelProperties, // returning true if they are equal. // // ModelProperty A // ModelProperty B //True if the names and Types of the specified ModelProperties // match, false otherwise. public static bool AreEquivalent(ModelProperty a, ModelProperty b) { return object.Equals(a.Name, b.Name) && object.Equals(a.PropertyType, b.PropertyType); } //// Gets the underlying value object of the specified ModelProperty. MarkupExtensions // (resources and such) will be returned as they are, with the exception of NullExtension, // which will be returned as null. // // ModelProperty to ---- open (can be null) //Underlying value object, if any public static object GetSafeRawValue(ModelProperty property) { return GetSafeValue(property, false); } //// Gets the underlying computed value object of the specified ModelProperty. MarkupExtensions // (resources and such) will be resolved into their final value. // // ModelProperty to ---- open (can be null) //Underlying value object, if any public static object GetSafeComputedValue(ModelProperty property) { return GetSafeValue(property, true); } private static object GetSafeValue(ModelProperty property, bool resolveReferences) { if (property == null) { return null; } object value; // We have to special case TextBlock due to IAddChild behavior with Text and Inlines if (resolveReferences && !(typeof(System.Windows.Controls.TextBlock).IsAssignableFrom(property.Parent.ItemType) && property.Name.Equals(System.Windows.Controls.TextBlock.TextProperty.Name))) { value = property.ComputedValue; } else { value = property.Value == null ? null : property.Value.GetCurrentValue(); } if (value == null || value.GetType().Equals(typeof(NullExtension))) { return null; } return value; } //// Looks for the x:Name or Name property of the given PropertyValue and returns it if found. // Note: this method is expensive because it evaluates all the sub-properties of the given // PropertyValue. // // PropertyValue instance to look at //Name if the PropertyValue defines one, null otherwise public static string GetPropertyName(PropertyValue propertyValue) { if (propertyValue == null) { return null; } if (propertyValue.HasSubProperties) { PropertyEntry nameProperty = propertyValue.SubProperties["Name"]; if (nameProperty != null) { return nameProperty.PropertyValue.StringValue; } } return null; } //// Returns ',' separated property name for sub-properties, going all the way // to the root ancestor in the property editing OM. (ie. you get strings // such as 'ContextMenu,IsEnabled' instead of just 'IsEnabled'. // // Property to get the name of //',' separated property name for sub-properties public static string GetSubPropertyHierarchyPath(PropertyEntry property) { if (property == null) { return null; } if (property.ParentValue == null) { return property.PropertyName; } StringBuilder sb = new StringBuilder(); do { if (sb.Length > 0) { sb.Insert(0, ','); } sb.Insert(0, property.PropertyName); property = property.ParentValue == null ? null : property.ParentValue.ParentProperty; } while (property != null && !(property is ModelPropertyIndexer)); return sb.ToString(); } //// Same as GetSubPropertyHierarchyPath(), but it looks up a cached version // of this path, if one exists, or calculates one from scratch and caches it // if it doesn't. // // Property to get the name of //',' separated property name for sub-properties public static string GetCachedSubPropertyHierarchyPath(PropertyEntry property) { ModelPropertyEntry mpe = property as ModelPropertyEntry; return mpe == null ? GetSubPropertyHierarchyPath(property) : mpe.SubPropertyHierarchyPath; } //// Determines whether the specified type is implement generic Ilist interface. // // The type. //// public static bool ImplementsIList(Type type) { bool ret = false; if (!type.IsGenericType) { ret = false; } Type[] interfaceTypes = type.GetInterfaces(); foreach (Type interfaceType in interfaceTypes) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IList < > )) { ret = true; break; } } return ret; } //true if the specified type is implement generic Ilist interface;otherwise,false . //// Determines whether the specified type is implement generic ICollection interface. // // The type. //// public static bool ImplementsICollection(Type type) { bool ret = false; if (!type.IsGenericType) { ret = false; } Type[] interfaceTypes = type.GetInterfaces(); foreach (Type interfaceType in interfaceTypes) { if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICollection < > )) { ret = true; break; } } return ret; } //true if the specified type is implement generic ICollection interface;otherwise,false . //// Tries to determine the common type ancestor of the specified types // // Type 1 // Type 2 //Common ancestor Type, if any public static Type GetCommonAncestor(Type t1, Type t2) { if (t1 == null || t2 == null) { return null; } if (t1 == typeof(object) || t2 == typeof(object)) { return typeof(object); } if (t1.IsAssignableFrom(t2)) { return t1; } while (t2 != typeof(object)) { if (t2.IsAssignableFrom(t1)) { return t2; } t2 = t2.BaseType; } return typeof(object); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10810,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyValue.cs/1305376/ModelPropertyValue.cs,4/26/2010 8:54:39 AM,15473 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Text; using System.Activities.Presentation; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.PropertyEditing.Resources; using System.Activities.Presentation.Internal.Properties; using System.Activities.Presentation.Internal.PropertyEditing.Editors; //// Concrete implementation of PropertyValue that delegates to ModelPropertyEntryBase for // all actions. // internal class ModelPropertyValue : PropertyValue { // Object used to mark a property value that should be cleared instead of set private static readonly object ClearValueMarker = new object(); // CultureInfo instance we use for formatting values so that they reflect what is in Xaml private static CultureInfo _xamlCultureInfo; //// Basic ctor // // Parent ModelPropertyEntryBase public ModelPropertyValue(ModelPropertyEntryBase parentProperty) : base(parentProperty) { } //// Returns the source of this property value // public override PropertyValueSource Source { get { return ParentModelPropertyEntry.Source; } } //// Returns true if this value represents the default value of the property // public override bool IsDefaultValue { get { return Source == DependencyPropertyValueSource.DefaultValue; } } //// Returns true if the value contained by this property is mixed // public override bool IsMixedValue { get { return ParentModelPropertyEntry.IsMixedValue; } } //// Returns true if custom TypeConverter exists and if it can convert // the value from string. // public override bool CanConvertFromString { get { return ParentModelPropertyEntry.Converter != null && ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string)); } } //// Gets a flag indicating whether this PropertyValue has sub properties // public override bool HasSubProperties { get { return ParentModelPropertyEntry.HasSubProperties; } } //// Gets the sub-properties of the PropertyValue // public override PropertyEntryCollection SubProperties { get { return ParentModelPropertyEntry.SubProperties; } } //// Gets a flag indicating whether this PropertyValue represents a collection // public override bool IsCollection { get { return ParentModelPropertyEntry.IsCollection; } } //// Gets the collection represented by this PropertyValue // public override PropertyValueCollection Collection { get { return ParentModelPropertyEntry.Collection; } } //// This is an internal helper to which we can bind and on which we fire PropertyChanged // event when the Name sub-property (if one exists) changes. More-specifically, // we bind to this property in CollectionEditor to display the Type as well as the // Name of the items in the collection. This property is accessed from XAML. // [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public PropertyValue NameSensitiveInstance { get { return this; } } //// Always catch exceptions // protected override bool CatchExceptions { get { return true; } } //// Gets an en-us CultureInfo that ignores user-specified settings // private static CultureInfo XamlCultureInfo { get { if (_xamlCultureInfo == null) { _xamlCultureInfo = new CultureInfo("en-us", false); } return _xamlCultureInfo; } } // Convenience accessor private ModelPropertyEntryBase ParentModelPropertyEntry { get { return (ModelPropertyEntryBase)this.ParentProperty; } } //// Validates the value using the TypeConverter, if one exists // // Value to validate protected override void ValidateValue(object valueToValidate) { // Noop. We used to rely on TypeConverter.IsValid() here, but it turns out // that a bunch of standard TypeConverters don't really work (eg. Int32TypeConverter // returns true for IsValid("abc") and EnumConverter returns false for // IsValid(MyEnum.One | MyEnum.Two) even if MyEnum if adorned with FlagsAttribute) } // Called when there exists a Name sub-property for this value and it changes internal void OnNameSubPropertyChanged() { // Updates XAML bindings (collection editor item-display-name-template for one) this.OnPropertyChanged("NameSensitiveInstance"); } //// Convert the specified string to a value // // //protected override object ConvertStringToValue(string stringToConvert) { if (this.ParentProperty.PropertyType == typeof(string)) { return stringToConvert; } else if (string.IsNullOrEmpty(stringToConvert)) { // If the type of this property is string: // // StringValue of '' -> set Value to '' // StringValue of null -> ClearValue() // // Otherwise // // StringValue of '' -> ClearValue() // StringValue of null -> ClearValue() // if (stringToConvert != null && typeof(string).Equals(this.ParentProperty.PropertyType)) { return null; } else { return ClearValueMarker; } } else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType) && stringToConvert.Equals(EditorUtilities.NullString, StringComparison.Ordinal)) { // PS 107537: Special case handling when converting a string to a nullable enum type. return null; } else if (this.ParentModelPropertyEntry.Converter != null && this.ParentModelPropertyEntry.Converter.CanConvertFrom(typeof(string))) { return this.ParentModelPropertyEntry.Converter.ConvertFromString(null, XamlCultureInfo, stringToConvert); } throw FxTrace.Exception.AsError(new InvalidOperationException(string.Format( CultureInfo.CurrentCulture, Resources.PropertyEditing_NoStringToValueConversion, this.ParentProperty.DisplayName))); } // // Convert the specified value to a string // // //protected override string ConvertValueToString(object valueToConvert) { string stringValue = string.Empty; if (valueToConvert == null) { if (typeof(IList).IsAssignableFrom(this.ParentProperty.PropertyType)) { stringValue = Resources.PropertyEditing_DefaultCollectionStringValue; } else if (EditorUtilities.IsNullableEnumType(this.ParentProperty.PropertyType)) { // PS 107537: Special case handling when converting a nullable enum type to a string. return EditorUtilities.NullString; } return stringValue; } else if ((stringValue = valueToConvert as string) != null) { return stringValue; } TypeConverter typeConverter = this.ParentModelPropertyEntry.Converter; if (valueToConvert is Array) { stringValue = Resources.PropertyEditing_DefaultArrayStringValue; } else if (valueToConvert is IList || valueToConvert is ICollection || ModelUtilities.ImplementsICollection(valueToConvert.GetType()) || ModelUtilities.ImplementsIList(valueToConvert.GetType())) { stringValue = Resources.PropertyEditing_DefaultCollectionStringValue; } else if (valueToConvert is IEnumerable) { stringValue = Resources.PropertyEditing_DefaultEnumerableStringValue; } else if (typeConverter != null && typeConverter.CanConvertTo(typeof(string))) { stringValue = typeConverter.ConvertToString(null, XamlCultureInfo, valueToConvert); } else { stringValue = valueToConvert.ToString(); } return stringValue ?? string.Empty; } // // Redirect the call to parent PropertyEntry // //protected override object GetValueCore() { return ParentModelPropertyEntry.GetValueCore(); } // // Redirect the call to parent PropertyEntry // // protected override void SetValueCore(object value) { if (value == ClearValueMarker) { ParentModelPropertyEntry.ClearValue(); } else { ParentModelPropertyEntry.SetValueCore(value); } } //// Apply the FlowDirection to the resource. // private void CheckAndSetFlowDirectionResource() { // Check if the value being edited is FlowDirection // and if so, reset the resource to the current value that the user is setting. // This will refresh the property inspector and all the string editors, showing "string" properties, // would have their FlowDirection set to the current value. if (ParentModelPropertyEntry.PropertyName.Equals(FrameworkElement.FlowDirectionProperty.Name)) { object value = Value; if (value != null) { PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"] = value; } } } //// Redirect the call to parent PropertyEntry // public override void ClearValue() { ParentModelPropertyEntry.ClearValue(); } //// Fires the appropriate PropertyChanged events // public void OnUnderlyingModelChanged() { CheckAndSetFlowDirectionResource(); this.NotifyRootValueChanged(); } //// Fires the appropriate PropertyChanged events // public void OnUnderlyingSubModelChanged() { this.NotifySubPropertyChanged(); } //// Called when there is an error setting or getting a PropertyValue. // Displays an error dialog. // // protected override void OnPropertyValueException(PropertyValueExceptionEventArgs e) { if (e.Source == PropertyValueExceptionSource.Set) { if (e.Exception != null) { Debug.WriteLine(e.Exception.ToString()); } ErrorReporting.ShowErrorMessage(e.Exception.Message); base.OnPropertyValueException(e); } else { base.OnPropertyValueException(e); } } //// Debuging-friendly ToString() // //[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public override string ToString() { try { string value; if (this.Value == null) { value = "{null}"; } else { value = this.StringValue; if (string.IsNullOrEmpty(value)) { value = "{empty}"; } } return string.Format(CultureInfo.CurrentCulture, "{0} (PropertyValue)", value ?? "{null}"); } catch { return base.ToString(); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10811,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyMerger.cs/1305376/ModelPropertyMerger.cs,4/26/2010 8:54:39 AM,11544 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Text; using System.Activities.Presentation.Model; // // Helper class that knows how to merge ModelProperties across multiple ModelItems // internal static class ModelPropertyMerger { private static IEnumerable> _emptyCollection; private static IEnumerable > EmptyCollection { get { if (_emptyCollection == null) { _emptyCollection = new List >(); } return _emptyCollection; } } // // Uber method that returns a list of list of ModelProperties that represent the // merged set of properties across the specified ModelItems // // ModelItems to examine // Count on ModelItems to examine //List of list of merged properties public static IEnumerable> GetMergedProperties(IEnumerable items, int itemCount) { return GetMergedPropertiesHelper(new ModelItemExpander(items, itemCount)); } // // Uber method that returns a list of list of ModelProperties that represent the // merged set of sub-properties across the values of the specified parent properties // // ModelProperties to examine //List of list of merged properties public static IEnumerable> GetMergedSubProperties(ICollection parentProperties) { return GetMergedPropertiesHelper(new SubPropertyExpander(parentProperties)); } // // Finds the consolidated default property name and returns it. If there is no shared // default property betweem the specified items, null is returned. // // Items to examine //Shared default property, if any. public static string GetMergedDefaultProperty(IEnumerableitems) { if (items == null) { return null; } bool firstIteration = true; string mergedDefaultProperty = null; foreach (ModelItem item in items) { string currentDefaultProperty = ExtensibilityAccessor.GetDefaultProperty(item.ItemType); if (firstIteration) { mergedDefaultProperty = currentDefaultProperty; } else if (!string.Equals(currentDefaultProperty, mergedDefaultProperty)) { mergedDefaultProperty = null; } if (string.IsNullOrEmpty(mergedDefaultProperty)) { return null; } firstIteration = false; } return mergedDefaultProperty; } // Optimization that speeds up the common case (single selection) private static IEnumerable > GetMergedPropertiesHelper(PropertyExpander expander) { // Check empty list if (expander == null || expander.ContainerCount == 0) { return EmptyCollection; } if (expander.ContainerCount == 1) { // Corner case - one object selected, don't bother with merging return GetFirstProperties(expander); } else { // Calculate the list anew return GetMergedPropertiesCore(expander); } } // Optimization that speeds up the common case (single selection) private static IEnumerable > GetFirstProperties(PropertyExpander expander) { IEnumerator > propertyContainers = expander.GetEnumerator(); propertyContainers.MoveNext(); if (propertyContainers.Current != null) { foreach (ModelProperty property in propertyContainers.Current) { yield return new ModelProperty[] { property }; } } } private static IEnumerable > GetMergedPropertiesCore(PropertyExpander expander) { Dictionary > counter = new Dictionary >(); int containerCounter = 0; foreach (IEnumerable properties in expander) { if (properties == null) { yield break; } foreach (ModelProperty property in properties) { IList existingModelPropertiesForProperty; if (!counter.TryGetValue(property.Name, out existingModelPropertiesForProperty)) { if (containerCounter == 0) { existingModelPropertiesForProperty = new List (expander.ContainerCount); counter[property.Name] = existingModelPropertiesForProperty; } else { // This property has not been encountered yet in the previous objects, // so skip it altogether. continue; } } if (existingModelPropertiesForProperty.Count < containerCounter) { // There has been a ModelItem in the list that didn't have this property, // so delete any record of it and skip it in the future. counter.Remove(property.Name); continue; } // Verify that the properties are equivalent if (containerCounter > 0 && !ModelUtilities.AreEquivalent( existingModelPropertiesForProperty[containerCounter - 1], property)) { // They are not, so scrap this property altogether counter.Remove(property.Name); continue; } existingModelPropertiesForProperty.Add(property); } containerCounter++; } foreach (KeyValuePair > pair in counter) { // Once again, if there is a property that is not shared by all // selected items, ignore it if (pair.Value.Count < containerCounter) { continue; } // We should not set the same instance to multiple properties, // so ignore types that are not value type or string in case of multi-selection if(pair.Value.Count > 1 && !(pair.Value[0].PropertyType.IsValueType || pair.Value[0].PropertyType.Equals(typeof(string)))) { continue; } yield return (IList )pair.Value; } } // // We use the same code to merge properties across a set of ModelItems as well // as to merge sub-properties across a set of ModelProperties. PropertyExpander // class is a helper that abstracts the difference between these two inputs, so // that the merge methods don't have to worry about it. // private abstract class PropertyExpander : IEnumerable> { public abstract int ContainerCount { get; } public abstract IEnumerator > GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // // Version of PropertyExpander that returns the properties of a set of ModelItems // private class ModelItemExpander : PropertyExpander { private IEnumerable_items; private int _itemCount; public ModelItemExpander(IEnumerable items, int itemCount) { _items = items; _itemCount = itemCount; } public override int ContainerCount { get { return _itemCount; } } public override IEnumerator > GetEnumerator() { if (_items == null) { yield break; } foreach (ModelItem item in _items) { if (item.Properties == null) { continue; } yield return item.Properties; } } } // // Version of PropertyExpander that returns the sub-properties of a set of // ModelProperty values. // private class SubPropertyExpander : PropertyExpander { private ICollection_parentProperties; public SubPropertyExpander(ICollection parentProperties) { _parentProperties = parentProperties; } public override int ContainerCount { get { return _parentProperties == null ? 0 : _parentProperties.Count; } } public override IEnumerator > GetEnumerator() { if (_parentProperties == null) { yield break; } foreach (ModelProperty property in _parentProperties) { yield return ExtensibilityAccessor.GetSubProperties(property); } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10812,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyIndexer.cs/1305376/ModelPropertyIndexer.cs,4/26/2010 8:54:39 AM,12649 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Text; using System.Activities.Presentation; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.Properties; // // ModelPropertyIndexer is used to represent ModelItems in a collection. As such // and unlike ModelProperty, the class wraps around a single ModelItem instead of // one or more ModelProperties. // internal class ModelPropertyIndexer : ModelPropertyEntryBase { private static readonly ICollection EmptyCollection = new object[0]; private ModelItem _modelItem; private int _index; private ModelPropertyValueCollection _parentCollection; private CachedValues _valueCache; //// Basic ctor. Note, however, that this class should only be created by ModelPropertyValueCollection // as that class ensures that the new instance is correctly added and removed from the // ModelItemMap. // // ModelItem to wrap around // Index of the ModelItem in the collection // Parent collection public ModelPropertyIndexer( ModelItem modelItem, int index, ModelPropertyValueCollection parentCollection) : base(parentCollection.ParentValue) { if (modelItem == null) { throw FxTrace.Exception.ArgumentNull("modelItem"); } if (parentCollection == null) { throw FxTrace.Exception.ArgumentNull("parentCollection"); } _modelItem = modelItem; _index = index; _parentCollection = parentCollection; _valueCache = new CachedValues(this); } //// Gets the index of the underlying ModelItem. If index < 0, this // ModelPropertyIndexer no longer belongs to a collection and setting its value // will fail. // public int Index { get { return _index; } internal set { _index = value; } } //// Gets the name category name of the parent collection // public override string CategoryName { get { return _parentCollection.ParentValue.ParentProperty.CategoryName; } } //// Gets the description of the parent collection // public override string Description { get { return _parentCollection.ParentValue.ParentProperty.Description; } } //// Gets the IsAdvanced flag of the parent collection // public override bool IsAdvanced { get { return _parentCollection.ParentValue.ParentProperty.IsAdvanced; } } //// Returns true // public override bool IsReadOnly { get { return true; } } //// Gets the index of this item as string // public override string PropertyName { get { return _index.ToString(CultureInfo.InvariantCulture); } } //// Gets the type of items in the parent collection // public override Type PropertyType { get { return _modelItem.ItemType; } } //// Returns null because there are no ValueEditors for values that belong to a collection // public override PropertyValueEditor PropertyValueEditor { get { // There are no ValueEditors for items in a collection return null; } } //// Returns an empty collection - there are no StandardValues for items in a collection // public override ICollection StandardValues { get { // There are no StandardValues for items in a collection return EmptyCollection; } } //// Returns false - ModelPropertyIndexers always wrap around a single ModelItem // public override bool IsMixedValue { get { return false; } } //// Returns Local - this PropertyEntry always contains a collection item value which is local // public override PropertyValueSource Source { get { return DependencyPropertyValueSource.Local; } } //// Gets the TypeConverter // public override TypeConverter Converter { get { return _valueCache.Converter; } } //// Gets the Type of the contained ModelItem // public override Type CommonValueType { get { return _modelItem.ItemType; } } //// Gets the sub-properties of the underlying item // public override PropertyEntryCollection SubProperties { get { return _valueCache.SubProperties; } } //// Gets the collection of the underlying ModelItem // public override PropertyValueCollection Collection { get { return _valueCache.Collection; } } //// Gets the depth of this property in the PI sub-property tree. // Since this class represents an item in the collection, it's depth // resets to -1 so that it's children start at depth 0 ( -1 + 1 = 0) again. // public override int Depth { get { return -1; } } //// Gets the underlying ModelItem // internal ModelItem ModelItem { get { return _modelItem; } } //// Gets a flag indicating whether the underlying collection instance has already been // initialized. Optimization. // internal override bool CollectionInstanceExists { get { return _valueCache.CollectionInstanceExists; } } //// Creates a new ModelPropertyValue instance // //New ModelPropertyValue instance protected override PropertyValue CreatePropertyValueInstance() { return new ModelPropertyValue(this); } //// Gets the actual object instance respresented by this class // //Actual object instance respresented by this class public override object GetValueCore() { return _modelItem.GetCurrentValue(); } //// Sets the value of the collection item at the same position as the // ModelItem represented by this class. Identical to removing the old // item and adding a new one // // Value to set public override void SetValueCore(object value) { throw FxTrace.Exception.AsError(new InvalidOperationException(Resources.PropertyEditing_ErrorSetValueOnIndexer)); } //// Throws an exception -- invalid operation // public override void ClearValue() { throw FxTrace.Exception.AsError(new InvalidOperationException(Resources.PropertyEditing_ClearIndexer)); } //// Opens a new ModelEditingScope with the specified description. // // Change description (may be null). //New, opened ModelEditingScope with the specified description internal override ModelEditingScope BeginEdit(string description) { return description == null ? _modelItem.BeginEdit() : _modelItem.BeginEdit(description); } //// Called when one of the sub-properties exposed by this class changes // protected override void OnUnderlyingSubModelChangedCore() { // Do nothing. There is nothing in CachedValues right now that would need to // be refreshed as a result of one of our sub-properties changing value } // Cached values that need to be nixed when the underlying ModelItem changes // (ie. someone calls SetValueCore()). Pretty much everything in here is an "expensive" // calculation which requires us to evaluate some attributes associated with the given // property of set of properties, so we cache the return values and keep that cache // in a single place so that it's easy to know what needs to be ----d when the underlying // ModelItem changes. private class CachedValues { private static readonly TypeConverter NoTypeConverter = new TypeConverter(); private ModelPropertyIndexer _parent; private TypeConverter _converter; private ModelPropertyEntryCollection _subProperties; private ModelPropertyValueCollection _collection; public CachedValues(ModelPropertyIndexer indexer) { _parent = indexer; } public TypeConverter Converter { get { if (_converter == null) { _converter = ExtensibilityAccessor.GetTypeConverter(_parent._modelItem); _converter = _converter ?? NoTypeConverter; } return _converter == NoTypeConverter ? null : _converter; } } public ModelPropertyEntryCollection SubProperties { get { if (_subProperties == null) { _subProperties = new ModelPropertyEntryCollection(_parent); } return _subProperties; } } public bool CollectionInstanceExists { get { return _collection != null; } } public ModelPropertyValueCollection Collection { get { if (_collection == null) { _collection = new ModelPropertyValueCollection(_parent.ModelPropertyValue); } return _collection; } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10813,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelPropertyEntry.cs/1438166/ModelPropertyEntry.cs,4/26/2010 8:54:39 AM,54222 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using System.Windows; using System.Windows.Data; using System.Windows.Markup; using System.Windows.Media; using System.Activities.Presentation; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Services; using System.Activities.Presentation.Internal.Properties; using System.Runtime; using System.Activities.Presentation.Internal.PropertyEditing.Editors; //// ModelPropertyEntry is a wrapper around Cider's ModelProperty and that // exposes its functionality through the PropertyEntry object model. It handles // all get / set / clear functionality. // internal class ModelPropertyEntry : ModelPropertyEntryBase, IComparable { private const string _textBlockInlinesPropertyName = "Inlines"; // Property names for TextBlock properties that require special handling private static string _textBlockTextPropertyName = System.Windows.Controls.TextBlock.TextProperty.Name; // Cached values that need to be nixed when the underlying ModelProperty changes // (ie. someone calls SetProperty()) private CachedValues _valueCache; // List of ModelProperties that this instance wraps around. It // is guaranteed to contain at least one ModelProperty instance (single // selection scenario), but it may contain multiple ModelProperty instances // (multi-select scenario) private List_properties = new List (); // Flag indicating whether this instance points to something valid. // Used both as a perf optimization from PropertyInspector.UpdateCategories() // as well as to disable the making of changes to ModelPropertyEntries // when the underlying ModelProperties are no longer available. private bool _disassociated; // Bool indicating whether this property is a wrapper around the Name property // (which we special case for display purposes) private bool _wrapsAroundNameProperty; // // Basic ctor that wraps around a single ModelProperty // // ModelProperty to wrap around // Parent PropertyValue, if any [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public ModelPropertyEntry(ModelProperty property, ModelPropertyValue parentValue) : base(parentValue) { _valueCache = new CachedValues(this); SetUnderlyingModelPropertyHelper(property, false); } //// Basic ctor that wraps around multiple ModelProperties in the // multi-select scenario. The code assumes that the ModelProperties in // the set all represent the same property (eg. Background) across different // ModelItems (eg. Button, Grid, and ComboBox). // // Set of ModelProperties to wrap around // Parent PropertyValue, if any [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public ModelPropertyEntry(IEnumerablepropertySet, ModelPropertyValue parentValue) : base(parentValue) { _valueCache = new CachedValues(this); SetUnderlyingModelPropertyHelper(propertySet, false); } // // Gets the name of the contained property // public override string PropertyName { get { return _properties[0].Name; } } //// Gets the display name of the contained property, if any. // Defaults to property name if none is found. // public override string DisplayName { get { return _valueCache.DisplayName; } } //// Gets the type of the contained property // public override Type PropertyType { get { return _properties[0].PropertyType; } } //// Gets the category name of the contained property // public override string CategoryName { get { return _valueCache.CategoryName; } } //// Gets the description of the contained property // public override string Description { get { return _valueCache.Description; } } //// Gets a flad indicating whether the property is read-only // public override bool IsReadOnly { get { return _valueCache.IsReadOnly; } } //// Gets a flag indicating whether the property is advanced // public override bool IsAdvanced { get { return _valueCache.IsAdvanced; } } //// Gets a flag indicating whether this property is browsable or not // (All properties are exposed through the object model. It's up to the // UI to make the display / don't-display decision) // public bool IsBrowsable { get { return _valueCache.IsBrowsable; } } //// Gets a collection of standard values that can be assigned to // the property // public override ICollection StandardValues { get { return _valueCache.StandardValues; } } //// Gets a flag indicating whether the list of StandardValues is complete // or whether the user can type a value that's different from the ones in the list // Note: this property is referenced from XAML // [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public bool StandardValuesExclusive { get { return _valueCache.StandardValuesExclusive; } } //// Gets the PropertyValueEditor for this property // public override PropertyValueEditor PropertyValueEditor { get { return _valueCache.PropertyValueEditor; } } //// Gets the TypeConverted for the contained property // public override TypeConverter Converter { get { return _valueCache.Converter; } } //// Gets the value Type for all contained properties if it matches, // null otherwise // public override Type CommonValueType { get { return _valueCache.CommonValueType; } } //// Returns true if the contained ModelProperties don't // share the same value // public override bool IsMixedValue { get { return _valueCache.IsMixedValue; } } //// Gets the source of the value contained by this property // public override PropertyValueSource Source { get { return _valueCache.Source; } } //// Gets the sub-properties of the contained property // public override PropertyEntryCollection SubProperties { get { return _valueCache.SubProperties; } } //// Gets the collection of PropertyValues if this ModelProperty represents // a collection // public override PropertyValueCollection Collection { get { return _valueCache.Collection; } } //// Gets a flag indicating whether the collection instance has already // been instantiated (perf optimization) // internal override bool CollectionInstanceExists { get { return _valueCache.CollectionInstanceExists; } } //// Gets the underlying collection of ModelProperties // internal ICollectionModelPropertySet { get { return _properties; } } // // Gets the first underlying ModelProperty for cases when // this class wraps around multiple // internal ModelProperty FirstModelProperty { get { return _properties[0]; } } //// Gets an order token for property ordering // internal PropertyOrder PropertyOrder { get { return _valueCache.PropertyOrder; } } //// Gets or sets the disassociated flag // internal bool Disassociated { get { return _disassociated; } set { _disassociated = value; } } //// Gets a flag indicating whether this instance represents an attached DP // internal bool IsAttached { get { return _valueCache.IsAttached; } } //// Gets a list of CategoryEditor types associated with this PropertyEntry // internal IEnumerableCategoryEditorTypes { get { return _valueCache.CategoryEditorTypes; } } // // Returns true if there are standard values for this property. // protected override bool HasStandardValues { get { return _valueCache.StandardValuesSupported; } } //// Replaces the underlying ModelProperty/ies with the specified ModelProperties. // Fires the appropriate PropertyChanged events // // Property set to wrap around public void SetUnderlyingModelProperty(IEnumerablepropertySet) { SetUnderlyingModelPropertyHelper(propertySet, true); } private void SetUnderlyingModelPropertyHelper(ModelProperty property, bool firePropertyValueChangedEvents) { if (property == null) { throw FxTrace.Exception.ArgumentNull("property"); } // Store the value ClearUnderlyingModelProperties(); AddUnderlyingModelProperty(property); // Clear any cached values RefreshCache(); if (firePropertyValueChangedEvents) { // Update the PropertyValue (always, except when it doesn't exist yet (ctor time)) this.ModelPropertyValue.OnUnderlyingModelChanged(); } } private void SetUnderlyingModelPropertyHelper(IEnumerable propertySet, bool firePropertyValueChangedEvents) { if (propertySet == null) { throw FxTrace.Exception.ArgumentNull("propertySet"); } // Attempt to store the values int count = 0; foreach (ModelProperty property in propertySet) { if (property == null) { continue; } if (count == 0) { ClearUnderlyingModelProperties(); } AddUnderlyingModelProperty(property); count++; } // Throw if the underlying property set was invalid if (count == 0) { throw FxTrace.Exception.AsError(new ArgumentException("Cannot set the underlying ModelProperty to an empty set.")); } // Clear any cached values RefreshCache(); if (firePropertyValueChangedEvents) { // Update the PropertyValue (always, except when it doesn't exist yet (ctor time)) this.ModelPropertyValue.OnUnderlyingModelChanged(); } } // Adds the property to the internal collection list and hooks into its PropertyChanged event private void AddUnderlyingModelProperty(ModelProperty property) { if (property == null) { return; } property.Parent.PropertyChanged += new PropertyChangedEventHandler(OnUnderlyingPropertyChanged); _properties.Add(property); _wrapsAroundNameProperty = "Name".Equals(property.Name); } internal void Disconnect() { foreach (ModelProperty property in _properties) { property.Parent.PropertyChanged -= new PropertyChangedEventHandler(OnUnderlyingPropertyChanged); } } // Removes all properties from the internal collection and unhooks from their PropertyChanged events private void ClearUnderlyingModelProperties() { foreach (ModelProperty property in _properties) { property.Parent.PropertyChanged -= new PropertyChangedEventHandler(OnUnderlyingPropertyChanged); } _properties.Clear(); _wrapsAroundNameProperty = false; } // Event handler for PropertyChanged event. Called whenever any of the underlying properties that // this ModelPropertyEntry wraps around changes. private void OnUnderlyingPropertyChanged(object sender, PropertyChangedEventArgs e) { if (!this.PropertyName.Equals(e.PropertyName)) { return; } this.OnUnderlyingModelChanged(); // If this property is a sub-property of some other property we know and care // about, notify the parents as well PropertyValue parentValue = this.ParentValue; while (parentValue != null) { ModelPropertyEntryBase parentProperty = (ModelPropertyEntryBase)parentValue.ParentProperty; parentProperty.OnUnderlyingSubModelChanged(); parentValue = parentProperty.ParentValue; } } // // Clear any cached values // protected override void RefreshCache() { base.RefreshCache(); _valueCache.ClearAll(); } //// Gets the underlying value as an object instance. Mixed values will // return null. // //Underlying value contained by this property. public override object GetValueCore() { if (this.IsMixedValue) { return null; } object retValue = ModelUtilities.GetSafeComputedValue(_properties[0]); return retValue; } //// Sets the value of the underlying property / ies. // // Value to set public override void SetValueCore(object value) { // If this ModelPropertyEntry instance is no longer hooked up into // the underlying model, ignore calls to SetValueCore() if (_disassociated) { return; } bool textBlockTextHackNeeded = false; ListtextBlockTextProperties = null; if (typeof(System.Windows.Controls.TextBlock).IsAssignableFrom(_properties[0].Parent.ItemType)) { textBlockTextHackNeeded = true; } // POSSIBLE OPTIMIZATION: remember which properties were altered. When on Idle we // receive global property changed events, ignore the ones we know about using (ModelEditingScope group = _properties[0].Parent.BeginEdit( string.Format( CultureInfo.CurrentCulture, Resources.PropertyEditing_UndoText, this.DisplayName))) { for (int i = 0; i < _properties.Count; i++) { if (textBlockTextHackNeeded && _properties[i].Name.Equals(_textBlockTextPropertyName)) { // We need to set Text after we clear inlines! if (textBlockTextProperties == null) { textBlockTextProperties = new List (); } textBlockTextProperties.Add(_properties[i]); continue; } _properties[i].SetValue(value); } // TextBlock has very bad IAddChild behavior with two properties contributing and having different // views into the content (Text and Inlines). To simplify editing, we clear Inlines when Text is set // which is what most users want anyways if (textBlockTextProperties != null) { foreach (ModelProperty textBlockTextProperty in textBlockTextProperties) { ModelProperty inlinesProperty = textBlockTextProperty.Parent.Properties[_textBlockInlinesPropertyName]; if (inlinesProperty != null && inlinesProperty.Collection != null) { inlinesProperty.Collection.Clear(); } textBlockTextProperty.SetValue(value); } } if (group != null) { group.Complete(); } } _valueCache.ClearValueRelatedCacheItems(); NotifyParentOfNameChanged(); } // // Clears the underlying property / ies. // public override void ClearValue() { // If this ModelPropertyEntry instance is no longer hooked up into // the underlying model, ignore calls to ClearValue() if (_disassociated) { return; } // POSSIBLE OPTIMIZATION: remember which properties were altered. When on Idle we // receive global property changed events, ignore the ones we know about using (ModelEditingScope group = _properties[0].Parent.BeginEdit( string.Format( CultureInfo.CurrentCulture, Resources.PropertyEditing_UndoText, this.DisplayName))) { for (int i = 0; i < _properties.Count; i++) { _properties[i].ClearValue(); } group.Complete(); } _valueCache.ClearValueRelatedCacheItems(); NotifyParentOfNameChanged(); } // If this property happens to wrap around the "Name" property, give our parent // (if one exists) a heads-up that the value has changed. We use this mechanism // to update display names of items in a collection editor. private void NotifyParentOfNameChanged() { if (!_wrapsAroundNameProperty) { return; } ModelPropertyValue parentValue = this.ParentValue as ModelPropertyValue; if (parentValue == null) { return; } // This PropertyEntry is the Name sub-property of another PropertyValue, // so let our parent know that its name has changed. parentValue.OnNameSubPropertyChanged(); } //// Called when the underlying ModelProperty changes. Clears any cached // values and fires the appropriate changed events. // internal void OnUnderlyingModelChanged() { _valueCache.ClearValueRelatedCacheItems(); this.ModelPropertyValue.OnUnderlyingModelChanged(); } //// Called when the sub-property of the underlying ModelProperty changes. // protected override void OnUnderlyingSubModelChangedCore() { _valueCache.ClearSubValueRelatedCacheItems(); } //// Creates new instance of ModelPropertyValue // //New instance of ModelPropertyValue protected override PropertyValue CreatePropertyValueInstance() { return new ModelPropertyValue(this); } //// Opens a new ModelEditingScope // // Change description (may be null) //A new, opened ModelEditingScope internal override ModelEditingScope BeginEdit(string description) { return description == null ? FirstModelProperty.Parent.BeginEdit() : FirstModelProperty.Parent.BeginEdit(description); } // IPropertyFilterTarget Members //// IPropertyFilterTarget method. We override the default behavior which matches // both property DisplayName as well as the property Type name. // // the predicate to match against //true if there is a match public override bool MatchesPredicate(PropertyFilterPredicate predicate) { return predicate == null ? false : predicate.Match(this.DisplayName); } // IComparable Members //// Compares 'this' with the object passed into it using the ModelPropertyEntryComparer, // which looks at both PropertyOrder as well as DisplayName to do the comparison // // Object to compare this instance to //Comparison result public int CompareTo(object obj) { return PropertyEntryPropertyOrderComparer.Instance.Compare(this, obj); } //// Debuging-friendly ToString() // //[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public override string ToString() { try { if (string.Equals(this.PropertyName, this.DisplayName)) { return string.Format(CultureInfo.CurrentCulture, "{0} (PropertyEntry)", this.PropertyName); } else { return string.Format(CultureInfo.CurrentCulture, "{0} (\"{1}\" - PropertyEntry)", this.PropertyName, this.DisplayName); } } catch { return base.ToString(); } } // Cached values that need to be nixed when the underlying ModelProperty changes // (ie. someone calls SetProperty()). Pretty much everything in here is an "expensive" // calculation which requires us to evaluate some attributes associated with the given // property or a set of properties, so we cache the return values and keep that cache // in a single place so that it's easy to know what needs to be ----d when the underlying // ModelProperties change. private class CachedValues { private static readonly PropertyValueEditor NoPropertyValueEditor = new PropertyValueEditor(); private static readonly PropertyOrder NoPropertyOrder = PropertyOrder.CreateAfter(PropertyOrder.Default); private static readonly TypeConverter NoTypeConverter = new TypeConverter(); private static readonly ValueSerializer NoSerializer = new NoValueSerializer(); private static readonly Type NoCommonValueType = typeof(CachedValues); // some private type that we can use as a marker private static readonly List NoCategoryEditorTypes = new List (); private static readonly PropertyValueSource NoSource = new NoPropertyValueSource(); private ModelPropertyEntry _parent; // Cached values private string _displayName; private string _categoryName; private string _description; private bool? _isAdvanced; private bool? _isBrowsable; private bool? _isReadOnly; private bool? _isAttached; private ArrayList _standardValues; private bool? _standardValuesExclusive; private bool? _standardValuesSupported; private PropertyValueEditor _propertyValueEditor; private bool? _isMixedValue; private PropertyValueSource _source; private ModelPropertyEntryCollection _subProperties; private ModelPropertyValueCollection _collection; private TypeConverter _converter; private ValueSerializer _valueSerializer; private Type _commonValueType; private PropertyOrder _propertyOrder; private IEnumerable _categoryEditorTypes; public CachedValues(ModelPropertyEntry parent) { _parent = parent; } // // Gets the display name of the contained property, if any. // Defaults to property name if none is found. // public string DisplayName { get { if (_displayName == null) { _displayName = ExtensibilityAccessor.GetDisplayName(_parent.FirstModelProperty) ?? _parent.PropertyName; } Fx.Assert(_displayName != null, "_displayName should not be null"); return _displayName; } } //// Pick the first category name // public string CategoryName { get { if (_categoryName == null) { _categoryName = ExtensibilityAccessor.GetCategoryName(_parent.FirstModelProperty); } Fx.Assert(_categoryName != null, "_categoryName should not be null"); return _categoryName; } } //// Pick the first description // public string Description { get { if (_description == null) { _description = ExtensibilityAccessor.GetDescription(_parent.FirstModelProperty); } Fx.Assert(_description != null, "_description should not be null"); return _description; } } //// OR mutliple values of IsAdvanced together // public bool IsAdvanced { get { if (_isAdvanced == null) { _isAdvanced = false; for (int i = 0; i < _parent._properties.Count; i++) { _isAdvanced |= ExtensibilityAccessor.GetIsAdvanced(_parent._properties[i]); if (_isAdvanced == true) { break; } } } Fx.Assert(_isAdvanced != null, "_isAdvanced should not be null"); return (bool)_isAdvanced; } } //// AND multiple values of IsBrowsable together // public bool IsBrowsable { get { if (_isBrowsable == null) { _isBrowsable = true; for (int i = 0; i < _parent._properties.Count; i++) { // Look for the BrowsableAttribute bool? temp = ExtensibilityAccessor.IsBrowsable(_parent._properties[i]); // Go by the IsReadOnly flag, if not found if (temp == null) { temp = !this.IsReadOnly; } // AND multiple values together _isBrowsable &= (bool)temp; if (_isBrowsable == false) { break; } } } Fx.Assert(_isBrowsable != null, "_isBrowsable should not be null"); return (bool)_isBrowsable; } } //// Gets a flags indicating whether this property is read only. // public bool IsReadOnly { get { if (_isReadOnly == null) { _isReadOnly = ExtensibilityAccessor.IsReadOnly( _parent._properties, new ExtensibilityAccessor.IsMixedValueEvaluator(delegate() { return this.IsMixedValue; })); Fx.Assert(_isReadOnly != null, "_isReadOnly should not be null"); } return (bool)_isReadOnly; } } //// Merge collection of standard values and only present the subset that exists in all. // We do fancy magic here because presenting the user with invalid set of StandardValues // could actually cause bad things to happen (read: exceptions when the value is actually changed) // public ICollection StandardValues { get { if (_standardValues == null) { // Note: this.Converter will return the converter associated with _parent._properties[0] if (ExtensibilityAccessor.GetStandardValuesSupported(this.Converter)) { _standardValues = ExtensibilityAccessor.GetStandardValues(this.Converter); } if (_standardValues == null) { _standardValues = new ArrayList(); } for (int i = 1; i < _parent._properties.Count && _standardValues.Count > 0; i++) { ArrayList nextSetOfValues = null; if (ExtensibilityAccessor.GetStandardValuesSupported(_parent._properties[i].Converter)) { nextSetOfValues = ExtensibilityAccessor.GetStandardValues(_parent._properties[i].Converter); } if (nextSetOfValues == null || nextSetOfValues.Count == 0) { // The AND of something and nothing = nothing, so clear any remaining list and exit _standardValues.Clear(); break; } for (int j = 0; j < _standardValues.Count; j++) { object expectedValue = _standardValues[j]; if (!nextSetOfValues.Contains(expectedValue)) { _standardValues.RemoveAt(j); j--; continue; } } } } Fx.Assert(_standardValues != null, "_standardValues should not be null"); return _standardValues; } } //// Gets a flag indicating whether the list of StandardValues is complete // or whether the user can type a value that's different from the ones in the list // public bool StandardValuesExclusive { get { if (_standardValuesExclusive == null) { _standardValuesExclusive = (this.Converter == null || this.Converter.GetStandardValuesExclusive()); } Fx.Assert(_standardValuesExclusive != null, "_standardValuesExclusive should not be null"); return (bool)_standardValuesExclusive; } } //// Gets a flag indicating whether the StandardValues list has any contents. // public bool StandardValuesSupported { get { if (_standardValuesSupported == null) { _standardValuesSupported = (this.Converter != null && this.Converter.GetStandardValuesSupported()); } Fx.Assert(_standardValuesSupported != null, "_standardValuesSupported should not be null"); return (bool)_standardValuesSupported; } } //// Pick the editor of the first ModelProperty // public PropertyValueEditor PropertyValueEditor { get { if (_propertyValueEditor == null) { _propertyValueEditor = ExtensibilityAccessor.GetCustomPropertyValueEditor(_parent.FirstModelProperty) ?? ExtensibilityAccessor.GetSubPropertyEditor(_parent.FirstModelProperty); if (_propertyValueEditor == null && _parent.PropertyType == typeof(bool)) { _propertyValueEditor = new BoolViewEditor(); } _propertyValueEditor = _propertyValueEditor == null ? NoPropertyValueEditor : _propertyValueEditor; } return _propertyValueEditor == NoPropertyValueEditor ? null : _propertyValueEditor; } } public bool IsMixedValue { get { if (_isMixedValue == null) { _isMixedValue = false; if (_parent._properties.Count > 1) { object mergedValue = null; string mergedValueString = null; ValueSerializer valueSerializer = null; for (int i = 0; i < _parent._properties.Count; i++) { ModelProperty property = _parent._properties[i]; if (i == 0) { // Note: Calling GetValue on ModelProperty has the potential to // to reset internal stores and, even though the value doesn't change, // we get a value changed notification. That notification clears // our _isMixedValue, which, in fact, we want to retain. // bool oldIsMixedValue = (bool)_isMixedValue; mergedValue = ModelUtilities.GetSafeRawValue(property); _isMixedValue = oldIsMixedValue; } else { // See comment above bool oldIsMixedValue = (bool)_isMixedValue; object nextValue = ModelUtilities.GetSafeRawValue(property); _isMixedValue = oldIsMixedValue; // Are the objects equal? if (object.Equals(mergedValue, nextValue)) { continue; } // No, so if any of them is null, we might as well bail if (mergedValue == null || nextValue == null) { _isMixedValue = true; break; } valueSerializer = valueSerializer ?? this.ValueSerializer; // If there is no ValueSerializer found, we can't // be clever and need to bail if (valueSerializer == null) { _isMixedValue = true; break; } // If we can't even convert the original value to string, // there is nothing to compare, so we bail // the CanConvertToString call may throw an ArgumentException, for // example if mergedValue isn't a supported type try { if (mergedValueString == null && !valueSerializer.CanConvertToString(mergedValue, null)) { _isMixedValue = true; break; } } catch (ArgumentException) { _isMixedValue = true; break; } if (mergedValueString == null) { mergedValueString = valueSerializer.ConvertToString(mergedValue, null); } // Finally, check to see if the nextValue can be converted to string // and, if so, compare it to the mergedValue. if (!valueSerializer.CanConvertToString(nextValue, null) || string.CompareOrdinal(mergedValueString, valueSerializer.ConvertToString(nextValue, null)) != 0) { _isMixedValue = true; break; } } } } } return (bool)_isMixedValue; } } //// Gets the source of the given property // public PropertyValueSource Source { get { if (_source == null && this.IsMixedValue) { _source = NoSource; } if (_source == null) { foreach (ModelProperty property in _parent._properties) { if (_source == null) { _source = ExtensibilityAccessor.GetPropertySource(property); // Default value if we can't figure out anything else (this should never happen) Fx.Assert(_source != null, "Could not figure out the source for property " + _parent.PropertyName); _source = _source ?? DependencyPropertyValueSource.Local; } else if (_source != ExtensibilityAccessor.GetPropertySource(property)) { _source = NoSource; break; } } } return _source == NoSource ? null : _source; } } public ModelPropertyEntryCollection SubProperties { get { if (_subProperties == null) { _subProperties = new ModelPropertyEntryCollection(_parent); } return _subProperties; } } public bool CollectionInstanceExists { get { return _collection != null; } } public ModelPropertyValueCollection Collection { get { if (_collection == null) { _collection = new ModelPropertyValueCollection(_parent.ModelPropertyValue); } return _collection; } } //// Pick the first converter // public TypeConverter Converter { get { if (_converter == null) { _converter = ExtensibilityAccessor.GetTypeConverter(_parent.FirstModelProperty) ?? NoTypeConverter; } return _converter == NoTypeConverter ? null : _converter; } } //// Gets the Type of value instance for this property. For multi-properties, // CommonValueType returns the Type of all properties if it matches, null otherwise. // public Type CommonValueType { get { if (_commonValueType == null) { foreach (ModelProperty modelProperty in _parent.ModelPropertySet) { object value = modelProperty.ComputedValue; if (value != null) { Type valueType = value.GetType(); if (_commonValueType == null) { _commonValueType = valueType; } else if (_commonValueType != valueType) { _commonValueType = NoCommonValueType; break; } } else { _commonValueType = NoCommonValueType; break; } } _commonValueType = _commonValueType ?? NoCommonValueType; } return _commonValueType == NoCommonValueType ? null : _commonValueType; } } //// Pick the first PropertyOrder // public PropertyOrder PropertyOrder { get { if (_propertyOrder == null) { _propertyOrder = ExtensibilityAccessor.GetPropertyOrder(_parent.FirstModelProperty); _propertyOrder = _propertyOrder ?? NoPropertyOrder; } return _propertyOrder == NoPropertyOrder ? null : _propertyOrder; } } //// Gets a list of CategoryEditor types associated with this PropertyEntry // public IEnumerableCategoryEditorTypes { get { if (_categoryEditorTypes == null) { if (_parent.FirstModelProperty.IsAttached) { _categoryEditorTypes = ExtensibilityAccessor.GetCategoryEditorTypes(_parent.FirstModelProperty.AttachedOwnerType); } _categoryEditorTypes = _categoryEditorTypes ?? NoCategoryEditorTypes; } return _categoryEditorTypes == NoCategoryEditorTypes ? null : _categoryEditorTypes; } } public bool IsAttached { get { if (_isAttached == null) { _isAttached = _parent.PropertyName.IndexOf('.') > -1; } return (bool)_isAttached; } } // // Gets the ValueSerializer corresponding to the property type // private ValueSerializer ValueSerializer { get { if (_valueSerializer == null) { _valueSerializer = ValueSerializer.GetSerializerFor(_parent.PropertyType) ?? NoSerializer; } return _valueSerializer == NoSerializer ? null : _valueSerializer; } } // Clear everything this class caches public void ClearAll() { _categoryName = null; _description = null; _isAdvanced = null; _isBrowsable = null; _propertyValueEditor = null; _propertyOrder = null; _categoryEditorTypes = null; _displayName = null; // Internal properties we don't bind to and, hence, // don't need to fire PropertyChanged event: _isAttached = null; ClearValueRelatedCacheItems(); _parent.OnPropertyChanged("CategoryName"); _parent.OnPropertyChanged("Description"); _parent.OnPropertyChanged("IsAdvanced"); _parent.OnPropertyChanged("IsBrowsable"); _parent.OnPropertyChanged("PropertyValueEditor"); _parent.OnPropertyChanged("PropertyOrder"); _parent.OnPropertyChanged("CategoryEditorTypes"); _parent.OnPropertyChanged("DisplayName"); } // Clear value-related things that this class caches public void ClearValueRelatedCacheItems() { _subProperties = null; _collection = null; _standardValues = null; _standardValuesExclusive = null; _converter = null; _commonValueType = null; _source = null; _isReadOnly = null; _valueSerializer = null; ClearSubValueRelatedCacheItems(); _parent.OnPropertyChanged("StandardValues"); _parent.OnPropertyChanged("StandardValuesExclusive"); _parent.OnPropertyChanged("Converter"); _parent.OnPropertyChanged("CommonValueType"); _parent.OnPropertyChanged("IsReadOnly"); // The following properties are only exposed by ModelPropertyEntry, not PropertyEntry. // People should bind to these properties through the PropertyValue. // However, if they ---- up in Xaml, the binding will still work and if that happens // we should try to update them when things change. _parent.OnPropertyChanged("SubProperties"); _parent.OnPropertyChanged("Collection"); _parent.OnPropertyChanged("Source"); } public void ClearSubValueRelatedCacheItems() { _isMixedValue = null; // The following property is only exposed by ModelPropertyEntry, not PropertyEntry. // People should bind to this property through the PropertyValue. // However, if they ---- up in Xaml, the binding will still work and if that happens // we should try to update them when things change. _parent.OnPropertyChanged("IsMixedValue"); } private class NoPropertyValueSource : PropertyValueSource { public NoPropertyValueSource() { } } private class NoValueSerializer : ValueSerializer { public NoValueSerializer() { } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10814,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Model/ModelCategoryEntry.cs/1438166/ModelCategoryEntry.cs,4/26/2010 8:54:39 AM,13364 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Model { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.Data; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.PropertyInspector; using System.Activities.Presentation; //// Cider's concrete implementation of CategoryEntry (CategoryBase comes from Sparkle // and it has a few extra goodies that we want to reuse). This class implements // INotifyCollectionChanged. We need to push this implementation to the base class // in v2. // internal class ModelCategoryEntry : CategoryBase, INotifyCollectionChanged { private ObservableCollectionWorkaround_basicProperties; private ObservableCollectionWorkaround _advancedProperties; // // Basic ctor // // Localized name for this category public ModelCategoryEntry(string categoryName) : base(categoryName) { _basicProperties = new ObservableCollectionWorkaround(); _advancedProperties = new ObservableCollectionWorkaround (); } public event NotifyCollectionChangedEventHandler CollectionChanged; // // Gets the advanced properties contained in this category // public override ObservableCollectionAdvancedProperties { get { return _advancedProperties; } } // // Gets the basic properties contained in this category // public override ObservableCollectionBasicProperties { get { return _basicProperties; } } // // Gets a flag indicating whether this category contains any properties // internal bool IsEmpty { get { return _advancedProperties.Count + _basicProperties.Count == 0; } } //// Returns either the basic or the advanced bucket based on the IsAdvanced flag // set in the PropertyEntry itself // // Property to examine //The corresponding basic or advanced bucket internal ObservableCollectionWorkaroundGetBucket(PropertyEntry property) { if (property == null) { throw FxTrace.Exception.ArgumentNull("property"); } return property.IsAdvanced ? _advancedProperties : _basicProperties; } // // Adds the given property to the specified property bucket (use // ModelCategoryEntry.BasicProperties, ModelCategoryEntry.AdvancedProperties, or // ModelCategoryEntry.GetBucket()) sorted using the specified comparer. // // Property to add // Property bucket to populate // Sort algorithm to use // If set to true, NotifyCollectionChanged event is fired internal void Add( PropertyEntry property, ObservableCollectionbucket, IComparer comparer) { Add(property, bucket, comparer, true); } // // Adds the given property to the specified property bucket (use // ModelCategoryEntry.BasicProperties, ModelCategoryEntry.AdvancedProperties, or // ModelCategoryEntry.GetBucket()) sorted using the specified comparer. // private void Add( PropertyEntry property, ObservableCollection bucket, IComparer comparer, bool fireCollectionChangedEvent) { if (property == null) { throw FxTrace.Exception.ArgumentNull("property"); } if (bucket == null) { throw FxTrace.Exception.ArgumentNull("bucket"); } if (comparer == null) { throw FxTrace.Exception.ArgumentNull("comparer"); } ObservableCollectionWorkaround castBucket = bucket as ObservableCollectionWorkaround ; int insertionIndex = 0; if (castBucket == null) { Debug.Fail("Invalid property bucket. The property sort order will be broken."); } else { insertionIndex = castBucket.BinarySearch(property, comparer); if (insertionIndex < 0) { insertionIndex = ~insertionIndex; } } bucket.Insert(insertionIndex, property); if (fireCollectionChangedEvent) { FirePropertiesChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property)); } } // // Removes and re-adds the specified property from this category, if it existed // there to begin with. Noop otherwise. // // Use this method to refresh the cate----zation of a property if it suddenly // becomes Advanced if it was Basic before, or if its IsBrowsable status changes. // // Property to refresh // Property bucket to repopulate // Comparer to use to reinsert the given property in its new place internal void Refresh(ModelPropertyEntry property, ObservableCollectionbucket, IComparer sortComparer) { if (property == null) { throw FxTrace.Exception.ArgumentNull("property"); } if (bucket != _basicProperties && bucket != _advancedProperties) { Debug.Fail("Invalid bucket specified. Property was not refreshed."); return; } // Let's see if we know about this property ObservableCollectionWorkaround collection; collection = _advancedProperties; int index = collection.BinarySearch(property, null); if (index < 0) { collection = _basicProperties; index = collection.BinarySearch(property, null); } // If not, noop if (index < 0) { return; } // We know about this property, so refresh it. It may have changed // somehow (eg. switched from basic to advanced, become hidden, etc.) // so make sure it's thrown into the right bucket. collection.RemoveAt(index); Add(property, bucket, sortComparer, false); } // // This is a work-around fix because Blend's CategoryBase does not handle null filters (valid value) // correctly. We need to ask Blend to eventually fix this issue. // // Filter to apply, can be null public override void ApplyFilter(PropertyFilter filter) { if (filter == null) { this.MatchesFilter = true; this.BasicPropertyMatchesFilter = true; this.AdvancedPropertyMatchesFilter = true; foreach (PropertyEntry property in this.BasicProperties) { property.ApplyFilter(filter); } foreach (PropertyEntry property in this.AdvancedProperties) { property.ApplyFilter(filter); } } else { base.ApplyFilter(filter); } } // Another Blend work-around - we expose all properties through the OM, not just the // Browsable ones. However, as a result, we need to cull the non-browsable ones from // consideration. Otherwise, empty categories may appear. protected override bool DoesPropertyMatchFilter(PropertyFilter filter, PropertyEntry property) { property.ApplyFilter(filter); bool isBrowsable = true; ModelPropertyEntry modelPropertyEntry = property as ModelPropertyEntry; if (modelPropertyEntry != null) { //display given property if it is browsable or isBrowsable = modelPropertyEntry.IsBrowsable || // it may not be browsable, but if there is a category editor associated - display it anyway (this.CategoryEditors != null && this.CategoryEditors.Count != 0); } return isBrowsable && property.MatchesFilter; } //// Sets the Disassociated flag on all contained properties to True // internal void MarkAllPropertiesDisassociated() { MarkAllPropertiesDisassociated(_basicProperties); MarkAllPropertiesDisassociated(_advancedProperties); } //// Sets the Disassociated flag on all contained attached properties to True // internal void MarkAttachedPropertiesDisassociated() { MarkAttachedPropertiesDisassociated(_basicProperties); MarkAttachedPropertiesDisassociated(_advancedProperties); } //// Removes all properties from this category whose Disassociated flag is set to True // internal void CullDisassociatedProperties() { bool propertiesCulled = false; propertiesCulled |= CullDisassociatedProperties(_basicProperties); propertiesCulled |= CullDisassociatedProperties(_advancedProperties); if (propertiesCulled) { FirePropertiesChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } private static void MarkAllPropertiesDisassociated(ObservableCollectionWorkaroundpropertyList) { foreach (ModelPropertyEntry property in propertyList) { property.Disassociated = true; } } private static void MarkAttachedPropertiesDisassociated(ObservableCollectionWorkaround propertyList) { foreach (ModelPropertyEntry property in propertyList) { if (property.IsAttached) { property.Disassociated = true; } } } private static bool CullDisassociatedProperties(ObservableCollectionWorkaround propertyList) { bool propertiesCulled = false; for (int i = propertyList.Count - 1; i >= 0; i--) { ModelPropertyEntry property = (ModelPropertyEntry)propertyList[i]; if (property.Disassociated) { property.Disconnect(); propertyList.RemoveAt(i); propertiesCulled = true; } } return propertiesCulled; } // INotifyCollectionChanged Members private void FirePropertiesChanged(NotifyCollectionChangedEventArgs collectionChangedEventArgs) { // Fire both "Properties" changed events OnPropertyChanged("Properties"); OnPropertyChanged("Item[]"); // as well as the appropriate collection-changed event if (CollectionChanged != null) { CollectionChanged(this, collectionChangedEventArgs); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10815,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/FromExpression/Framework/Tolerances.cs/1305376/Tolerances.cs,4/26/2010 8:54:39 AM,1207 // -------------------------------------------------------------------- // Description: // Provides helper functions handling move and hit detection // tolerances. // // Copyright (c) Microsoft Corporation. All Rights Reserved. // ------------------------------------------------------------------- //Cider comment: // - Class has many members that we don't use. // - I have removed them to avoid FXCop warning and lowering code coverage //From \\authoring\Sparkle\Source\1.0.1083.0\Common\Source\Framework namespace System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework { using System; using System.Windows; using System.Windows.Media.Media3D; internal static class Tolerances { //Cider private static readonly double ZeroThreshold = 2.2204460492503131e-015; private const double ZeroThreshold = 2.2204460492503131e-015; public static bool NearZero(double d) { return Math.Abs(d) < Tolerances.ZeroThreshold; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. 10816,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/ExtensibilityAccessor.cs/1305376/ExtensibilityAccessor.cs,4/26/2010 8:54:39 AM,38108 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing { using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Windows; using System.Windows.Data; using System.Windows.Markup; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.PropertyInspector; using System.Activities.Presentation.Internal.PropertyEditing.Editors; using ModelUtilities = System.Activities.Presentation.Internal.PropertyEditing.Model.ModelUtilities; // // Static helper class that contains all extensibility-related code. No other code // under PropertyEditing should be looking up attributes and interpretting them. // In most cases, the methods here delegate to Sparkle's binaries to make sure that // both products behave consistently. // internal static class ExtensibilityAccessor { // Cache of Types to their respective DefaultProperties private static Dictionary_defaultPropertyCache = new Dictionary (); // // Gets the name of the category that the specified ModelProperty belongs to // // ModelProperty to examine //Name of the category that the specified ModelProperty belongs to public static string GetCategoryName(ModelProperty property) { CategoryAttribute attribute = GetAttribute(property); if (attribute == null || string.IsNullOrEmpty(attribute.Category)) { return CategoryAttribute.Default.Category; } else { return attribute.Category; } } // // Gets the StandardValues that are exposed by the specified TypeConverter // // TypeConverter to examine //StandardValues that are exposed by the specified TypeConverter public static ArrayList GetStandardValues(TypeConverter converter) { if (converter == null) { return null; } if (!converter.GetStandardValuesSupported()) { return null; } ICollection values = converter.GetStandardValues(); if (values == null) { return null; } // unwrap ModelItems if that's what the converter gives us ArrayList convertedValues = new ArrayList(values.Count); foreach (object value in values) { ModelItem item = value as ModelItem; if (item != null) { convertedValues.Add(item.GetCurrentValue()); } else { convertedValues.Add(value); } } return convertedValues; } //// Gets a flag indicating if a further call to GetStandardValues will // give back a non-zero collection. // // The type converter to check. //True if the type converter supports standard values. public static bool GetStandardValuesSupported(TypeConverter converter) { return (converter != null && converter.GetStandardValuesSupported()); } //// Look for and return any custom PropertyValueEditor defined for the specified ModelProperty // // ModelProperty to examine //A custom PropertyValueEditor for the specified ModelProperty (may be null) public static PropertyValueEditor GetCustomPropertyValueEditor(ModelProperty property) { if (property == null) { return null; } PropertyValueEditor editor = ExtensibilityMetadataHelper.GetValueEditor(property.Attributes, MessageLogger.Instance); //if property is a generic type, check for designer defined at generic type definition if (editor == null && property.PropertyType.IsGenericType) { Type genericType = property.PropertyType.GetGenericTypeDefinition(); editor = ExtensibilityMetadataHelper.GetValueEditor(TypeDescriptor.GetAttributes(genericType), MessageLogger.Instance); } return editor; } //// Returns an instance of SubPropertyEditor if the specified ModelProperty can be edited // using sub-properties, null otherwise. // // ModelProperty to examine //An instance of SubPropertyEditor if the specified ModelProperty can be edited // using sub-properties, null otherwise. public static PropertyValueEditor GetSubPropertyEditor(ModelProperty property) { if (property == null) { return null; } if (property.Converter == null || property.Converter.GetPropertiesSupported() == false) { // if it's a property of a generic type, check for converter defined at the property of generic type definition if (property.Parent.ItemType.IsGenericType) { Type genericType = property.Parent.ItemType.GetGenericTypeDefinition(); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(genericType); PropertyDescriptor propertyDescriptor = properties.Find(property.Name, false); if (propertyDescriptor != null) { // wrap the converter and check if it supports GetProperties() TypeConverter converter = new ModelTypeConverter(((IModelTreeItem)property.Parent).ModelTreeManager, propertyDescriptor.Converter); if (converter.GetPropertiesSupported()) { return SubPropertyViewEditor.Instance; } } } return null; } //Dont support Arrays if (typeof(Array).IsAssignableFrom(property.PropertyType)) { return null; } return SubPropertyViewEditor.Instance; } //// ----s open the specified Type and looks for EditorAttributes that represent // CategoryEditors - returns the Types of those editors, if any are found, as a list // // Type to ---- open //List of CategoryEditors associated with the specified type, if any. Null otherwise. public static IEnumerableGetCategoryEditorTypes(Type ownerType) { List editorTypes = null; foreach (EditorAttribute editorAttribute in GetAttributes (ownerType)) { // A ---- attempt at using the same extensibility code Type editorType = ExtensibilityMetadataHelper.GetCategoryEditorType(editorAttribute, MessageLogger.Instance); if (editorType == null) { continue; } if (editorTypes == null) { editorTypes = new List (); } editorTypes.Add(editorType); } return editorTypes; } // // Decides whether the specified ModelProperty should be advanced // // ModelProperty to look up //True if the property should be advanced, false otherwise public static bool GetIsAdvanced(ModelProperty property) { EditorBrowsableAttribute browsable = GetAttribute(property); if (browsable == null) { return false; } return browsable.State == EditorBrowsableState.Advanced; } // // Decides whether the specified CategoryEditor should be advanced. // Note: Blend uses custom, baked-in logic to determine whether a given CategoryEditor // is advanced or not. The logic is the same as the one here, but we can't share it // because they don't expose it. In v2, this is definitely something we should share. // // CategoryEditor to look up //True if the specified editor should be advanced, false otherwise public static bool GetIsAdvanced(CategoryEditor editor) { EditorBrowsableAttribute browsable = GetAttribute(editor.GetType()); if (browsable == null) { return false; } return browsable.State == EditorBrowsableState.Advanced; } // // Looks up the DefaultPropertyAttribute on the given type and returns the default property, // if any. // // Type to look up //Default property associated with the specified type, if any. public static string GetDefaultProperty(Type type) { if (type == null) { return null; } string defaultProperty; if (_defaultPropertyCache.TryGetValue(type, out defaultProperty)) { return defaultProperty; } DefaultPropertyAttribute dpa = GetAttribute(type); defaultProperty = dpa == null ? null : dpa.Name; defaultProperty = defaultProperty == null ? null : defaultProperty.Trim(); _defaultPropertyCache[type] = defaultProperty; return defaultProperty; } // // Attempts to look up a custom display name from the DisplayNameAttribute. // Returns null if the attribute is not defined. // // ModelProperty to examine //Custom DisplayName for the property, if any. public static string GetDisplayName(ModelProperty property) { DisplayNameAttribute displayNameAttribute = GetAttribute(property); if (displayNameAttribute == null) { return null; } string displayName = displayNameAttribute.DisplayName; if (string.IsNullOrEmpty(displayName)) { return null; } return displayName; } // // Gets the description associated with the specified ModelProperty // // ModelProperty to examine //The description associated with the specified ModelProperty public static string GetDescription(ModelProperty property) { DescriptionAttribute description = GetAttribute(property); if (description == null || string.IsNullOrEmpty(description.Description)) { return DescriptionAttribute.Default.Description; } return description.Description; } // // Instantiates a TypeConverter from a potential TypeConverterAttribute, if one exists. // // ModelItem to examine //Instantiated TypeConverter from a potential TypeConverterAttribute, if one exists, // null otherwise. public static TypeConverter GetTypeConverter(ModelItem item) { return InstantiateTypeConverter(GetAttribute(item)); } // // Gets the TypeConverter associated with the specified ModelProperty, returning // null when no TypeConverter is found. // // property to examine //Associated TypeConverter if one exists, null otherwise. public static TypeConverter GetTypeConverter(ModelProperty property) { return property == null ? null : property.Converter; } //// Computes the IsReadOnly flag for the specified set of properties, ORing // results together for sets of properties larger than 1. // // Properties to examine // Delegate that evaluates the IsMixedValue flag for // the passed in property values (added as an optimization, since we don't always require // the value and it may be computationally expensive) //Flag indicating whether the set of properties is read only or not public static bool IsReadOnly(Listproperties, IsMixedValueEvaluator isMixedValueEvaluator) { if (properties == null || properties.Count == 0) { Debug.Fail("ExtensibilityAccessor.IsReadOnly: No properties specified."); return true; } Type propertyType = properties[0].PropertyType; // ILists are readonly only if value is null if (typeof(IList).IsAssignableFrom(propertyType)) { if (OrReadOnlyValues(properties)) { IList list = null; if (isMixedValueEvaluator != null) { list = isMixedValueEvaluator() ? null : (ModelUtilities.GetSafeRawValue(properties[0]) as IList); } else { Debug.Fail("No delegate to evaluate IsMixedValue specified."); } if (list == null) { return true; } } return false; } // Arrays and ICollections are readonly if (typeof(Array).IsAssignableFrom(propertyType) || typeof(ICollection).IsAssignableFrom(propertyType)) { return true; } // Types that implement ONLY ICollection<> or ONLY IList<> (meaning they // don't also implement ICollection or IList, which we handle above) // are also readonly if (ModelUtilities.ImplementsICollection(propertyType) || ModelUtilities.ImplementsIList(propertyType)) { return true; } // Otherwise, go off of the IsReadOnly value in ModelProperty return OrReadOnlyValues(properties); } // // Looks up and returns the BrowsableAttribute on the specified property. // // ModelProperty to examine //True, if the property is marked as browsable, false if it is // marked as non-browsable, null if it is unmarked. public static bool? IsBrowsable(ModelProperty property) { if (property == null) { return false; } // Check if the Browsable(true) attribute is explicitly defined. BrowsableAttribute browsable = GetAttribute(property); // If explicit browsable then honor that. if (browsable != null) { return browsable.Browsable; } return null; } // // Gets the PropertyOrder token associated with the given ModelProperty // // ModelProperty to examine //Associated PropertyOrder token if one exists, null otherwise. public static PropertyOrder GetPropertyOrder(ModelProperty property) { if (property == null) { return null; } PropertyOrderAttribute attr = GetAttribute(property); if (attr == null) { return null; } return attr.Order; } // // Returns the list of NewItemTypesAttributes that are associated with the // specified ModelProperty. Note that we should never be returning attributes // from any of the public methods of ExtensibilityAccessor. The only reason // why we do so here is to pass them to a Blend API that requires it. However, // this is a design flaw and we should not follow suite elsewhere. // This method is guaranteed not to return null. // // ModelProperty instance to look up //List of NewItemTypesSttributes associated with the given ModelProperty. public static ListGetNewItemTypesAttributes(ModelProperty property) { List newItemTypesList = new List (); foreach (NewItemTypesAttribute newItemTypesAttribute in GetAttributes (property)) { // if there is no custom ItemFactory defined if (newItemTypesAttribute.FactoryType == typeof(NewItemFactory)) { foreach (Type type in newItemTypesAttribute.Types) { //Check if the type "IsConcreteWithDefaultCtor" if (EditorUtilities.IsConcreteWithDefaultCtor(type)) { newItemTypesList.Add(new NewItemTypesAttribute(type)); } } } else { newItemTypesList.Add(newItemTypesAttribute); } } return newItemTypesList; } // // Examines the specified ModelProperty for NewItemTypesAttributes and, if found, returns // an enumerable of all NewItemFactoryTypeModels specified through them. // // ModelProperty instance to look up //Returns an enumerable of all NewItemFactoryTypeModels specified through custom // NewItemFactoryTypeModels, if any. public static IEnumerableGetNewItemFactoryTypeModels(ModelProperty modelProperty, Size desiredIconSize) { List attributes = GetNewItemTypesAttributes(modelProperty); if (attributes == null) { yield break; } foreach (NewItemTypesAttribute attribute in attributes) { NewItemFactory factory = (NewItemFactory)Activator.CreateInstance(attribute.FactoryType); foreach (Type type in attribute.Types) { NewItemFactoryTypeModel model = null; if (attribute.FactoryType == typeof(NewItemFactory)) { if (EditorUtilities.IsConcreteWithDefaultCtor(type)) { model = new NewItemFactoryTypeModel(type, factory, MessageLogger.Instance); } } else { model = new NewItemFactoryTypeModel(type, factory, MessageLogger.Instance); } if (model != null) { model.DesiredSize = desiredIconSize; yield return model; } } } } // // Gets all relevant sub-properties from the given ModelItem // // Item to examine //Sub-properties exposed by the given ModelItem public static ListGetSubProperties(ModelItem item) { if (item == null) { return null; } // First, see if there is a custom TypeConverter. If so, get the subProperties // from there. Otherwise, get all subProperties including those that aren't browsable, // since the Browsability call should be made by the UI, not by the model. return GetTypeConverterSubProperties(item) ?? GetAllSubProperties(item); } // // Gets all relevant sub-properties from the value of the specified // ModelProperty // // ModelProperty to examine //Sub-properties exposed by the value of the specified ModelProperty public static ListGetSubProperties(ModelProperty property) { if (property.Value == null || ModelUtilities.GetSafeRawValue(property) == null) { return null; } // First, see if there is a custom TypeConverter. If so, get the subProperties // from there. Otherwise, get all subProperties including those that aren't browsable, // since the Browsability call should be made by the UI, not by the model. return GetTypeConverterSubProperties(property) ?? GetAllSubProperties(property); } // // try / catch wrapper artound Activator.CreateInstance() // // Type to instantiate //Instantiated object or null on error [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public static object SafeCreateInstance(Type type) { try { return Activator.CreateInstance(type); } catch { // ignore errors... } return null; } //// Returns the property source based on the following heuristic (in line with // Blend's behavior): // // Xaml Source // ------------------------------------------------------------------- // "123" Local // "{Binding}" DataBound // not specified (default value) Default // not specified (inherited value) Inherited // not specified (from style) Inherited // "{DynamicResource ...}" LocalDynamicResource // "{StaticResource ...}" LocalStaticResource // "{x:Static ...}" SystemResource // "{TemplateBinding ...}" TemplateBinding // "{CustomMarkup ...}" CustomMarkupExtension // // // Property to examine //Source of the specified property, if any public static PropertyValueSource GetPropertySource(ModelProperty property) { if (property == null) { return null; } ModelItem valueItem = property.Value; PropertyValueSource source = null; // Binding or any other known markup extension? if (valueItem != null) { Type valueType = valueItem.ItemType; if (IsStaticExtension(valueType)) { source = DependencyPropertyValueSource.SystemResource; } else if (typeof(StaticResourceExtension).IsAssignableFrom(valueType)) { source = DependencyPropertyValueSource.LocalStaticResource; } else if (typeof(DynamicResourceExtension).IsAssignableFrom(valueType)) { source = DependencyPropertyValueSource.LocalDynamicResource; } else if (typeof(TemplateBindingExtension).IsAssignableFrom(valueType)) { source = DependencyPropertyValueSource.TemplateBinding; } else if (typeof(Binding).IsAssignableFrom(valueType)) { source = DependencyPropertyValueSource.DataBound; } else if (typeof(MarkupExtension).IsAssignableFrom(valueType)) { source = DependencyPropertyValueSource.CustomMarkupExtension; } } // If not, is this a local, inherited, or default value? if (source == null) { if (property.IsSet) { source = DependencyPropertyValueSource.Local; } else { object value = property.ComputedValue; if (object.Equals(value, property.DefaultValue)) { source = DependencyPropertyValueSource.DefaultValue; } else if (valueItem != null && valueItem.Source != property) { source = DependencyPropertyValueSource.Inherited; } } } return source; } // Helper method that ORs the ModelProperty.IsReadOnly values together and // returns the result private static bool OrReadOnlyValues(Listproperties) { if (properties == null) { return true; } for (int i = 0; i < properties.Count; i++) { if (properties[i].IsReadOnly) { return true; } } return false; } // Helper method to find if the propertyvalueeditor is reusable for the // given properties collection. public static bool IsEditorReusable(IEnumerable properties) { if (properties == null) { return true; } foreach(ModelProperty property in properties) { // even if one property says the editor is not reusable, then // the editor is not reusable for this whole list. if (!ExtensibilityMetadataHelper.IsEditorReusable(property.Attributes)) { return false; } } return true; } // Hack to deal with {x:Static ...} extensions. The Cider Markup code currently // replaces all StaticExtensions with internal versions of the same class. // Once bug 100647 is fixed this code can go away. private static bool IsStaticExtension(Type type) { return type != null && ( typeof(StaticExtension).IsAssignableFrom(type) || string.Equals("System.Activities.Presentation.Internal.Xaml.Builtins.StaticExtension", type.FullName)); } // Gets all subProperties from the TypeConverter, if one is explicitely specified private static List GetTypeConverterSubProperties(ModelItem item) { return GetTypeConverterSubPropertiesHelper(item, null); } // Gets all subProperties from the TypeConverter, if one is explicitely specified private static List GetTypeConverterSubProperties(ModelProperty property) { TypeConverter propertySpecificConverter = property.Converter; return GetTypeConverterSubPropertiesHelper(property.Value, propertySpecificConverter); } private static List GetTypeConverterSubPropertiesHelper(ModelItem item, TypeConverter customConverter) { if (item == null) { return null; } List subProperties = null; TypeConverter converter = customConverter; if (converter == null) { // See if there is a converter associated with the item type itself converter = ExtensibilityAccessor.GetTypeConverter(item); } if (converter != null) { PropertyDescriptorCollection subPropertyDescriptors = converter.GetProperties(item.GetCurrentValue()); if (subPropertyDescriptors != null && subPropertyDescriptors.Count > 0) { foreach (PropertyDescriptor subPropertyDescriptor in subPropertyDescriptors) { ModelProperty subProperty = item.Properties[subPropertyDescriptor.Name]; // We want to expose all properties through the model regardless of whether they // are browsable or not. That distinction should be made by the UI utilizing it if (subProperty != null) { if (subProperties == null) { subProperties = new List (); } subProperties.Add(subProperty); } } } } return subProperties; } // Gets all subProperties that exist private static List GetAllSubProperties(ModelItem item) { if (item == null) { return null; } ModelPropertyCollection subModelProperties = item.Properties; if (subModelProperties == null) { return null; } List subProperties = null; // We want to expose all properties through the model regardless of whether they // are browsable or not. That distinction should be made by the UI utilizing it foreach (ModelProperty subModelProperty in subModelProperties) { if (subProperties == null) { subProperties = new List (); } subProperties.Add(subModelProperty); } return subProperties; } // Gets all subProperties that exist private static List GetAllSubProperties(ModelProperty property) { return GetAllSubProperties(property.Value); } [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")] [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")] private static TypeConverter InstantiateTypeConverter(TypeConverterAttribute typeConverterAttribute) { if (typeConverterAttribute == null) { return null; } try { Type typeConverterType = Type.GetType(typeConverterAttribute.ConverterTypeName); if (typeConverterType != null) { return (TypeConverter)Activator.CreateInstance(typeConverterType); } } catch (Exception) { // Ignore failures. In the future, log these somewhere for 3rd parties to see and debug. } return null; } // GetAttributes() and GetAttribute () public static T GetAttribute (ModelProperty property) where T : Attribute { return GetAttribute (property == null ? null : property.Attributes); } public static T GetAttribute (ModelItem item) where T : Attribute { return GetAttribute (item == null ? null : item.Attributes); } public static T GetAttribute (Type type) where T : Attribute { return GetAttribute (type == null ? null : TypeDescriptor.GetAttributes(type)); } public static IEnumerable GetAttributes (ModelProperty property) where T : Attribute { return GetAttributes (property == null ? null : property.Attributes); } public static IEnumerable GetAttributes (Type type) where T : Attribute { return GetAttributes (type == null ? null : TypeDescriptor.GetAttributes(type)); } // Note: Calling AttributeCollection[typeof(MyAttribute)] creates a default attribute if // the specified attribute is not found. That's generally not what we want. public static T GetAttribute (AttributeCollection attributes) where T : Attribute { T foundAttribute = null; if (attributes != null) { foreach (Attribute attribute in attributes) { if (typeof(T).IsAssignableFrom(attribute.GetType())) { foundAttribute = attribute as T; } } } return foundAttribute; } // Note: Calling AttributeCollection[typeof(MyAttribute)] creates a default attribute if // the specified attribute is not found. That's generally not what we want. private static IEnumerable GetAttributes (AttributeCollection attributes) where T : Attribute { if (attributes != null) { foreach (Attribute attribute in attributes) { if (typeof(T).IsAssignableFrom(attribute.GetType())) { yield return (T)attribute; } } } } // // Delegate intended to wrap logic that evaluates the IsMixedValue flag of // some property or set of properties. // //True if values are mixed, false otherwise public delegate bool IsMixedValueEvaluator(); // private class MessageLogger : IMessageLogger { private static MessageLogger _instance = new MessageLogger(); public static MessageLogger Instance { get { return _instance; } } public void Clear() { } public void Write(string text) { Debug.Write(text); } public void WriteLine(string text) { Debug.WriteLine(text); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10817,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Editors/SubPropertyEditor.cs/1305376/SubPropertyEditor.cs,4/26/2010 8:54:39 AM,23176 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Editors { using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.Windows; using System.Windows.Automation.Peers; using System.Windows.Controls; using System.Windows.Data; using System.Runtime; using System.Activities.Presentation.Model; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.PropertyInspector; using System.Activities.Presentation.Internal.PropertyEditing.Automation; using System.Activities.Presentation.Internal.PropertyEditing.Model; using ModelUtilities = System.Activities.Presentation.Internal.PropertyEditing.Model.ModelUtilities; using System.Activities.Presentation.Internal.PropertyEditing.Resources; using System.Activities.Presentation.Internal.PropertyEditing.Selection; using System.Activities.Presentation.Internal.PropertyEditing.State; //// We use the SubPropertyEditor to replace the entire property row // when the property exposes its subproperties. This control is _not_ // just used within the value-editing portion of a property row. // We cheat because we can. // internal class SubPropertyEditor : Control, INotifyPropertyChanged, ISelectionStop { //// PropertyEntry is used to store the currently displayed PropertyEntry // public static readonly DependencyProperty PropertyEntryProperty = DependencyProperty.Register( "PropertyEntry", typeof(PropertyEntry), typeof(SubPropertyEditor), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyEntryChanged))); //// Boolean used to indicate whether the sub-properties are being shown or not. As an optimization, // we don't actually expose the PropertyValue's sub-properties through SelectiveSubProperties until // the sub-property expando-pane has been open at least once. // public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register( "IsExpanded", typeof(bool), typeof(SubPropertyEditor), new PropertyMetadata(false, new PropertyChangedCallback(OnIsExpandedChanged))); //// Exposes the currently selected QuickType in the QuickType drop-down. Essentially, // the value of this DP is plumbed through to reflect the value of _quickTypeView.CurrentItem // public static readonly DependencyProperty CurrentQuickTypeProperty = DependencyProperty.Register( "CurrentQuickType", typeof(NewItemFactoryTypeModel), typeof(SubPropertyEditor), new PropertyMetadata(null, new PropertyChangedCallback(OnCurrentQuickTypeChanged))); private ICollectionView _quickTypeView; private ObservableCollection_quickTypeCollection; private bool _ignoreInternalChanges; private bool _exposedSubProperties; private ItemsControl _subPropertyListControl; // // Basic ctor // public SubPropertyEditor() { _quickTypeCollection = new ObservableCollection(); _quickTypeView = CollectionViewSource.GetDefaultView(_quickTypeCollection); _quickTypeView.CurrentChanged += new EventHandler(OnCurrentQuickTypeChanged); } // Automation public event PropertyChangedEventHandler PropertyChanged; // Internal event we fire for the sake of SubPropertyEditorAutomationPeer that // causes it to refresh its offered set of children internal event EventHandler VisualsChanged; public PropertyEntry PropertyEntry { get { return (PropertyEntry)this.GetValue(PropertyEntryProperty); } set { this.SetValue(PropertyEntryProperty, value); } } public bool IsExpanded { get { return (bool)this.GetValue(IsExpandedProperty); } set { this.SetValue(IsExpandedProperty, value); } } public NewItemFactoryTypeModel CurrentQuickType { get { return (NewItemFactoryTypeModel)this.GetValue(CurrentQuickTypeProperty); } set { this.SetValue(CurrentQuickTypeProperty, value); } } // // Gets a flag indicating whether QuickTypes exist // public bool HasQuickTypes { get { return _quickTypeCollection.Count > 0; } } //// Returns a list of available QuickTypes (collection of NewItemFactoryTypeModel instances) // public ICollectionView QuickTypes { get { return _quickTypeView; } } //// Exposes PropertyValue.SubProperties when the IsExpanded flag first gets set to true // and forever thereafter (or at least until the current PropertyValue changes and we // collapse the sub-properties) // public IEnumerableSelectiveSubProperties { get { if (!_exposedSubProperties) { if (!this.IsExpanded) { yield break; } _exposedSubProperties = true; } PropertyEntry parent = this.PropertyEntry; if (parent == null) { yield break; } foreach (ModelPropertyEntry subProperty in parent.PropertyValue.SubProperties) { if (subProperty.IsBrowsable) { yield return subProperty; } } } } // // Gets a flag indicating whether the sub-property editor can be expanded or not. // public bool IsExpandable { get { return this.HasQuickTypes && this.CurrentQuickType != null; } } //// Gets a SelectionPath to itself. // public SelectionPath Path { get { return PropertySelectionPathInterpreter.Instance.ConstructSelectionPath(this.PropertyEntry); } } //// Gets a description of the contained property // to expose through automation // public string Description { get { PropertyEntry property = this.PropertyEntry; if (property != null) { return string.Format( CultureInfo.CurrentCulture, Properties.Resources.PropertyEditing_SelectionStatus_Property, this.PropertyEntry.PropertyName); } return string.Empty; } } //// Exposes the ItemsControl used to display the list of sub-properties. UI-specific // private ItemsControl SubPropertyListControl { get { if (_subPropertyListControl == null) { _subPropertyListControl = VisualTreeUtils.GetNamedChild(this, "PART_SubPropertyList"); Fx.Assert(_subPropertyListControl != null, "UI for SubPropertyEditor changed. Need to update SubPropertyEditor class logic."); } return _subPropertyListControl; } } // Keyboard Navigation protected override AutomationPeer OnCreateAutomationPeer() { return new SubPropertyEditorAutomationPeer(this); } // Properties // PropertyEntry DP // When the displayed PropertyEntry changes, make sure we update the UI and hook into the // new PropertyEntry's notification mechanism private static void OnPropertyEntryChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { SubPropertyEditor theThis = obj as SubPropertyEditor; if (theThis == null) { return; } PropertyEntry oldValue = e.OldValue as PropertyEntry; if (oldValue != null) { oldValue.PropertyValue.RootValueChanged -= new EventHandler(theThis.OnPropertyValueRootValueChanged); } PropertyEntry newValue = e.NewValue as PropertyEntry; if (newValue != null) { newValue.PropertyValue.RootValueChanged += new EventHandler(theThis.OnPropertyValueRootValueChanged); } theThis.RefreshVisuals(); } private void OnPropertyValueRootValueChanged(object sender, EventArgs e) { if (_ignoreInternalChanges) { return; } RefreshVisuals(); } // IsExpanded DP private static void OnIsExpandedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { SubPropertyEditor theThis = obj as SubPropertyEditor; if (theThis == null) { return; } bool newIsExpanded = (bool)e.NewValue; PropertyEntry containedProperty = theThis.PropertyEntry; // Store the new expansion state if (containedProperty != null) { PropertyState state = PropertyStateContainer.Instance.GetPropertyState( ModelUtilities.GetCachedSubPropertyHierarchyPath(containedProperty)); state.SubPropertiesExpanded = newIsExpanded; } // If we are expanded but we never exposed the sub-properties to anyone before, // fire a signal saying that a list of sub-properties may be now available, so that // UI DataBindings refresh themselves if (newIsExpanded == true && theThis._exposedSubProperties == false) { theThis.FireSubPropertiesListChangedEvents(); } } // CurrentQuickType DP // This method gets called when the DP changes private static void OnCurrentQuickTypeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { SubPropertyEditor theThis = obj as SubPropertyEditor; if (theThis == null) { return; } if (theThis._ignoreInternalChanges) { return; } theThis._quickTypeView.MoveCurrentTo(e.NewValue); theThis.ExpandSubProperties(); theThis.FireSubPropertiesListChangedEvents(); } // This method gets called when the CurrentItem on _quickTypeView changes private void OnCurrentQuickTypeChanged(object sender, EventArgs e) { if (_ignoreInternalChanges) { return; } NewItemFactoryTypeModel selectedTypeModel = _quickTypeView.CurrentItem as NewItemFactoryTypeModel; if (selectedTypeModel == null) { return; } Fx.Assert(this.PropertyEntry != null, "PropertyEntry should not be null"); if (this.PropertyEntry == null) { return; } bool previousValue = IgnoreInternalChanges(); try { this.PropertyEntry.PropertyValue.Value = selectedTypeModel.CreateInstance(); } finally { NoticeInternalChanges(previousValue); } } protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { if (e.Property == PropertyContainer.OwningPropertyContainerProperty) { // A quick and dirty way to register this instance as the implementation of // ISelectionBranchPoint that controls the expansion / collapse of this control // OnOwningPropertyContainerChanged((PropertyContainer)e.OldValue, (PropertyContainer)e.NewValue); } base.OnPropertyChanged(e); } private void OnOwningPropertyContainerChanged(PropertyContainer oldValue, PropertyContainer newValue) { if (oldValue != null) { PropertySelection.ClearSelectionStop(oldValue); PropertySelection.ClearIsSelectionStopDoubleClickTarget(oldValue); } if (newValue != null) { PropertySelection.SetSelectionStop(newValue, this); PropertySelection.SetIsSelectionStopDoubleClickTarget(newValue, true); } } // Visual Lookup Helpers // // Looks for and returns the specified sub-property // // Sub-property to look up //Corresponding PropertyEntry if found, null otherwise. internal PropertyEntry FindSubPropertyEntry(string propertyName) { if (string.IsNullOrEmpty(propertyName)) { return null; } foreach (PropertyEntry property in SelectiveSubProperties) { if (property.PropertyName.Equals(propertyName)) { return property; } } return null; } //// Looks for and returns the PropertyContainer used to display // the specified PropertyEntry // // Property to look for //Corresponding PropertyContainer if found, null otherwise. internal PropertyContainer FindSubPropertyEntryVisual(PropertyEntry property) { if (property == null) { return null; } ItemsControl subPropertyListControl = this.SubPropertyListControl; if (subPropertyListControl == null) { return null; } return subPropertyListControl.ItemContainerGenerator.ContainerFromItem(property) as PropertyContainer; } // Helpers private void RefreshVisuals() { RefreshQuickTypes(); RestoreIsExpandedState(); FireVisualsChangedEvents(); } private void RefreshQuickTypes() { bool previousValue = IgnoreInternalChanges(); try { _quickTypeCollection.Clear(); PropertyEntry containedProperty = this.PropertyEntry; if (containedProperty == null) { return; } ModelProperty property = ((ModelPropertyEntry)containedProperty).FirstModelProperty; Type containerValueType = ((ModelPropertyEntryBase)containedProperty).CommonValueType; NewItemFactoryTypeModel selectedFactoryModel = null; Type defaultItemType = GetDefaultItemType(property); // Find all elligible NewItemFactoryTypes declared through metadata IEnumerablefactoryModels = ExtensibilityAccessor.GetNewItemFactoryTypeModels( property, ResourceUtilities.GetDesiredTypeIconSize(this)); if (factoryModels != null) { foreach (NewItemFactoryTypeModel factoryModel in factoryModels) { _quickTypeCollection.Add(factoryModel); if (selectedFactoryModel == null) { if (object.Equals(containerValueType, factoryModel.Type)) { selectedFactoryModel = factoryModel; } } if (defaultItemType != null && object.Equals(defaultItemType, factoryModel.Type)) { defaultItemType = null; } } } //add a null value - user should always have an option to clear property value NewItemFactoryTypeModel nullTypeFactoryTypeModel = new NewItemFactoryTypeModel(null, new NullItemFactory()); // Add a default item type based on the property type (if it wasn't also added through // metadata) if (defaultItemType != null) { NewItemFactoryTypeModel defaultItemFactoryTypeModel = new NewItemFactoryTypeModel(defaultItemType, new NewItemFactory()); _quickTypeCollection.Add(defaultItemFactoryTypeModel); if (selectedFactoryModel == null) { if (object.Equals(containerValueType, defaultItemFactoryTypeModel.Type)) { selectedFactoryModel = defaultItemFactoryTypeModel; } else if (containerValueType == null) { selectedFactoryModel = nullTypeFactoryTypeModel; } } } _quickTypeCollection.Add(nullTypeFactoryTypeModel); // Make sure the currently selected value on the CollectionView reflects the // actual value of the property _quickTypeView.MoveCurrentTo(selectedFactoryModel); this.CurrentQuickType = selectedFactoryModel; } finally { NoticeInternalChanges(previousValue); } } private static Type GetDefaultItemType(ModelProperty property) { if (property == null) { return null; } Type propertyType = property.PropertyType; if (EditorUtilities.IsConcreteWithDefaultCtor(propertyType)) { return propertyType; } return null; } private void RestoreIsExpandedState() { bool newIsExpanded = false; PropertyEntry property = this.PropertyEntry; if (property != null) { PropertyState state = PropertyStateContainer.Instance.GetPropertyState( ModelUtilities.GetCachedSubPropertyHierarchyPath(property)); newIsExpanded = state.SubPropertiesExpanded; } this.IsExpanded = newIsExpanded; _exposedSubProperties = false; } private void ExpandSubProperties() { this.IsExpanded = true; } // Change Notification Helpers private bool IgnoreInternalChanges() { bool previousValue = _ignoreInternalChanges; _ignoreInternalChanges = true; return previousValue; } private void NoticeInternalChanges(bool previousValue) { _ignoreInternalChanges = previousValue; } private void FireVisualsChangedEvents() { // Fire updated events OnPropertyChanged("HasQuickTypes"); OnPropertyChanged("QuickTypes"); FireSubPropertiesListChangedEvents(); } private void FireSubPropertiesListChangedEvents() { OnPropertyChanged("IsExpandable"); OnPropertyChanged("SelectiveSubProperties"); if (VisualsChanged != null) { VisualsChanged(this, EventArgs.Empty); } } // INotifyPropertyChanged Members private void OnPropertyChanged(string propertyName) { Fx.Assert(!string.IsNullOrEmpty(propertyName), "Can't raise OnPropertyChanged event without a valid property name."); if (PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } // ISelectionStop Members //NullItemFactory - this class is used to provide a null entry in quick types list - it is required to allow user //to clear value of an object. sealed class NullItemFactory : NewItemFactory { public override object CreateInstance(Type type) { //no input type is allowed - we never create instance of anything Fx.Assert(type == null, "NullItemFactory supports only null as type parameter"); return null; } public override string GetDisplayName(Type type) { //no input type is allowed - we always return (null) string Fx.Assert(type == null, "NullItemFactory supports only null as type parameter"); return "(null)"; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10818,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Editors/RTLValueConverter.cs/1305376/RTLValueConverter.cs,4/26/2010 8:54:39 AM,2638 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Editors { using System; using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Windows.Data; using System.Windows; using System.Activities.Presentation.Internal.PropertyEditing.Model; using System.Activities.Presentation.Internal.PropertyEditing.Resources; using System.Activities.Presentation.PropertyEditing; using System.Activities.Presentation; using System.Runtime; // // Checks the property entry and converts it // to appropriate FlowDirection value which is returned back. // [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] internal class RTLValueConveter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { FlowDirection returnValue = FlowDirection.LeftToRight; Fx.Assert(values.Length == 3, "Incorrect values in the MultiValueConverter!"); if (values.Length == 3) { ModelPropertyEntry propertyEntry = values[1] as ModelPropertyEntry; if (propertyEntry != null) { if (!propertyEntry.DisplayName.Equals("Name")) { if (targetType == typeof(FlowDirection)) { object propertyValue = values[0]; if (propertyValue == null || propertyValue.GetType() == typeof(string)) { //customize it to controls FlowDirection Property returnValue = (FlowDirection)PropertyInspectorResources.GetResources()["SelectedControlFlowDirectionRTL"]; } } } } } return returnValue; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw FxTrace.Exception.AsError(new NotImplementedException()); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. 10819,/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/cdf/src/NetFx40/Tools/System.Activities.Presentation/System/Activities/Presentation/Base/Core/Internal/PropertyEditing/Editors/FlagStringConverter.cs/1305376/FlagStringConverter.cs,4/26/2010 8:54:39 AM,3013 //---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //--------------------------------------------------------------- namespace System.Activities.Presentation.Internal.PropertyEditing.Editors { using System.Windows.Data; using System.Globalization; using System.Collections.Generic; using System.Runtime; using System.Collections; sealed class FlagStringConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value != null) { return value.ToString(); } else { return string.Empty; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { Type flagType = parameter as Type; Fx.Assert(flagType != null && flagType.IsEnum, "TargetType should be enum"); Fx.Assert(Attribute.IsDefined(flagType, typeof(FlagsAttribute)), "FlagType should have flags attribute"); if (value == null) { return Enum.ToObject(flagType, 0); } string str = (value as string).ToUpperInvariant(); str = str.Trim(); if (str.Equals(string.Empty) || str.Equals("0")) { return Enum.ToObject(flagType, 0); } DictionaryflagDictionary = GenerateFlagDictionary(flagType); int flagsIntValue = 0; string[] names = str.Split(','); foreach (string name in names) { string flagName = name.Trim(); if (flagDictionary.ContainsKey(flagName)) { flagsIntValue |= (int)flagDictionary[flagName]; flagDictionary.Remove(flagName); } else { throw FxTrace.Exception.AsError(new ArgumentException(string.Format(CultureInfo.CurrentUICulture, SR.InvalidFlagName, value, flagType.Name))); } } return Enum.ToObject(flagType, flagsIntValue); } static Dictionary GenerateFlagDictionary(Type flagType) { Dictionary flagDictionary = new Dictionary (); string[] flagNames = flagType.GetEnumNames(); Array flagValues = flagType.GetEnumValues(); for (int i = 0; i < flagNames.Length; i++) { flagDictionary.Add(flagNames[i].ToUpperInvariant(), flagValues.GetValue(i)); } return flagDictionary; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlUtf8RawTextWriter.cs
- CommandField.cs
- RuntimeConfigLKG.cs
- ExpressionStringBuilder.cs
- TextModifierScope.cs
- XmlWriterSettings.cs
- WsatRegistrationHeader.cs
- SqlClientWrapperSmiStreamChars.cs
- CommandLibraryHelper.cs
- Int32KeyFrameCollection.cs
- GridViewUpdateEventArgs.cs
- HttpRequestWrapper.cs
- Window.cs
- WebPartMovingEventArgs.cs
- InstanceDataCollectionCollection.cs
- CharacterMetricsDictionary.cs
- ScriptControlManager.cs
- ControlBuilderAttribute.cs
- MonikerHelper.cs
- MetadataUtil.cs
- CommentEmitter.cs
- Point4DConverter.cs
- SecurityKeyUsage.cs
- Decoder.cs
- Serializer.cs
- StyleBamlRecordReader.cs
- ModelTreeEnumerator.cs
- KeyTimeConverter.cs
- SchemaElement.cs
- LocalValueEnumerator.cs
- PartialTrustVisibleAssembly.cs
- Monitor.cs
- ObjectListItem.cs
- coordinatorfactory.cs
- SamlAuthorizationDecisionClaimResource.cs
- ObjectFactoryCodeDomTreeGenerator.cs
- DataTableReader.cs
- XmlSerializationReader.cs
- GPPOINT.cs
- BinHexDecoder.cs
- securitycriticaldataformultiplegetandset.cs
- TypeNameConverter.cs
- ScrollChrome.cs
- BuildManager.cs
- CommandBinding.cs
- EncoderReplacementFallback.cs
- ObjectPersistData.cs
- COSERVERINFO.cs
- ToolStripOverflowButton.cs
- DefaultValidator.cs
- TimeEnumHelper.cs
- CannotUnloadAppDomainException.cs
- ListManagerBindingsCollection.cs
- ManagementScope.cs
- ComponentCollection.cs
- InstanceStoreQueryResult.cs
- FileAuthorizationModule.cs
- VerticalAlignConverter.cs
- DataGridViewTopLeftHeaderCell.cs
- CacheEntry.cs
- ToolStripLabel.cs
- Label.cs
- SQLDecimalStorage.cs
- XmlSchemaValidator.cs
- SiteMapSection.cs
- DiscriminatorMap.cs
- CollectionType.cs
- PersonalizationAdministration.cs
- ValueCollectionParameterReader.cs
- MdImport.cs
- HttpResponseInternalWrapper.cs
- SingleResultAttribute.cs
- ColumnResizeAdorner.cs
- URLBuilder.cs
- JavaScriptObjectDeserializer.cs
- XmlNodeReader.cs
- QilReference.cs
- OdbcConnectionOpen.cs
- WebRequestModuleElement.cs
- _FixedSizeReader.cs
- tibetanshape.cs
- RichTextBoxConstants.cs
- COM2ExtendedBrowsingHandler.cs
- XamlFigureLengthSerializer.cs
- SocketException.cs
- CellTreeNode.cs
- DataBoundLiteralControl.cs
- SoapIgnoreAttribute.cs
- WinEventHandler.cs
- DropDownList.cs
- TracedNativeMethods.cs
- HtmlInputFile.cs
- ByteStreamMessageEncoderFactory.cs
- DecimalStorage.cs
- KnownBoxes.cs
- DPTypeDescriptorContext.cs
- SessionEndingCancelEventArgs.cs
- wgx_commands.cs
- OleDbWrapper.cs
- Int64Animation.cs