Browse Source

Merge pull request #1513 from amis92/parent-ambient-provider

Support StaticResource in Bindings inside DataTemplate
pull/1535/merge
Steven Kirk 9 years ago
committed by GitHub
parent
commit
11e348a4bd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      samples/BindingTest/MainWindow.xaml
  2. 5
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs
  3. 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  4. 14
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  5. 2
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github
  6. 8
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs
  7. 3
      src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs
  8. 71
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs
  9. 79
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

8
samples/BindingTest/MainWindow.xaml

@ -1,4 +1,5 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:vm="clr-namespace:BindingTest.ViewModels;assembly=BindingTest"
xmlns:local="clr-namespace:BindingTest;assembly=BindingTest">
<Window.Styles>
@ -6,6 +7,9 @@
<Setter Property="FontSize" Value="18"/>
</Style>
</Window.Styles>
<Window.Resources>
<vm:TestItem x:Key="SharedItem" StringValue="shared" />
</Window.Resources>
<TabControl>
<TabItem Header="Basic">
@ -40,6 +44,10 @@
<TextBlock FontSize="16" Text="Binding Sources"/>
<TextBox Watermark="Value of first TextBox" UseFloatingWatermark="True"
Text="{Binding #first.Text, Mode=TwoWay}"/>
<TextBox Watermark="Value of SharedItem.StringValue" UseFloatingWatermark="True"
Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
<TextBox Watermark="Value of SharedItem.StringValue (duplicate)" UseFloatingWatermark="True"
Text="{Binding StringValue, Source={StaticResource SharedItem}, Mode=TwoWay}"/>
</StackPanel>
<StackPanel Margin="18" Gap="4" Width="200" HorizontalAlignment="Left">
<TextBlock FontSize="16" Text="Scheduler"/>

5
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoaderPortableXaml.cs

@ -194,11 +194,12 @@ namespace Avalonia.Markup.Xaml
return result;
}
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null)
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null)
{
var writer = AvaloniaXamlObjectWriter.Create(
reader.SchemaContext,
context);
context,
parentAmbientProvider);
XamlServices.Transform(reader, writer);
writer.ApplyAllDelayedProperties();

7
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs

@ -36,13 +36,6 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// Look upwards though the ambient context for IResourceProviders which might be able
// to give us the resource.
//
// TODO: If we're in a template then only the ambient values since the root of the
// template wil be included here. We need some way to get hold of the parent ambient
// context and search that. See the test:
//
// StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File
//
foreach (var ambientValue in ambientValues)
{
// We override XamlType.CanAssignTo in BindingXamlType so the results we get back

14
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -11,7 +11,8 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
public static AvaloniaXamlObjectWriter Create(
XamlSchemaContext schemaContext,
AvaloniaXamlContext context)
AvaloniaXamlContext context,
IAmbientProvider parentAmbientProvider = null)
{
var nameScope = new AvaloniaNameScope { Instance = context?.RootInstance };
@ -23,8 +24,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
};
return new AvaloniaXamlObjectWriter(schemaContext,
writerSettings.WithContext(context),
nameScope);
writerSettings.WithContext(context),
nameScope,
parentAmbientProvider);
}
private readonly DelayedValuesHelper _delayedValuesHelper = new DelayedValuesHelper();
@ -34,9 +36,9 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private AvaloniaXamlObjectWriter(
XamlSchemaContext schemaContext,
XamlObjectWriterSettings settings,
AvaloniaNameScope nameScope
)
: base(schemaContext, settings)
AvaloniaNameScope nameScope,
IAmbientProvider parentAmbientProvider)
: base(schemaContext, settings, parentAmbientProvider)
{
_nameScope = nameScope;
}

2
src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github

@ -1 +1 @@
Subproject commit c0664014455392ac221a765e66f9837704339b6f
Subproject commit faa952f3a05b4bdf2986d686f4154b1ab084508a

8
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateContent.cs

