Browse Source

Merge branch 'master' into feature/skia-api-lease

pull/8656/head
Max Katz 4 years ago
committed by GitHub
parent
commit
2fd6e542b7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 56
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  2. 5
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  3. 8
      src/Avalonia.Base/Controls/Templates/ITemplateResult.cs
  4. 3
      src/Avalonia.Base/Controls/Templates/TemplateResult.cs
  5. 2
      src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs
  6. 13
      src/Avalonia.Base/Rendering/Composition/Compositor.cs
  7. 3
      src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs
  8. 17
      src/Avalonia.Controls/Converters/StringFormatConverter.cs
  9. 3
      src/Avalonia.Controls/ListBox.cs
  10. 2
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  11. 2
      src/Avalonia.Controls/TreeView.cs
  12. 30
      src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml
  13. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  14. 90
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  15. 9
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  16. 18
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs
  17. 5
      src/Markup/Avalonia.Markup/Data/TemplateBinding.cs
  18. 49
      tests/Avalonia.Controls.UnitTests/ListBoxTests.cs
  19. 133
      tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs
  20. 89
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  21. 542
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs
  22. 38
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  23. 43
      tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs
  24. 207
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs

56
src/Avalonia.Base/Controls/ResourceDictionary.cs

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls.Templates;
namespace Avalonia.Controls
{
@ -29,7 +30,11 @@ namespace Avalonia.Controls
public object? this[object key]
{
get => _inner?[key];
get
{
TryGetValue(key, out var value);
return value;
}
set
{
Inner[key] = value;
@ -119,6 +124,12 @@ namespace Avalonia.Controls
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
public void AddDeferred(object key, Func<IServiceProvider?, object?> factory)
{
Inner.Add(key, new DeferredItem(factory));
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
}
public void Clear()
{
if (_inner?.Count > 0)
@ -143,10 +154,8 @@ namespace Avalonia.Controls
public bool TryGetResource(object key, out object? value)
{
if (_inner is not null && _inner.TryGetValue(key, out value))
{
if (TryGetValue(key, out value))
return true;
}
if (_mergedDictionaries != null)
{
@ -165,12 +174,28 @@ namespace Avalonia.Controls
public bool TryGetValue(object key, out object? value)
{
if (_inner is not null)
return _inner.TryGetValue(key, out value);
if (_inner is not null && _inner.TryGetValue(key, out value))
{
if (value is DeferredItem deffered)
{
_inner[key] = value = deffered.Factory(null) switch
{
ITemplateResult t => t.Result,
object v => v,
_ => null,
};
}
return true;
}
value = null;
return false;
}
public IEnumerator<KeyValuePair<object, object?>> GetEnumerator()
{
return _inner?.GetEnumerator() ?? Enumerable.Empty<KeyValuePair<object, object?>>().GetEnumerator();
}
void ICollection<KeyValuePair<object, object?>>.Add(KeyValuePair<object, object?> item)
{
@ -198,12 +223,17 @@ namespace Avalonia.Controls
return false;
}
public IEnumerator<KeyValuePair<object, object?>> GetEnumerator()
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal bool ContainsDeferredKey(object key)
{
return _inner?.GetEnumerator() ?? Enumerable.Empty<KeyValuePair<object, object?>>().GetEnumerator();
}
if (_inner is not null && _inner.TryGetValue(key, out var result))
{
return result is DeferredItem;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
return false;
}
void IResourceProvider.AddOwner(IResourceHost owner)
{
@ -258,5 +288,11 @@ namespace Avalonia.Controls
}
}
}
private class DeferredItem
{
public DeferredItem(Func<IServiceProvider?, object?> factory) => Factory = factory;
public Func<IServiceProvider?, object?> Factory { get; }
}
}
}

5
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -132,6 +132,11 @@ namespace Avalonia.Controls
{
_target.OwnerChanged += OwnerChanged;
_owner = _target.Owner;
if (_owner is object)
{
_owner.ResourcesChanged += ResourcesChanged;
}
}
protected override void Deinitialize()

8
src/Avalonia.Base/Controls/Templates/ITemplateResult.cs

@ -0,0 +1,8 @@
namespace Avalonia.Controls.Templates
{
public interface ITemplateResult
{
public object? Result { get; }
public INameScope NameScope { get; }
}
}

3
src/Avalonia.Controls/Templates/TemplateResult.cs → src/Avalonia.Base/Controls/Templates/TemplateResult.cs

@ -1,9 +1,10 @@
namespace Avalonia.Controls.Templates
{
public class TemplateResult<T>
public class TemplateResult<T> : ITemplateResult
{
public T Result { get; }
public INameScope NameScope { get; }
object? ITemplateResult.Result => Result;
public TemplateResult(T result, INameScope nameScope)
{

2
src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs

@ -71,7 +71,7 @@ public class CompositingRenderer : IRendererWithCompositor
if(_queuedUpdate)
return;
_queuedUpdate = true;
Dispatcher.UIThread.Post(_update, DispatcherPriority.Composition);
_compositor.InvokeWhenReadyForNextCommit(_update);
}
/// <inheritdoc/>

13
src/Avalonia.Base/Rendering/Composition/Compositor.cs

@ -33,6 +33,7 @@ namespace Avalonia.Rendering.Composition
internal IEasing DefaultEasing { get; }
private List<Action>? _invokeOnNextCommit;
private readonly Stack<List<Action>> _invokeListPool = new();
private Task? _lastBatchCompleted;
/// <summary>
/// Creates a new compositor on a specified render loop that would use a particular GPU
@ -86,7 +87,7 @@ namespace Avalonia.Rendering.Composition
if (_invokeOnNextCommit != null)
ScheduleCommitCallbacks(batch.Completed);
return batch.Completed;
return _lastBatchCompleted = batch.Completed;
}
async void ScheduleCommitCallbacks(Task task)
@ -139,5 +140,15 @@ namespace Avalonia.Rendering.Composition
_invokeOnNextCommit ??= _invokeListPool.Count > 0 ? _invokeListPool.Pop() : new();
_invokeOnNextCommit.Add(action);
}
public void InvokeWhenReadyForNextCommit(Action action)
{
if (_lastBatchCompleted == null || _lastBatchCompleted.IsCompleted)
Dispatcher.UIThread.Post(action, DispatcherPriority.Composition);
else
_lastBatchCompleted.ContinueWith(
static (_, state) => Dispatcher.UIThread.Post((Action)state!, DispatcherPriority.Composition),
action);
}
}
}

