当ViewModel的一个属性被更新时,其他属性被异步更新。
Todo.cshtml:
@page "/todo"
<h1>Todo (@todos.Count(todo => !todo.IsDone))</h1>
<ul>
@foreach (var todo in todos)
{
<li>
<input type="checkbox" bind="@todo.IsDone" />
<input bind="@todo.Title" />
</li>
}
</ul>
<input placeholder="Something todo" bind="@newTodo"/>
<button onclick="@AddTodo">Add todo</button>
@functions {
IList<TodoItem> todos = new List<TodoItem>();
string newTodo;
void AddTodo()
{
if (!string.IsNullOrWhiteSpace(newTodo))
{
todos.Add(new TodoItem { Title = newTodo });
newTodo = string.Empty;
}
}
}
TodoItem.cs:
public class TodoItem
{
private bool _isDone;
public string Title { get; set; }
public bool IsDone
{
get => _isDone;
set
{
_isDone = value;
Task.Run(() =>
{
//Simulate work
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
//Update property
Title = Title + " - Done";
});
}
}
}
在同步状态下(没有Task.Run)可以正常工作,但在异步状态下,不会更新UI。
我需要说明要用StateHasChanged()
更新的UI:
https://github.com/aspnet/Blazor/issues/1413
但是我无法在TodoItem中调用此方法(并且我不希望TodoItem知道Blazor组件)。
您是否有解决方法来更新UI?
答案 0 :(得分:3)
您应该执行以下操作:
在您的课程中定义一个动作委托:
public event Action OnChange;
在该类中,按如下方式定义方法NotifyStateChanged()
:
private void NotifyStateChanged() => OnChange?.Invoke();
此方法触发OnChange
事件。完成任何任务后,应从逻辑中调用此方法。
在您的待办事项组件中,将StateHasChanged
方法添加到TodoItem
类中使用的事件委托中,从而:
@functions
{
protected override void OnInit()
{
state.OnChange += StateHasChanged;
}
}
希望这会有所帮助。如果是这样,请不要拒绝接受它作为答案
答案 1 :(得分:1)
简单的答案是“在修改您的变量后立即开火StateHasChanged();
” :
Task.Run(() =>
{
//Simulate work
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
//Update property
Title = Title + " - Done";
StateHasChanged();
});
由于您的方法是异步的,因此重写为:
Task.Run(async () => //<--here async
{
//Simulate async work
Task.Run( async () => {
await Task.Run( () => {} ); //<--- await
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
});
//Update property
Title = Title + " - Done";
StateHasChanged();
});
为避免使用反模式并编写清晰的代码,您的ModelView可能有一个公共事件来通知用户界面已更改,只需将UI上的该事件连接到StateHasChanged();
。
我在这里写了修改为执行此操作的Blazor Counter示例:
@page "/counter"
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" onclick="@IncrementCount">
Click me @s <!-- here var -->
</button>
@functions {
int currentCount = 0;
string s = "";
void IncrementCount()
{
currentCount++;
Task.Run(() =>
{
//Simulate work
Task.Run( async () => {
await Task.Run( () => {} );
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));}
);
//Update property
s = s + " - Done";
StateHasChanged();
});
}
}
}