Blazor 与 InputSelect 的双向数据绑定永远不会进入 VM 属性集

问题描述 投票:0回答:1

在 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 被调用?谢谢...

input combobox blazor viewmodel 2-way-object-databinding
1个回答
0
投票

[礼貌]我不确定你的处理方式是否正确。应该不需要级联。

我还删除了

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;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.