更新异步属性

时间:2018-11-18 18:26:42

标签: c# .net blazor

当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?

2 个答案:

答案 0 :(得分:3)

您应该执行以下操作:

  1. 在您的课程中定义一个动作委托:

    public event Action OnChange;
    
  2. 在该类中,按如下方式定义方法NotifyStateChanged()

    private void NotifyStateChanged() => OnChange?.Invoke();
    

    此方法触发OnChange事件。完成任何任务后,应从逻辑中调用此方法。

  3. 在您的待办事项组件中,将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();
            });
        }
    }
}