Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/ReactiveUI.Tests/Mocks/MockBindListItemViewModel.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Gets or sets displayed name of the crumb.
/// </summary>
public string Name
{
get => _name;
set => this.RaiseAndSetIfChanged(ref _name, value);
}
}
}
80 changes: 80 additions & 0 deletions src/ReactiveUI.Tests/Mocks/MockBindListView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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;

namespace ReactiveUI.Tests
{
/// <summary>
/// MockBindListView.
/// </summary>
/// <seealso cref="System.Windows.Controls.UserControl" />
public class MockBindListView : UserControl, IViewFor<MockBindListViewModel>
{
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(nameof(ViewModel), typeof(MockBindListViewModel), typeof(MockBindListView), new PropertyMetadata(null));

/// <summary>
/// Initializes a new instance of the <see cref="MockBindListView"/> class.
/// </summary>
public MockBindListView()
{
ItemList = new();

var ms = new MemoryStream(Encoding.UTF8.GetBytes(@"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<StackPanel Orientation=""Horizontal"">
<TextBlock
VerticalAlignment=""Stretch""
Text=""{Binding Name}""
TextAlignment=""Center"" />
</StackPanel>
</DataTemplate> "));
ItemList.ItemTemplate = (DataTemplate)XamlReader.Load(ms);
var ms1 = new MemoryStream(Encoding.UTF8.GetBytes(@"
<ItemsPanelTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<StackPanel Orientation=""Horizontal"" />
</ItemsPanelTemplate> "));
ItemList.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(ms1);

ViewModel = new();
this.WhenActivated(d =>
{
this.OneWayBind(ViewModel, vm => vm.ListItems, v => v.ItemList.ItemsSource).DisposeWith(d);
this.WhenAnyValue(v => v.ItemList.SelectedItem)
.Where(i => i != null)
.Cast<MockBindListItemViewModel>()
.Do(_ => ItemList.UnselectAll())
.InvokeCommand(this, v => v.ViewModel!.SelectItem).DisposeWith(d);
});
}

/// <summary>
/// Gets or sets the ViewModel corresponding to this specific View. This should be
/// a DependencyProperty if you're using XAML.
/// </summary>
public MockBindListViewModel? ViewModel
{
get => (MockBindListViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}

public ListView ItemList { get; }

object? IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (MockBindListViewModel?)value;
}
}
}
74 changes: 74 additions & 0 deletions src/ReactiveUI.Tests/Mocks/MockBindListViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using DynamicData;

namespace ReactiveUI.Tests
{
public class MockBindListViewModel : ReactiveObject
{
private readonly ObservableAsPropertyHelper<MockBindListItemViewModel?> _activeItem;
private readonly ReadOnlyObservableCollection<MockBindListItemViewModel> _listItems;

static MockBindListViewModel()
{
Splat.Locator.CurrentMutable.Register(() => new MockBindListView(), typeof(IViewFor<MockBindListViewModel>));
}

/// <summary>
/// Initializes a new instance of the <see cref="MockBindListViewModel"/> class.
/// </summary>
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);

ActiveListItem.Connect().ObserveOn(RxApp.MainThreadScheduler).Bind(out _listItems).Subscribe();
}

/// <summary>
/// Gets the item that is currently loaded in the list.
/// Add or remove elements to modify the list.
/// </summary>
public ISourceList<MockBindListItemViewModel> ActiveListItem { get; } = new SourceList<MockBindListItemViewModel>();

/// <summary>
/// Gets the deepest item of the currect list. (Last element of ActiveListItem).
/// </summary>
public MockBindListItemViewModel? ActiveItem => _activeItem.Value;

/// <summary>
/// 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.
/// </summary>
public ReactiveCommand<MockBindListItemViewModel, Unit> SelectItem { get; }

/// <summary>
/// Gets the list items.
/// </summary>
/// <value>
/// The list items.
/// </value>
public ReadOnlyObservableCollection<MockBindListItemViewModel> ListItems => _listItems;
}
}
40 changes: 40 additions & 0 deletions src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
}
}
18 changes: 9 additions & 9 deletions src/ReactiveUI.Tests/ReactiveUI.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="MSBuild.Sdk.Extras">
<Project Sdk="MSBuild.Sdk.Extras">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
Expand Down Expand Up @@ -60,19 +60,15 @@
</ItemGroup>
</When>
<When Condition=" $(TargetFramework.StartsWith('net4'))">
<PropertyGroup>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ReactiveUI.Winforms\ReactiveUI.Winforms.csproj" />
<ProjectReference Include="..\ReactiveUI.Wpf\ReactiveUI.Wpf.csproj" />
<ProjectReference Include="..\ReactiveUI.Blend\ReactiveUI.Blend.csproj" />

<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="WindowsBase" />
<Reference Include="UIAutomationTypes" />
<Reference Include="UIAutomationProvider" />

<Compile Include="Platforms\common-gui\**\*.cs" />
<Compile Include="Platforms\wpf\**\*.cs" />
<Compile Include="Platforms\Winforms\**\*.cs" />
Expand All @@ -81,6 +77,10 @@
</When>
</Choose>

<ItemGroup>
<Page Remove="Mocks\MockBindListView.xaml" />
</ItemGroup>

<ItemGroup>
<Compile Update="Platforms\Winforms\Mocks\TestForm.Designer.cs">
<DesignTime>True</DesignTime>
Expand Down
27 changes: 27 additions & 0 deletions src/ReactiveUI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down