0

I have a seemingly simple issue: a popup that's causing me trouble looks like this in a stripped-down version (the complete version has a spinner UserControl within the Border element, but the strange behavior is the same with or without). The XAML is this:

<Popup Name="PleaseWaitPopup" Placement="Center" IsOpen="False" StaysOpen="True" Opened="PopupOpened" Closed="PopupClosed">
    <Border Width="200" Height="200" Padding="20" Background="#222">
        <StackPanel Orientation="Vertical">
            <TextBlock Name="WaitHeadTxt" Margin="0 0 0 36" Style="{StaticResource PopupHeadStyle}" VerticalAlignment="Top" FontSize="16"></TextBlock>
            <Border Width="60" Height="60">
            </Border>
        </StackPanel>
    </Border>
</Popup>

All of the elements (PopupOpened(), PopupClosed(), PopupHeadStyle) are well tested and work fine in lots of other popups within the same project.

In response to a user action, I want to open this popup before starting something that takes several seconds to complete (trying to hook up a device via WiFi). The code is again simple:

PleaseWaitPopup.IsOpen = true;
try
{
    wifiDeviceProvider = new PtpIpProvider();
    DeviceManager.AddDevice(wifiDeviceProvider.Connect("192.168.1.1"));
}
catch (Exception ex)
{
...
}

In my test case, I don't connect an external device, so the WiFi connection attempt comes back with a timeout after 10s. The popup consistently only opens AFTER the timeout, which I don’t get. No other popup is open at this point.

I tried this with other action code (an FTP transfer rather than the WiFi connection) - the issue remained, so it's unlikely that the WiFi connection code has anything to do with this. Tried making things asynchronous by opening the popup, or the WiFi connection, or both, in a separate thread via "this.Dispatcher.Invoke(() => { ... });" , but none of this made any difference.

Any ideas what I am missing here? Must be something silly, but I can't seem to figure it out. Thanks!

Jim
  • 2,974
  • 2
  • 19
  • 29
Lon
  • 73
  • 2
  • 11

1 Answers1

0

Your WiFi code looks to be blocking the UI thread before it gets chance to update the UI. So it updates once it returns from that (i.e. the timeout).

As a basic workaround you could use something like:

PleaseWaitPopup.IsOpen = true;

Task.Run(() =>
{
    try
    {
        wifiDeviceProvider = new PtpIpProvider();
        DeviceManager.AddDevice(wifiDeviceProvider.Connect("192.168.1.1"));
    }
    catch (Exception ex)
    {
        ...
    }
});

Be aware that catch block is then not on the UI thread though, so don't try updating the UI in there without targetting the UI thread. Also be aware that the task will return immediately in that code - you'd need to make some changes if wanted to await the task.

Ideally you'd want to go full async. I'd recommend reading Stephen Cleary's articles for more information on async best practices. e.g. Async/Await - Best Practices in Asynchronous Programming

Tone
  • 1,701
  • 1
  • 17
  • 18
  • Guess I really need to read up on async programming. Coming from a sequential programming background, it's anything but intuitive to me why a subsequent action would block a preceding UI update, especially a single-command one, even though I understand it triggers a flurry of consecutive actions. The Task.Run approach still won't solve my problem fully as the popup will open but the spinner (for the same reason) won't spin, but, as I said, I don't understand async programming. – Lon Oct 31 '16 at 13:43
  • As I understand it, Windows apps work with message queues, so although your subsequent action comes after it has been **added** to the queue, the message processing isn't immediate. So you're setting `IsOpen`, but then locking the UI thread with the WiFi calls before Windows gets chance to process it. Another alternative is to use a continuation task - see [this answer](http://stackoverflow.com/questions/4331262/task-continuation-on-ui-thread/12335614#12335614). You'd start your spinner before the `Task.Run`, then in the continuation task you'd mark it as finished (e.g. disable the spinner). – Tone Oct 31 '16 at 18:21