1 namespace System.Activities.Presentation.PropertyEditing {
2 using System.Globalization;
4 using System.Collections;
5 using System.ComponentModel;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Activities.Presentation.Internal.Properties;
9 using System.Activities.Presentation;
12 /// This class provides a data model for the underlying property value.
14 public abstract class PropertyValue : INotifyPropertyChanged {
16 private PropertyEntry _parentProperty;
19 /// Creates a PropertyValue. For host infrastructure.
21 /// <param name="parentProperty">The PropertyEntry that corresponds to this PropertyValue</param>
22 /// <exception cref="ArgumentNullException">When parentProperty is null</exception>
23 protected PropertyValue(PropertyEntry parentProperty) {
24 if (parentProperty == null)
25 throw FxTrace.Exception.ArgumentNull("parentProperty");
27 _parentProperty = parentProperty;
31 /// Event that gets fired when any properties of the PropertyValue class change.
32 /// Note that a "Value" and "StringValue" property changed notification gets fired
33 /// either when the Value or StringValue get set to a new instance OR when any
34 /// sub-properties of this PropertyValue change.
36 public event PropertyChangedEventHandler PropertyChanged;
39 /// Event fired when the Value or StringValue properties of this class
40 /// get updated with new instances.
42 public event EventHandler RootValueChanged;
45 /// Event fired when the any of the sub-properties of this PropertyValue
46 /// or its sub-properties get updated with new value instances.
48 public event EventHandler SubPropertyChanged;
51 /// Gets the parent PropertyEntry.
53 public PropertyEntry ParentProperty { get { return _parentProperty; } }
56 /// Gets a PropertyValueSource that contains information
57 /// about where this value is coming from.
59 public abstract PropertyValueSource Source { get; }
62 /// Returns true if Value is the default value for the property
64 public abstract bool IsDefaultValue { get; }
67 /// Returns true if this value represents a property for multiple objects with
68 /// more than one value - for example 2 Buttons with different values for Background
69 /// If this property is true then Value will return null and and StringValue will return
72 public abstract bool IsMixedValue { get; }
75 /// Throws if the value is invalid
77 /// <param name="valueToValidate">value to validate</param>
78 protected abstract void ValidateValue(object valueToValidate);
81 /// Gets a flag indicating whether the underlying value can be converted from a string
83 public abstract bool CanConvertFromString { get; }
86 /// Returns the given string as a value - used to convert StringValue to Value
87 /// Typical implementations would use the TypeConverter for the underlying property
88 /// This method should not catch exceptions, it should propagate them.
90 protected abstract object ConvertStringToValue(string value);
93 /// Returns the value as a String - used to convert Value to StringValue
94 /// Typical implementations would use the TypeConverter for the underlying property
96 protected abstract string ConvertValueToString(object value);
99 /// Gets the underlying property value.
101 protected abstract object GetValueCore();
104 /// Sets the underlying property value. This method should not catch
105 /// exceptions, but allow them to propagate.
107 protected abstract void SetValueCore(object value);
110 /// Clears this value such that it is unset.
112 public abstract void ClearValue();
117 /// Gets or sets the underlying property value. Both Value and StringValue
118 /// will raise the appropriate change notifications.
120 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")]
121 [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")]
125 object returnValue = null;
127 if (this.CatchExceptions) {
129 returnValue = GetValueCore();
131 catch (Exception ex) {
132 OnPropertyValueException(new PropertyValueExceptionEventArgs(
134 CultureInfo.CurrentCulture,
135 Resources.Error_ValueGetFailed),
137 PropertyValueExceptionSource.Get,
142 returnValue = GetValueCore();
155 bool isValidationException = ex is ValidationException;
157 //show error message if we do catch exception or exception is ValidationException
158 if (this.CatchExceptions || isValidationException)
160 OnPropertyValueException(new PropertyValueExceptionEventArgs(
161 string.Format(CultureInfo.CurrentCulture, Resources.Error_ValueSetFailed),
163 PropertyValueExceptionSource.Set,
167 //rethrow if we do not catch exception or exception is ValidationException (it should be handled by the control)
168 if (!this.CatchExceptions || isValidationException)
176 private void SetValueImpl(object value) {
177 ValidateValue(value);
179 NotifyValueChanged();
180 OnRootValueChanged();
184 // StringValue Property
187 /// Gets or sets the underlying property value as a string. Both Value and StringValue
188 /// will raise the appropriate change notifications.
190 [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Propagating the error might cause VS to crash")]
191 [SuppressMessage("Reliability", "Reliability108", Justification = "Propagating the error might cause VS to crash")]
192 public string StringValue
195 string returnValue = string.Empty;
197 //If there is an error event handler then use it otherwise let the exception
199 if (this.CatchExceptions) {
201 //Caching opportunity here
202 returnValue = this.ConvertValueToString(this.Value);
204 catch (Exception ex) {
205 OnPropertyValueException(new PropertyValueExceptionEventArgs(
207 CultureInfo.CurrentCulture,
208 Resources.Error_CannotConvertValueToString),
210 PropertyValueExceptionSource.Get,
215 //Caching opportunity here
216 returnValue = this.ConvertValueToString(this.Value);
222 //If there is an error event handler then use it otherwise let the exception
224 if (CatchExceptions) {
226 this.Value = this.ConvertStringToValue(value);
228 catch (Exception ex) {
229 OnPropertyValueException(
230 new PropertyValueExceptionEventArgs(
232 CultureInfo.CurrentCulture,
233 Resources.Error_CannotUpdateValueFromStringValue),
235 PropertyValueExceptionSource.Set,
240 this.Value = this.ConvertStringToValue(value);
249 /// Gets a flag indicating whether the type of this property
250 /// supports sub-properties. Typical implementations will use a TypeConverter
251 /// to verify whether sub-properties exist.
253 public abstract bool HasSubProperties { get; }
256 /// Gets a collection of sub-properties as PropertyEntry instances
258 public abstract PropertyEntryCollection SubProperties { get; }
264 /// Gets a flag indicating whether this PropertyValue models a property
265 /// whose value is a collection.
267 public abstract bool IsCollection { get; }
270 /// Gets a collection of PropertyValue instances that correspond to the items
271 /// in the collection when IsCollection is true.
273 public abstract PropertyValueCollection Collection { get; }
279 /// Event for host implementations to use for error handling. Raised when StringValue or Value throws
280 /// and CatchExceptions is true. If CatchExceptions is false, the exception will be thrown up the stack.
282 public event EventHandler<PropertyValueExceptionEventArgs> PropertyValueException;
285 /// Gets a boolean indicating whether exceptions thrown during value gets and sets
286 /// should be caught or propagated directly to the caller. By default, exceptions
287 /// are caught if there is at least one subscriber to the PropertyValueException event.
289 protected virtual bool CatchExceptions {
291 return PropertyValueException != null;
296 /// Called when PropertyValue get or set fails. Default implementation raises the
297 /// PropertyValueException event.
299 /// <param name="e">PropertyValueExceptionEventArgs</param>
300 /// <exception cref="ArgumentNullException">When e is null</exception>
301 protected virtual void OnPropertyValueException(PropertyValueExceptionEventArgs e) {
303 throw FxTrace.Exception.ArgumentNull("e");
305 if (PropertyValueException != null)
306 PropertyValueException(this, e);
310 // Notification Helpers
313 /// Raises change notification for all properties. This should be called when
314 /// the underlying object is changed externally (for example Button.Width is
315 /// changed on the design surface)
317 protected virtual void NotifyRootValueChanged() {
318 //When Value is updated or the model is reset we
319 //need to fire an "everything has changed" notification
321 //This doesn't appear to work at all...
322 //PropertyChanged(this, new PropertyChangedEventArgs(""));
324 //So notify these key changes individually
325 OnPropertyChanged("IsDefaultValue");
326 OnPropertyChanged("IsMixedValue");
327 OnPropertyChanged("IsCollection");
328 OnPropertyChanged("Collection");
329 OnPropertyChanged("HasSubProperties");
330 OnPropertyChanged("SubProperties");
331 OnPropertyChanged("Source");
332 OnPropertyChanged("CanConvertFromString");
334 NotifyValueChanged();
335 OnRootValueChanged();
339 /// Called to raise the SubPropertyChanged event. This method should be called
340 /// when one of the sub-properties of this property changes. It raises changed
341 /// events for Value, StringValue, and SubProperty
343 protected void NotifySubPropertyChanged() {
344 NotifyValueChanged();
345 OnSubPropertyChanged();
349 /// Called to raise the changed events for Value and StringValue. This method
350 /// should only be called to trigger the refresh of the visual representation
351 /// of this value. If the value content actually changes, call NotifyRootValueChanged
354 private void NotifyValueChanged() {
355 OnPropertyChanged("Value");
356 NotifyStringValueChanged();
360 /// Raise change notification for StringValue
362 private void NotifyStringValueChanged() {
363 OnPropertyChanged("StringValue");
367 /// Called to raise the RootValueChanged event
369 private void OnRootValueChanged() {
370 if (RootValueChanged != null)
371 RootValueChanged(this, EventArgs.Empty);
375 /// Called to raise the SubPropertyChanged event
377 private void OnSubPropertyChanged() {
378 if (SubPropertyChanged != null)
379 SubPropertyChanged(this, EventArgs.Empty);
383 // INotifyPropertyChanged
386 /// Raises the PropertyChanged event. Subclasses that override this method
387 /// should call the base class implementation.
389 /// <param name="e"></param>
390 protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
391 if (PropertyChanged != null)
392 PropertyChanged(this, e);
396 /// Raises the PropertyChanged event.
398 /// <param name="propertyName"></param>
399 protected virtual void OnPropertyChanged(string propertyName) {
400 if (PropertyChanged != null)
401 OnPropertyChanged(new PropertyChangedEventArgs(propertyName));