From 7f0c502c296e5936385a36dd99e3e97ad0cb5ea4 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 12:51:26 +0200 Subject: [PATCH 01/29] add simple unit test for multibinding --- .../AvaloniaObjectTests_MultiBinding.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs new file mode 100644 index 0000000000..30693e2cb3 --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs @@ -0,0 +1,59 @@ +// 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.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Avalonia.Data; +using Avalonia.Data.Converters; +using Xunit; + +namespace Avalonia.Base.UnitTests +{ + public class AvaloniaObjectTests_MultiBinding + { + [Fact] + public void Should_Update() + { + var target = new Class1(); + + var b = new Subject(); + + var mb = new MultiBinding() + { + Converter = StringJoinConverter, + Bindings = new[] + { + b.ToBinding() + } + }; + target.Bind(Class1.FooProperty, mb); + + Assert.Equal(null, target.Foo); + + b.OnNext("Foo"); + + Assert.Equal("Foo", target.Foo); + + b.OnNext("Bar"); + + Assert.Equal("Bar", target.Foo); + } + + private static IMultiValueConverter StringJoinConverter = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); + + private class Class1 : AvaloniaObject + { + public static readonly StyledProperty FooProperty = + AvaloniaProperty.Register("Foo"); + + public string Foo + { + get => GetValue(FooProperty); + set => SetValue(FooProperty, value); + } + } + } +} From 613ba3ffd2e207220aa56ab4bd2006210a63119c Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 12:52:09 +0200 Subject: [PATCH 02/29] add another test for multibinding --- .../AvaloniaObjectTests_MultiBinding.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs index 30693e2cb3..19ffb014d4 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs @@ -42,6 +42,35 @@ namespace Avalonia.Base.UnitTests Assert.Equal("Bar", target.Foo); } + [Fact] + public void Should_Update_With_Multiple_Bindings() + { + var target = new Class1(); + + var bindings = Enumerable.Range(0, 3).Select(i => new BehaviorSubject("Empty")).ToArray(); + + var mb = new MultiBinding() + { + Converter = StringJoinConverter, + Bindings = bindings.Select(b => b.ToBinding()).ToArray() + }; + target.Bind(Class1.FooProperty, mb); + + Assert.Equal("Empty,Empty,Empty", target.Foo); + + bindings[0].OnNext("Foo"); + + Assert.Equal("Foo,Empty,Empty", target.Foo); + + bindings[1].OnNext("Bar"); + + Assert.Equal("Foo,Bar,Empty", target.Foo); + + bindings[2].OnNext("Baz"); + + Assert.Equal("Foo,Bar,Baz", target.Foo); + } + private static IMultiValueConverter StringJoinConverter = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); private class Class1 : AvaloniaObject From 50deb04e7ab060e42c078a51c30e5b529019dbb9 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 12:56:25 +0200 Subject: [PATCH 03/29] add failing unit test for #3692 --- .../AvaloniaObjectTests_MultiBinding.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs index 19ffb014d4..8560966fb7 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs @@ -71,6 +71,34 @@ namespace Avalonia.Base.UnitTests Assert.Equal("Foo,Bar,Baz", target.Foo); } + [Fact] + public void Should_Update_When_Null_Value_In_Bindings() + { + var target = new Class1(); + + var b = new Subject(); + + var mb = new MultiBinding() + { + Converter = StringJoinConverter, + Bindings = new[] + { + b.ToBinding() + } + }; + target.Bind(Class1.FooProperty, mb); + + Assert.Equal(null, target.Foo); + + b.OnNext("Foo"); + + Assert.Equal("Foo", target.Foo); + + b.OnNext(null); + + Assert.Equal("", target.Foo); + } + private static IMultiValueConverter StringJoinConverter = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); private class Class1 : AvaloniaObject From ec92b90e81601a040fd9d25d0b70d5aebeb49e7c Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 12:57:27 +0200 Subject: [PATCH 04/29] add a test for multibinding with StringFormat --- .../AvaloniaObjectTests_MultiBinding.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs index 8560966fb7..ab413dd550 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs @@ -99,6 +99,30 @@ namespace Avalonia.Base.UnitTests Assert.Equal("", target.Foo); } + [Fact] + public void Should_Update_When_Null_Value_In_Bindings_With_StringFormat() + { + var target = new Class1(); + + var b = new Subject(); + + var mb = new MultiBinding() + { + StringFormat = "Converted: {0}", + Bindings = new[] + { + b.ToBinding() + } + }; + target.Bind(Class1.FooProperty, mb); + + Assert.Equal(null, target.Foo); + b.OnNext("Foo"); + Assert.Equal("Converted: Foo", target.Foo); + b.OnNext(null); + Assert.Equal("Converted: ", target.Foo); + } + private static IMultiValueConverter StringJoinConverter = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); private class Class1 : AvaloniaObject From 3dc5b8bda19f1031e95760f4964098e4b01c5f64 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 12:59:30 +0200 Subject: [PATCH 05/29] add failing test for the core reason of the issue #3692 --- .../AvaloniaObjectTests_MultiBinding.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs index ab413dd550..757160a0a4 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_MultiBinding.cs @@ -123,6 +123,44 @@ namespace Avalonia.Base.UnitTests Assert.Equal("Converted: ", target.Foo); } + [Fact] + public void MultiValueConverter_Should_Not_Skip_Valid_Null_ReferenceType_Value() + { + var target = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); + + object value = target.Convert(new[] { "Foo", "Bar", "Baz" }, typeof(string), null, CultureInfo.InvariantCulture); + + Assert.Equal("Foo,Bar,Baz", value); + + value = target.Convert(new[] { null, "Bar", "Baz" }, typeof(string), null, CultureInfo.InvariantCulture); + + Assert.Equal(",Bar,Baz", value); + } + + [Fact] + public void MultiValueConverter_Should_Not_Skip_Valid_Default_ValueType_Value() + { + var target = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); + + IList create(string[] values) => + values.Select(v => (object)(v != null ? new StringValueTypeWrapper() { Value = v } : default)).ToList(); + + object value = target.Convert(create(new[] { "Foo", "Bar", "Baz" }), typeof(string), null, CultureInfo.InvariantCulture); + + Assert.Equal("Foo,Bar,Baz", value); + + value = target.Convert(create(new[] { null, "Bar", "Baz" }), typeof(string), null, CultureInfo.InvariantCulture); + + Assert.Equal(",Bar,Baz", value); + } + + private struct StringValueTypeWrapper + { + public string Value; + + public override string ToString() => Value; + } + private static IMultiValueConverter StringJoinConverter = new FuncMultiValueConverter(v => string.Join(",", v.ToArray())); private class Class1 : AvaloniaObject From 6e54db32e94d1166fa74b0391eafe1ddd6a49d9b Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 23 Mar 2020 13:22:58 +0200 Subject: [PATCH 06/29] fix #3692 --- .../Data/Converters/FuncMultiValueConverter.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs index 6e1c4cb0e3..887641b5c3 100644 --- a/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs +++ b/src/Avalonia.Base/Data/Converters/FuncMultiValueConverter.cs @@ -30,7 +30,23 @@ namespace Avalonia.Data.Converters /// public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) { - var converted = values.OfType().ToList(); + //standard OfType skip null values, even they are valid for the Type + static IEnumerable OfTypeWithDefaultSupport(IList list) + { + foreach (object obj in list) + { + if (obj is TIn result) + { + yield return result; + } + else if (Equals(obj, default(TIn))) + { + yield return default(TIn); + } + } + } + + var converted = OfTypeWithDefaultSupport(values).ToList(); if (converted.Count == values.Count) { From 764e7aff01a276677c96614bbb89f9436ce1d499 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 23 Mar 2020 21:11:00 +0100 Subject: [PATCH 07/29] Initial refactor of win32 window. --- samples/ControlCatalog/Pages/DialogsPage.xaml | 1 + .../ControlCatalog/Pages/DialogsPage.xaml.cs | 31 +- src/Avalonia.Controls/Window.cs | 2 + .../Avalonia.Win32/WindowImpl.WndProc.cs | 436 +++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 1002 ++++++----------- 5 files changed, 784 insertions(+), 688 deletions(-) create mode 100644 src/Windows/Avalonia.Win32/WindowImpl.WndProc.cs diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml b/samples/ControlCatalog/Pages/DialogsPage.xaml index 60f8e3656e..1ed5c0cbfd 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml @@ -9,5 +9,6 @@ + diff --git a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs index d207689223..0e266dac2b 100644 --- a/samples/ControlCatalog/Pages/DialogsPage.xaml.cs +++ b/samples/ControlCatalog/Pages/DialogsPage.xaml.cs @@ -61,14 +61,29 @@ namespace ControlCatalog.Pages new DecoratedWindow().ShowDialog(GetWindow()); }; this.FindControl