I am trying to get a handle on ReaxtiveX/ReaxtiveUI and tried to make a really simple program; a TextBox, a TextBlock, and a Button. I want the TextBox to have an initial value and whatever is typed in the TextBox to show in the TextBlock. When the button is clicked, the text in the TextBox will be changed, and therefore so will the text in the TextBlock. I got the TextBlock to update just fine, but can't get the button to work.
Here's my code
MainWindowViewModel.cs:
public class MainWindowViewModel : ReactiveObject
{
//private string _inputText;
//public string InputText
//{
// get => _inputText;
// set => this.RaiseAndSetIfChanged(ref _inputText, value);
//}
private ObservableAsPropertyHelper<string> _inputText;
public string InputText => _inputText.Value;
private ObservableAsPropertyHelper<string> _copyText;
public string CopyText => _copyText.Value;
public ReactiveCommand<Unit, string> ChangeInputText { get; }
public MainWindowViewModel ()
{
//InputText = "Starting Text";
_inputText = ChangeInputText.ToProperty(this, nameof(InputText), "Starting Text");
_copyText = this
.WhenAnyValue(x => x.InputText)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(x => x.ToUpperInvariant())
.DistinctUntilChanged()
.WhereNotNull()
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.CopyText);
ChangeInputText = ReactiveCommand.Create<Unit, string>(_ => { return "Text Changed"; });
}
}
MainWindow.xaml.cs
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
{
public MainWindow ()
{
InitializeComponent();
ViewModel = new MainWindowViewModel();
this.WhenActivated(d =>
{
this.Bind(ViewModel, vm => vm.InputText, v => v.testTextBox.Text).DisposeWith(d);
this.OneWayBind(ViewModel, vm => vm.CopyText, v => v.copyTextBlock.Text).DisposeWith(d);
this.BindCommand(ViewModel, vm => vm.ChangeInputText, v => v.changeButton).DisposeWith(d);
});
}
}
MainWindow.xaml
<Border Padding="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox x:Name="testTextBox"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3" />
<Button x:Name="changeButton"
Grid.Row="1"
Grid.Column="1"
Margin="5"
Content="Change Text" />
<Border BorderThickness="1"
BorderBrush="Black"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="3">
<TextBlock x:Name="copyTextBlock" />
</Border>
</Grid>
</Border>
I started with InputText
as a property and set the initial value in the ViewModel ctor (before I tried to implement the button command). It worked, with the TextBox having an initial value, and its contents mirrored in the TextBlock. When I tried to implement the button command I changed InputText
to an OAPH because, as far as I know, you cant use properties with ToProperty()
.
Now when I run the project I get this
What am I doing wrong?
EDIT:
Here is the working ViewModel after Glenn Watson helped me.
public class MainWindowViewModel : ReactiveObject
{
//Made Imput text a property instead of an OAPH
private string _inputText;
public string InputText
{
get => _inputText;
set => this.RaiseAndSetIfChanged(ref _inputText, value);
}
private readonly ObservableAsPropertyHelper<string> _copyText;
public string CopyText => _copyText.Value;
public ReactiveCommand<Unit, string> ChangeInputText { get; }
public MainWindowViewModel ()
{
InputText = "Starting Text";
ChangeInputText = ReactiveCommand.Create<Unit, string>(_ => { return "Text Changed"; });//.BindTo(this, x => x.InputText);
ChangeInputText.BindTo(this, x => x.InputText); //Added BindTo
_copyText = this
.WhenAnyValue(x => x.InputText)
.ObserveOn(RxApp.TaskpoolScheduler)
.Select(x => x.ToUpperInvariant())
.DistinctUntilChanged()
.WhereNotNull()
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.CopyText);
}
}
question from:
https://stackoverflow.com/questions/65929451/how-to-set-a-textbox-value-with-a-button-click-in-reactiveui