-2

I use MVVM. There is a slow command. I want to use a log to indicate where is the execution. xaml:

 <TextBlock Height="200" Text="{Binding Log.Content, UpdateSourceTrigger=PropertyChanged}"></TextBlock>

code:

Log.addLine("Ledger initialized.");
// slow1 operation
Log.addLine("slow1 operation.");

The textblock is only updated when the whole command is finished. Both message appears at the same time when the GUI is active.

Istvan Heckl
  • 864
  • 10
  • 22
  • 1
    Your slow action is blocking the UI thread so as a result it becomes unresponsive for the duration of your slow operation. So once the operation completes the resources are available to update the ui (TextBlock). You could use async/await or the BackgroundWorker class to keep your UI responsive for the entire operation. – BionicCode Jul 08 '17 at 11:53
  • but if I use background worker then the GUI will be reposnsive while executing the slow command and this is could cause additional problems. – Istvan Heckl Jul 08 '17 at 12:52
  • Nothing in your post indicates an actual question per se. It _seems_ you are dealing with a long-running operation that prevents the UI from updating while in progress; the marked duplicate resolves that issue (and indeed, is the _only_ correct way to resolve that issue). If you have additional constraints that you did not bother to mention in your question, you may ask a _new_ question about how to address those. Make sure when you do you provide a good [mcve] that shows clearly what those constraints and problems are. – Peter Duniho Jul 08 '17 at 19:15

2 Answers2

0

In this example I used async/await to keep the UI responsive so that it is able to update itself at any time. Otherwise the long running task would have to complete before any update can occur.

Because the long running task was started synchronously on the UI thread before (in your example), the thread was busy executing your method, so no resources have been available to refresh the UI. Executing the method asynchronously executes it on a different thread so that the UI thread is free to execute UI related operations e.g. rendering or updating bindings.

Example

The view:

    <Window x:Class="TestRange.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:TestRange"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <local:ViewModel x:Key="ViewModel" />
        </Window.Resources>
        <StackPanel DataContext="{StaticResource ViewModel}">
            <Button Content="Long Running Task" Command="{Binding LongRunningCommand}" />
            <TextBlock Text="{Binding Text}" />
        </StackPanel> 
</Window>

The ViewModel:

class ViewModel : INotifyPropertyChanged
  {
    public ViewModel()
    {         
    }

    public ICommand LongRunningCommand => new RelayCommand(
      async (param) => await this.LongRunningTaskAsync().ConfigureAwait(false), (param) => true);

    public async Task LongRunningTaskAsync()
    {
      await Task.Run(
        () =>
        {
          for (int i = 0; i < 3; i++)
          {
            Thread.Sleep(5000);
            this.Text = $"Iteration #{i + 1}  completed";
          }
        });
    }

    private string text;   
    public string Text
    {
      get { return this.text; }
      set 
      { 
        this.text = value; 
        OnPropertyChanged();
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
      this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }
BionicCode
  • 1
  • 4
  • 28
  • 44
-1

Try executing slow method (presumably blocking method) in async manner, something like this:

Log.addLine("Ledger initialized.");   
await Task.Run(() =>
{
    SlowMethod();
});
Log.addLine("slow1 operation.");
vvucetic
  • 479
  • 7
  • 15
  • I thougt of that but it is not my intention. I do not want the GUI to be responsive after starting the command for the slow operation. It could cause other problems. I just want to see how far the slow operation is gone. – Istvan Heckl Jul 08 '17 at 12:31
  • Why down vote? I really don't understand what you mean when you say "how far the slow operations is gone". – vvucetic Jul 08 '17 at 12:34
  • The down vote was not me. I want to see if the first slow operation is finished, it the second is finished, etc. But I am happy if the gui is not responsive while the slow operations are working. – Istvan Heckl Jul 08 '17 at 12:42
  • If I understand correctly, logs are eventually showed but after all slow operations are finished? Looks like GUI is not responsive. If you could make GUI responsive so you can see logs updated, but block other buttons or actions in order to disable user from starting another action. Maybe some kind of loading indicator like http://wpftoolkit.codeplex.com/wikipage?title=BusyIndicator&referringTitle=Home – vvucetic Jul 08 '17 at 12:54
  • @IstvanHeckl wanting the UI to be active (show 1st log message) but not responsive sounds like a strange combination. Instead of making your UI not responsive, consider utilizing a busy indicator (just search for *WPF Busy Indicator*) for the UI and run your long running work in a different thread. – grek40 Jul 08 '17 at 15:18