Update a property asynchronous











up vote
3
down vote

favorite












When a property of my ViewModel is updated, other property is updated asynchronous.



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";
});
}
}
}


In synchronous (without Task.Run) this work fine, but in asynchronous the UI isn't updated.



I need explain the UI to update with StateHasChanged():
https://github.com/aspnet/Blazor/issues/1413



But I can't call this method in TodoItem (and I don't want TodoItem know Blazor component).



Have you a solution to update the UI?










share|improve this question




















  • 1




    Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
    – Kirk Woll
    Nov 18 at 19:23










  • Yes, I think my VM need update the view or notify the view.
    – Orwel
    Nov 19 at 20:47















up vote
3
down vote

favorite












When a property of my ViewModel is updated, other property is updated asynchronous.



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";
});
}
}
}


In synchronous (without Task.Run) this work fine, but in asynchronous the UI isn't updated.



I need explain the UI to update with StateHasChanged():
https://github.com/aspnet/Blazor/issues/1413



But I can't call this method in TodoItem (and I don't want TodoItem know Blazor component).



Have you a solution to update the UI?










share|improve this question




















  • 1




    Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
    – Kirk Woll
    Nov 18 at 19:23










  • Yes, I think my VM need update the view or notify the view.
    – Orwel
    Nov 19 at 20:47













up vote
3
down vote

favorite









up vote
3
down vote

favorite











When a property of my ViewModel is updated, other property is updated asynchronous.



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";
});
}
}
}


In synchronous (without Task.Run) this work fine, but in asynchronous the UI isn't updated.



I need explain the UI to update with StateHasChanged():
https://github.com/aspnet/Blazor/issues/1413



But I can't call this method in TodoItem (and I don't want TodoItem know Blazor component).



Have you a solution to update the UI?










share|improve this question















When a property of my ViewModel is updated, other property is updated asynchronous.



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";
});
}
}
}


In synchronous (without Task.Run) this work fine, but in asynchronous the UI isn't updated.



I need explain the UI to update with StateHasChanged():
https://github.com/aspnet/Blazor/issues/1413



But I can't call this method in TodoItem (and I don't want TodoItem know Blazor component).



Have you a solution to update the UI?







c# .net blazor






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 at 7:45









Uwe Keim

27.4k30128210




27.4k30128210










asked Nov 18 at 18:26









Orwel

481416




481416








  • 1




    Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
    – Kirk Woll
    Nov 18 at 19:23










  • Yes, I think my VM need update the view or notify the view.
    – Orwel
    Nov 19 at 20:47














  • 1




    Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
    – Kirk Woll
    Nov 18 at 19:23










  • Yes, I think my VM need update the view or notify the view.
    – Orwel
    Nov 19 at 20:47








1




1




Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
– Kirk Woll
Nov 18 at 19:23




Why not add a callback event/handler in TodoItem (like IsDoneComplete) of type Action that your view registers in order to call StateHasChanged?
– Kirk Woll
Nov 18 at 19:23












Yes, I think my VM need update the view or notify the view.
– Orwel
Nov 19 at 20:47




Yes, I think my VM need update the view or notify the view.
– Orwel
Nov 19 at 20:47












2 Answers
2






active

oldest

votes

















up vote
3
down vote



accepted










You should do the following:





  1. Define an action delegate in your class:



    public event Action OnChange;



  2. In this very class define a method NotifyStateChanged() as follows:



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


    This method triggers the OnChange event. You should call this method from your logic after fulfilling whatever task it does.




  3. In your todo Component, add the StateHasChanged method to the event delegate used in your TodoItem class thus:



    @functions
    {
    protected override void OnInit()
    {
    state.OnChange += StateHasChanged;
    }
    }



Hope this helps. If it does, please don't fail to accept it as the answer






