Plane.Sdk3/PlaneGcsSdk.Contract_Shared/Copters/PLObservableObject.cs

414 lines
15 KiB
C#
Raw Normal View History

2017-02-27 02:02:19 +08:00
#define CMNATTR
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Linq.Expressions;
// ReSharper disable RedundantUsingDirective
using System.Linq;
using System.Threading;
// ReSharper restore RedundantUsingDirective
#if CMNATTR
using System.Runtime.CompilerServices;
#endif
namespace Plane.Copters
{
/// <summary>
/// A base class for objects of which the properties must be observable (based on MvvmLight's ObservableObject).
/// </summary>
//// [ClassInfo(typeof(ViewModelBase))]
public class PLObservableObject : INotifyPropertyChanged /*, INotifyPropertyChanging*/
{
/// <summary>
/// 与 UI 线程关联的 <see cref="SynchronizationContext"/> 实例。
/// </summary>
protected SynchronizationContext _uiSyncContext;
/// <summary>
/// 创建 <see cref="EHObservableObject"/> 的实例。
/// </summary>
/// <param name="uiSyncContext"></param>
public PLObservableObject(SynchronizationContext uiSyncContext)
{
_uiSyncContext = uiSyncContext;
}
/// <summary>
/// Occurs after a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Provides access to the PropertyChanged event handler to derived classes.
/// </summary>
protected PropertyChangedEventHandler PropertyChangedHandler
{
get
{
return PropertyChanged;
}
}
#if !PORTABLE && !SL4 && !WINDOWS_UWP
/// <summary>
/// Occurs before a property value changes.
/// </summary>
public event PropertyChangingEventHandler PropertyChanging;
/// <summary>
/// Provides access to the PropertyChanging event handler to derived classes.
/// </summary>
protected PropertyChangingEventHandler PropertyChangingHandler
{
get
{
return PropertyChanging;
}
}
#endif
/// <summary>
/// Verifies that a property name exists in this ViewModel. This method
/// can be called before the property is used, for instance before
/// calling RaisePropertyChanged. It avoids errors when a property name
/// is changed but some places are missed.
/// </summary>
/// <remarks>This method is only active in DEBUG mode.</remarks>
/// <param name="propertyName">The name of the property that will be
/// checked.</param>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
var myType = GetType();
#if NETFX_CORE
if (!string.IsNullOrEmpty(propertyName)
&& myType.GetRuntimeProperty(propertyName) == null)
{
throw new ArgumentException("Property not found", propertyName);
}
#else
if (!string.IsNullOrEmpty(propertyName)
&& myType.GetProperty(propertyName) == null)
{
#if !SILVERLIGHT
var descriptor = this as ICustomTypeDescriptor;
if (descriptor != null)
{
if (descriptor.GetProperties()
.Cast<PropertyDescriptor>()
.Any(property => property.Name == propertyName))
{
return;
}
}
#endif
throw new ArgumentException("Property not found", propertyName);
}
#endif
}
#if !PORTABLE && !SL4 && !WINDOWS_UWP
#if CMNATTR
/// <summary>
/// Raises the PropertyChanging event if needed.
/// </summary>
/// <remarks>If the propertyName parameter
/// does not correspond to an existing property on the current class, an
/// exception is thrown in DEBUG configuration only.</remarks>
/// <param name="propertyName">(optional) The name of the property that
/// changed.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
protected virtual void RaisePropertyChanging(
[CallerMemberName] string propertyName = null)
#else
/// <summary>
/// Raises the PropertyChanging event if needed.
/// </summary>
/// <remarks>If the propertyName parameter
/// does not correspond to an existing property on the current class, an
/// exception is thrown in DEBUG configuration only.</remarks>
/// <param name="propertyName">The name of the property that
/// changed.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
protected virtual void RaisePropertyChanging(
string propertyName)
#endif
{
VerifyPropertyName(propertyName);
var handler = PropertyChanging;
if (handler != null)
{
handler(this, new PropertyChangingEventArgs(propertyName));
}
}
#endif
#if CMNATTR
/// <summary>
/// Raises the PropertyChanged event if needed.
/// </summary>
/// <remarks>If the propertyName parameter
/// does not correspond to an existing property on the current class, an
/// exception is thrown in DEBUG configuration only.</remarks>
/// <param name="propertyName">(optional) The name of the property that
/// changed.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
protected virtual void RaisePropertyChanged(
[CallerMemberName] string propertyName = null)
#else
/// <summary>
/// Raises the PropertyChanged event if needed.
/// </summary>
/// <remarks>If the propertyName parameter
/// does not correspond to an existing property on the current class, an
/// exception is thrown in DEBUG configuration only.</remarks>
/// <param name="propertyName">The name of the property that
/// changed.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
protected virtual void RaisePropertyChanged(
string propertyName)
#endif
{
VerifyPropertyName(propertyName);
var handler = PropertyChanged;
if (handler != null)
{
#if NETFX_CORE
var e = new PropertyChangedEventArgs(propertyName);
if (SynchronizationContext.Current == _uiSyncContext) handler(this, e);
else _uiSyncContext.Post(() => handler(this, e));
#else
handler(this, new PropertyChangedEventArgs(propertyName));
#endif
}
}
#if !PORTABLE && !SL4 && !WINDOWS_UWP
/// <summary>
/// Raises the PropertyChanging event if needed.
/// </summary>
/// <typeparam name="T">The type of the property that
/// changes.</typeparam>
/// <param name="propertyExpression">An expression identifying the property
/// that changes.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
[SuppressMessage(
"Microsoft.Design",
"CA1006:GenericMethodsShouldProvideTypeParameter",
Justification = "This syntax is more convenient than other alternatives.")]
protected virtual void RaisePropertyChanging<T>(Expression<Func<T>> propertyExpression)
{
var handler = PropertyChanging;
if (handler != null)
{
var propertyName = GetPropertyName(propertyExpression);
handler(this, new PropertyChangingEventArgs(propertyName));
}
}
#endif
/// <summary>
/// Raises the PropertyChanged event if needed.
/// </summary>
/// <typeparam name="T">The type of the property that
/// changed.</typeparam>
/// <param name="propertyExpression">An expression identifying the property
/// that changed.</param>
[SuppressMessage(
"Microsoft.Design",
"CA1030:UseEventsWhereAppropriate",
Justification = "This cannot be an event")]
[SuppressMessage(
"Microsoft.Design",
"CA1006:GenericMethodsShouldProvideTypeParameter",
Justification = "This syntax is more convenient than other alternatives.")]
protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var handler = PropertyChanged;
if (handler != null)
{
var propertyName = GetPropertyName(propertyExpression);
#if NETFX_CORE
var e = new PropertyChangedEventArgs(propertyName);
if (SynchronizationContext.Current == _uiSyncContext) handler(this, e);
else _uiSyncContext.Post(() => handler(this, e));
#else
handler(this, new PropertyChangedEventArgs(propertyName));
#endif
}
}
/// <summary>
/// Extracts the name of a property from an expression.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="propertyExpression">An expression returning the property's name.</param>
/// <returns>The name of the property returned by the expression.</returns>
/// <exception cref="ArgumentNullException">If the expression is null.</exception>
/// <exception cref="ArgumentException">If the expression does not represent a property.</exception>
[SuppressMessage(
"Microsoft.Design",
"CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "This syntax is more convenient than the alternatives."),
SuppressMessage(
"Microsoft.Design",
"CA1006:DoNotNestGenericTypesInMemberSignatures",
Justification = "This syntax is more convenient than the alternatives.")]
protected static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var body = propertyExpression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException("Invalid argument", "propertyExpression");
}
var property = body.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException("Argument is not a property", "propertyExpression");
}
return property.Name;
}
/// <summary>
/// Assigns a new value to the property. Then, raises the
/// PropertyChanged event if needed.
/// </summary>
/// <typeparam name="T">The type of the property that
/// changed.</typeparam>
/// <param name="propertyExpression">An expression identifying the property
/// that changed.</param>
/// <param name="field">The field storing the property's value.</param>
/// <param name="newValue">The property's value after the change
/// occurred.</param>
/// <returns>True if the PropertyChanged event has been raised,
/// false otherwise. The event is not raised if the old
/// value is equal to the new value.</returns>
[SuppressMessage(
"Microsoft.Design",
"CA1006:DoNotNestGenericTypesInMemberSignatures",
Justification = "This syntax is more convenient than the alternatives."),
SuppressMessage(
"Microsoft.Design",
"CA1045:DoNotPassTypesByReference",
MessageId = "1#",
Justification = "This syntax is more convenient than the alternatives.")]
protected bool Set<T>(
Expression<Func<T>> propertyExpression,
ref T field,
T newValue)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return false;
}
#if !PORTABLE && !SL4 && !WINDOWS_UWP
RaisePropertyChanging(propertyExpression);
#endif
field = newValue;
RaisePropertyChanged(propertyExpression);
return true;
}
/// <summary>
/// Assigns a new value to the property. Then, raises the
/// PropertyChanged event if needed.
/// </summary>
/// <typeparam name="T">The type of the property that
/// changed.</typeparam>
/// <param name="propertyName">The name of the property that
/// changed.</param>
/// <param name="field">The field storing the property's value.</param>
/// <param name="newValue">The property's value after the change
/// occurred.</param>
/// <returns>True if the PropertyChanged event has been raised,
/// false otherwise. The event is not raised if the old
/// value is equal to the new value.</returns>
[SuppressMessage(
"Microsoft.Design",
"CA1045:DoNotPassTypesByReference",
MessageId = "1#",
Justification = "This syntax is more convenient than the alternatives.")]
protected bool Set<T>(
string propertyName,
ref T field,
T newValue)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return false;
}
#if !PORTABLE && !SL4 && !WINDOWS_UWP
RaisePropertyChanging(propertyName);
#endif
field = newValue;
// ReSharper disable ExplicitCallerInfoArgument
RaisePropertyChanged(propertyName);
// ReSharper restore ExplicitCallerInfoArgument
return true;
}
#if CMNATTR
/// <summary>
/// Assigns a new value to the property. Then, raises the
/// PropertyChanged event if needed.
/// </summary>
/// <typeparam name="T">The type of the property that
/// changed.</typeparam>
/// <param name="field">The field storing the property's value.</param>
/// <param name="newValue">The property's value after the change
/// occurred.</param>
/// <param name="propertyName">(optional) The name of the property that
/// changed.</param>
/// <returns>True if the PropertyChanged event has been raised,
/// false otherwise. The event is not raised if the old
/// value is equal to the new value.</returns>
protected bool Set<T>(
ref T field,
T newValue,
[CallerMemberName] string propertyName = null)
{
return Set(propertyName, ref field, newValue);
}
#endif
}
}