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
IEnumerator ea = 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.
//
// true if the specified type is implement generic Ilist interface;otherwise, false .
//
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;
}
//
// Determines whether the specified type is implement generic ICollection interface.
//
// The type.
//
// true if the specified type is implement generic ICollection interface;otherwise, false .
//
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;
}
//
// 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(IEnumerable items)
{
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(IEnumerable propertySet, 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 ICollection ModelPropertySet
{
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 IEnumerable CategoryEditorTypes
{
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(IEnumerable propertySet)
{
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;
List textBlockTextProperties = 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 IEnumerable CategoryEditorTypes
{
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 ObservableCollection AdvancedProperties
{
get {
return _advancedProperties;
}
}
//
// Gets the basic properties contained in this category
//
public override ObservableCollection BasicProperties
{
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 ObservableCollectionWorkaround GetBucket(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,
ObservableCollection bucket,
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, ObservableCollection bucket, 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(ObservableCollectionWorkaround propertyList)
{
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 IEnumerable GetCategoryEditorTypes(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(List properties, 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 List GetNewItemTypesAttributes(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 IEnumerable GetNewItemFactoryTypeModels(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 List GetSubProperties(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 List GetSubProperties(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(List properties)
{
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 IEnumerable SelectiveSubProperties
{
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
IEnumerable factoryModels =
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);
}
Dictionary flagDictionary = 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
- DelimitedListTraceListener.cs
- DrawingAttributeSerializer.cs
- AssemblyCacheEntry.cs
- Assembly.cs
- webproxy.cs
- ServiceTimeoutsBehavior.cs
- Parsers.cs
- DispatchWrapper.cs
- XmlArrayItemAttribute.cs
- parserscommon.cs
- ImageInfo.cs
- HtmlForm.cs
- TextProperties.cs
- DiffuseMaterial.cs
- ScopelessEnumAttribute.cs
- WindowsRegion.cs
- ToolStripControlHost.cs
- RuntimeConfigLKG.cs
- WebEventTraceProvider.cs
- ImplicitInputBrush.cs
- Roles.cs
- ProxyGenerationError.cs
- MenuBindingsEditor.cs
- XPathExpr.cs
- SqlGenerator.cs
- StructuralType.cs
- DbTransaction.cs
- UIElementPropertyUndoUnit.cs
- AnnotationResourceCollection.cs
- OleDbCommand.cs
- FloatUtil.cs
- HttpModulesSection.cs
- Stylus.cs
- ProofTokenCryptoHandle.cs
- CodeArrayIndexerExpression.cs
- ServiceDebugBehavior.cs
- AlphabeticalEnumConverter.cs
- LongValidatorAttribute.cs
- LicenseProviderAttribute.cs
- ItemTypeToolStripMenuItem.cs
- MarshalByValueComponent.cs
- MetadataExchangeClient.cs
- IntSecurity.cs
- DeviceOverridableAttribute.cs
- DataSourceHelper.cs
- ServiceParser.cs
- SafeNativeMethods.cs
- ControlPaint.cs
- TraceUtility.cs
- CriticalHandle.cs
- OleDbSchemaGuid.cs
- Decoder.cs
- Int32Rect.cs
- ScaleTransform3D.cs
- WindowClosedEventArgs.cs
- DSACryptoServiceProvider.cs
- DesignerView.cs
- XamlSerializationHelper.cs
- webproxy.cs
- PolyQuadraticBezierSegment.cs
- DataObject.cs
- CustomErrorCollection.cs
- TypefaceMap.cs
- ellipse.cs
- COM2PropertyDescriptor.cs
- MappingSource.cs
- SymbolTable.cs
- BaseResourcesBuildProvider.cs
- ColumnMapTranslator.cs
- EmptyCollection.cs
- HotSpot.cs
- XmlNode.cs
- EntityDataSourceConfigureObjectContext.cs
- ApplicationTrust.cs
- SoapMessage.cs
- TreeNodeCollection.cs
- AppDomainProtocolHandler.cs
- Message.cs
- ScaleTransform.cs
- AggregateException.cs
- OleDbConnectionInternal.cs
- ManualWorkflowSchedulerService.cs
- GraphicsContext.cs
- _ProxyRegBlob.cs
- SecUtil.cs
- MSAAWinEventWrap.cs
- ReachVisualSerializer.cs
- PrimitiveXmlSerializers.cs
- Transform3DGroup.cs
- TreeNodeCollection.cs
- PropertyNames.cs
- messageonlyhwndwrapper.cs
- storepermission.cs
- ComplexLine.cs
- Int32AnimationUsingKeyFrames.cs
- Msec.cs
- COM2IVsPerPropertyBrowsingHandler.cs
- UnmanagedHandle.cs
- TextServicesHost.cs
- ADRole.cs