3
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositor.cs

@ -108,6 +108,7 @@ namespace Avalonia.Rendering.Composition.Server
private void RenderCore()
{
ApplyPendingBatches();
CompletePendingBatches();
foreach(var animation in _activeAnimations)
_animationsToUpdate.Add(animation);
@ -119,8 +120,6 @@ namespace Avalonia.Rendering.Composition.Server
foreach (var t in _activeTargets)
t.Render();
CompletePendingBatches();
}
public void AddCompositionTarget(ServerCompositionTarget target)

17
src/Avalonia.Controls/Converters/StringFormatConverter.cs

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Data;
using Avalonia.Data.Converters;
namespace Avalonia.Controls.Converters;
@ -15,13 +14,17 @@ public class StringFormatConverter : IMultiValueConverter
{
public object? Convert(IList<object?> values, Type targetType, object? parameter, CultureInfo culture)
{
try
if (values[0] is string format)
{
return string.Format((string)values[0]!, values.Skip(1).ToArray());
}
catch (Exception e)
{
return new BindingNotification(e, BindingErrorType.Error);
try
{
return string.Format(format, values.Skip(1).ToArray());
}
catch
{
return AvaloniaProperty.UnsetValue;
}
}
return AvaloniaProperty.UnsetValue;
}
}

3
src/Avalonia.Controls/ListBox.cs

@ -6,6 +6,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Selection;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.VisualTree;
namespace Avalonia.Controls
@ -157,7 +158,7 @@ namespace Avalonia.Controls
e.Source,
true,
e.KeyModifiers.HasAllFlags(KeyModifiers.Shift),
e.KeyModifiers.HasAllFlags(KeyModifiers.Control),
e.KeyModifiers.HasAllFlags(AvaloniaLocator.Current.GetRequiredService<PlatformHotkeyConfiguration>().CommandModifiers),
point.Properties.IsRightButtonPressed);
}
}

2
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -187,7 +187,7 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Remove:
if ((e.OldStartingIndex >= FirstIndex && e.OldStartingIndex < NextIndex) ||
if (e.OldStartingIndex < NextIndex ||
panel.Children.Count > ItemCount)
{
RecycleContainersOnRemove();

2
src/Avalonia.Controls/TreeView.cs

@ -529,7 +529,7 @@ namespace Avalonia.Controls
e.Source,
true,
e.KeyModifiers.HasAllFlags(KeyModifiers.Shift),
e.KeyModifiers.HasAllFlags(KeyModifiers.Control),
e.KeyModifiers.HasAllFlags(AvaloniaLocator.Current.GetRequiredService<PlatformHotkeyConfiguration>().CommandModifiers),
point.Properties.IsRightButtonPressed);
}
}

