当我的 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
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";
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
在同步(没有 Task.Run)中,这个工作正常,但在异步中,UI 不会更新。
我需要解释要更新的 UI StateHasChanged():https :
//github.com/aspnet/Blazor/issues/1413
但是我不能在 TodoItem 中调用这个方法(而且我不希望 TodoItem 知道 Blazor 组件)。
您有更新 UI 的解决方案吗?
您应该执行以下操作:
在你的类中定义一个动作委托:
public event Action OnChange;
Run Code Online (Sandbox Code Playgroud)
在这个类中定义一个方法NotifyStateChanged()如下:
private void NotifyStateChanged() => OnChange?.Invoke();
Run Code Online (Sandbox Code Playgroud)
该方法触发OnChange事件。在完成它所做的任何任务后,您应该从逻辑中调用此方法。
在您的待办事项组件中,将StateHasChanged方法添加到您的TodoItem类中使用的事件委托中:
@functions
{
protected override void OnInit()
{
state.OnChange += StateHasChanged;
}
}
Run Code Online (Sandbox Code Playgroud)
简单的答案是“修改你的变量后就触发StateHasChanged();” :
Task.Run(() =>
{
//Simulate work
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
//Update property
Title = Title + " - Done";
StateHasChanged();
});
Run Code Online (Sandbox Code Playgroud)
因为您的方法是异步的,所以重写为:
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();
});
Run Code Online (Sandbox Code Playgroud)
为了避免反模式并编写干净的代码,您的 ModelView 可能有一个公共事件来让 UI 知道它已更改,只需将 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();
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
已编辑
StateHasChanged不再支持来自线程的调用。只是改变:
Task.Run(() =>
{
//Simulate work
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
//Update property
Title = Title + " - Done";
StateHasChanged();
});
Run Code Online (Sandbox Code Playgroud)
经过
Invoke( () =>
{
...
Run Code Online (Sandbox Code Playgroud)