2012-04-05 33 views
18

Tôi đang sử dụng điều khiển trực quan trong dự án của tôi từ thư viện mà tôi không có nguồn.
Mất quá nhiều thời gian để cập nhật (200 mili giây) cho khả năng phản hồi giao diện người dùng tốt với ba trong số các điều khiển này trên màn hình cùng một lúc. (Tôi có thể cần phải cập nhật cả ba cùng một lúc, khiến giao diện người dùng của tôi bị kẹt cho ~ 600ms trong khi tất cả đều đang suy nghĩ).Chạy điều khiển WPF trong một chủ đề khác

Tôi đã đọc một số bài đăng về TaskSchedulers và bắt đầu điều tra các tính năng nhiệm vụ song song như một cách để chạy từng điều khiển trong chuỗi của riêng chúng. Nền tảng sẽ là đa lõi, vì vậy tôi muốn tận dụng lợi thế của việc xử lý đồng thời.

Vấn đề là tôi thậm chí không biết những gì tôi không biết gì về thế nào để đi về việc này, mặc dù ..

Có một mẫu thiết kế thích hợp để chạy một điều khiển trong một thread riêng biệt từ chủ đề giao diện người dùng chính trong WPF?

Cụ thể: đây là điều khiển bản đồ của bên thứ ba, khi được cấp vị trí mới hoặc mức thu phóng mất quá nhiều thời gian để vẽ lại (~ 200 mili giây). Có lẽ ba trong số những cập nhật này ở mức tối đa 4Hz - rõ ràng là chúng sẽ không theo kịp ..
Tôi đã đóng gói điều khiển WPF trong một usercontrol và cần chạy từng cá thể trong chuỗi riêng của nó, trong khi vẫn giữ lại đầu vào của người dùng (ví dụ như nhấp chuột).

CẬP NHẬT: trong khi tôi cảm thấy xung quanh để có giải pháp, tôi đã thực hiện các bước sau cho đến nay.
Chủ đề chính (Giao diện người dùng) của tôi sinh ra một chuỗi tạo cửa sổ mới chứa điều khiển được đề cập và xác định vị trí chính xác (để có vẻ như nó chỉ là điều khiển bình thường).

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

đâu CommandQueue là một Queue Thread-safe BlockingCollection để gửi lệnh vào bản đồ (di chuyển vị trí, vv).
Vấn đề bây giờ là tôi có thể hoặc

  • có đầu vào người sử dụng do System.Windows.Threading.Dispatcher.Run() gọi
  • hoặc chặn trên CommandQueue, nghe cho các lệnh được gửi bởi các chủ đề chính

tôi không thể quay chờ lệnh, bởi vì nó sẽ hấp thụ tất cả các CPU thread của tôi!
Có thể chặn máy bơm thông báo sự kiện có hoạt động không?

+0

điều khiển Tất cả WPF phải được cập nhật trên thread UI. Tuy nhiên, chúng tôi có thể trợ giúp nếu bạn cung cấp một số chi tiết về điều khiển bạn đang sử dụng và bất kỳ mã nào bạn đã viết để cập nhật/điền vào nó. –

+0

@CameronPeters, bạn có chắc chắn không thể có nhiều hơn một “chuỗi giao diện người dùng”? – svick

+0

Hiện tại, tôi có nhiều luồng 'UI' đang chạy (nhờ Threading.Dispatcher.Run()), nhưng không thể chặn chúng chờ tín hiệu. – DefenestrationDay

Trả lời

11

Vâng, tôi có một phương pháp mà làm việc - nhưng nó có thể cũng không phải là thanh lịch nhất ..

Tôi có một cửa sổ chứa bên thứ ba của tôi (chậm rendering) kiểm soát trong XAML.

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

    // ... 

} 

chính của tôi (UI) contructs chủ đề và bắt đầu cửa sổ này trên một sợi:

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

sau đó tôi nhận được một tay cầm vào cửa sổ trong chủ đề (sau khi nó đã được khởi tạo):

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

.. và sau khi đặt lệnh lên hàng đợi an toàn chủ đề được chia sẻ với cửa sổ luồng:

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

.. Tôi để cho nó biết nó có thể kiểm tra hàng đợi:

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

Cửa sổ có thể nhận được thông điệp của tôi bởi vì nó đã nối vào cửa sổ tin nhắn:

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

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

đồi khế, đồi nó rồi có thể kiểm tra:

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

..và sau đó thực hiện trên chuỗi!

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

Dễ dàng như vậy .. :)

+1

nó thực sự là một công việc tuyệt vời bạn đã làm .. đánh giá cao ở đây –

5

Tôi đã nhìn vào điều này là tốt, và các thông tin liên quan nhất mà tôi có thể tìm được trong bài viết trên blog này (tuy nhiên tôi đã không kiểm tra nó chưa):

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

nó tạo ra một HostVisual trên thread UI, sau đó quay lên một chủ đề nền, tạo ra một MediaElement, đặt nó bên trong một VisualTarget (mà trỏ trở lại HostVisual), và đặt nó tất cả bên trong VisualTargetPresentationSource của chúng tôi.

Vấn đề với phương pháp này dường như người dùng sẽ không thể tương tác với các điều khiển đang chạy trong chuỗi mới.

Các vấn đề liên quan