Last active
May 8, 2018 23:35
-
-
Save toeb/f2842322af1efbadafe7757fb10d9146 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Windows.Markup; | |
using System.Windows.Data; | |
using System.Globalization; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Core.Common.Injection; | |
using System.Xml; | |
using System.Diagnostics; | |
namespace Core.Common.Wpf.Export | |
{ | |
/// <summary> A base class for Evaluating Extensions - Extensions which can contain recursive bound elements </summary> | |
/// | |
/// <remarks> Toeb, 2018-05-09. </remarks> | |
public abstract class EvaluatingExtensionBase : MarkupExtension, IMultiValueConverter | |
{ | |
public EvaluatingExtensionBase(object[] arguments) { this.MultiArguments = arguments; } | |
protected abstract object Evaluate(object[] values); | |
public object[] MultiArguments { get; private set; } | |
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) | |
{ | |
return _ast.Evaluate(new Queue<object>(values)); | |
} | |
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) | |
{ | |
throw new NotImplementedException(); | |
} | |
private class AstNode | |
{ | |
public override string ToString() | |
{ | |
return $"Ast Input {InputObject} Value {Value}"; | |
} | |
public object Value { get; internal set; } | |
public MarkupExtension Provider | |
{ | |
get; set; | |
} | |
public object InputObject | |
{ | |
get; set; | |
} | |
public AstNode[] Children | |
{ | |
get; set; | |
} | |
public IEnumerable<AstNode> Descendants | |
{ | |
get | |
{ | |
return SelfAndDescendents.Skip(1); | |
} | |
} | |
public IEnumerable<AstNode> SelfAndDescendents | |
{ | |
get | |
{ | |
yield return this; | |
foreach (var child in Children.SelectMany(c => c.SelfAndDescendents)) | |
{ | |
yield return child; | |
} | |
} | |
} | |
public static AstNode Parse(object current) | |
{ | |
var result = new AstNode(); | |
result.InputObject = current; | |
result.Children = Array.Empty<AstNode>(); | |
if (current is MultiBinding mb) | |
{ | |
if (mb.ConverterParameter is EvaluatingExtensionBase evaluatingExtensionBase) | |
{ | |
result.Children = evaluatingExtensionBase.MultiArguments.Select(a => Parse(a)).ToArray(); | |
} | |
else | |
{ | |
throw new InvalidOperationException($"only multibindings of type {nameof(EvaluatingExtensionBase)} are supported"); | |
} | |
} | |
if (current is MarkupExtension extension) | |
{ | |
result.Provider = extension; | |
} | |
return result; | |
} | |
public object Evaluate(Queue<object> values) | |
{ | |
if (Provider is MultiBinding multi) | |
{ | |
if (multi.Converter is EvaluatingExtensionBase evb) | |
{ | |
var items = Children.Select(child => child.Evaluate(values)).ToArray(); | |
return evb.Evaluate(items); | |
} | |
} | |
if (Provider != null) | |
{ | |
if (values.Count == 0) | |
{ | |
return null; | |
} | |
return values.Dequeue(); | |
} | |
return InputObject; | |
} | |
public object ProvideValue(IServiceProvider serviceProvider) | |
{ | |
if (Provider == null) | |
{ | |
Value = InputObject; | |
} | |
else | |
{ | |
Value = Provider.ProvideValue(serviceProvider); | |
} | |
return Value; | |
} | |
} | |
private MultiBinding _binding; | |
private AstNode _ast; | |
public object TargetObject { get; private set; } | |
public object TargetProperty { get; private set; } | |
public IServiceProvider ServiceProvider { get; private set; } | |
public override object ProvideValue(IServiceProvider serviceProvider) | |
{ | |
TargetObject = serviceProvider.GetService<IProvideValueTarget>()?.TargetObject; | |
TargetProperty = serviceProvider.GetService<IProvideValueTarget>()?.TargetProperty; | |
ServiceProvider = serviceProvider; | |
OnProvideValue(serviceProvider); | |
if (_binding != null) | |
{ | |
throw new InvalidOperationException("provide value may only be called once"); | |
} | |
_binding = new MultiBinding() { Converter = this }; | |
_binding.ConverterParameter = this; | |
_ast = AstNode.Parse(_binding); | |
var provided = _ast | |
.Descendants | |
.Select(n => n.ProvideValue(serviceProvider)) | |
.Where(v => v != null) | |
.ToArray(); | |
var bindingExpressions = provided.OfType<BindingExpression>().ToArray(); | |
foreach (var binding in bindingExpressions) | |
{ | |
_binding.Bindings.Add(binding.ParentBindingBase); | |
} | |
return _binding.ProvideValue(serviceProvider); | |
} | |
protected virtual void OnProvideValue(IServiceProvider provider) | |
{ | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment