0

I have a trivially simple window:

<Window x:Class="Prestress.UI.StatusWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:Prestress.UI"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        SizeToContent="WidthAndHeight"
        WindowStyle="None"
        ShowInTaskbar="False"
        WindowStartupLocation="CenterOwner">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Content="{Binding Path=StatusText, Source={x:Static l:ProjectProperties.Instance}}"/>
    </Grid>
</Window>

with an equally trivial code-behind:

public partial class StatusWindow : Window
{
    public StatusWindow()
    {
        Topmost = true;
        InitializeComponent();
    }
}

This window is just meant to show some messages to the user while the program runs in the background. The only instance of this class is contained within the following:

public sealed class ProjectProperties : DependencyObject, INotifyPropertyChanged
{
    static ProjectProperties instance = new ProjectProperties();
    public static StatusWindow Status = new StatusWindow();
    string statusText;
    public static ProjectProperties Instance { get { return instance; } }
    public string StatusText
    {
        get { return statusText; }
        set
        {
            statusText = value;
            EmitPropertyChanged("StatusText");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void EmitPropertyChanged(string property)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public static void UpdateStatusWindow(string s)
    {
        Instance.StatusText = s;
    }
    public static void ShowStatusWindow()
    {
        Status.InitializeComponent();
        Status.Show();
    }
}

Where ProjectProperties is a singleton class which contains a static copy of this window.

The first message it must present is "Validating data". This is done by calling

ProjectProperties.UpdateStatusWindow("Validating data.");
ProjectProperties.ShowStatusWindow();

The result is this

blank window

However, this window is soon followed by a MessageBox which complains about an error in what the user inputted. When this MessageBox appears, the window is refreshed, showing the desired text.

updated window

This seems odd to me. As can be seen, even when blank the window has already resized itself so that the text will fit. All that's missing is to paint the text. My first attempt didn't even include the .InitializeComponent() line when calling .ShowStatusWindow(), but then I saw this and tried adding it, to no success. Any ideas on what's going on here?

Community
  • 1
  • 1
Wasabi
  • 2,879
  • 3
  • 26
  • 48
  • `Any ideas on what's going on here?` - Yes. Your code is completely wrong. First of all you're probably blocking the UI thread with other code which is why your message never appears. Second you don't inherit from DependencyObject and implement `INotifyPropertyChanged` at the same time. Your ProjectProperties class suffers from thread affinity and thus it's useless. Remove the DependencyObject inheritance and do your background stuff in a background thread, using async await. – Federico Berasategui Sep 03 '14 at 02:50
  • 1
    Looking at your code again, it doesn't even compile. Please provide real code or at least code that resembles the real code. – Federico Berasategui Sep 03 '14 at 02:52
  • The `INotifyPropertyChanged` is implemented trivialy as well. `public void EmitPropertyChanged(string property) { PropertyChanged(this, new PropertyChangedEventArgs(property)); }` (where **PropertyChanged** is the EventHandler). There's basically zero error-checking, but this is super-preliminary code. – Wasabi Sep 03 '14 at 02:55
  • I've updated the OP with a direct copy-paste of `ProjectProperties`. Also, the class also inherits `DependencyObject` because I understand that's the best way of dealing with **ICollectionView** members (which have been removed here because they have nothing to do with the issue at hand) via `Get/SetValue`. – Wasabi Sep 03 '14 at 03:05
  • 1
    your code doesn't make much sense to me. The purpose of DataBinding is to decouple the business logic from the UI. You're coupling your ProjectProperties class to the UI by having it reference StatusWindow. Why don't you either choose to do it completely right or completely wrong? you're doing 50/50 right now. Anyways, your problem is caused by not using proper threading. Use async await on your background code. – Federico Berasategui Sep 03 '14 at 03:08

2 Answers2

0

You are probably "Validating Data" on your UI thread. If this validation is a heavy, the UI thread gets busy doing the validation and is not able to render UI (it's primary purpose).

You could show the message window with "Validating Data" then start a background thread (see BackgroundWorker) for your validation and finally in it's RunWorkerCompleted event update your UpdateStatus() showing "Validation complete" (or what ever suits you). This will not block the UI thread and should solve your message text not being displayed issue.

bit
  • 4,407
  • 1
  • 28
  • 50
  • I disagree with the second part. The OP's singleton implementation seems fine. Also known as ["Jon Skeet Singleton #4"](http://csharpindepth.com/articles/general/singleton.aspx). – Federico Berasategui Sep 03 '14 at 03:18
  • @HighCore .That was an interesting read.. thanks! Agreed and updated the answer. – bit Sep 03 '14 at 03:26
0

You code itself looks fine to me. However, the key part it does not show is that it must use the GUI thread to do some validation work behind which takes some time. And because of this the GUI thread is blocked until all the work is finished then the GUI is updated.

In order to achieve what you want you need to use another thread to do the validation so that the GUI thread can go ahead to update the GUI without being blocked.

You can either use BackgroundWorker as suggested by others which is a much more robust option, or simply use Dispatcher.Invoke() from the validation thread to update the GUI.

Stephen Zeng
  • 2,748
  • 21
  • 18