// 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
{
///
/// Turns an into a subject that can be bound two-way with
/// a value converter.
///
public class ExpressionSubject : ISubject, IDescription
{
private readonly ExpressionObserver _inner;
private readonly Type _targetType;
private readonly object _fallbackValue;
private readonly BindingPriority _priority;
///
/// Initializes a new instance of the class.
///
/// The .
/// The type to convert the value to.
public ExpressionSubject(ExpressionObserver inner, Type targetType)
: this(inner, targetType, DefaultValueConverter.Instance)
{
}
///
/// Initializes a new instance of the class.
///
/// The .
/// The type to convert the value to.
/// The value converter to use.
///
/// A parameter to pass to .
///
///
/// The value to use when the binding is unable to produce a value.
///
/// The binding priority.
public ExpressionSubject(
ExpressionObserver inner,
Type targetType,
IValueConverter converter,
object converterParameter = null,
object fallbackValue = null,
BindingPriority priority = BindingPriority.LocalValue)
{
Contract.Requires(inner != null);
Contract.Requires(targetType != null);
Contract.Requires(converter != null);
_inner = inner;
_targetType = targetType;
Converter = converter;
ConverterParameter = converterParameter;
_fallbackValue = fallbackValue;
_priority = priority;
}
///
/// Gets the converter to use on the expression.
///
public IValueConverter Converter { get; }
///
/// Gets a parameter to pass to .
///
public object ConverterParameter { get; }
///
string IDescription.Description => _inner.Expression;
///
public void OnCompleted()
{
}
///
public void OnError(Exception error)
{
}
///
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);
}
}
///
public IDisposable Subscribe(IObserver 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;
}
}
}