30
src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml

@ -41,13 +41,31 @@
<StreamGeometry x:Key="ButtonSpinnerIncreaseButtonIcon">M0,9 L10,0 20,9 19,10 10,2 1,10 z</StreamGeometry>
<StreamGeometry x:Key="ButtonSpinnerDecreaseButtonIcon">M0,1 L10,10 20,1 19,0 10,8 1,0 z</StreamGeometry>
<ControlTheme x:Key="FluentButtonSpinnerRepeatButton" TargetType="RepeatButton" BasedOn="{StaticResource {x:Type RepeatButton}}">
<Setter Property="CornerRadius" Value="0"/>
<ControlTheme x:Key="FluentButtonSpinnerRepeatButton" TargetType="RepeatButton">
<Setter Property="MinWidth" Value="34" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPointerOver}" />
</Style>
<Style Selector="^:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPressed}" />
</Style>
<Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderBrush" Value="{TemplateBinding BorderBrush}" />
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}"/>
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}" />
</Style>
</ControlTheme>
@ -83,7 +101,6 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource ButtonSpinnerLeftThickness}}"
CornerRadius="0"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"
@ -99,7 +116,6 @@
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness, Converter={StaticResource ButtonSpinnerLeftThickness}}"
CornerRadius="0"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Foreground="{TemplateBinding Foreground}"

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -60,6 +60,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
InsertAfter<TypeReferenceResolver>(
new XDataTypeTransformer());
InsertBefore<DeferredContentTransformer>(
new AvaloniaXamlIlDeferredResourceTransformer()
);
// After everything else
InsertBefore<NewObjectTransformer>(
new AddNameScopeRegistration(),

90
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs

@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Linq;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
{
internal class AvaloniaXamlIlDeferredResourceTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (!(node is XamlPropertyAssignmentNode pa) || pa.Values.Count != 2)
return node;
if (!ShouldBeDeferred(pa.Values[1]))
return node;
var types = context.GetAvaloniaTypes();
if (pa.Property.DeclaringType == types.ResourceDictionary && pa.Property.Name == "Content")
{
pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
pa.PossibleSetters = new List<IXamlPropertySetter>
{
new XamlDirectCallPropertySetter(types.ResourceDictionaryDeferredAdd),
};
}
else if (pa.Property.Name == "Resources" && pa.Property.Getter.ReturnType.Equals(types.IResourceDictionary))
{
pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
pa.PossibleSetters = new List<IXamlPropertySetter>
{
new AdderSetter(pa.Property.Getter, types.ResourceDictionaryDeferredAdd),
};
}
return node;
}
private static bool ShouldBeDeferred(IXamlAstValueNode node)
{
// XAML compiler is currently strict about value types, allowing them to be created only through converters.
// At the moment it should be safe to not defer structs.
return !node.Type.GetClrType().IsValueType;
}
class AdderSetter : IXamlPropertySetter, IXamlEmitablePropertySetter<IXamlILEmitter>
{
private readonly IXamlMethod _getter;
private readonly IXamlMethod _adder;
public AdderSetter(IXamlMethod getter, IXamlMethod adder)
{
_getter = getter;
_adder = adder;
TargetType = getter.DeclaringType;
Parameters = adder.ParametersWithThis().Skip(1).ToList();
}
public IXamlType TargetType { get; }
public PropertySetterBinderParameters BinderParameters { get; } = new PropertySetterBinderParameters
{
AllowMultiple = true
};
public IReadOnlyList<IXamlType> Parameters { get; }
public void Emit(IXamlILEmitter emitter)
{
var locals = new Stack<XamlLocalsPool.PooledLocal>();
// Save all "setter" parameters
for (var c = Parameters.Count - 1; c >= 0; c--)
{
var loc = emitter.LocalsPool.GetLocal(Parameters[c]);
locals.Push(loc);
emitter.Stloc(loc.Local);
}
emitter.EmitCall(_getter);
while (locals.Count > 0)
using (var loc = locals.Pop())
emitter.Ldloc(loc.Local);
emitter.EmitCall(_adder, true);
}
}
}
}

