テキストボックス2つとフォームのタイトルを同じ値になるようにしたい。
各々コントロールで別のReactivePropertyをバインドし、Subscribeで値の変更の通知をうけ、ほかのコントロールソースになっている値も変更するようにコードを書きました。
ソースコード
ファイル名:MainWindow.xaml
<Window
x:Class="AddressBar02.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:AddressBar02"
mc:Ignorable="d"
Title="{Binding Title.Value,Mode=OneWay}"
Height="450" Width="800"
FontFamily="MS UI GOTHIC"
FontSize="16"
xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<i:Interaction.Behaviors>
<local:ViewModelCleanupBehavior />
</i:Interaction.Behaviors>
<Window.Resources>
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="Margin" Value="8,2,8,2" />
<Setter Property="Padding" Value="8,2,8,2" />
<Setter Property="FontSize" Value="18" />
</Style>
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Margin" Value="4,2,4,2" />
<Setter Property="Padding" Value="2,2,2,2" />
<Setter Property="FontSize" Value="18" />
</Style>
</Window.Resources>
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top"> <!-- DockPanel:上 -->
<DockPanel>
<StackPanel DockPanel.Dock="Left"
Orientation="Horizontal"> <!-- スタック:水平 DockPanel:左-->
<!-- 左ボタンエリア-->
<Button Style="{StaticResource ButtonStyle}"
Content='↑' />
</StackPanel>
<StackPanel DockPanel.Dock="Right"
Orientation="Horizontal"> <!-- スタック:水平 DockPanel:右-->
<!-- 右ボタンエリア-->
<Button Style="{StaticResource ButtonStyle}"
Content='+' />
</StackPanel>
<TextBox Style="{StaticResource TextBoxStyle}"
Text="{Binding AddressText.Value}" /><!-- DockPanel:全体-->
</DockPanel>
</StackPanel>
<WrapPanel x:Name="Dummy"><!-- DockPanel:全体 -->
<TextBox Margin="50,100,0,0" Width="200"
Text="{Binding DummyText.Value}" />
</WrapPanel>
</DockPanel>
</Grid>
</Window>
ファイル名:MainWindowViewModel.cs
using System.Diagnostics;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;
using System.Reactive.Linq;
namespace AddressBar02;
public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
{
// INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
// IDisposable
private CompositeDisposable Disposable { get; } = [];
/**************************************************************************
* プロパティ
**************************************************************************/
public ReactiveProperty<string> Title { get; private set; }
public ReactiveProperty<string> AddressText { get; private set; }
public ReactiveProperty<string> DummyText { get; private set; }
private const string DefaultText = "Title";
public MainWindowViewModel()
{
PropertyChanged += (s, e) => {};
// タイトル
Title = new ReactiveProperty<string>(DefaultText).AddTo(this.Disposable);
AddressText = new ReactiveProperty<string>(DefaultText).AddTo(this.Disposable);
AddressText.Skip(1).Subscribe(x =>
{
Debug.Print($"AddressText-Subscribe:{x}");
//if (x != Title.Value)
//{
Title.Value = x; // 変更された値xをTitle.Valueへセット
//}
});
// ダミーテキスト
DummyText = new ReactiveProperty<string>(DefaultText).AddTo(this.Disposable);
// ダミーテキストの購読
DummyText.Skip(1).Subscribe(x =>
{
Debug.Print($"DummyText-Subscribe:{x}");
//if (x != Title.Value)
//{
Title.Value = x; // 変更された値xをTitle.Valueへセット
//}
});
// タイトルの購読
Title.Skip(1).Subscribe(x=>
{
Debug.Print($"Title-Subscribe:{x}");
//if (x != AddressText.Value)
//{
AddressText.Value = x; // 変更された値xをAddressText.Valueへセット
//}
//if (x != DummyText.Value)
//{
DummyText.Value = x; // 変更された値xをDummyText.Valueへセット
//}
});
}
public void Dispose()
{
Debug.WriteLine("Dispose()");
Disposable.Dispose();
}
}
説明
Viewのボタンは機能しません。
テキストボックスが2つありますが、いずれかのテキストボックスの文字を変更すると、他方のテキストボックス値も変更され、タイトルの文字も変更されます。
Subscribe()でほかのコントロールとバインドしているReactivePropertyの値を変更しているので、Subscribe()の無限ループが発生するのではないかと危惧したのですが、どうやら発生していないようです。



C#のReactivePropertyで同じ値を代入した場合Subscribeが実行されるか確認する
値が変更されることをきっかけにSubscribeが実行されるはずなので、同じ値を代入した場合Subscribeは実行されないと思われるので確認してみます。public ReactiveProperty<string> Title { get...

C#のReactivePropertyで初回のSubscribeをスキップする方法
ReactivePropertyを初期化する際、値の変更とみなされSubscribeが実行されます。実行されると都合が悪い場合以下の様にするとSubscribe初回をスキップすることが出来るようです。public ReactivePrope...
同じ値を保持するのに別のReactivePropertyを用意しているあたり、納得いかない部分もありますが、変更元のコントロール応じて処理を分けるために、このようなコードになりました。
コメント