2

During a lengthy (about 1 minute) process I am trying to log some progress by writing time-stamped messages to a text control. But all messages appear at once. Apparently, all PropertyChanged events are queued until my busy process is done, and received by the text control all at once. How can I kind of 'flush' the events in the middle of my busy process? I searched but could not find a Flush/Update/Dispatch call to immediately process queued events.

A multi threaded solution is in question 1194620, but I would first like to avoid multithreading if possible. In older environments (C++, .Net Winforms/ASP) there were always system calls like Update to interrupt a busy process to handle pending events.

Edit: Please don't tell me that that a lengthy process should be in another thread. I agree. But this is inherited code, and before I would even think about converting to multithreaded, I first need to log certain events to understand what it does. Besides, this app has many other problems that need to be fixed first. Also, after fixing problems, the lengthy process might not be lenghty anymore.

The method of writing strings from anywhere in de code I found in question 18888937 and works fine.

This is the code-behind. Edit: I added the call to the solution in the Accepted Answer.

public partial class App : Application, INotifyPropertyChanged
{
    /// <summary>
    /// Property for the log message for the TextBlock control
    /// </summary>
    public string StartupMessage
    {
        get { return _StartupMessage; }
        set
        {
            if (_StartupMessage.Length == 0)
            {
                _StartupMessage = string.Format("{0:HH-mm-ss} {1}", 
                                          DateTime.Now, value);
            }
            else
            {
                _StartupMessage = string.Format("{0}{1}{2:HH-mm-ss} {3}",
                  _StartupMessage, Environment.NewLine, DateTime.Now, value);
            }
            OnPropertyChanged("StartupMessage");
        }
    }
    private string _StartupMessage = "";

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
            DoEvents();//see the accepted answer below
        }
    }

this is the text control:

<TextBlock x:Name="textblock_StartupMessages" 
             Margin="10" TextWrapping="Wrap" 
  Text="{Binding Path=StartupMessage, Source={x:Static Application.Current}}">
</TextBlock>

and here is how I place messages from another place in the code:

public class AllRoutesViewModel : ViewModelBase
{
    public AllRoutesViewModel()
    {
        (System.Windows.Application.Current as App).StartupMessage = 
                                           "start of AllRoutesViewModel()";
Community
  • 1
  • 1
Roland
  • 4,619
  • 7
  • 49
  • 81
  • 1
    Are you looking for [Dispatcher.PushFrame](http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.pushframe.aspx)? – Scott Solmer Sep 05 '14 at 14:11
  • 2
    Why are you agains't running the lenghty operation in a background thread? – Yuval Itzchakov Sep 05 '14 at 14:12
  • You wouldn't want to do this: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherframe.aspx because it sucks. However, as you seem determined to avoid a multi-threaded approach, I think it's probably your only choice. – spender Sep 05 '14 at 14:14
  • @YuvalItzchakov You are right, but this is inherited code and redesigning it is not trivial. In any case I am now in the analysis phase and need a log tool. Ideally I will fix some bottlenecks and shorten the process, going multi threaded will not make it faster. – Roland Sep 05 '14 at 14:24
  • @Roland I have not implied that making your app *multithreaded* will make it faster. What it *will* do is free up the UI message loop to process incoming messages, such as your `NotifyPropertyChanged` – Yuval Itzchakov Sep 05 '14 at 14:26
  • @spender Why does it suck? Too bad as it sounds like a solution, except that I cannot get it done at first strike. – Roland Sep 05 '14 at 14:28
  • @YuvalItzchakov I agree with you on principles, but I just can't believe that WPF does not have a simple Update command, like older frameworks. In fact, if there isn't, I will rather give up on this and do with a delayed log, as I do have time stamps. – Roland Sep 05 '14 at 14:33
  • @Roland : Why does it suck? Because IMO it's a reversion to the days of co-operative multi-tasking where it's down to the developer to yield in time to allow the UI to update smoothly. Particularly on multicore systems, why not run on a different thread and allow the UI to do its own thing? It's really not hard to marshal calls back to the UI from another thread. When you want to do more with your UI, will you be happy having the framerate driven by this process? – spender Sep 05 '14 at 14:39
  • @Roland Why do you think WPF needs a `Update` command? I see no logical reason for that. That is exactly what background threads are for. You could try prioritizing the queuing of the events using `Dispatcher.BeginInvoke` and passing `DispatcherPriority`, althought it isn't guaranteed to make a difference while you have a long running operation blocking the thread. – Yuval Itzchakov Sep 05 '14 at 14:39
  • @Okuma.Scott could you please put your comment in an Answer so that I can accept it? – Roland Sep 05 '14 at 15:26
  • @YuvalItzchakov An `Update` command is just an alternative for multithreading. This is not religion. There are more ways to Rome. Often you can implement an algorithm with recursion or a loop. If my lengthy process consists of a loop with a zillion cycles, what is wrong with calling `Update` every so many cycles? Why is multithreading `better`? Why is it wrong to have a choice? What if one of the options is not possible? In my case MT is not an option for today, because of unknown inherited code. Thanks anyway for your comments. – Roland Sep 05 '14 at 15:56

2 Answers2

4

avoid multithreading if possible. In older environments (C++, .Net Winforms/ASP) there were always system calls like Update to interrupt a busy process to handle pending events.

This is attempting a design pattern on a system which was designed not to behave like the systems you mentioned.

Long running operations should not be done on the GUI thread in WPF.

Notify property change only works when the GUI thread is not blocked because it is inherently a GUI process. The code you have is blocking the GUI thread. If you properly run the task in a background worker, or an async task and properly update your property, the notify will make the GUI behave visually as you actually want and expect.

But by the design you present, to graphically do this is impossible. The best answer is to learn the WPF design pattern and follow it, instead of forcing a different technologies design pattern.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
  • +1 for your closing sentence. OP should stop trying to bang square pegs into round holes. – spender Sep 05 '14 at 15:03
  • Perfect discussion for the vrijmibo (dutch: vrijdag middag borrel, english: Friday Afternoon Party) with some beer. Avoid MultiThreading: after A. Einstein K.I.S.S. Design pattern: agreed. Impossible: no, it works, thanks Okuma Scott. Redesign of this inherited code: great, if my boss allows me. Code sucks: we all have such experiences. But this is all off-topic. I had a valid question, got a decent solution, saved my day. Have a nice weekend. – Roland Sep 05 '14 at 15:48
  • @Roland It is understood that you are working within the constraints of existing code which at times sadly *trumps* proper design. – ΩmegaMan Sep 05 '14 at 16:17
  • @OmegaMan `sadly` yes, but `gladly` it allows me to earn a living. But you should put this in perspective, the inherited code is much much sadder than this MT/Update issue, while I see fixing problems with this code as playing a great game for a salary. Thanks all of you here for helping me out today and inspiring me to keep up good design. – Roland Sep 05 '14 at 22:41
1

You might consider using Dispatcher.PushFrame.

More information is available about the class here.

Also, here is the relevant code sample from MDSN (slightly modified):

using System.Windows.Threading;  //DispatcherFrame, needs ref to WindowsBase

//[SecurityPermissionAttribute(SecurityAction.Demand, Flags =   SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

While this solution might give you want you want in this case, I have to agree with what others have said about design patterns. Please consider something like MVVM in the future.

Scott Solmer
  • 3,871
  • 6
  • 44
  • 72
  • Thanks, this works. The link is for .Net4.5, but .Net4 is also supported. I added the using. Also, the `SecurityPermissionAttribute` was not recognized in my project, but it works also without, so I commented it out. – Roland Sep 05 '14 at 15:38