Browse Source
Conflicts: tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Property.cspull/894/head
147 changed files with 2246 additions and 805 deletions
@ -1,5 +1,5 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Moq" Version="4.7.1" /> |
<PackageReference Include="Moq" Version="4.7.25" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -0,0 +1,32 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics.ViewModels |
||||
|
{ |
||||
|
public class ViewModelBase : INotifyPropertyChanged |
||||
|
{ |
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
[NotifyPropertyChangedInvocator] |
||||
|
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null) |
||||
|
{ |
||||
|
if (!EqualityComparer<T>.Default.Equals(field, value)) |
||||
|
{ |
||||
|
field = value; |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
[NotifyPropertyChangedInvocator] |
||||
|
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) |
||||
|
{ |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Reactive.Linq; |
||||
|
using System.Reflection; |
||||
|
|
||||
|
namespace Avalonia.Diagnostics.Views |
||||
|
{ |
||||
|
internal static class PropertyChangedExtenions |
||||
|
{ |
||||
|
public static IObservable<T> GetObservable<T>(this INotifyPropertyChanged source, string propertyName) |
||||
|
{ |
||||
|
Contract.Requires<ArgumentNullException>(source != null); |
||||
|
Contract.Requires<ArgumentNullException>(propertyName != null); |
||||
|
|
||||
|
var property = source.GetType().GetTypeInfo().GetDeclaredProperty(propertyName); |
||||
|
|
||||
|
if (property == null) |
||||
|
{ |
||||
|
throw new ArgumentException($"Property '{propertyName}' not found on '{source}."); |
||||
|
} |
||||
|
|
||||
|
return Observable.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>( |
||||
|
e => source.PropertyChanged += e, |
||||
|
e => source.PropertyChanged -= e) |
||||
|
.Where(e => e.EventArgs.PropertyName == propertyName) |
||||
|
.Select(_ => (T)property.GetValue(source)) |
||||
|
.StartWith((T)property.GetValue(source)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,9 +1,16 @@ |
|||||
// Copyright (c) The Avalonia Project. All rights reserved.
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Avalonia.Input.Raw; |
||||
|
|
||||
namespace Avalonia.Input |
namespace Avalonia.Input |
||||
{ |
{ |
||||
public interface IInputDevice |
public interface IInputDevice |
||||
{ |
{ |
||||
|
/// <summary>
|
||||
|
/// Processes raw event. Is called after preprocessing by InputManager
|
||||
|
/// </summary>
|
||||
|
/// <param name="ev"></param>
|
||||
|
void ProcessRawEvent(RawInputEventArgs ev); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,50 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Avalonia.Direct2D1.Media; |
||||
|
using Avalonia.Platform; |
||||
|
using Avalonia.Rendering; |
||||
|
using SharpDX; |
||||
|
using DirectWriteFactory = SharpDX.DirectWrite.Factory; |
||||
|
|
||||
|
namespace Avalonia.Direct2D1 |
||||
|
{ |
||||
|
class ExternalRenderTarget : IRenderTarget |
||||
|
{ |
||||
|
private readonly IExternalDirect2DRenderTargetSurface _externalRenderTargetProvider; |
||||
|
private readonly DirectWriteFactory _dwFactory; |
||||
|
private SharpDX.Direct2D1.RenderTarget _target; |
||||
|
public ExternalRenderTarget(IExternalDirect2DRenderTargetSurface externalRenderTargetProvider, |
||||
|
DirectWriteFactory dwFactory) |
||||
|
{ |
||||
|
_externalRenderTargetProvider = externalRenderTargetProvider; |
||||
|
_dwFactory = dwFactory; |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_target?.Dispose(); |
||||
|
_target = null; |
||||
|
} |
||||
|
|
||||
|
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer) |
||||
|
{ |
||||
|
_target = _target ?? _externalRenderTargetProvider.CreateRenderTarget(); |
||||
|
_externalRenderTargetProvider.BeforeDrawing(); |
||||
|
return new DrawingContextImpl(visualBrushRenderer, _target, _dwFactory, null, () => |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
_externalRenderTargetProvider.AfterDrawing(); |
||||
|
} |
||||
|
catch (SharpDXException ex) when ((uint) ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
|
||||
|
{ |
||||
|
_target?.Dispose(); |
||||
|
_target = null; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Avalonia.Direct2D1 |
||||
|
{ |
||||
|
public interface IExternalDirect2DRenderTargetSurface |
||||
|
{ |
||||
|
SharpDX.Direct2D1.RenderTarget CreateRenderTarget(); |
||||
|
void BeforeDrawing(); |
||||
|
void AfterDrawing(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,134 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Collections; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Base.UnitTests.Collections |
||||
|
{ |
||||
|
public class AvaloniaListExtenionsTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Creates_Initial_Items() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Add() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.Add(4); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Insert() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.Insert(1, 4); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Remove() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.Remove(2); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_RemoveRange() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.RemoveRange(1, 2); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Move() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.Move(2, 0); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_MoveRange() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.MoveRange(1, 2, 0); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Replace() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source[1] = 4; |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void CreateDerivedList_Handles_Clear() |
||||
|
{ |
||||
|
var source = new AvaloniaList<int>(new[] { 0, 1, 2, 3 }); |
||||
|
var target = source.CreateDerivedList(x => new Wrapper(x)); |
||||
|
|
||||
|
source.Clear(); |
||||
|
|
||||
|
var result = target.Select(x => x.Value).ToList(); |
||||
|
|
||||
|
Assert.Equal(source, result); |
||||
|
} |
||||
|
|
||||
|
private class Wrapper |
||||
|
{ |
||||
|
public Wrapper(int value) |
||||
|
{ |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
public int Value { get; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Layout; |
||||
|
using Avalonia.UnitTests; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
namespace Avalonia.Benchmarks.Layout |
||||
|
{ |
||||
|
[MemoryDiagnoser] |
||||
|
public class Measure : IDisposable |
||||
|
{ |
||||
|
private IDisposable _app; |
||||
|
private TestRoot root; |
||||
|
private List<Control> controls = new List<Control>(); |
||||
|
|
||||
|
public Measure() |
||||
|
{ |
||||
|
_app = UnitTestApplication.Start(TestServices.RealLayoutManager); |
||||
|
|
||||
|
var panel = new StackPanel(); |
||||
|
root = new TestRoot { Child = panel }; |
||||
|
controls.Add(panel); |
||||
|
CreateChildren(panel, 3, 5); |
||||
|
LayoutManager.Instance.ExecuteInitialLayoutPass(root); |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
_app.Dispose(); |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void Remeasure_Half() |
||||
|
{ |
||||
|
var random = new Random(1); |
||||
|
|
||||
|
foreach (var control in controls) |
||||
|
{ |
||||
|
if (random.Next(2) == 0) |
||||
|
{ |
||||
|
control.InvalidateMeasure(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
LayoutManager.Instance.ExecuteLayoutPass(); |
||||
|
} |
||||
|
|
||||
|
private void CreateChildren(IPanel parent, int childCount, int iterations) |
||||
|
{ |
||||
|
for (var i = 0; i < childCount; ++i) |
||||
|
{ |
||||
|
var control = new StackPanel(); |
||||
|
parent.Children.Add(control); |
||||
|
|
||||
|
if (iterations > 0) |
||||
|
{ |
||||
|
CreateChildren(control, childCount, iterations - 1); |
||||
|
} |
||||
|
|
||||
|
controls.Add(control); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,291 @@ |
|||||
|
// Copyright (c) The Avalonia Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Linq; |
||||
|
using Avalonia.Controls.Presenters; |
||||
|
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.LogicalTree; |
||||
|
using Avalonia.UnitTests; |
||||
|
using Avalonia.VisualTree; |
||||
|
using Moq; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Controls.UnitTests.Presenters |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Tests for ContentControls that are hosted in a control template.
|
||||
|
/// </summary>
|
||||
|
public class ContentPresenterTests_InTemplate |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Register_With_Host_When_TemplatedParent_Set() |
||||
|
{ |
||||
|
var host = new Mock<IContentPresenterHost>(); |
||||
|
var target = new ContentPresenter(); |
||||
|
|
||||
|
target.SetValue(Control.TemplatedParentProperty, host.Object); |
||||
|
|
||||
|
host.Verify(x => x.RegisterContentPresenter(target)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_Control_Should_Set_Child() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
var child = new Border(); |
||||
|
|
||||
|
target.Content = child; |
||||
|
|
||||
|
Assert.Equal(child, target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_Control_Should_Update_Logical_Tree() |
||||
|
{ |
||||
|
var (target, parent) = CreateTarget(); |
||||
|
var child = new Border(); |
||||
|
|
||||
|
target.Content = child; |
||||
|
|
||||
|
Assert.Equal(parent, child.GetLogicalParent()); |
||||
|
Assert.Equal(new[] { child }, parent.GetLogicalChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_Control_Should_Update_Visual_Tree() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
var child = new Border(); |
||||
|
|
||||
|
target.Content = child; |
||||
|
|
||||
|
Assert.Equal(target, child.GetVisualParent()); |
||||
|
Assert.Equal(new[] { child }, target.GetVisualChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_String_Should_Create_TextBlock() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "Foo"; |
||||
|
|
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
Assert.Equal("Foo", ((TextBlock)target.Child).Text); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_String_Should_Update_Logical_Tree() |
||||
|
{ |
||||
|
var (target, parent) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "Foo"; |
||||
|
|
||||
|
var child = target.Child; |
||||
|
Assert.Equal(parent, child.GetLogicalParent()); |
||||
|
Assert.Equal(new[] { child }, parent.GetLogicalChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Content_To_String_Should_Update_Visual_Tree() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "Foo"; |
||||
|
|
||||
|
var child = target.Child; |
||||
|
Assert.Equal(target, child.GetVisualParent()); |
||||
|
Assert.Equal(new[] { child }, target.GetVisualChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clearing_Control_Content_Should_Update_Logical_Tree() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
var child = new Border(); |
||||
|
|
||||
|
target.Content = child; |
||||
|
target.Content = null; |
||||
|
|
||||
|
Assert.Equal(null, child.GetLogicalParent()); |
||||
|
Assert.Empty(target.GetLogicalChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clearing_Control_Content_Should_Update_Visual_Tree() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
var child = new Border(); |
||||
|
|
||||
|
target.Content = child; |
||||
|
target.Content = null; |
||||
|
|
||||
|
Assert.Equal(null, child.GetVisualParent()); |
||||
|
Assert.Empty(target.GetVisualChildren()); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Control_Content_Should_Not_Be_NameScope() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = new TextBlock(); |
||||
|
|
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
Assert.Null(NameScope.GetNameScope((Control)target.Child)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void DataTemplate_Created_Control_Should_Be_NameScope() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "Foo"; |
||||
|
|
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
Assert.NotNull(NameScope.GetNameScope((Control)target.Child)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Assigning_Control_To_Content_Should_Not_Set_DataContext() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
target.Content = new Border(); |
||||
|
|
||||
|
Assert.False(target.IsSet(Control.DataContextProperty)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Assigning_NonControl_To_Content_Should_Set_DataContext_On_UpdateChild() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
Assert.Equal("foo", target.DataContext); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Use_ContentTemplate_If_Specified() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.ContentTemplate = new FuncDataTemplate<string>(_ => new Canvas()); |
||||
|
target.Content = "Foo"; |
||||
|
|
||||
|
Assert.IsType<Canvas>(target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Update_If_ContentTemplate_Changed() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "Foo"; |
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
|
||||
|
target.ContentTemplate = new FuncDataTemplate<string>(_ => new Canvas()); |
||||
|
Assert.IsType<Canvas>(target.Child); |
||||
|
|
||||
|
target.ContentTemplate = null; |
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Assigning_Control_To_Content_After_NonControl_Should_Clear_DataContext() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
Assert.True(target.IsSet(Control.DataContextProperty)); |
||||
|
|
||||
|
target.Content = new Border(); |
||||
|
|
||||
|
Assert.False(target.IsSet(Control.DataContextProperty)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Recycles_DataTemplate() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
target.DataTemplates.Add(new FuncDataTemplate<string>(_ => new Border(), true)); |
||||
|
|
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
var control = target.Child; |
||||
|
Assert.IsType<Border>(control); |
||||
|
|
||||
|
target.Content = "bar"; |
||||
|
Assert.Same(control, target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Detects_DataTemplate_Doesnt_Match_And_Doesnt_Recycle() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
target.DataTemplates.Add(new FuncDataTemplate<string>(x => x == "foo", _ => new Border(), true)); |
||||
|
|
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
var control = target.Child; |
||||
|
Assert.IsType<Border>(control); |
||||
|
|
||||
|
target.Content = "bar"; |
||||
|
Assert.IsType<TextBlock>(target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Detects_DataTemplate_Doesnt_Support_Recycling() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
target.DataTemplates.Add(new FuncDataTemplate<string>(_ => new Border(), false)); |
||||
|
|
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
var control = target.Child; |
||||
|
Assert.IsType<Border>(control); |
||||
|
|
||||
|
target.Content = "bar"; |
||||
|
Assert.NotSame(control, target.Child); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Reevaluates_DataTemplates_When_Recycling() |
||||
|
{ |
||||
|
var (target, _) = CreateTarget(); |
||||
|
|
||||
|
target.DataTemplates.Add(new FuncDataTemplate<string>(x => x == "bar", _ => new Canvas(), true)); |
||||
|
target.DataTemplates.Add(new FuncDataTemplate<string>(_ => new Border(), true)); |
||||
|
|
||||
|
target.Content = "foo"; |
||||
|
|
||||
|
var control = target.Child; |
||||
|
Assert.IsType<Border>(control); |
||||
|
|
||||
|
target.Content = "bar"; |
||||
|
Assert.IsType<Canvas>(target.Child); |
||||
|
} |
||||
|
|
||||
|
(ContentPresenter presenter, ContentControl templatedParent) CreateTarget() |
||||
|
{ |
||||
|
var templatedParent = new ContentControl |
||||
|
{ |
||||
|
Template = new FuncControlTemplate<ContentControl>(x => |
||||
|
new ContentPresenter |
||||
|
{ |
||||
|
Name = "PART_ContentPresenter", |
||||
|
}), |
||||
|
}; |
||||
|
var root = new TestRoot { Child = templatedParent }; |
||||
|
|
||||
|
templatedParent.ApplyTemplate(); |
||||
|
|
||||
|
return ((ContentPresenter)templatedParent.Presenter, templatedParent); |
||||
|
} |
||||
|
|
||||
|
private class TestContentControl : ContentControl |
||||
|
{ |
||||
|
public IControl Child { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue