在 Blazor 组件内,我尝试将视图模型类的实例绑定到 InputSelect,以便我可以为枚举属性选择一个值。虚拟机还有一个可以绑定到文本输入的字符串属性。 VM 实现 INotifyPropertyChanged,就像在 WPF 应用程序中一样,其中枚举属性数据绑定到 XAML ComboBox。
元素显示选定的枚举属性值,正如您对双向绑定所期望的那样。
每当我更改输入框中的字符串值时,就会根据我在那里设置的断点调用字符串属性的集合。但是枚举属性集中的类似断点永远不会被击中,即使
它必然会更新,而且我正在尝试找出我缺少的内容。我知道如果我同时使用 @bind-Value 和更改处理程序函数,我会收到错误,因为有两个更改处理程序,但如何让第一个更改处理程序执行我的命令?开始怀念 WPF 的自动化机制!
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));
}
}
}
组件,Comp.razor:
@using BlazorProj.Viewmodels;
<CascadingValue Value=@ViewModel Name="ViewModel">
@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" />
}
</CascadingValue>
@code {
[CascadingParameter(Name = "ViewModel")]
MyVM ViewModel { get; set; }
}
CompPage.razor:
@page "/"
@using BlazorProj.Viewmodels;
@inject MyVM ViewModel
<CascadingValue Value=@ViewModel Name="ViewModel">
<Tests />
</CascadingValue>
@code {
}
我必须做什么才能让 DoMoreStuff 被调用?谢谢...
[礼貌]我不确定你的处理方式是否正确。应该不需要级联。
我还删除了
ObservableCollection
,我认为您只是用它来检测模型中的变化。我使用 EventCallback
来代替,每次编辑值并触发父级中的渲染时都会调用它。
这是我认为您想要实现的目标的另一种方法[我可能离题了!]。
编辑:
<InputSelect class="form-select mb-3"
TValue="int"
@bind-Value:get="this.GetChoiceOption"
@bind-Value:set="this.OnChoiceChanged">
<option selected disabled value="-1"> -- Select an Option to Edit -- </option>
@foreach (var choice in Choices)
{
<option value="@((int)choice.Key)">@choice.OptionText</option>
}
</InputSelect>
<InputText class="form-control mb-3"
hidden="@_noDelection"
@bind-Value:get="GetOptionText"
@bind-Value:set="this.OnChoiceTextChanged" />
@code {
[Parameter] public List<ChoiceOption> Choices { get; set; } = new();
[Parameter] public EventCallback ValuesChanged { get; set; }
private ChoiceOption? _choiceOption;
private int GetChoiceOption => _choiceOption is null ? -1 : (int)_choiceOption.Key;
private string GetOptionText => _choiceOption is null || _choiceOption.OptionText is null ? string.Empty : _choiceOption.OptionText;
private bool _noDelection => _choiceOption is null;
private Task OnChoiceChanged(int value)
{
_choiceOption = Choices.FirstOrDefault(item => (int)item.Key == value);
return Task.CompletedTask;
}
private async Task OnChoiceTextChanged(string value)
{
@if (_choiceOption is not null)
_choiceOption.OptionText = value;
await ValuesChanged.InvokeAsync();
}
}
演示页面:
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<EnumEditor Choices="_choices" ValuesChanged="this.OnValuesChanged" />
<div class="bg-dark text-white">
@foreach(var choice in _choices)
{
<pre>@choice.Key - @choice.OptionText</pre>
}
</div>
@code{
private List<ChoiceOption> _choices = new();
protected override void OnInitialized()
{
foreach (Choice choice in Enum.GetValues<Choice>())
{
_choices.Add(new() { Key = choice, OptionText = Enum.GetName<Choice>(choice) });
}
}
private Task OnValuesChanged()
{
return Task.CompletedTask;
}
}