绑定属性在更改时不更新

Posted

技术标签:

【中文标题】绑定属性在更改时不更新【英文标题】:Bound property not updating upon change 【发布时间】:2019-03-24 23:03:06 【问题描述】:

在我的 Blazor 应用中,我的视图中有以下输入字段:

<input bind="@amount.Display" type="text" />

这绑定到使用以下访问器定义的属性:

get

    return _display;

set

    var parsed = Decimal.Parse(value);
    _display = parsed.ToString("F2");

真正的访问器逻辑比这更复杂,但我已经在上面尽可能地简化了它,同时仍然保留了让我感到困惑的行为。

基本上,当用户输入“2”,然后制表符或点击输入字段时,我希望它自动转换为“2.00”。奇怪的是,这种转换似乎只有在用户输入的字符串表示的数字与已经存在的数字不同时才会发生。例如,如果输入字段当前的值为“1.00”并且我输入“2”,我正确地以“2.00”结束。但是,如果输入字段的值为“2.00”并且我输入“2”,则它仍然是“2”。就好像在后一种情况下没有调用 set 访问器,我无法想象为什么。

【问题讨论】:

【参考方案1】:

如果你想强制 UI 重新渲染,在 Blazor 中我建议你使用StateHasChanged()

get

    return _display;

set

    var parsed = Decimal.Parse(value);
    _display = parsed.ToString("F2");
    StateHasChanged();

Blazor 不重新渲染的真正原因是:它试图节省重建 html。由于 Blazor 知道您将属性绑定到 input,因此它认为无需在设置属性后触发重新渲染。

更新工作:

    用户输入文本“5”并按 Tab 生成了 HTML onchange 事件 Blazor 捕获 onchange 并调用 amount.Display = "5"; 完成。

【讨论】:

我试过这个,但它不起作用。这个建议也无法解释我描述的差异:为什么输入字段更新不一致? 更新工作流,Blazor 如何更改属性。更改值后没有理由重新渲染 UI,因此很可能没有更新。 肯定不行。 -- 顺便说一句,它绑定到一个字符串而不是十进制值。出于所有意图和目的,值 DID 发生了变化,以前是“2.00”,现在只是“2”。 -- 从 "2.00" 和 "1" 更改应该没有区别,都是不同的字符串值。 StateHasChanged 适用于某些处理程序,但不适用于 onchange :/【参考方案2】:

主要问题是如果 Blazor 认为文本框的值没有改变,它就不会更新文本框。您的示例存在的部分问题是值(您的属性)的内部表示有时与您在文本框中看到的内容不一致(22.00)。但就 Blazor 而言,如果属性值没有改变,它不会将绑定传播回文本框。

我玩弄了您的代码的许多变体(使用 valueonchange 手动“绑定”),但最终如果您的代码完成更新 value 属性并且值与之前相同之前,什么都不会发生。

我能想到的唯一解决方法是通过使用异步引入一个非常丑陋的 hack。执行此操作时,事件循环在 await 之后完成(blazor 将接受 2 的新值),并且 blazor 将再次检查从那时起恢复 await 调用时是否有任何更改这是新一轮的事件循环。由于新值现在将是 2.00 而不是 2,它会刷新文本框。

@functions 
    private string value;

    private string Value
    
        get => value;
        set => SetValue(value);
    

    private async void SetValue(string value)
    
        this.value = value;
        await Task.Delay(1);
        this.value = decimal.Parse(value).ToString("F2");
        StateHasChanged();
    

希望比我更了解 Blazor 的人可以提供一些不那么残暴的东西。但它确实有效。 (顺便说一句,Task.Delay(0) 确实 工作,大概是由于一些优化,其中任务已经完成,因此没有获得退出事件循环的承诺。)

注意: 一旦 Blazor 格式字符串(即format-value="yyyy-MM-dd")接受数字,您可能可以通过将值存储为小数并直接在 razor 标记中使用格式字符串来简化此操作。

【讨论】:

正如我在另一条评论中所说的,我很奇怪“2”会被认为与“2.00”的值相同。输入字段类型是文本,不是数字;并且有问题的属性是一个字符串,而不是一个小数(你和我都知道这个字符串恰好表示一个数字,但这应该是无关紧要的)。那么,为什么“2”不能被认为与“2.00”完全不同呢? 因为 Blazor 正在比较您的 属性 的值。假设您将文本框设置为2,并且第一次将其转换为2.00。您的属性值是字符串2.00。现在,假设您删除该值并输入2。由于您的绑定,它将您的属性的值设置为2,并且您的设置器将覆盖它,以便最终值为2.00。 Blazor 看到原始值 (2.00) 与新值 (2.00) 相同,并决定无事可做,因此文本框中的值不会更新。 (它是双向绑定,但它是短路的) 哦,天哪,我明白了。我想其他 cmets 试图告诉我同样的事情,而我直到现在才明白。与此同时,我可能只是使用一些 javascript 来格式化输入。感谢您的帮助。 从你的描述来看,这听起来像是 Blazor 中的一个错误,因为它知道它用不同的值调用了 setter,是的,以前是“2.00”,现在又是“2.00”- - 但是在这两个调用之间它调用了值为“2”的setter,因为它绑定到一个属性——所以它确实改变了,它是“2.00”和“2”,现在它应该是“2.00”再次......但因为 Blazor 忽略了 setter 调用,所以它被显示为“2”的值。这显然是错误的行为。

以上是关于绑定属性在更改时不更新的主要内容,如果未能解决你的问题,请参考以下文章

WPF:解决数据绑定时不更新数据的问题

Angular2中的属性更改时数据绑定不更新

父属性更改时嵌套属性的 WPF 绑定更新通知

复选框值在 false 时不绑定到对象?

在 WPF .net core 5 中运行时更改应用程序文化时如何更新属性绑定

附加属性未更新它绑定到的值 (MVVM)