9
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -98,6 +98,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType TextDecorations { get; }
public IXamlType TextTrimming { get; }
public IXamlType ISetter { get; }
public IXamlType IResourceDictionary { get; }
public IXamlType ResourceDictionary { get; }
public IXamlMethod ResourceDictionaryDeferredAdd { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -218,6 +221,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations");
TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming");
ISetter = cfg.TypeSystem.GetType("Avalonia.Styling.ISetter");
IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary");
ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,
cfg.TypeSystem.GetType("System.Func`2").MakeGenericType(
cfg.TypeSystem.GetType("System.IServiceProvider"),
XamlIlTypes.Object));
}
}

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

@ -39,6 +39,8 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
targetType = setter.Property.PropertyType;
}
var previousWasControlTheme = false;
// Look upwards though the ambient context for IResourceNodes
// which might be able to give us the resource.
foreach (var parent in stack.Parents)
@ -47,6 +49,21 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
{
return ColorToBrushConverter.Convert(value, targetType);
}
// HACK: Temporary fix for #8678. Hard-coded to only work for the DevTools main
// window as we don't want 3rd parties to start relying on this hack.
//
// We need to implement compile-time merging of resource dictionaries and this
// hack can be removed.
if (previousWasControlTheme &&
parent is ResourceDictionary hack &&
hack.Owner?.GetType().FullName == "Avalonia.Diagnostics.Views.MainWindow" &&
hack.Owner.TryGetResource(ResourceKey, out value))
{
return ColorToBrushConverter.Convert(value, targetType);
}
previousWasControlTheme = parent is ControlTheme;
}
if (provideTarget.TargetObject is IControl target &&
@ -69,3 +86,4 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
}
}
}

5
src/Markup/Avalonia.Markup/Data/TemplateBinding.cs

@ -19,6 +19,7 @@ namespace Avalonia.Data
private bool _isSetterValue;
private IStyledElement _target = default!;
private Type? _targetType;
private bool _hasProducedValue;
public TemplateBinding()
{
@ -143,10 +144,12 @@ namespace Avalonia.Data
}
PublishNext(value);
_hasProducedValue = true;
}
else
else if (_hasProducedValue)
{
PublishNext(AvaloniaProperty.UnsetValue);
_hasProducedValue = false;
}
}

49
tests/Avalonia.Controls.UnitTests/ListBoxTests.cs

@ -9,7 +9,6 @@ using Avalonia.Data;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.Threading;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;
@ -19,7 +18,7 @@ namespace Avalonia.Controls.UnitTests
public class ListBoxTests
{
private MouseTestHelper _mouse = new MouseTestHelper();
[Fact]
public void Should_Use_ItemTemplate_To_Create_Item_Content()
{
@ -433,6 +432,47 @@ namespace Avalonia.Controls.UnitTests
}
}
[Fact]
public void ListBox_Should_Be_Valid_After_Remove_Of_Item_In_NonVisibleArea()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var items = new AvaloniaList<string>(Enumerable.Range(1, 30).Select(v => v.ToString()));
var wnd = new Window() { Width = 100, Height = 100, IsVisible = true };
var target = new ListBox()
{
AutoScrollToSelectedItem = true,
Height = 100,
Width = 50,
VirtualizationMode = ItemVirtualizationMode.Simple,
ItemTemplate = new FuncDataTemplate<object>((c, _) => new Border() { Height = 10 }),
Items = items,
};
wnd.Content = target;
var lm = wnd.LayoutManager;
lm.ExecuteInitialLayoutPass();
//select last / scroll to last item
target.SelectedItem = items.Last();
lm.ExecuteLayoutPass();
//remove the first item (in non realized area of the listbox)
items.Remove("1");
lm.ExecuteLayoutPass();
Assert.Equal("30", target.ItemContainerGenerator.ContainerFromIndex(items.Count - 1).DataContext);
Assert.Equal("29", target.ItemContainerGenerator.ContainerFromIndex(items.Count - 2).DataContext);
Assert.Equal("28", target.ItemContainerGenerator.ContainerFromIndex(items.Count - 3).DataContext);
Assert.Equal("27", target.ItemContainerGenerator.ContainerFromIndex(items.Count - 4).DataContext);
Assert.Equal("26", target.ItemContainerGenerator.ContainerFromIndex(items.Count - 5).DataContext);
}
}
[Fact]
public void Clicking_Item_Should_Raise_BringIntoView_For_Correct_Control()
{
@ -656,7 +696,6 @@ namespace Avalonia.Controls.UnitTests
public string Value { get; }
}
[Fact]
public void SelectedItem_Validation()
{
@ -670,11 +709,11 @@ namespace Avalonia.Controls.UnitTests
};
Prepare(target);
var exception = new System.InvalidCastException("failed validation");
var textObservable = new BehaviorSubject<BindingNotification>(new BindingNotification(exception, BindingErrorType.DataValidationError));
target.Bind(ComboBox.SelectedItemProperty, textObservable);
Assert.True(DataValidationErrors.GetHasErrors(target));
Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception }));
}

