A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

284 lines
7.9 KiB

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Data.Core;
using Avalonia.UnitTests;
using Xunit;
#nullable enable
namespace Avalonia.Base.UnitTests.Data.Core
{
public class BindingExpressionTests_SetValue
{
[Fact]
public void Should_Set_Simple_Property_Value()
{
var data = new Person { Name = "Frank" };
var target = BindingExpression.Create(data, o => o.Name);
using (target.ToObservable().Subscribe(_ => { }))
{
target.WriteValueToSource("Kups");
}
Assert.Equal("Kups", data.Name);
}
[Fact]
public void Should_Set_Attached_Property_Value()
{
var data = new AvaloniaObject();
var target = BindingExpression.Create(data, o => o[DockPanel.DockProperty]);
using (target.ToObservable().Subscribe(_ => { }))
{
target.WriteValueToSource(Dock.Right);
}
Assert.Equal(Dock.Right, data[DockPanel.DockProperty]);
}
[Fact]
public void Should_Set_Indexed_Value()
{
var data = new { Foo = new[] { "foo" } };
var target = BindingExpression.Create(data, o => o.Foo[0]);
using (target.ToObservable().Subscribe(_ => { }))
{
target.WriteValueToSource("bar");
}
Assert.Equal("bar", data.Foo[0]);
GC.KeepAlive(data);
}
[Fact]
public void Should_Set_Value_On_Simple_Property_Chain()
{
var data = new Person { Pet = new Dog { Name = "Fido" } };
var target = BindingExpression.Create(data, o => o.Pet!.Name);
using (target.ToObservable().Subscribe(_ => { }))
{
target.WriteValueToSource("Rover");
}
Assert.Equal("Rover", data.Pet.Name);
}
[Fact]
public void Should_Not_Try_To_Set_Value_On_Broken_Chain()
{
var data = new Person { Pet = new Dog { Name = "Fido" } };
var target = BindingExpression.Create(data, o => o.Pet!.Name);
// Ensure the UntypedBindingExpression's subscriptions are kept active.
using (target.ToObservable()!.OfType<string?>().Subscribe(x => { }))
{
data.Pet = null;
Assert.False(target.WriteValueToSource("Rover"));
}
}
[Fact]
public void SetValue_Should_Return_False_For_Missing_Property()
{
var data = new Person { Pet = new Cat() };
var target = BindingExpression.Create(data, o => (o.Pet as Dog)!.IsBarky);
using (target.ToObservable().Subscribe(_ => { }))
{
Assert.False(target.WriteValueToSource("baz"));
}
GC.KeepAlive(data);
}
[Fact]
public void SetValue_Should_Notify_New_Value_With_Inpc()
{
var data = new Person();
var target = BindingExpression.Create(data, o => o.Name);
var result = new List<object?>();
target.ToObservable().Subscribe(result.Add);
target.WriteValueToSource("Frank");
Assert.Equal(new[] { null, "Frank" }, result);
GC.KeepAlive(data);
}
[Fact]
public void SetValue_Should_Notify_New_Value_Without_Inpc()
{
var data = new Snail();
var target = BindingExpression.Create(data, o => o.Name);
var result = new List<object?>();
target.ToObservable().Subscribe(result.Add);
target.WriteValueToSource("Frank");
Assert.Equal(new[] { null, "Frank" }, result);
GC.KeepAlive(data);
}
[Fact]
public void SetValue_Should_Return_False_For_Missing_Object()
{
var data = new Person();
var target = BindingExpression.Create(data, o => (o.Pet as Dog)!.Name);
using (target.ToObservable().Subscribe(_ => { }))
{
Assert.False(target.WriteValueToSource("Fido"));
}
GC.KeepAlive(data);
}
[Fact]
public void SetValue_Should_Use_Converter()
{
var data = new Person { Name = "Frank" };
var target = BindingExpression.Create(
data,
o => o.Name,
converter: new CaseConverter());
using (target.ToObservable().Subscribe(_ => { }))
{
Assert.True(target.WriteValueToSource("Kups"));
}
Assert.Equal("kups", data.Name);
GC.KeepAlive(data);
}
[Fact]
public void TwoWay_Binding_Should_Not_Write_Unchanged_Value_Back_To_Property_With_Converter()
{
var data = new Cat();
var control = new Control();
var target = BindingExpression.Create(
data,
o => o.WhiskerCount,
converter: new CaseConverter(),
mode: BindingMode.TwoWay);
var instance = new InstancedBinding(
target,
BindingMode.TwoWay,
BindingPriority.LocalValue);
BindingOperations.Apply(control, Visual.OpacityProperty, instance);
Assert.Equal(4, control.Opacity);
Assert.Equal(9, data.Lives);
data.WhiskerCount = 3;
Assert.Equal(3, control.Opacity);
Assert.Equal(8, data.Lives);
GC.KeepAlive(data);
}
private interface IAnimal
{
string? Name { get; }
}
private class Person : NotifyingBase
{
private string? _name;
private IAnimal? _pet;
public string? Name
{
get => _name;
set
{
_name = value;
RaisePropertyChanged(nameof(Name));
}
}
public IAnimal? Pet
{
get => _pet;
set
{
_pet = value;
RaisePropertyChanged(nameof(Pet));
}
}
}
private class Animal : NotifyingBase, IAnimal
{
private string? _name;
public string? Name
{
get => _name;
set
{
_name = value;
RaisePropertyChanged(nameof(Name));
}
}
}
private class Dog : Animal
{
public bool IsBarky { get; set; }
}
private class Cat : Animal
{
private int _whiskerCount= 4;
public int WhiskerCount
{
get => _whiskerCount;
set
{
_whiskerCount = value;
RaisePropertyChanged(nameof(WhiskerCount));
--Lives;
}
}
public int Lives { get; private set; } = 9;
}
private class Snail : IAnimal
{
public string? Name { get; set; }
}
private class CaseConverter : IValueConverter
{
public static readonly CaseConverter Instance = new();
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value?.ToString()?.ToUpper();
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value?.ToString()?.ToLower();
}
}
}
}