@ -10,8 +10,10 @@ namespace Avalonia.Markup.Xaml.Templates
public class TemplateContent
{
public TemplateContent(IEnumerable<NamespaceDeclaration> namespaces, XamlReader reader)
public TemplateContent(IEnumerable<NamespaceDeclaration> namespaces, XamlReader reader,
IAmbientProvider ambientProvider)
{
ParentAmbientProvider = ambientProvider;
List = new XamlNodeList(reader.SchemaContext);
//we need to rpeserve all namespace and prefixes to writer
@ -26,9 +28,11 @@ namespace Avalonia.Markup.Xaml.Templates
public XamlNodeList List { get; }
private IAmbientProvider ParentAmbientProvider { get; }
public IControl Load()
{
return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader());
return (IControl)AvaloniaXamlLoader.LoadFromReader(List.GetReader(), parentAmbientProvider: ParentAmbientProvider);
}
public static IControl Load(object templateContent)

3
src/Markup/Avalonia.Markup.Xaml/Templates/TemplateLoader.cs

@ -14,7 +14,8 @@ namespace Avalonia.Markup.Xaml.Templates
{
var tdc = (ITypeDescriptorContext)serviceProvider;
var ns = tdc.GetService<IXamlNamespaceResolver>();
return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader);
var ambientProvider = tdc.GetService<IAmbientProvider>();
return new TemplateContent(ns.GetNamespacePrefixes(), xamlReader, ambientProvider);
}
public override XamlReader Save(object value, IServiceProvider serviceProvider)

71
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/BindingExtensionTests.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Text;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
{
public class BindingExtensionTests
{
[Fact]
public void BindingExtension_Binds_To_Source()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<x:String x:Key='text'>foobar</x:String>
</Window.Resources>
<TextBlock Name='textBlock' Text='{Binding Source={StaticResource text}}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBlock = window.FindControl<TextBlock>("textBlock");
window.Show();
Assert.Equal("foobar", textBlock.Text);
}
}
private IDisposable StyledWindow(params (string, string)[] assets)
{
var services = TestServices.StyledWindow.With(
assetLoader: new MockAssetLoader(assets),
theme: () => new Styles
{
WindowStyle(),
});
return UnitTestApplication.Start(services);
}
private Style WindowStyle()
{
return new Style(x => x.OfType<Window>())
{
Setters =
{
new Setter(
Window.TemplateProperty,
new FuncControlTemplate<Window>(x =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = x[!Window.ContentProperty],
}))
}
};
}
}
}

79
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/StaticResourceExtensionTests.cs

@ -323,7 +323,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
[Fact(Skip = "Not yet supported by Portable.Xaml")]
[Fact]
public void StaticResource_Can_Be_Assigned_To_Property_In_ControlTemplate_In_Styles_File()
{
var styleXaml = @"
@ -361,9 +361,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
var border = (Border)button.GetVisualChildren().Single();
var brush = (SolidColorBrush)border.Background;
// To make this work we somehow need to be able to get hold of the parent ambient
// context from Portable.Xaml. See TODO in StaticResourceExtension.
Assert.Equal(0xff506070, brush.Color.ToUint32());
}
}
@ -417,6 +415,79 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
}
}
[Fact]
public void StaticResource_Can_Be_Assigned_To_Binding_Converter_In_DataTemplate()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Resources>
<local:TestValueConverter x:Key='converter' Append='bar'/>
<DataTemplate x:Key='PurpleData'>
<TextBlock Name='textBlock' Text='{Binding Converter={StaticResource converter}}'/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Name='presenter' Content='foo' ContentTemplate='{StaticResource PurpleData}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
window.DataContext = "foo";
var presenter = window.FindControl<ContentPresenter>("presenter");
window.Show();
var textBlock = (TextBlock)presenter.GetVisualChildren().Single();
Assert.NotNull(textBlock);
Assert.Equal("foobar", textBlock.Text);
}
}
[Fact]
public void StaticResource_Is_Correctly_Chosen_From_Within_DataTemplate()
{
// this tests if IAmbientProviders in DataTemplate contexts are in correct order
// if they wouldn't be, Purple brush would be bound to
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.MarkupExtensions;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Resources>
<local:TestValueConverter x:Key='converter' Append='-bar'/>
<SolidColorBrush x:Key='brush' Color='Purple'/>
<DataTemplate x:Key='WhiteData'>
<Border>
<Border.Resources>
<SolidColorBrush x:Key='brush' Color='White'/>
</Border.Resources>
<TextBlock Name='textBlock' Text='{Binding Color, Source={StaticResource brush}, Converter={StaticResource converter}}' Foreground='{StaticResource brush}' />
</Border>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content='foo' ContentTemplate='{StaticResource WhiteData}'/>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
window.Show();
var textBlock = window.GetVisualDescendants().OfType<TextBlock>().Single();
Assert.NotNull(textBlock);
Assert.Equal("White-bar", textBlock.Text);
}
}
[Fact]
public void Control_Property_Is_Not_Updated_When_Parent_Is_Changed()
{

Loading…
Cancel
Save