133
tests/Avalonia.Controls.UnitTests/ListBoxTests_Single.cs

@ -5,10 +5,12 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -60,104 +62,123 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Clicking_Item_Should_Select_It()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
ApplyTemplate(target);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
}
}
[Fact]
public void Clicking_Selected_Item_Should_Not_Deselect_It()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
ApplyTemplate(target);
target.SelectedIndex = 0;
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
target.SelectedIndex = 0;
_mouse.Click(target.Presenter.Panel.Children[0]);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal(0, target.SelectedIndex);
}
}
[Fact]
public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
ApplyTemplate(target);
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
_mouse.Click(target.Presenter.Panel.Children[0]);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal(0, target.SelectedIndex);
}
}
[Fact]
public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Toggle,
};
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Toggle,
};
ApplyTemplate(target);
target.SelectedIndex = 0;
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
target.SelectedIndex = 0;
_mouse.Click(target.Presenter.Panel.Children[0]);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(-1, target.SelectedIndex);
Assert.Equal(-1, target.SelectedIndex);
}
}
[Fact]
public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
};
ApplyTemplate(target);
target.SelectedIndex = 0;
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
target.SelectedIndex = 0;
_mouse.Click(target.Presenter.Panel.Children[0]);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal(0, target.SelectedIndex);
}
}
[Fact]
public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
ApplyTemplate(target);
target.SelectedIndex = 1;
var target = new ListBox
{
Template = new FuncControlTemplate(CreateListBoxTemplate),
Items = new[] { "Foo", "Bar", "Baz " },
SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
ApplyTemplate(target);
target.SelectedIndex = 1;
_mouse.Click(target.Presenter.Panel.Children[0]);
_mouse.Click(target.Presenter.Panel.Children[0]);
Assert.Equal(0, target.SelectedIndex);
Assert.Equal(0, target.SelectedIndex);
}
}
[Fact]

89
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -14,6 +14,7 @@ using Avalonia.Controls.Selection;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Markup.Data;
using Avalonia.Platform;
@ -1115,42 +1116,48 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz " },
};
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[1]);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz " },
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[1]);
var panel = target.Presenter.Panel;
var panel = target.Presenter.Panel;
Assert.Equal(
KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
panel.Children[1]);
Assert.Equal(
KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
panel.Children[1]);
}
}
[Fact]
public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement()
{
var items = new ObservableCollection<string>(new[] { "Foo", "Bar", "Baz " });
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = items,
};
var items = new ObservableCollection<string>(new[] { "Foo", "Bar", "Baz " });
Prepare(target);
var target = new ListBox
{
Template = Template(),
Items = items,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
Prepare(target);
_helper.Down(target.Presenter.Panel.Children[1]);
_helper.Down(target.Presenter.Panel.Children[1]);
items.RemoveAt(1);
items.RemoveAt(1);
var panel = target.Presenter.Panel;
var panel = target.Presenter.Panel;
Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
}
}
[Fact]
@ -1230,31 +1237,37 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
};
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[3]);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[3]);
Assert.Equal(3, target.SelectedIndex);
Assert.Equal(3, target.SelectedIndex);
}
}
[Fact]
public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
};
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[3]);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
Prepare(target);
_helper.Down((Interactive)target.Presenter.Panel.Children[3]);
Assert.Equal(new[] { ":pressed", ":selected" }, target.Presenter.Panel.Children[3].Classes);
Assert.Equal(new[] { ":pressed", ":selected" }, target.Presenter.Panel.Children[3].Classes);
}
}
[Fact]

542
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests_Multiple.cs

