0
votes

I am developing a Windows Form that will take user's latitude and longitude, perform some long calculations and display user's location and some routes on the map. The map will be shown in a separate WPF window using Bing Maps API. Since the calculations that need to be performed after user enters latitude and longitude are complicated and take a lot of time, I need to perform those calculations in a background thread and call new WPF window from this background thread.

At first, I wasn't able to open WPF window from a background thread because of thread affinity, but I fixed it after setting apartment state of the thread to ApartmentState.STA. Here is the procedure that gets called after user enters latitude and longitude and presses on a button on Winform:

private void button1_Click(object sender, EventArgs e)
{
    Thread t1 = new Thread(foo);
    t1.SetApartmentState(ApartmentState.STA);
    t1.IsBackground = true;
    t1.Start();
}

The procedure that gets executed when thread starts is foo(). I have read on the forums that I need to call System.Windows.Threading.Dispatcher.Run() so that WPF window doesn't close as soon as thread finishes:

private void foo()
{
    MainWindow window = new MainWindow(lat.Text, lon.Text);
    window.Show();
    window.Closed += (sender2, e2) => window.Dispatcher.InvokeShutdown();
    System.Windows.Threading.Dispatcher.Run();
}

WPF window is called MainWindow. Here is its code:

public partial class MainWindow : Window
{
    public MainWindow(double latitude, double longitude)
    {
        InitializeComponent();

        myMap123.Focus();

        //Set the map mode to Aerial with labels
        myMap123.Mode = new AerialMode(true);
    }
}

Finally, in case some of you need xaml file for WPF window, here it is:

<Window x:Class="WPFTestApplication.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF" 
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <m:Map CredentialsProvider="KEY" x:Name="myMap123"/>
    </Grid>
</Window>

When I input some latitude and longitude and press on the button, WPF window does appear, but the Bing Map doesn't show world map. Instead, background is grey, and if I try to zoom, or change the size of window, System.NullReferenceException (Object reference not set to an instance of an object) gets thrown. Debugger tells me that exception is thrown at line System.Windows.Threading.Dispatcher.Run();. However, if I do not try to center the map and don't set map to Aerial mode, everything works fine. What should I do so that I can call WPF window from background thread and change Bing Map's properties at runtime without any problems? I already checked that everything would have worked fine if WPF window was getting called from the main UI thread. Interestingly, if I close WPF window and press on the button again, new WPF window shows Bing Maps normally and System.NullReferenceException doesn't get thrown.

1
Why do you want to display the new window in a new thread? Why not just invoke via Dispatcher back onto the main WPF UI thread when creating the new window?Peter Duniho
well, how would the main thread know when background thread is done with its calculations? Wouldn't that require the user to press on some other button to display the map once background thread is finished?user3623874
That depends on the Map object. But worst-case scenario, you run the "calculations" operation on a background thread (i.e. via Task<T>) and then when the operations complete, use Dispatcher.Invoke() to notify the main thread. Unfortunately, your code example doesn't actually show how this "calculations" operation occurs, so it's not possible to answer in a specific way.Peter Duniho

1 Answers

0
votes

I ended up calling BeginInvoke() on one of my UI elements. It allowed me to make a thread that created that UI element to execute a function that I pass as an argument.