という問題が出されました。 うんうんと頭をひねった結果こうなりました…ちょっと全ループしてるのがダサい。ReactiveProperty 2.x前提です。
まず、表示するデータ。
using Reactive.Bindings; namespace DupItemColorApp { public class PersonViewModel { public ReactiveProperty<string> Name { get; private set; } public ReactiveProperty<int> Age { get; private set; } public ReactiveProperty<string> BackgroundColor { get; private set; } public PersonViewModel() { this.Name = new ReactiveProperty<string>(); this.Age = new ReactiveProperty<int>(); this.BackgroundColor = new ReactiveProperty<string>("White"); } public static PersonViewModel Create(string name, int age) { var result = new PersonViewModel(); result.Name.Value = name; result.Age.Value = age; return result; } } }
こいつのBackgroundColorをいじってDataGridのRowの色を変えるつもりです。
プロパティの変更と要素の変更時に全走査!ダサい…もっといい方法募集中。一応連続での値の変更なんかに対応するためにThrottle入れて緩和してる。
using Reactive.Bindings; using Reactive.Bindings.Extensions; using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reactive.Linq; namespace DupItemColorApp { public class MainWindowViewModel { private static readonly string[] Names = new[] { "hoge", "fuga", "piyo", "foo", "bar", "bazz", "fizz", }; private readonly ObservableCollection<PersonViewModel> people = new ObservableCollection<PersonViewModel>(); public ReadOnlyReactiveCollection<PersonViewModel> People { get; private set; } public ReactiveCommand AddCommand { get; private set; } public MainWindowViewModel() { this.People = this.people.ToReadOnlyReactiveCollection(); this.People.ObserveElementObservableProperty(x => x.Name) .Throttle(TimeSpan.FromMilliseconds(500)) .Subscribe(x => { Console.WriteLine("Name changed."); foreach (var item in this.People.ToArray()) { item.BackgroundColor.Value = "White"; } this.ChangeBackgroundColor(); }); this.People.CollectionChangedAsObservable() .Throttle(TimeSpan.FromMilliseconds(500)) .Subscribe(_ => this.ChangeBackgroundColor()); this.AddCommand = new ReactiveCommand(); this.AddCommand.Subscribe(_ => { for (int i = 0; i < 1000; i++) { var name = Names.OrderBy(x => Guid.NewGuid()).First(); this.people.Add(PersonViewModel.Create(name, 0)); } }); } private void ChangeBackgroundColor() { Debug.WriteLine("ChangeBackgroundColor called."); foreach (var item in this.People.ToArray().GroupBy(x => x.Name.Value).Where(x => x.Count() > 1).SelectMany(x => x)) { item.BackgroundColor.Value = "Red"; } } } }
んで、XAML。これはバインドしてるだけ。
<Window x:Class="DupItemColorApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DupItemColorApp" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:MainWindowViewModel /> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition /> </Grid.RowDefinitions> <Menu> <MenuItem Header="Add" Command="{Binding AddCommand}" /> </Menu> <DataGrid Grid.Row="1" ItemsSource="{Binding People}" AutoGenerateColumns="False" UseLayoutRounding="True"> <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="Background" Value="{Binding BackgroundColor.Value}" /> </Style> </DataGrid.RowStyle> <DataGrid.Columns> <DataGridTextColumn Header="Name" Binding="{Binding Name.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> <DataGridTextColumn Header="Age" Binding="{Binding Age.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> </DataGrid.Columns> </DataGrid> </Grid> </Window>