Running a WPF control in another thread

后端 未结 2 1763
温柔的废话
温柔的废话 2020-12-08 03:31

I am using a visual control in my project that is from a library that I do not have the source to.
It takes too long to update (200ms, roughly) for good UI responsivenes

相关标签:
2条回答
  • 2020-12-08 03:31

    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.. :)

    0 讨论(0)
  • 2020-12-08 03:46

    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.

    0 讨论(0)
提交回复
热议问题