diff --git a/samples/BindingTest/MainWindow.paml b/samples/BindingTest/MainWindow.paml
index c782786e42..cfe10d3ab4 100644
--- a/samples/BindingTest/MainWindow.paml
+++ b/samples/BindingTest/MainWindow.paml
@@ -10,7 +10,7 @@
-
+
diff --git a/src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs b/src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs
index 2ea6a54599..18b464a211 100644
--- a/src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs
+++ b/src/Markup/Perspex.Markup.Xaml/Context/PropertyAccessor.cs
@@ -10,6 +10,7 @@ using OmniXaml.TypeConversion;
using OmniXaml.Typing;
using Perspex.Controls;
using Perspex.Data;
+using Perspex.Markup.Xaml.Data;
using Perspex.Styling;
namespace Perspex.Markup.Xaml.Context
@@ -116,25 +117,28 @@ namespace Perspex.Markup.Xaml.Context
IValueContext context,
IBinding binding)
{
- if (property != null)
+ if (property == null)
{
- IPerspexObject treeAnchor = null;
+ return false;
+ }
- if (!(instance is IControl))
- {
- // HACK: StoredInstances not exposed on ITopDownValueContext.
- var tdvc = (TopDownValueContext)context.TopDownValueContext;
- treeAnchor = (IControl)tdvc.StoredInstances
- .Select(x => x.Instance)
- .OfType()
- .LastOrDefault();
- }
+ var control = instance as IControl;
+
+ if (control != null)
+ {
+ DelayedBinding.Add(control, property, binding);
+ }
+ else
+ {
+ IPerspexObject treeAnchor = context.TopDownValueContext.StoredInstances
+ .Select(x => x.Instance)
+ .OfType()
+ .LastOrDefault();
((IPerspexObject)instance).Bind(property, binding, treeAnchor);
- return true;
}
- return false;
+ return true;
}
}
}
diff --git a/src/Markup/Perspex.Markup.Xaml/Data/DelayedBinding.cs b/src/Markup/Perspex.Markup.Xaml/Data/DelayedBinding.cs
new file mode 100644
index 0000000000..95e09497a2
--- /dev/null
+++ b/src/Markup/Perspex.Markup.Xaml/Data/DelayedBinding.cs
@@ -0,0 +1,95 @@
+// 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.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Perspex.Controls;
+using Perspex.Data;
+
+namespace Perspex.Markup.Xaml.Data
+{
+ ///
+ /// Provides delayed bindings for controls.
+ ///
+ ///
+ /// The XAML engine applies its bindings in a delayed manner where bindings are only applied
+ /// when a control is added to the visual tree. This was done because applying bindings as soon
+ /// as controls are created means that long-form bindings (i.e. bindings that don't use the
+ /// `{Binding}` markup extension) don't work as the binding is applied to the property before
+ /// the binding properties are set, and looking at WPF it uses a similar mechanism for bindings
+ /// that come from XAML.
+ ///
+ public static class DelayedBinding
+ {
+ private static ConditionalWeakTable> _entries =
+ new ConditionalWeakTable>();
+
+ ///
+ /// Adds a delayed binding to a control.
+ ///
+ /// The control.
+ /// The property on the control to bind to.
+ /// The binding.
+ public static void Add(IControl target, PerspexProperty property, IBinding binding)
+ {
+ if (target.IsAttachedToVisualTree)
+ {
+ target.Bind(property, binding);
+ }
+ else
+ {
+ List bindings;
+
+ if (!_entries.TryGetValue(target, out bindings))
+ {
+ bindings = new List();
+ _entries.Add(target, bindings);
+
+ // TODO: Make this a weak event listener.
+ target.AttachedToVisualTree += ApplyBindings;
+ }
+
+ bindings.Add(new Entry(binding, property));
+ }
+ }
+
+ ///
+ /// Applies any delayed bindings to a control.
+ ///
+ /// The control.
+ public static void ApplyBindings(IControl control)
+ {
+ List bindings;
+
+ if (_entries.TryGetValue(control, out bindings))
+ {
+ foreach (var binding in bindings)
+ {
+ control.Bind(binding.Property, binding.Binding);
+ }
+
+ _entries.Remove(control);
+ }
+ }
+
+ private static void ApplyBindings(object sender, VisualTreeAttachmentEventArgs e)
+ {
+ var target = (IControl)sender;
+ ApplyBindings(target);
+ target.AttachedToVisualTree -= ApplyBindings;
+ }
+
+ private class Entry
+ {
+ public Entry(IBinding binding, PerspexProperty property)
+ {
+ Binding = binding;
+ Property = property;
+ }
+
+ public IBinding Binding { get; }
+ public PerspexProperty Property { get; }
+ }
+ }
+}
diff --git a/src/Markup/Perspex.Markup.Xaml/OmniXAML b/src/Markup/Perspex.Markup.Xaml/OmniXAML
index c2b86b9d1a..43698917d0 160000
--- a/src/Markup/Perspex.Markup.Xaml/OmniXAML
+++ b/src/Markup/Perspex.Markup.Xaml/OmniXAML
@@ -1 +1 @@
-Subproject commit c2b86b9d1ae638c788f44bc63d17911986d766fb
+Subproject commit 43698917d00c5aaf789605fd7434798a748fba89
diff --git a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
index 54ac88b2dd..f59483a22e 100644
--- a/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
+++ b/src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
@@ -76,6 +76,7 @@
+
diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs
index 8ecd552284..e1c93ff3ac 100644
--- a/src/Perspex.Controls/Control.cs
+++ b/src/Perspex.Controls/Control.cs
@@ -323,7 +323,8 @@ namespace Perspex.Controls
{
if (_initCount == 0)
{
- throw new InvalidOperationException("BeginInit was not called.");
+ ++_initCount;
+ //throw new InvalidOperationException("BeginInit was not called.");
}
if (--_initCount == 0 && !_styled)
diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs
index ffefacfbc3..48fcc7b698 100644
--- a/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs
+++ b/tests/Perspex.Markup.Xaml.UnitTests/Xaml/BindingTests.cs
@@ -25,6 +25,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Xaml
var button = window.FindControl