@ -10,8 +10,10 @@ using Avalonia.Controls.Selection;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.UnitTests;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests.Primitives
@ -701,261 +703,290 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Shift_Selecting_From_No_Selection_Selects_From_Start()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target));
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1, 2 }, SelectedContainers(target));
}
}
[Fact]
public void Ctrl_Selecting_Raises_SelectionChanged_Events()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
SelectionChangedEventArgs receivedArgs = null;
SelectionChangedEventArgs receivedArgs = null;
target.SelectionChanged += (_, args) => receivedArgs = args;
target.SelectionChanged += (_, args) => receivedArgs = args;
void VerifyAdded(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.AddedItems);
Assert.Empty(receivedArgs.RemovedItems);
}
void VerifyAdded(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.AddedItems);
Assert.Empty(receivedArgs.RemovedItems);
}
void VerifyRemoved(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.RemovedItems);
Assert.Empty(receivedArgs.AddedItems);
}
void VerifyRemoved(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.RemovedItems);
Assert.Empty(receivedArgs.AddedItems);
}
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
VerifyAdded("Bar");
VerifyAdded("Bar");
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
VerifyAdded("Baz");
VerifyAdded("Baz");
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
VerifyAdded("Qux");
VerifyAdded("Qux");
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
VerifyRemoved("Bar");
VerifyRemoved("Bar");
}
}
[Fact]
public void Ctrl_Selecting_SelectedItem_With_Multiple_Selection_Active_Sets_SelectedItem_To_Next_Selection()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
Assert.Equal(2, target.SelectedIndex);
Assert.Equal("Baz", target.SelectedItem);
Assert.Equal(new[] { "Baz", "Qux" }, target.SelectedItems);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(new[] { "Bar", "Baz", "Qux" }, target.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
Assert.Equal(2, target.SelectedIndex);
Assert.Equal("Baz", target.SelectedItem);
Assert.Equal(new[] { "Baz", "Qux" }, target.SelectedItems);
}
}
[Fact]
public void Ctrl_Selecting_Non_SelectedItem_With_Multiple_Selection_Active_Leaves_SelectedItem_The_Same()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal("Bar", target.SelectedItem);
}
}
[Fact]
public void Should_Ctrl_Select_Correct_Item_When_Duplicate_Items_Are_Present()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[3]);
_helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: KeyModifiers.Control);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
Assert.Equal(new[] { 3, 4 }, SelectedContainers(target));
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[3]);
_helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: KeyModifiers.Control);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
Assert.Equal(new[] { 3, 4 }, SelectedContainers(target));
}
}
[Fact]
public void Should_Shift_Select_Correct_Item_When_Duplicates_Are_Present()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[3]);
_helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 3, 4, 5 }, SelectedContainers(target));
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[3]);
_helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 3, 4, 5 }, SelectedContainers(target));
}
}
[Fact]
public void Can_Shift_Select_All_Items_When_Duplicates_Are_Present()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target));
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[5], modifiers: KeyModifiers.Shift);
var panel = target.Presenter.Panel;
Assert.Equal(new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" }, target.SelectedItems);
Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, SelectedContainers(target));
}
}
[Fact]
public void Shift_Selecting_Raises_SelectionChanged_Events()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Qux" },
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
SelectionChangedEventArgs receivedArgs = null;
SelectionChangedEventArgs receivedArgs = null;
target.SelectionChanged += (_, args) => receivedArgs = args;
target.SelectionChanged += (_, args) => receivedArgs = args;
void VerifyAdded(params string[] selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(selection, receivedArgs.AddedItems);
Assert.Empty(receivedArgs.RemovedItems);
}
void VerifyAdded(params string[] selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(selection, receivedArgs.AddedItems);
Assert.Empty(receivedArgs.RemovedItems);
}
void VerifyRemoved(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.RemovedItems);
Assert.Empty(receivedArgs.AddedItems);
}
void VerifyRemoved(string selection)
{
Assert.NotNull(receivedArgs);
Assert.Equal(new[] { selection }, receivedArgs.RemovedItems);
Assert.Empty(receivedArgs.AddedItems);
}
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
_helper.Click((Interactive)target.Presenter.Panel.Children[1]);
VerifyAdded("Bar");
VerifyAdded("Bar");
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Shift);
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Shift);
VerifyAdded("Baz" ,"Qux");
VerifyAdded("Baz", "Qux");
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Shift);
receivedArgs = null;
_helper.Click((Interactive)target.Presenter.Panel.Children[2], modifiers: KeyModifiers.Shift);
VerifyRemoved("Qux");
VerifyRemoved("Qux");
}
}
[Fact]
public void Duplicate_Items_Are_Added_To_SelectedItems_In_Order()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
Assert.Equal(new[] { "Foo" }, target.SelectedItems);
Assert.Equal(new[] { "Foo" }, target.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[4], modifiers: KeyModifiers.Control);
Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
Assert.Equal(new[] { "Foo", "Bar" }, target.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[3], modifiers: KeyModifiers.Control);
Assert.Equal(new[] { "Foo", "Bar", "Foo" }, target.SelectedItems);
Assert.Equal(new[] { "Foo", "Bar", "Foo" }, target.SelectedItems);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Control);
Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.SelectedItems);
Assert.Equal(new[] { "Foo", "Bar", "Foo", "Bar" }, target.SelectedItems);
}
}
[Fact]
@ -1158,70 +1189,79 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Left_Click_On_SelectedItem_Should_Clear_Existing_Selection()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
Assert.Equal(3, target.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
Assert.Equal(1, target.SelectedItems.Count);
Assert.Equal(new[] { "Foo", }, target.SelectedItems);
Assert.Equal(new[] { 0 }, SelectedContainers(target));
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
Assert.Equal(3, target.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
Assert.Equal(1, target.SelectedItems.Count);
Assert.Equal(new[] { "Foo", }, target.SelectedItems);
Assert.Equal(new[] { 0 }, SelectedContainers(target));
}
}
[Fact]
public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
target.SelectAll();
Assert.Equal(3, target.SelectedItems.Count);
Assert.Equal(3, target.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
_helper.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
Assert.Equal(3, target.SelectedItems.Count);
Assert.Equal(3, target.SelectedItems.Count);
}
}
[Fact]
public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Shift);
Assert.Equal(2, target.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right);
Assert.Equal(1, target.SelectedItems.Count);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[1], modifiers: KeyModifiers.Shift);
Assert.Equal(2, target.SelectedItems.Count);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right);
Assert.Equal(1, target.SelectedItems.Count);
}
}
[Fact]
@ -1253,41 +1293,47 @@ namespace Avalonia.Controls.UnitTests.Primitives
[Fact]
public void Shift_Right_Click_Should_Not_Select_Multiple()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Shift);
Assert.Equal(1, target.SelectedItems.Count);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Shift);
Assert.Equal(1, target.SelectedItems.Count);
}
}
[Fact]
public void Ctrl_Right_Click_Should_Not_Select_Multiple()
{
var target = new ListBox
using (UnitTestApplication.Start())
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedItems.Count);
var target = new ListBox
{
Template = Template(),
Items = new[] { "Foo", "Bar", "Baz" },
ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
_helper.Click((Interactive)target.Presenter.Panel.Children[0]);
_helper.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Right, modifiers: KeyModifiers.Control);
Assert.Equal(1, target.SelectedItems.Count);
}
}
[Fact]

