Xamarin.PropertyEditing/Xamarin.PropertyEditing/IObjectEditor.cs

166 строки
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Xamarin.PropertyEditing
{
public class EditorPropertyChangedEventArgs
: EventArgs
{
/// <param name="property">The property that was updated, or <c>null</c> if a full refresh is required.</param>
public EditorPropertyChangedEventArgs (IPropertyInfo property, PropertyVariation variation = null)
{
Property = property;
Variation = variation;
}
/// <summary>
/// Gets the property that was changed, or <c>null</c> for a full refresh.
/// </summary>
public IPropertyInfo Property
{
get;
}
/// <summary>
/// Gets the variation that was changed, if any.
/// </summary>
public PropertyVariation Variation
{
get;
}
}
public interface IObjectEditor
{
/// <summary>
/// Gets the target object being edited.
/// </summary>
object Target { get; }
/// <summary>
/// Gets the real type of the <see cref="Target"/>.
/// </summary>
ITypeInfo TargetType { get; }
/// <remarks>
/// These properties should be minimally equatable to the same property for another object of the same type. This includes
/// for base class properties for two different derived types.
/// </remarks>
IReadOnlyCollection<IPropertyInfo> Properties { get; }
/// <summary>
/// Gets a mapping between known properties (such as CommonBrush.Opacity) and their provider counterparts.
/// </summary>
/// <remarks>
/// Should only include known properties present in <see cref="Properties"/>. Can be <c>null</c> if there are none.
/// </remarks>
IReadOnlyDictionary<IPropertyInfo, KnownProperty> KnownProperties { get; }
/// <summary>
/// Gets the parent object editor for the object this editor represents or <c>null</c> if none.
/// </summary>
IObjectEditor Parent { get; }
/// <remarks>
/// These are children that do not participate in the standard element hierarchy, such as segments in a segmented control.
/// Note that objects defined by editors do not need to match real objects in the real hierarchy, they can be faked. An existing
/// iOS property chooser itself can be mapped to having an object editor for its actual object.
/// </remarks>
IReadOnlyList<IObjectEditor> DirectChildren { get; }
/// <summary>
/// Raised when a property's value changes. <see cref="EditorPropertyChangedEventArgs.Property"/> can be <c>null</c> for a full refresh.
/// </summary>
event EventHandler<EditorPropertyChangedEventArgs> PropertyChanged;
/// <param name="property">The property to get retrieve assignable types for.</param>
/// <param name="childTypes">Whether or not to return assignable types for a property's children rather than the property itself.</param>
/// <returns>An <see cref="AssignableTypesResult"/> instance containing the assignable types. No assignable types should use an empty array and not return <c>null</c>.</returns>
Task<AssignableTypesResult> GetAssignableTypesAsync (IPropertyInfo property, bool childTypes);
/*
* Dealing with async values in the context of what's possible across all target platforms is a bit complex.
* While implicit safety may be able to be ensured, it would be exhaustive to reason it out and could change
* at any moment, so we need an explicit safety strategy. As we make value gets and sets async, we open ourselves
* to race conditions even on just the UI thread. Value changes can come from the user, or from the object editor
* at any point. So, we break these changes into two categories: Blocking UI & Waiting Values.
*
* Anything that needs to deal with the current state of the values will Wait asynchronously on prior changes to
* finish. These largely involve value changes from the object editor, which just acts as a INPC and does not
* contain the value itself, so it will be safe and the latest value when queried upon reaching active state in
* the wait queue (see AsyncWorkQueue). Selected objects (when >1) need to wait on values as they need to be
* able to calculate the intersection values.
*
* Anything that needs to deal with user input will need to block its UI. We simply can't reason about the user
* changing values while waiting for previous changes, because they value they're changing may change underneath
* them or become invalid as a result of a prior pending change.
*/
/// <summary>
/// Gets or sets variations of <paramref name="property"/> values.
/// </summary>
/// <param name="property"></param>
/// <returns>
/// Each <see cref="PropertyVariation"/> that has a value defined for the given <paramref name="property"/>.
/// No variation set should be returned for a neutral value, it's assumed there is one.
/// </returns>
Task<IReadOnlyCollection<PropertyVariation>> GetPropertyVariantsAsync (IPropertyInfo property);
/// <summary>
/// Removes a variant version of a property.
/// </summary>
Task RemovePropertyVariantAsync (IPropertyInfo property, PropertyVariation variant);
/// <remarks>
/// <para>For the <see cref="ValueSource.Default"/> or <see cref="ValueSource.Unset"/> sources, implementers should
/// ignore <paramref name="value"/>'s <see cref="ValueInfo{T}.Value"/> property and unset the value completely.
/// For XML based backings this usually means removing the attribute altogether. Implementers should not simply set
/// the value to its default value as this would still be a local value and override inheritance, styles, etc.</para>
/// <para>When <paramref name="value"/>'s <see cref="ValueInfo{T}.Source"/> is <see cref="ValueSource.Resource"/>,
/// the <see cref="ValueInfo{T}.SourceDescriptor"/> will be set to a <see cref="Resource"/> instance. Implementers
/// need not see whether the resource contains a value itself. For a source of <see cref="ValueSource.Binding"/>,
/// the <see cref="ValueInfo{T}.SourceDescriptor"/> will be the binding object created.</para>
/// <para>When the <paramref name="property"/>'s <see cref="IPropertyInfo.Type"/> is an <c>object</c>
/// <see cref="ValueInfo{T}.ValueDescriptor"/> will contain an <see cref="ITypeInfo"/> representing the real type
/// of the value.</para>
/// <para>When the <see cref="ValueInfo{T}.Source"/> is <see cref="ValueSource.Local"/> and <see cref="ValueInfo{T}.Value"/>
/// is the same as the default value, implementers should consider unsetting the value such that the subsequent
/// <see cref="GetValueAsync{T}(IPropertyInfo, PropertyVariation)"/> would return <see cref="ValueSource.Default"/>
/// for <see cref="ValueInfo{T}.Source"/>. This allows users to clear the value for a property and have it remove
/// the attribute for XML backed platforms without having to issue an <see cref="ValueSource.Unset"/>.</para>
/// <para>Before the returned task completes, in order:
/// 1. <see cref="GetValueAsync{T}(IPropertyInfo, PropertyVariation)"/> must be able to retrieve the new value.
/// 2. <see cref="PropertyChanged"/> should fire with the appropriate property.
/// For defensive purposes, consumers will not assume the <paramref name="value"/> they pass in will be the same
/// as the new value and as a result will re-query the value upon the assumed <see cref="PropertyChanged"/> firing.
/// There should not be a case where <see cref="PropertyChanged"/> is not fired when SetValueAsync is called, consumers
/// will do basic verification before calling. Even <see cref="ValueInfo{T}.Source"/> changes with the value staying the
/// same is a change in the property.
/// </para>
/// <para>
/// For <paramref name="variations"/>, a set call for a <see cref="PropertyVariation"/> that is not yet set should be
/// considered a "create new variation" call. Similarly, a call in which <paramref name="value"/>'s source is <see cref="ValueSource.Unset"/>
/// should be considered a "delete variation" call when <paramref name="variations"/> is set.
/// </para>
/// </remarks>
Task SetValueAsync<T> (IPropertyInfo property, ValueInfo<T> value, PropertyVariation variations = null);
/// <remarks>
/// <para>Implementers should strive to include the actual value of resources or bindings for <see cref="ValueInfo{T}.Value"/>
/// wherever possible.</para>
/// <para>If the platform can know the value of a property when unset, it should return that value and the <see cref="ValueSource.Default"/>
/// source. If the platform only knows that the value is unset, use <see cref="ValueSource.Unset"/> instead.</para>
///<para>When the property's value <see cref="ValueInfo{T}.Source"/> is <see cref="ValueSource.Resource"/>,
/// the <see cref="ValueInfo{T}.SourceDescriptor"/> should be set to a <see cref="Resource"/> instance. Implementers should
/// strive to retrieve the resource's value and use <see cref="Resource{T}"/> instead. For a source of
/// <see cref="ValueSource.Binding"/>, the <see cref="ValueInfo{T}.SourceDescriptor"/> will be the binding object created.</para>
/// <para>When the <paramref name="property"/>'s <see cref="IPropertyInfo.Type"/> is an <c>object</c>
/// <see cref="ValueInfo{T}.ValueDescriptor"/> should contain an <see cref="ITypeInfo"/> representing the real type
/// of the value.</para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="property"/> is <c>null</c>.</exception>
Task<ValueInfo<T>> GetValueAsync<T> (IPropertyInfo property, PropertyVariation variations = null);
}
}