From fc6bd9a23e5a967759c2256892f319e3d138f12d Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Fri, 9 Jul 2021 04:02:03 +0100 Subject: [PATCH 1/5] feature BindList Added BindList for use with SourceList --- ...provalTests.ReactiveUI.net472.approved.txt | 10 +++ ...provalTests.ReactiveUI.net5.0.approved.txt | 9 ++ ...ests.ReactiveUI.netcoreapp3.1.approved.txt | 9 ++ .../Mocks/MockBindListItemViewModel.cs | 23 +++++ .../Mocks/MockBindListView.cs | 88 +++++++++++++++++++ .../Mocks/MockBindListViewModel.cs | 60 +++++++++++++ .../windows-xaml/PropertyBindingTest.cs | 40 +++++++++ src/ReactiveUI.Tests/ReactiveUI.Tests.csproj | 14 ++- src/ReactiveUI.sln | 27 ++++++ .../Property/IPropertyBinderImplementation.cs | 23 ++++- .../Property/PropertyBinderImplementation.cs | 31 ++++++- .../Property/PropertyBindingMixins.cs | 23 +++++ 12 files changed, 352 insertions(+), 5 deletions(-) create mode 100644 src/ReactiveUI.Tests/Mocks/MockBindListItemViewModel.cs create mode 100644 src/ReactiveUI.Tests/Mocks/MockBindListView.cs create mode 100644 src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt index 64d3ecc199..19b951fe6a 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt @@ -245,6 +245,10 @@ namespace ReactiveUI where TViewModel : class where TView : class, ReactiveUI.IViewFor ; + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class + ; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class ; @@ -509,6 +513,9 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -544,6 +551,9 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt index 256721d4fe..7f9abf7029 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt @@ -243,6 +243,9 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -504,6 +507,9 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -539,6 +545,9 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt index eed9be0073..1744d03f73 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt @@ -241,6 +241,9 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -502,6 +505,9 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -537,6 +543,9 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + where TView : class, ReactiveUI.IViewFor + where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListItemViewModel.cs b/src/ReactiveUI.Tests/Mocks/MockBindListItemViewModel.cs new file mode 100644 index 0000000000..e44f2378ef --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/MockBindListItemViewModel.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class MockBindListItemViewModel : ReactiveObject + { + private string _name = string.Empty; + + public MockBindListItemViewModel(string name) => Name = name; + + /// + /// Gets or sets displayed name of the crumb. + /// + public string Name + { + get => _name; + set => this.RaiseAndSetIfChanged(ref _name, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs new file mode 100644 index 0000000000..06e0346b2c --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs @@ -0,0 +1,88 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.IO; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Markup; +using ReactiveUI; + +namespace ReactiveUI.Tests +{ + /// + /// MockBindListView. + /// + /// + public partial class MockBindListView : UserControl, IViewFor + { + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register(nameof(ViewModel), typeof(MockBindListViewModel), typeof(MockBindListView), new PropertyMetadata(null)); + + /// + /// Initializes a new instance of the class. + /// + public MockBindListView() + { + // InitializeComponent(); + ItemList = new(); + + var ms = new MemoryStream(Encoding.UTF8.GetBytes(@" + + + + + + ")); + ItemList.ItemTemplate = (DataTemplate)XamlReader.Load(ms); + var ms1 = new MemoryStream(Encoding.UTF8.GetBytes(@" + + + ")); + ItemList.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(ms1); + + ViewModel = new(); + this.WhenActivated(d => + { + this.BindList(ViewModel, vm => vm.ActiveListItem, v => v.ItemList.ItemsSource).DisposeWith(d); + this.WhenAnyValue(v => v.ItemList.SelectedItem) + .Where(i => i != null) + .Cast() + .Do(_ => ItemList.UnselectAll()) + .InvokeCommand(this, v => v.ViewModel!.SelectItem).DisposeWith(d); + }); + } + + /// + /// Gets or sets the ViewModel corresponding to this specific View. This should be + /// a DependencyProperty if you're using XAML. + /// + public MockBindListViewModel? ViewModel + { + get => (MockBindListViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + public ListView ItemList { get; } + + object? IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (MockBindListViewModel?)value; + } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs b/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs new file mode 100644 index 0000000000..0192f5b4e7 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using DynamicData; + +namespace ReactiveUI.Tests +{ + public class MockBindListViewModel : ReactiveObject + { + private readonly ObservableAsPropertyHelper _activeItem; + + static MockBindListViewModel() + { + Splat.Locator.CurrentMutable.Register(() => new MockBindListView(), typeof(IViewFor)); + } + + /// + /// Initializes a new instance of the class. + /// + public MockBindListViewModel() + { + SelectItem = ReactiveCommand.Create((MockBindListItemViewModel item) => + { + ActiveListItem.Edit(l => + { + var index = l.IndexOf(item); + for (var i = l.Count - 1; i > index; i--) + { + l.RemoveAt(i); + } + }); + }); + + ActiveListItem.Connect().Select(_ => ActiveListItem.Count > 0 ? ActiveListItem.Items.ElementAt(ActiveListItem.Count - 1) : null) + .ToProperty(this, vm => vm.ActiveItem, out _activeItem); + } + + /// + /// Gets the item that is currently loaded in the list. + /// Add or remove elements to modify the list. + /// + public ISourceList ActiveListItem { get; } = new SourceList(); + + /// + /// Gets the deepest item of the currect list. (Last element of ActiveListItem). + /// + public MockBindListItemViewModel? ActiveItem => _activeItem.Value; + + /// + /// Gets the items to be represented by the selected item which is passed as a parameter. + /// Only this item and its ancestors are kept, the rest of the items are removed. + /// + public ReactiveCommand SelectItem { get; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs index 1543d4a25b..54b34a75e7 100644 --- a/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs @@ -8,10 +8,14 @@ using System.Globalization; using System.Linq; using System.Reactive; +using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; +using System.Windows; +using DynamicData; using DynamicData.Binding; +using ReactiveUI.Tests.Wpf; using Xunit; #if NETFX_CORE @@ -689,5 +693,41 @@ public void BindWithFuncToTriggerUpdateTest() dis.Dispose(); Assert.True(dis.IsDisposed); } + + [StaFact] + public void BindListFunctionalTest() + { + var window = new WpfTestWindow(); + var view = new MockBindListView(); + window.RootGrid.Children.Add(view); + + var loaded = new RoutedEventArgs + { + RoutedEvent = FrameworkElement.LoadedEvent + }; + + window.RaiseEvent(loaded); + view.RaiseEvent(loaded); + var test1 = new MockBindListItemViewModel("Test1"); + view.ViewModel?.ActiveListItem.Add(test1); + Assert.Equal(1, view.ItemList.Items.Count); + Assert.Equal(test1, view.ViewModel!.ActiveItem); + + var test2 = new MockBindListItemViewModel("Test2"); + view.ViewModel?.ActiveListItem.Add(test2); + Assert.Equal(2, view.ItemList.Items.Count); + Assert.Equal(test2, view.ViewModel!.ActiveItem); + + var test3 = new MockBindListItemViewModel("Test3"); + view.ViewModel?.ActiveListItem.Add(test3); + Assert.Equal(3, view.ItemList.Items.Count); + Assert.Equal(test3, view.ViewModel!.ActiveItem); + + view.ItemList.SelectedItem = view.ItemList.Items.GetItemAt(0); + Assert.Equal(1, view.ItemList.Items.Count); + Assert.Equal(test1, view.ViewModel!.ActiveItem); + + window.Dispatcher.InvokeShutdown(); + } } } diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj index f70c13f4a3..5393e923a2 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -60,18 +60,22 @@ + + true + true + - + @@ -81,6 +85,10 @@ + + + + True diff --git a/src/ReactiveUI.sln b/src/ReactiveUI.sln index 0762ed0ad2..4175d99f02 100644 --- a/src/ReactiveUI.sln +++ b/src/ReactiveUI.sln @@ -59,6 +59,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Drawing", "React EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.XamForms.Tests", "ReactiveUI.XamForms.Tests\ReactiveUI.XamForms.Tests.csproj", "{46D5C71E-2E58-4454-BE3A-30B9047A2D1E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{2AE709FA-BE58-4287-BFD3-E80BEB605125}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -153,6 +157,29 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {464CB812-F99F-401B-BE4C-E8F0515CD19D} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {F5A6E11B-B074-4A1C-B937-267D840E31DF} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {DDF89A7A-5CC9-4243-98E4-462860D5D963} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {C11F6165-6142-476F-83F1-CFEBC330C769} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {84A9E530-93D7-4108-9887-690127F70AF5} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {B311B0EC-CEF3-45E6-BA7A-EC6AB58E7E7D} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {2ADE0A50-5012-4341-8F4F-97597C2D6920} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {15CF3AA6-9F7C-4F23-BAE7-4A93352E94B6} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {1AC71A71-F5F3-4F96-BDA9-A9DC7F572DB9} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {7DE43BB9-5AC8-446A-8D8B-88C9201D802E} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {20750BB4-36DD-4F8C-B970-D7809810EC98} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {404B0F3F-7343-4E54-A863-F27B99FE788B} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {7ED6D69F-138F-40BD-9F37-3E4050E4D19B} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {CD8B19A9-316E-4FBC-8F0C-87ADC6AAD684} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {36FC3269-B7D0-4D79-A54A-B26B6190E8A2} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {0C7EDFF0-80BE-4FFC-A1B9-0C48043B71F3} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {86430CEC-BAA3-4ED4-A90D-982437137D19} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {366789D4-4117-46CE-864F-BF1201F35319} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {1FD70B38-E2FB-4A46-BE07-C0F6ACE6FD90} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + {999D555D-C567-457C-95F7-8AD61310C56E} = {EF7ED1B0-00E4-4CD0-9741-0D1D4463B8EC} + {46D5C71E-2E58-4454-BE3A-30B9047A2D1E} = {2AE709FA-BE58-4287-BFD3-E80BEB605125} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9326B58C-0AD3-4527-B3F4-86B54673C62E} EndGlobalSection diff --git a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs index 346d054ea4..6b89c6704d 100644 --- a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs @@ -5,7 +5,7 @@ using System; using System.Linq.Expressions; - +using DynamicData; using Splat; namespace ReactiveUI @@ -253,5 +253,26 @@ IDisposable BindTo( object? conversionHint, IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; + + /// + /// Create a one way list binding from the viewmodel to the view. The view list property will + /// be automatically updated to reflect the viewmodel source list property. + /// + /// The type of the view that is bound. + /// The type of the view model that is bound. + /// The type of the data stored in the list. + /// The type of the property bound on the view model. + /// The view used for the binding. + /// A dummy viewmodel parameter, used to infer the viewmodel type. + /// The source property in the viewmodel that contains the list. + /// The target property in the view to bind the list to. + /// An object that when disposed, disconnects the binding. + IDisposable BindList( + TView view, + TViewModel? viewModel, + Expression?>> vmProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor; } } diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs index 1c6ad8f67b..760f8a34bb 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs @@ -13,7 +13,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; - +using DynamicData; using Splat; namespace ReactiveUI @@ -254,6 +254,35 @@ public IDisposable BindTo( return disposable; } + /// + public IDisposable BindList( + TView view, + TViewModel? viewModel, + Expression?>> vmProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor + { + IDisposable? lastBinding = null; + + return + + // Get latest viewmodel and get latest non-null list from viewmodel property + view.WhenAnyValue(v => v.ViewModel) + .Where(vm => vm != null) + .Select(vm => vm.WhenAnyValue(vmProperty!)) + .Switch() + .Where(sourceList => sourceList != null) + .Do(_ => lastBinding?.Dispose()) // Clean up last list binding + .Select(sourceList => + { // Create new list binding + lastBinding = sourceList!.Connect().Bind(out var list).Subscribe(); + return list; + }) + .Finally(() => lastBinding?.Dispose()) // When the observable is disposed, dispose the list binding too + .BindTo(view, viewProperty); // Bind the new bindable list to the view property + } + internal static IBindingTypeConverter? GetConverterForTypes(Type lhs, Type rhs) => _typeConverterCache.Get((lhs, rhs)); diff --git a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs index bb10d33dcf..03c9e08a3f 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs @@ -6,6 +6,7 @@ using System; using System.Linq.Expressions; using System.Reactive; +using DynamicData; namespace ReactiveUI { @@ -353,5 +354,27 @@ public static IDisposable BindTo( IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class => _binderImplementation.BindTo(@this, target, property, conversionHint, vmToViewConverterOverride); + + /// + /// Create a one way list binding from the viewmodel to the view. The view list property will + /// be automatically updated to reflect the viewmodel source list property. + /// + /// The type of the view that is bound. + /// The type of the view model that is bound. + /// The type of the data stored in the list. + /// The type of the property bound on the view model. + /// The view used for the binding. + /// A dummy viewmodel parameter, used to infer the viewmodel type. + /// The source property in the viewmodel that contains the list. + /// The target property in the view to bind the list to. + /// An object that when disposed, disconnects the binding. + public static IDisposable BindList( + this TView view, + TViewModel? viewModel, + Expression?>> vmProperty, + Expression> viewProperty) + where TViewModel : class + where TView : class, IViewFor => + _binderImplementation.BindList(view, viewModel, vmProperty, viewProperty); } } From b47b0fe93df040f89d03de2ae5472d0637d6627f Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Fri, 9 Jul 2021 04:18:17 +0100 Subject: [PATCH 2/5] tidy code --- src/ReactiveUI.Tests/Mocks/MockBindListView.cs | 6 ------ src/ReactiveUI.Tests/ReactiveUI.Tests.csproj | 8 -------- 2 files changed, 14 deletions(-) diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs index 06e0346b2c..1dbfbf690b 100644 --- a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs +++ b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs @@ -35,12 +35,6 @@ public MockBindListView() - - - From 863cc2d4eca8e49bb2347c75ef837bddc8b0fea1 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Fri, 9 Jul 2021 05:11:21 +0100 Subject: [PATCH 3/5] changed SourceList to ReadOnlyObservableCollection --- .../Mocks/MockBindListView.cs | 2 +- .../Mocks/MockBindListViewModel.cs | 16 +++++++++++++++- .../Property/IPropertyBinderImplementation.cs | 4 ++-- .../Property/PropertyBinderImplementation.cs | 19 +++---------------- .../Property/PropertyBindingMixins.cs | 4 ++-- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs index 1dbfbf690b..13530db6f7 100644 --- a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs +++ b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs @@ -52,7 +52,7 @@ public MockBindListView() ViewModel = new(); this.WhenActivated(d => { - this.BindList(ViewModel, vm => vm.ActiveListItem, v => v.ItemList.ItemsSource).DisposeWith(d); + this.BindList(ViewModel, vm => vm.ListItems, v => v.ItemList.ItemsSource).DisposeWith(d); this.WhenAnyValue(v => v.ItemList.SelectedItem) .Where(i => i != null) .Cast() diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs b/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs index 0192f5b4e7..15e0b36ec4 100644 --- a/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs +++ b/src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs @@ -3,6 +3,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. +using System; +using System.Collections.ObjectModel; using System.Linq; using System.Reactive; using System.Reactive.Linq; @@ -13,6 +15,7 @@ namespace ReactiveUI.Tests public class MockBindListViewModel : ReactiveObject { private readonly ObservableAsPropertyHelper _activeItem; + private readonly ReadOnlyObservableCollection _listItems; static MockBindListViewModel() { @@ -36,8 +39,11 @@ public MockBindListViewModel() }); }); - ActiveListItem.Connect().Select(_ => ActiveListItem.Count > 0 ? ActiveListItem.Items.ElementAt(ActiveListItem.Count - 1) : null) + ActiveListItem.Connect() + .Select(_ => ActiveListItem.Count > 0 ? ActiveListItem.Items.ElementAt(ActiveListItem.Count - 1) : null) .ToProperty(this, vm => vm.ActiveItem, out _activeItem); + + ActiveListItem.Connect().ObserveOn(RxApp.MainThreadScheduler).Bind(out _listItems).Subscribe(); } /// @@ -56,5 +62,13 @@ public MockBindListViewModel() /// Only this item and its ancestors are kept, the rest of the items are removed. /// public ReactiveCommand SelectItem { get; } + + /// + /// Gets the list items. + /// + /// + /// The list items. + /// + public ReadOnlyObservableCollection ListItems => _listItems; } } diff --git a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs index 6b89c6704d..2e9e15a8de 100644 --- a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs @@ -4,8 +4,8 @@ // See the LICENSE file in the project root for full license information. using System; +using System.Collections.ObjectModel; using System.Linq.Expressions; -using DynamicData; using Splat; namespace ReactiveUI @@ -270,7 +270,7 @@ IDisposable BindTo( IDisposable BindList( TView view, TViewModel? viewModel, - Expression?>> vmProperty, + Expression?>> vmProperty, Expression> viewProperty) where TViewModel : class where TView : class, IViewFor; diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs index 760f8a34bb..ca9e3264dc 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs @@ -5,8 +5,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; +using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -258,14 +257,10 @@ public IDisposable BindTo( public IDisposable BindList( TView view, TViewModel? viewModel, - Expression?>> vmProperty, + Expression?>> vmProperty, Expression> viewProperty) where TViewModel : class - where TView : class, IViewFor - { - IDisposable? lastBinding = null; - - return + where TView : class, IViewFor => // Get latest viewmodel and get latest non-null list from viewmodel property view.WhenAnyValue(v => v.ViewModel) @@ -273,15 +268,7 @@ public IDisposable BindList( .Select(vm => vm.WhenAnyValue(vmProperty!)) .Switch() .Where(sourceList => sourceList != null) - .Do(_ => lastBinding?.Dispose()) // Clean up last list binding - .Select(sourceList => - { // Create new list binding - lastBinding = sourceList!.Connect().Bind(out var list).Subscribe(); - return list; - }) - .Finally(() => lastBinding?.Dispose()) // When the observable is disposed, dispose the list binding too .BindTo(view, viewProperty); // Bind the new bindable list to the view property - } internal static IBindingTypeConverter? GetConverterForTypes(Type lhs, Type rhs) => _typeConverterCache.Get((lhs, rhs)); diff --git a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs index 03c9e08a3f..70ae30e70f 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs @@ -4,9 +4,9 @@ // See the LICENSE file in the project root for full license information. using System; +using System.Collections.ObjectModel; using System.Linq.Expressions; using System.Reactive; -using DynamicData; namespace ReactiveUI { @@ -371,7 +371,7 @@ public static IDisposable BindTo( public static IDisposable BindList( this TView view, TViewModel? viewModel, - Expression?>> vmProperty, + Expression?>> vmProperty, Expression> viewProperty) where TViewModel : class where TView : class, IViewFor => From 4c564609bdddc25ea9d055216105bf7242755e51 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Fri, 9 Jul 2021 10:19:04 +0100 Subject: [PATCH 4/5] Corrected API test files --- .../API/ApiApprovalTests.ReactiveUI.net472.approved.txt | 6 +++--- .../API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt | 6 +++--- .../ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt index 19b951fe6a..eacbafd779 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt @@ -245,7 +245,7 @@ namespace ReactiveUI where TViewModel : class where TView : class, ReactiveUI.IViewFor ; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class ; @@ -513,7 +513,7 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) @@ -551,7 +551,7 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt index 7f9abf7029..ca1c6abecb 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt @@ -243,7 +243,7 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) @@ -507,7 +507,7 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) @@ -545,7 +545,7 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt index 1744d03f73..a1514796b6 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt @@ -241,7 +241,7 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) @@ -505,7 +505,7 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) @@ -543,7 +543,7 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) + public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) where TView : class, ReactiveUI.IViewFor where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) From 12369abfb9defb028927ac65ba5f4f8514ba8498 Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Fri, 9 Jul 2021 21:39:29 +0100 Subject: [PATCH 5/5] down graded to tests only --- ...provalTests.ReactiveUI.net472.approved.txt | 10 -------- ...provalTests.ReactiveUI.net5.0.approved.txt | 9 -------- ...ests.ReactiveUI.netcoreapp3.1.approved.txt | 9 -------- .../Mocks/MockBindListView.cs | 6 ++--- .../Property/IPropertyBinderImplementation.cs | 23 +------------------ .../Property/PropertyBinderImplementation.cs | 22 +++--------------- .../Property/PropertyBindingMixins.cs | 23 ------------------- 7 files changed, 6 insertions(+), 96 deletions(-) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt index eacbafd779..64d3ecc199 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net472.approved.txt @@ -245,10 +245,6 @@ namespace ReactiveUI where TViewModel : class where TView : class, ReactiveUI.IViewFor ; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class - ; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class ; @@ -513,9 +509,6 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -551,9 +544,6 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt index ca1c6abecb..256721d4fe 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net5.0.approved.txt @@ -243,9 +243,6 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -507,9 +504,6 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -545,9 +539,6 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt index a1514796b6..eed9be0073 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp3.1.approved.txt @@ -241,9 +241,6 @@ namespace ReactiveUI ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor; - System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class; System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -505,9 +502,6 @@ namespace ReactiveUI public ReactiveUI.IReactiveBinding> Bind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public System.IDisposable BindList(TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public System.IDisposable BindTo(System.IObservable observedChange, TTarget? target, System.Linq.Expressions.Expression> propertyExpression, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public ReactiveUI.IReactiveBinding OneWayBind(TViewModel? viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) @@ -543,9 +537,6 @@ namespace ReactiveUI public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable? signalViewUpdate, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter? viewToVMConverterOverride = null) where TViewModel : class where TView : class, ReactiveUI.IViewFor { } - public static System.IDisposable BindList(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression?>> vmProperty, System.Linq.Expressions.Expression> viewProperty) - where TView : class, ReactiveUI.IViewFor - where TViewModel : class { } public static System.IDisposable BindTo(this System.IObservable @this, TTarget? target, System.Linq.Expressions.Expression> property, object? conversionHint = null, ReactiveUI.IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class { } public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) diff --git a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs index 13530db6f7..70cda838ff 100644 --- a/src/ReactiveUI.Tests/Mocks/MockBindListView.cs +++ b/src/ReactiveUI.Tests/Mocks/MockBindListView.cs @@ -10,7 +10,6 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Markup; -using ReactiveUI; namespace ReactiveUI.Tests { @@ -18,7 +17,7 @@ namespace ReactiveUI.Tests /// MockBindListView. /// /// - public partial class MockBindListView : UserControl, IViewFor + public class MockBindListView : UserControl, IViewFor { public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel), typeof(MockBindListViewModel), typeof(MockBindListView), new PropertyMetadata(null)); @@ -28,7 +27,6 @@ public partial class MockBindListView : UserControl, IViewFor public MockBindListView() { - // InitializeComponent(); ItemList = new(); var ms = new MemoryStream(Encoding.UTF8.GetBytes(@" @@ -52,7 +50,7 @@ public MockBindListView() ViewModel = new(); this.WhenActivated(d => { - this.BindList(ViewModel, vm => vm.ListItems, v => v.ItemList.ItemsSource).DisposeWith(d); + this.OneWayBind(ViewModel, vm => vm.ListItems, v => v.ItemList.ItemsSource).DisposeWith(d); this.WhenAnyValue(v => v.ItemList.SelectedItem) .Where(i => i != null) .Cast() diff --git a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs index 2e9e15a8de..346d054ea4 100644 --- a/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/IPropertyBinderImplementation.cs @@ -4,8 +4,8 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Collections.ObjectModel; using System.Linq.Expressions; + using Splat; namespace ReactiveUI @@ -253,26 +253,5 @@ IDisposable BindTo( object? conversionHint, IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class; - - /// - /// Create a one way list binding from the viewmodel to the view. The view list property will - /// be automatically updated to reflect the viewmodel source list property. - /// - /// The type of the view that is bound. - /// The type of the view model that is bound. - /// The type of the data stored in the list. - /// The type of the property bound on the view model. - /// The view used for the binding. - /// A dummy viewmodel parameter, used to infer the viewmodel type. - /// The source property in the viewmodel that contains the list. - /// The target property in the view to bind the list to. - /// An object that when disposed, disconnects the binding. - IDisposable BindList( - TView view, - TViewModel? viewModel, - Expression?>> vmProperty, - Expression> viewProperty) - where TViewModel : class - where TView : class, IViewFor; } } diff --git a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs index ca9e3264dc..1c6ad8f67b 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBinderImplementation.cs @@ -5,14 +5,15 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Subjects; -using DynamicData; + using Splat; namespace ReactiveUI @@ -253,23 +254,6 @@ public IDisposable BindTo( return disposable; } - /// - public IDisposable BindList( - TView view, - TViewModel? viewModel, - Expression?>> vmProperty, - Expression> viewProperty) - where TViewModel : class - where TView : class, IViewFor => - - // Get latest viewmodel and get latest non-null list from viewmodel property - view.WhenAnyValue(v => v.ViewModel) - .Where(vm => vm != null) - .Select(vm => vm.WhenAnyValue(vmProperty!)) - .Switch() - .Where(sourceList => sourceList != null) - .BindTo(view, viewProperty); // Bind the new bindable list to the view property - internal static IBindingTypeConverter? GetConverterForTypes(Type lhs, Type rhs) => _typeConverterCache.Get((lhs, rhs)); diff --git a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs index 70ae30e70f..bb10d33dcf 100644 --- a/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs +++ b/src/ReactiveUI/Bindings/Property/PropertyBindingMixins.cs @@ -4,7 +4,6 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Collections.ObjectModel; using System.Linq.Expressions; using System.Reactive; @@ -354,27 +353,5 @@ public static IDisposable BindTo( IBindingTypeConverter? vmToViewConverterOverride = null) where TTarget : class => _binderImplementation.BindTo(@this, target, property, conversionHint, vmToViewConverterOverride); - - /// - /// Create a one way list binding from the viewmodel to the view. The view list property will - /// be automatically updated to reflect the viewmodel source list property. - /// - /// The type of the view that is bound. - /// The type of the view model that is bound. - /// The type of the data stored in the list. - /// The type of the property bound on the view model. - /// The view used for the binding. - /// A dummy viewmodel parameter, used to infer the viewmodel type. - /// The source property in the viewmodel that contains the list. - /// The target property in the view to bind the list to. - /// An object that when disposed, disconnects the binding. - public static IDisposable BindList( - this TView view, - TViewModel? viewModel, - Expression?>> vmProperty, - Expression> viewProperty) - where TViewModel : class - where TView : class, IViewFor => - _binderImplementation.BindList(view, viewModel, vmProperty, viewProperty); } }