38
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -18,6 +18,7 @@ using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.UnitTests;
using JetBrains.Annotations;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests
@ -885,28 +886,31 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (UnitTestApplication.Start())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
target.SelectAll();
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
target.SelectAll();
AssertChildrenSelected(target, tree[0]);
Assert.Equal(5, target.SelectedItems.Count);
AssertChildrenSelected(target, tree[0]);
Assert.Equal(5, target.SelectedItems.Count);
_mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
_mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
Assert.Equal(5, target.SelectedItems.Count);
Assert.Equal(5, target.SelectedItems.Count);
}
}
[Fact]

43
tests/Avalonia.Markup.UnitTests/Data/TemplateBindingTests.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
@ -217,6 +218,37 @@ namespace Avalonia.Markup.UnitTests.Data
}
}
[Fact]
public void Should_Not_Pass_UnsetValue_To_MultiBinding_During_ApplyTemplate()
{
var converter = new MultiConverter();
var source = new Button
{
Content = "foo",
Template = new FuncControlTemplate<Button>((parent, _) =>
new ContentPresenter
{
[~ContentPresenter.ContentProperty] = new MultiBinding
{
Converter = converter,
Bindings =
{
new TemplateBinding(ContentControl.ContentProperty),
}
}
}),
};
source.ApplyTemplate();
var target = (ContentPresenter)source.GetVisualChildren().Single();
// #8672 was caused by TemplateBinding passing "unset" to the MultiBinding during
// ApplyTemplate as the TemplatedParent property doesn't get setup until after the
// binding is initiated.
Assert.Equal(new[] { "foo" }, converter.Values);
}
private class PrefixConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
@ -247,5 +279,16 @@ namespace Avalonia.Markup.UnitTests.Data
return null;
}
}
private class MultiConverter : IMultiValueConverter
{
public List<object> Values { get; } = new();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
Values.AddRange(values);
return values.FirstOrDefault();
}
}
}
}

