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 .