Home > other >  Update TextBoxes bound to properties backed by the same local variable
Update TextBoxes bound to properties backed by the same local variable

Time:04-13

In an App using ReactiveUI, I have two properties backed by the same local variable. Then the properties are used to bind to two textboxes where one showing the value and the other showing the value times 2. I need to have one box reflects the change in the other box.

ViewModel:

public class AppViewModel : ReactiveObject
{
    private int? _boxCommon;
    public int? boxOrg
    {
        get => _boxCommon;
        set
        {
            Trace.WriteLine($"**** Check1: {value}");
            _ = this.RaiseAndSetIfChanged(ref _boxCommon, value);
        }
    }
    public int? boxDouble
    {
        get => _boxCommon * 2;
        set
        {
            Trace.WriteLine($"**** Check2: {value}");
            _ = this.RaiseAndSetIfChanged(ref _boxCommon, (int)value / 2);
        }
    }

    private int? lastVal;

    public AppViewModel()
    {
        _boxCommon = 10;
        lastVal = 10;
        
        // Reflect changes to the other box.
        this.WhenAnyValue(x => x.boxOrg)
            .Throttle(TimeSpan.FromMilliseconds(800))
            .Select((t) =>
            {
                if (t != lastVal)
                {
                    return t * 2;
                }
                else
                {
                    return boxDouble;
                }
            })
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe((t) => boxDouble = t);  // seems reduntdant cause both backed by the same var.

        this.WhenAnyValue(x => x.boxDouble)
            .Throttle(TimeSpan.FromMilliseconds(800))
            .Select((t) =>
            {
                if (t != lastVal)
                {

                    return t / 2;
                }
                else
                {
                    return boxOrg;
                }
            })
            .ObserveOn(RxApp.MainThreadScheduler)
            .Subscribe((t) => boxOrg = t);            
    }
}

View:

<reactiveui:ReactiveWindow 
    x:Class="ReactiveDemo.MainWindow"
    x:TypeArguments="reactivedemo:AppViewModel"
    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:reactivedemo="clr-namespace:ReactiveDemo"
    xmlns:reactiveui="http://reactiveui.net"
    Title="Test" Height="150" Width="200"
    mc:Ignorable="d">
    <Grid Margin="12">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="OrgNum: "/>
        <TextBox Grid.Column="1" x:Name="orgBox" />
        <TextBlock Grid.Row="1" Text="DoubleNum: "/>
        <TextBox Grid.Column="1" Grid.Row="1" x:Name="doubleBox" />
    </Grid>
</reactiveui:ReactiveWindow>

View code behind:

public MainWindow()
{
    InitializeComponent();
    ViewModel = new AppViewModel();

    this.WhenActivated(disposableRegistration =>
    {
        // Notice we don't have to provide a converter, on WPF a global converter is
        // registered which knows how to convert a boolean into visibility.
        this.Bind(ViewModel,
            viewModel => viewModel.boxOrg,
            view => view.orgBox.Text)
            .DisposeWith(disposableRegistration);
        this.Bind(ViewModel,
            viewModel => viewModel.boxDouble,
            view => view.doubleBox.Text)
            .DisposeWith(disposableRegistration);

    });
}

When I make changes in one box, the Trace does print out the new value set to the other box. However, the GUI element is not updated with the new value. New to WPF and ReactiveUI, any pointers are greatly appreciated.

CodePudding user response:

You would rather use ValueConverter than your solution:

ViewModel:

private int _boxSingle;
public int BoxSingle
{
    get => _boxSingle;
    set
    {
        this.RaiseAndSetIfChanged(ref _boxSingle, value);
    }
}

Bindings:

this.Bind(ViewModel, vm => vm.BoxSingle, view => view.textBox1.Text)
    .DisposeWith(d);

this.Bind(ViewModel, vm => vm.BoxSingle, view => view.textBox2.Text, 
        v => (v * 2).ToString(), 
        v => int.Parse(v) / 2 )
    .DisposeWith(d);
  • Related