207
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs

@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
@ -66,6 +67,212 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}
}
[Fact]
public void Item_Is_Added_To_ResourceDictionary_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='Red' Color='Red' />
</ResourceDictionary>";
var resources = (ResourceDictionary)AvaloniaRuntimeXamlLoader.Load(xaml);
Assert.True(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Added_To_ResourceDictionary_Is_UnDeferred_On_Read()
{
using (StyledWindow())
{
var xaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<SolidColorBrush x:Key='Red' Color='Red' />
</ResourceDictionary>";
var resources = (ResourceDictionary)AvaloniaRuntimeXamlLoader.Load(xaml);
Assert.True(resources.ContainsDeferredKey("Red"));
Assert.IsType<SolidColorBrush>(resources["Red"]);
Assert.False(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Is_Added_To_Window_Resources_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<SolidColorBrush x:Key='Red' Color='Red' />
</Window.Resources>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var resources = (ResourceDictionary)window.Resources;
Assert.True(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Is_Added_To_Window_MergedDictionaries_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<SolidColorBrush x:Key='Red' Color='Red' />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var resources = (ResourceDictionary)window.Resources.MergedDictionaries[0];
Assert.True(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Is_Added_To_Style_Resources_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<Style xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Style.Resources>
<SolidColorBrush x:Key='Red' Color='Red' />
</Style.Resources>
</Style>";
var style = (Style)AvaloniaRuntimeXamlLoader.Load(xaml);
var resources = (ResourceDictionary)style.Resources;
Assert.True(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Is_Added_To_Styles_Resources_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<Styles xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Styles.Resources>
<SolidColorBrush x:Key='Red' Color='Red' />
</Styles.Resources>
</Styles>";
var style = (Styles)AvaloniaRuntimeXamlLoader.Load(xaml);
var resources = (ResourceDictionary)style.Resources;
Assert.True(resources.ContainsDeferredKey("Red"));
}
}
[Fact]
public void Item_Can_Be_StaticReferenced_As_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<SolidColorBrush x:Key='Red' Color='Red' />
</Window.Resources>
<Button>
<Button.Resources>
<StaticResource x:Key='Red2' ResourceKey='Red' />
</Button.Resources>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var windowResources = (ResourceDictionary)window.Resources;
var buttonResources = (ResourceDictionary)((Button)window.Content!).Resources;
Assert.True(windowResources.ContainsDeferredKey("Red"));
Assert.True(buttonResources.ContainsDeferredKey("Red2"));
}
}
[Fact]
public void Item_StaticReferenced_Is_UnDeferred_On_Read()
{
using (StyledWindow())
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Window.Resources>
<SolidColorBrush x:Key='Red' Color='Red' />
</Window.Resources>
<Button>
<Button.Resources>
<StaticResource x:Key='Red2' ResourceKey='Red' />
</Button.Resources>
</Button>
</Window>";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
var windowResources = (ResourceDictionary)window.Resources;
var buttonResources = (ResourceDictionary)((Button)window.Content!).Resources;
Assert.IsType<SolidColorBrush>(buttonResources["Red2"]);
Assert.False(windowResources.ContainsDeferredKey("Red"));
Assert.False(buttonResources.ContainsDeferredKey("Red2"));
}
}
[Fact]
public void Value_Type_With_Parse_Converter_Should_Not_Be_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Color x:Key='Red'>Red</Color>
</ResourceDictionary>";
var resources = (ResourceDictionary)AvaloniaRuntimeXamlLoader.Load(xaml);
Assert.False(resources.ContainsDeferredKey("Red"));
Assert.IsType<Color>(resources["Red"]);
}
}
[Fact]
public void Value_Type_With_Ctor_Converter_Should_Not_Be_Deferred()
{
using (StyledWindow())
{
var xaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Thickness x:Key='Margin'>1 1 1 1</Thickness>
</ResourceDictionary>";
var resources = (ResourceDictionary)AvaloniaRuntimeXamlLoader.Load(xaml);
Assert.False(resources.ContainsDeferredKey("Margin"));
Assert.IsType<Thickness>(resources["Margin"]);
}
}
private IDisposable StyledWindow(params (string, string)[] assets)
{
var services = TestServices.StyledWindow.With(

Loading…
Cancel
Save