A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

134 lines
4.4 KiB

// Copyright (c) The Perspex Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Globalization;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Perspex.Data;
using Perspex.Utilities;
namespace Perspex.Markup.Data
{
/// <summary>
/// Turns an <see cref="ExpressionObserver"/> into a subject that can be bound two-way with
/// a value converter.
/// </summary>
public class ExpressionSubject : ISubject<object>, IDescription
{
private readonly ExpressionObserver _inner;
private readonly Type _targetType;
private readonly object _fallbackValue;
private readonly BindingPriority _priority;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
/// </summary>
/// <param name="inner">The <see cref="ExpressionObserver"/>.</param>
/// <param name="targetType">The type to convert the value to.</param>
public ExpressionSubject(ExpressionObserver inner, Type targetType)
: this(inner, targetType, DefaultValueConverter.Instance)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionObserver"/> class.
/// </summary>
/// <param name="inner">The <see cref="ExpressionObserver"/>.</param>
/// <param name="targetType">The type to convert the value to.</param>
/// <param name="converter">The value converter to use.</param>
/// <param name="converterParameter">
/// A parameter to pass to <paramref name="converter"/>.
/// </param>
/// <param name="fallbackValue">
/// The value to use when the binding is unable to produce a value.
/// </param>
/// <param name="priority">The binding priority.</param>
public ExpressionSubject(
ExpressionObserver inner,
Type targetType,
IValueConverter converter,
object converterParameter = null,
object fallbackValue = null,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires<ArgumentNullException>(inner != null);
Contract.Requires<ArgumentNullException>(targetType != null);
Contract.Requires<ArgumentNullException>(converter != null);
_inner = inner;
_targetType = targetType;
Converter = converter;
ConverterParameter = converterParameter;
_fallbackValue = fallbackValue;
_priority = priority;
}
/// <summary>
/// Gets the converter to use on the expression.
/// </summary>
public IValueConverter Converter { get; }
/// <summary>
/// Gets a parameter to pass to <see cref="Converter"/>.
/// </summary>
public object ConverterParameter { get; }
/// <inheritdoc/>
string IDescription.Description => _inner.Expression;
/// <inheritdoc/>
public void OnCompleted()
{
}
/// <inheritdoc/>
public void OnError(Exception error)
{
}
/// <inheritdoc/>
public void OnNext(object value)
{
var type = _inner.ResultType;
if (type != null)
{
var converted = Converter.ConvertBack(
value,
type,
ConverterParameter,
CultureInfo.CurrentUICulture);
if (converted == PerspexProperty.UnsetValue)
{
converted = TypeUtilities.Default(type);
}
_inner.SetValue(converted, _priority);
}
}
/// <inheritdoc/>
public IDisposable Subscribe(IObserver<object> observer)
{
return _inner.Select(ConvertValue).Subscribe(observer);
}
private object ConvertValue(object value)
{
var converted = Converter.Convert(
value,
_targetType,
ConverterParameter,
CultureInfo.CurrentUICulture);
if (converted == PerspexProperty.UnsetValue && _fallbackValue != null)
{
converted = _fallbackValue;
}
return converted;
}
}
}