Inside a Blazor component, I'm trying to bind an instance of my viewmodel class to an InputSelect so I can select a value for an enum property. The VM also has a string property I can bind to a text input. The VM implements INotifyPropertyChanged, just like it did inside a WPF app, where the enum property was databound to a XAML ComboBox. A <p> element displays the selected enum property value, as you'd expect from two-way binding.
Whenever I change the string value in the input box, the string property's set gets called, per a breakpoint I've set there. But a similar breakpoint in the enum property's set never gets hit, even though the <p> it's bound to updates, and I'm trying to figure out what I'm missing. I know that if I use both @bind-Value and a change handler function, I get an error because then there are two change handlers, but how do I get the first change handler to do my bidding? Starting to miss WPF's automation mechanisms!
MyVM.cs:
namespace BlazorProj.Viewmodels {
public enum Choice {
Choice1,
Choice2,
Choice3,
Choice4
}
public class ChoiceOption {
public Choice Key { get; set; }
public string OptionText { get; set; }
}
public class MyVM : INotifyPropertyChanged {
public event PropertyChangedEventHandler? PropertyChanged;
private string _strValue;
private Choice? _selectedChoice;
public string StrValue {
get { return _strValue; }
set {
if (_strValue != value) {
_strValue = value;
NotifyListeners();
}
}
}
public Choice? SelectedChoice {
get { return _selectedChoice; }
set {
if (_selectedChoice != value) {
_selectedChoice = value;
DoMoreStuff();
NotifyListeners();
}
}
}
public ObservableCollection<ChoiceOption> ChoiceOptions { get; set; } // Gets populated elsewhere
private void DoMoreStuff() {
// ...Stuff done when new choice selected
}
private void NotifyListeners([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The component, Comp.razor:
@using BlazorProj.Viewmodels;
@if (ViewModel == null) {
<p><em>Loading...</em></p>
} else {
<InputSelect @bind-Value="@ViewModel.SelectedChoice">
@if (ViewModel?.ChoiceOptions != null) {
@foreach (var opt in ViewModel.ChoiceOptions) {
<option value="@opt.Key">@opt.OptionText</option>
}
} else {
<option value="">No options</option>
}
</InputSelect>
<p style="color:blue">@ViewModel.SelectedChoice.ToString()</p>
<input @bind="@ViewModel.StrValue" />
}
@code {
MyVM ViewModel { get; set; }
}
CompPage.razor:
@page "/"
@using BlazorProj.Viewmodels;
@inject MyVM ViewModel
<Comp />
@code {
}
What must I do so DoMoreStuff gets called? Thanks...
EDIT: I've discovered that the set and DoMoreStuff DO get called if the InputSelect has focus and I hit the up or down arrow key, so I need to make it work for selecting from the drop-down list as well. Also, removed "Casading..." stuff since it wasn't needed, and corrected CompPage.razor to display a Comp instead of a Tests.
EDIT 2: I've now discovered that DoMoreStuff DOES get called and does stuff on selecting from the drop-down list, even if the breakpoint doesn't get hit. I guess this is a VS debugging issue?
[Polite] I not sure you're going about this the right way. There should be no need for Cascading.
I've also removed the
ObservableCollectionwhich I think you're just using to detect changes in the model. I use anEventCallbackinstead which is called every time a value is edited and triggers a render in the parent.Here's my alternative approach to what I think your trying to achieve [I may be wide of the mark!].
The editor:
And the Demo page: