Running a WPF control in another thread Running a WPF control in another thread wpf wpf

Running a WPF control in another thread


Well, I have a method that works - but it may well not be the most elegant..

I have a window that contains my third party (slow-rendering) control in the XAML.

public partial class MapWindow : Window{    private ConcurrentQueue<MapCommand> _mapCommandQueue;    private HwndSource _source;    // ...}

My main (UI) thread contructs and starts this window on a thread:

_leftTopThread = new Thread(() =>{   _topLeftMap = new MapWindow()   {      WindowStartupLocation = WindowStartupLocation.Manual,      CommandQueue = _leftMapCommendQueue,   };    _topLeftMap.Show();    System.Windows.Threading.Dispatcher.Run();});_leftTopThread.SetApartmentState(ApartmentState.STA);_leftTopThread.IsBackground = true;_leftTopThread.Name = "LeftTop";_leftTopThread.Start();

I then get a handle to the window in the thread (after it has initialised):

private IntPtr LeftHandMapWindowHandle{    get    {        if (_leftHandMapWindowHandle == IntPtr.Zero)        {            if (!_topLeftMap.Dispatcher.CheckAccess())            {                _leftHandMapWindowHandle = (IntPtr)_topLeftMap.Dispatcher.Invoke(                  new Func<IntPtr>(() => new WindowInteropHelper(_topLeftMap).Handle)                );            }            else            {                _leftHandMapWindowHandle = new WindowInteropHelper(_topLeftMap).Handle;            }        }        return _leftHandMapWindowHandle;    }}

.. and after putting a command onto the thread-safe queue that is shared with the threaded window:

var command = new MapCommand(MapCommand.CommandType.AircraftLocation, new object[] {RandomLatLon});_leftMapCommendQueue.Enqueue(command);

.. I let it know it can check the queue:

PostMessage(LeftHandMapWindowHandle, MapWindow.WmCustomCheckForCommandsInQueue, IntPtr.Zero, IntPtr.Zero);

The window can receive my message because it has hooked into the window messages:

protected override void OnSourceInitialized(EventArgs e){    base.OnSourceInitialized(e);    _source = PresentationSource.FromVisual(this) as HwndSource;    if (_source != null) _source.AddHook(WndProc);}

..which it then can check:

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) // {    // Handle messages...    var result = IntPtr.Zero;    switch (msg)    {        case WmCustomCheckForCommandsInQueue:            CheckForNewTasks();            break;    }    return result;}

..and then execute on the thread!

private void CheckForNewTasks(){    MapCommand newCommand;    while (_mapCommandQueue.TryDequeue(out newCommand))    {        switch (newCommand.Type)        {            case MapCommand.CommandType.AircraftLocation:                SetAircraftLocation((LatLon)newCommand.Arguments[0]);                break;            default:                Console.WriteLine(String.Format("Unknown command '0x{0}'for window", newCommand.Type));                break;        }    }}

Easy as that.. :)


I've been looking into this as well, and the most relevant information I could find was in this blog post (however I have not tested it yet):

http://blogs.msdn.com/b/dwayneneed/archive/2007/04/26/multithreaded-ui-hostvisual.aspx

it creates a HostVisual on the UI thread, then spins up a background thread, creates a MediaElement, puts it inside a VisualTarget (which points back to the HostVisual), and puts it all inside our hacky VisualTargetPresentationSource.

The problem with this method is that apparently the user won't be able to interact with the controls that are running in the new thread.