From dab821249efd5631352345d9ae32b7784885447a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 16 Oct 2015 02:03:59 +0200 Subject: [PATCH] Got basic XAML control templates working. --- .../XamlTestApplicationPcl/XamlTestApp.paml | 7 ++- .../Binding/XamlBindingDefinition.cs | 5 +- .../Binding/XamlTemplateBinding.cs | 54 +++++++++++++++++++ .../Context/PerspexXamlMemberValuePlugin.cs | 23 ++++++-- .../TemplateBindingExtension.cs | 28 ++++++++++ .../Perspex.Markup.Xaml.csproj | 2 + src/Perspex.Base/PerspexObjectExtensions.cs | 36 +++++++++++++ 7 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 src/Markup/Perspex.Markup.Xaml/Binding/XamlTemplateBinding.cs create mode 100644 src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs diff --git a/samples/XamlTestApplicationPcl/XamlTestApp.paml b/samples/XamlTestApplicationPcl/XamlTestApp.paml index e08382ec1a..1bd1140e55 100644 --- a/samples/XamlTestApplicationPcl/XamlTestApp.paml +++ b/samples/XamlTestApplicationPcl/XamlTestApp.paml @@ -11,8 +11,11 @@ - - + + diff --git a/src/Markup/Perspex.Markup.Xaml/Binding/XamlBindingDefinition.cs b/src/Markup/Perspex.Markup.Xaml/Binding/XamlBindingDefinition.cs index c571036d4d..b7f8f67ad0 100644 --- a/src/Markup/Perspex.Markup.Xaml/Binding/XamlBindingDefinition.cs +++ b/src/Markup/Perspex.Markup.Xaml/Binding/XamlBindingDefinition.cs @@ -9,13 +9,16 @@ namespace Perspex.Markup.Xaml.Binding { public XamlBindingDefinition( string sourcePropertyPath, - BindingMode bindingMode) + BindingMode bindingMode, + bool isTemplateBinding = false) { SourcePropertyPath = sourcePropertyPath; BindingMode = bindingMode; + IsTemplateBinding = isTemplateBinding; } public string SourcePropertyPath { get; } public BindingMode BindingMode { get; } + public bool IsTemplateBinding { get; } } } \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Binding/XamlTemplateBinding.cs b/src/Markup/Perspex.Markup.Xaml/Binding/XamlTemplateBinding.cs new file mode 100644 index 0000000000..29b4f7b997 --- /dev/null +++ b/src/Markup/Perspex.Markup.Xaml/Binding/XamlTemplateBinding.cs @@ -0,0 +1,54 @@ +// 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.Reactive.Linq; +using OmniXaml.TypeConversion; +using Perspex.Controls; +using Perspex.Styling; + +namespace Perspex.Markup.Xaml.Binding +{ + public class XamlTemplateBinding + { + private readonly ITypeConverterProvider _typeConverterProvider; + + public XamlTemplateBinding() + { + } + + public XamlTemplateBinding(ITypeConverterProvider typeConverterProvider) + { + _typeConverterProvider = typeConverterProvider; + } + + public string SourcePropertyPath { get; set; } + + public BindingMode BindingMode { get; set; } + + public void Bind(PerspexObject instance, PerspexProperty targetProperty) + { + instance.GetObservable(Control.TemplatedParentProperty) + .Where(x => x != null) + .OfType() + .Take(1) + .Subscribe(x => BindToTemplatedParent(instance, targetProperty, x)); + } + + private void BindToTemplatedParent( + PerspexObject instance, + PerspexProperty targetProperty, + PerspexObject templatedParent) + { + var sourceProperty = templatedParent.FindRegistered(SourcePropertyPath); + + if (sourceProperty == null) + { + throw new InvalidOperationException( + $"The property {SourcePropertyPath} could not be found on {instance.GetType()}."); + } + + instance.Bind(targetProperty, templatedParent.GetObservable(sourceProperty), BindingPriority.Style); + } + } +} \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs b/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs index 36e1d03ea8..7053eb3e95 100644 --- a/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs +++ b/src/Markup/Perspex.Markup.Xaml/Context/PerspexXamlMemberValuePlugin.cs @@ -109,13 +109,26 @@ namespace Perspex.Markup.Xaml.Context $"Cannot find '{propertyName}' on '{instance.GetType()}"); } - var binding = new XamlBinding + if (!def.IsTemplateBinding) { - BindingMode = def.BindingMode, - SourcePropertyPath = def.SourcePropertyPath, - }; + var binding = new XamlBinding + { + BindingMode = def.BindingMode, + SourcePropertyPath = def.SourcePropertyPath, + }; - binding.Bind(perspexObject, property); + binding.Bind(perspexObject, property); + } + else + { + var binding = new XamlTemplateBinding + { + BindingMode = def.BindingMode, + SourcePropertyPath = def.SourcePropertyPath, + }; + + binding.Bind(perspexObject, property); + } } } diff --git a/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs new file mode 100644 index 0000000000..6dbcc64c4f --- /dev/null +++ b/src/Markup/Perspex.Markup.Xaml/MarkupExtensions/TemplateBindingExtension.cs @@ -0,0 +1,28 @@ +// 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 OmniXaml; +using Perspex.Markup.Xaml.Binding; + +namespace Perspex.Markup.Xaml.MarkupExtensions +{ + public class TemplateBindingExtension : MarkupExtension + { + public TemplateBindingExtension() + { + } + + public TemplateBindingExtension(string path) + { + Path = path; + } + + public override object ProvideValue(MarkupExtensionContext extensionContext) + { + return new XamlBindingDefinition(Path, Mode, true); + } + + public string Path { get; set; } + public BindingMode Mode { get; set; } + } +} \ No newline at end of file diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj index f7a1408f5d..3fbcd117a4 100644 --- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj +++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj @@ -40,6 +40,7 @@ + @@ -55,6 +56,7 @@ + diff --git a/src/Perspex.Base/PerspexObjectExtensions.cs b/src/Perspex.Base/PerspexObjectExtensions.cs index cc595e0130..e7485bee44 100644 --- a/src/Perspex.Base/PerspexObjectExtensions.cs +++ b/src/Perspex.Base/PerspexObjectExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using System.Reactive.Linq; namespace Perspex @@ -51,6 +52,41 @@ namespace Perspex return observable.Subscribe(e => SubscribeAdapter(e, handler)); } + /// + /// Finds a registered property on a by name. + /// + /// The object. + /// + /// The property name. If an attached property it should be in the form + /// "OwnerType.PropertyName". + /// + /// + /// The registered property or null if no matching property found. + /// + public static PerspexProperty FindRegistered(this PerspexObject o, string name) + { + Contract.Requires(o != null); + Contract.Requires(name != null); + + var parts = name.Split('.'); + + if (parts.Length < 1 || parts.Length > 2) + { + throw new ArgumentException("Invalid property name."); + } + + if (parts.Length == 1) + { + return o.GetRegisteredProperties() + .FirstOrDefault(x => !x.IsAttached && x.Name == parts[0]); + } + else + { + return o.GetRegisteredProperties() + .FirstOrDefault(x => x.IsAttached && x.OwnerType.Name == parts[0] && x.Name == parts[1]); + } + } + /// /// Observer method for .