C#のReactivePropertyを使い異なるコントロールで同じ値を保持するようにする。

C# コンピュータ
C#

テキストボックス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つありますが、いずれかのテキストボックスの文字を変更すると、他方のテキストボックス値も変更され、タイトルの文字も変更されます。

同じ値を保持するのに別のReactivePropertyを用意しているあたり、納得いかない部分もありますが、変更元のコントロール応じて処理を分けるために、このようなコードになりました。

コメント