share|improve this answer






























    up vote
    1
    down vote













    Easy answer is "just fire StateHasChanged(); after modify your var":



            Task.Run(() =>
    {
    //Simulate work
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
    //Update property
    Title = Title + " - Done";
    StateHasChanged();
    });


    Because your method is async, rewrite as:



            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();
    });


    To avoid anti-pattern and write clean code, your ModelView may have a public event to let know UI it has changed, just connect this event on UI to StateHasChanged();.



    I write here the Blazor Counter sample modified to do this:



    @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();
    });
    }
    }
    }





    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53364149%2fupdate-a-property-asynchronous%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      3
      down vote



      accepted










      You should do the following:





      1. Define an action delegate in your class:



        public event Action OnChange;



      2. In this very class define a method NotifyStateChanged() as follows:



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


        This method triggers the OnChange event. You should call this method from your logic after fulfilling whatever task it does.




      3. In your todo Component, add the StateHasChanged method to the event delegate used in your TodoItem class thus:



        @functions
        {
        protected override void OnInit()
        {
        state.OnChange += StateHasChanged;
        }
        }



      Hope this helps. If it does, please don't fail to accept it as the answer






      share|improve this answer



























        up vote
        3
        down vote



        accepted










        You should do the following:





        1. Define an action delegate in your class:



          public event Action OnChange;



        2. In this very class define a method NotifyStateChanged() as follows:



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


          This method triggers the OnChange event. You should call this method from your logic after fulfilling whatever task it does.




        3. In your todo Component, add the StateHasChanged method to the event delegate used in your TodoItem class thus:



          @functions
          {
          protected override void OnInit()
          {
          state.OnChange += StateHasChanged;
          }
          }



        Hope this helps. If it does, please don't fail to accept it as the answer






        share|improve this answer

























          up vote
          3
          down vote



          accepted







          up vote
          3
          down vote



          accepted






          You should do the following:





          1. Define an action delegate in your class:



            public event Action OnChange;



          2. In this very class define a method NotifyStateChanged() as follows:



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


            This method triggers the OnChange event. You should call this method from your logic after fulfilling whatever task it does.




          3. In your todo Component, add the StateHasChanged method to the event delegate used in your TodoItem class thus:



            @functions
            {
            protected override void OnInit()
            {
            state.OnChange += StateHasChanged;
            }
            }



          Hope this helps. If it does, please don't fail to accept it as the answer






          share|improve this answer














          You should do the following:





          1. Define an action delegate in your class:



            public event Action OnChange;



          2. In this very class define a method NotifyStateChanged() as follows:



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


            This method triggers the OnChange event. You should call this method from your logic after fulfilling whatever task it does.




          3. In your todo Component, add the StateHasChanged method to the event delegate used in your TodoItem class thus:



            @functions
            {
            protected override void OnInit()
            {
            state.OnChange += StateHasChanged;
            }
            }



          Hope this helps. If it does, please don't fail to accept it as the answer







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 19 at 20:50









          Kirk Woll

          60.5k16157172




          60.5k16157172










          answered Nov 18 at 22:29









          Issac

          1,0391312




          1,0391312
























              up vote
              1
              down vote













              Easy answer is "just fire StateHasChanged(); after modify your var":



                      Task.Run(() =>
              {
              //Simulate work
              System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
              //Update property
              Title = Title + " - Done";
              StateHasChanged();
              });


              Because your method is async, rewrite as:



                      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();
              });


              To avoid anti-pattern and write clean code, your ModelView may have a public event to let know UI it has changed, just connect this event on UI to StateHasChanged();.



              I write here the Blazor Counter sample modified to do this:



              @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();
              });
              }
              }
              }





              share|improve this answer



























                up vote
                1
                down vote













                Easy answer is "just fire StateHasChanged(); after modify your var":



                        Task.Run(() =>
                {
                //Simulate work
                System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
                //Update property
                Title = Title + " - Done";
                StateHasChanged();
                });


                Because your method is async, rewrite as:



                        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();
                });


                To avoid anti-pattern and write clean code, your ModelView may have a public event to let know UI it has changed, just connect this event on UI to StateHasChanged();.



                I write here the Blazor Counter sample modified to do this:



                @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();
                });
                }
                }
                }





                share|improve this answer

























                  up vote
                  1
                  down vote










                  up vote
                  1
                  down vote









                  Easy answer is "just fire StateHasChanged(); after modify your var":



                          Task.Run(() =>
                  {
                  //Simulate work
                  System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
                  //Update property
                  Title = Title + " - Done";
                  StateHasChanged();
                  });


                  Because your method is async, rewrite as:



                          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();
                  });


                  To avoid anti-pattern and write clean code, your ModelView may have a public event to let know UI it has changed, just connect this event on UI to StateHasChanged();.



                  I write here the Blazor Counter sample modified to do this:



                  @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();
                  });
                  }
                  }
                  }





                  share|improve this answer














                  Easy answer is "just fire StateHasChanged(); after modify your var":



                          Task.Run(() =>
                  {
                  //Simulate work
                  System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
                  //Update property
                  Title = Title + " - Done";
                  StateHasChanged();
                  });


                  Because your method is async, rewrite as:



                          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();
                  });


                  To avoid anti-pattern and write clean code, your ModelView may have a public event to let know UI it has changed, just connect this event on UI to StateHasChanged();.



                  I write here the Blazor Counter sample modified to do this:



                  @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();
                  });
                  }
                  }
                  }






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Nov 19 at 7:52

























                  answered Nov 19 at 7:42









                  dani herrera

                  27.4k344107




                  27.4k344107






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53364149%2fupdate-a-property-asynchronous%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                      Alcedinidae

                      Can an atomic nucleus contain both particles and antiparticles? [duplicate]