2012-04-05 2 views
18

Я использую визуальный контроль в своем проекте, который из библиотеки, к которой у меня нет источника.
Требуется слишком много времени, чтобы обновить (примерно 200 мс) для хорошей реакции интерфейса с тремя из этих элементов управления на экране одновременно. (Мне может потребоваться обновить все три сразу, что оставляет мой пользовательский интерфейс застрял на ~ 600 мс, пока они все думают).Запуск WPF-управления в другом потоке

Я прочитал несколько сообщений о TaskSchedulers и начинаю исследовать функции задачи Parallel как способ запуска каждого из этих элементов управления в своем потоке. Платформа будет многоядерной, поэтому я хочу воспользоваться одновременной обработкой.

Проблема заключается в том, что я даже не знаю, что я не знаю о том, как идти об этом, хотя ..

Есть ли подходящий шаблон дизайна для запуска элемента управления в отдельном потоке из основной поток пользовательского интерфейса в WPF?

В частности: это элемент управления третьей стороной, что при заданном новом местоположении или уровне масштабирования слишком много времени для перерисовки (~ 200 мс). Возможно, три из этих обновлений не превышают 4 Гц - очевидно, что они не будут поддерживать вверх.
Я инкапсулировал элемент управления WPF в usercontrol и должен запускать каждый экземпляр в своем собственном потоке, сохраняя при этом вход пользователя (щелчки мыши, например).

ОБНОВЛЕНИЕ: В то время как я чувствую себя вокруг решения, я реализовал следующее до сих пор.
My main (UI) thread создает поток, который создает новое окно, содержащее данный элемент управления, и находит его в правильном положении (так что это выглядит как обычный контроль).

_leftTopThread = new Thread(() => 
{ 
    _topLeftMap = new MapWindow() 
    { 
     WindowStartupLocation = WindowStartupLocation.Manual, 
     Width = leftLocation.Width, 
     Height = leftLocation.Height, 
     Left = leftLocation.X, 
     Top = leftLocation.Y, 
     CommandQueue = _leftMapCommandQueue, 
    }; 

    _topLeftMap.Show(); 
    System.Windows.Threading.Dispatcher.Run(); 

}); 

_leftTopThread.SetApartmentState(ApartmentState.STA); 
_leftTopThread.IsBackground = true; 
_leftTopThread.Name = "LeftTop"; 
_leftTopThread.Start(); 

Где CommandQueue является Thread-safe BlockingCollection очереди для отправки команд на карте (перемещение местоположение и т.д.).
Проблема теперь, что я могу либо

  • есть ввод данных пользователем в связи с System.Windows.Threading.Dispatcher.Run() вызова
  • или блока на CommandQueue, прослушивание команд, посылаемых в главном потоке

I не может открутить ожидание команд, потому что он впитал бы весь мой процессор потока!
Возможно ли заблокировать и?

+0

управления Все WPF должны быть обновлены в потоке пользовательского интерфейса. Тем не менее, мы можем помочь, если вы предоставите некоторые сведения об используемом элементе управления и любом коде, который вы написали, чтобы обновить/заполнить его. –

+0

@CameronPeters, вы уверены, что не может быть более одного «потока пользовательского интерфейса»? – svick

+0

В настоящее время у меня несколько потоков «UI» (благодаря Threading.Dispatcher.Run()), но они не могут заблокировать их, ожидая сигналов. – DefenestrationDay

ответ

11

Ну, у меня есть метод, который работает - но это может быть и не самым элегантным ..

У меня есть окно, которое содержит мою третью сторону (медленно-рендеринг) управления в XAML.

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

    // ... 

} 

Моей главная (UI) contructs нити и запускает окно на волоске:

_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(); 

Затем я получить дескриптор окна в потоке (после того, как он инициализирован):

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; 
    } 
} 

.. и после ввода команды на потокобезопасное очереди, которая совместно с резьбовым окном:

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

.. Я дал ему знать, он может проверить очереди:

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

окно может получить мое сообщение, потому что он зацепил в окне сообщений:

protected override void OnSourceInitialized(EventArgs e) 
{ 
    base.OnSourceInitialized(e); 

    _source = PresentationSource.FromVisual(this) as HwndSource; 
    if (_source != null) _source.AddHook(WndProc); 
} 

..which это тогда может проверить:

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; 
} 

..и затем выполнить на резьбе!

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; 
     } 
    } 
} 

Легко, как .. :)

+1

это действительно отличная работа, которую вы сделали .. такие вещи редко бывают оценили здесь –

5

Я смотрел на это, а также и наиболее актуальной информация, которую я смог найти в этом блоге (однако я не проверял еще):

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

это создает HostVisual в потоке пользовательского интерфейса, затем закручивает фоновый поток, создает MediaElement, помещает его внутри VisualTarget (который указывает на HostVisual) и помещает все это в наш хакерский VisualTargetPresentationSource.

Проблема с этим методом заключается в том, что, по-видимому, пользователь не сможет взаимодействовать с элементами управления, которые работают в новом потоке.