2010-10-06 30 views
5

Trong ứng dụng WPF của tôi, tôi lưu trữ nội dung Win32 bằng HwndHost. Tuy nhiên, việc tạo một HwndHost không tạo ra cửa sổ gốc. Thay vào đó, điều này được thực hiện trong phương thức BuildWindowCore() bị ghi đè được WPF gọi một vài lần sau đó.Buộc khởi tạo một HwndHost

Nội dung được lưu trữ của tôi cần xử lý cửa sổ của cửa sổ gốc để khởi tạo riêng. Thật không may, không có cách nào tôi có thể buộc việc tạo ra các cửa sổ (tức là có WPF gọi BuildWindowCore), vì vậy tôi có một thread thứ hai mà thăm dò ý kiến ​​HwndHost cho đến khi nó đã được khởi tạo.

Trong .NET 4.0/WPF 4.0, một phương thức mới WindowInteropHelper.EnsureHandle() đã được thêm vào. Tôi đã hy vọng điều này sẽ giải quyết tình hình, nhưng nó chỉ hoạt động cho một cửa sổ, không phải là một HwndHost (mà không xuất phát từ cửa sổ). Bạn có một gợi ý những gì tôi có thể làm thay vào đó?

EDIT:

Tôi quên thêm một số khó khăn hơn cho một giải pháp khả thi:

  1. Các HwndHost được đặt trong một điều khiển mà, tùy thuộc vào thiết lập người dùng, có thể là một đứa trẻ của ứng dụng của chính cửa sổ hoặc có thể được đặt trong một Cửa sổ mới (thông qua trình quản lý ghép nối của bên thứ ba). Điều này có nghĩa là trong quá trình tạo cửa sổ, tôi không biết chắc chắn cửa sổ cha mẹ (và do đó nó sẽ là hWnd).
  2. Trong khi mã gốc cần hWnd trong quá trình khởi tạo, cửa sổ chỉ được hiển thị khi người dùng yêu cầu hiển thị (tức là lúc đầu vô hình). Cần hiển thị cửa sổ, chỉ để ẩn nó ngay lập tức, nên tránh, nếu có thể.

Trả lời

3

Dường như không có giải pháp hoàn hảo nào. Tôi đã thay đổi cách tiếp cận của mình một chút so với thời điểm câu hỏi được hỏi:

Trong hàm tạo của lớp dẫn xuất HwndHost, tôi có (có thể) cha mẹ hWnd làm một trong các tham số. Sau đó tôi tạo cửa sổ gốc bằng cách sử dụng phương thức gốc CreateWindow(), sử dụng phụ huynh đã cho hWnd. Tôi lưu trữ hWnd được tạo ra trong một thuộc tính riêng biệt, mà tôi sử dụng ở khắp mọi nơi thay vì thuộc tính Xử lý của HwndHost. Bằng cách đó, tôi không cần phải hiển thị cửa sổ (điều này giải quyết ràng buộC# 2).

Trong phương pháp được ghi đè BuildWindowCore(), tôi so sánh cha mẹ được cung cấp hWnd với phương pháp tôi đã đưa ra trong hàm tạo. Nếu chúng khác nhau, tôi sửa lại cửa sổ được lưu trữ của tôi bằng phương thức gốc SetParent() (điều này giải quyết ràng buộC# 1). Lưu ý rằng điều này phụ thuộc vào không ai lưu trữ phụ huynh hWnd!

Trong mã, các bộ phận có liên quan (kiểm tra bỏ qua):

public class Win32Window : HwndHost 
{ 
    public Win32Window(IntPtr parentHwnd) 
    { 
     this.ParentHwnd = parentHwnd; 
     this.Win32Handle = NativeMethods.CreateWindowEx(/* parameters omitted*/); 
    } 

    public IntPtr Win32Handle { get; private set; } 
    private IntPtr ParentHwnd { get; set; } 

    protected override HandleRef BuildWindowCore(HandleRef hwndParent) 
    { 
     if (hwndParent.Handle != this.ParentHwnd) 
     { 
      NativeMethods.SetParent(this.Win32Handle, hwndParent.Handle); 
     } 

     return new HandleRef(this, this.Win32Handle); 
    } 
} 
1

Tôi có một tình huống tương tự và tôi giải quyết nó bằng cách làm như sau:

1) Tạo một lớp có nguồn gốc HwndHost mà phải mất một hình chữ nhật như một đối số nhà xây dựng (sau này được sử dụng trong BuildWindowCore):

public class MyHwndHost : HwndHost 
{ 
    public MyHwndHost(Rect boundingBox) 
    { 
     BoundingBox = boundingBox; 
    } 
} 

2) Tạo một cửa sổ WPF với một yếu tố biên giới trẻ:

<Window Loaded="Window_Loaded"> 
    <Border Name="HostElement" /> 
</Window> 

3) Tạo dụ HwndHost và thêm nó vào th e cửa sổ trong xử lý Window_Loaded:

void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
    MyHwndHost host = new MyHwndHost(LayoutInformation.GetLayoutSlot(HostElement)); 
    HostElement.Child = host; 
} 

4) Cũng trong xử lý Window_Loaded, vượt qua HWND để khởi tạo của lớp mẹ đẻ của bạn hoặc thông qua P/Invoke hoặc C++/CLI. Tôi có lớp bản địa của tôi được thiết lập để sử dụng HWND đó làm cha mẹ của nó và nó tạo ra HWND của riêng mình khi còn nhỏ.

+0

Có hai vấn đề: 1) Tôi không biết phụ huynh hWnd, vì điều khiển sau đó được trình quản lý docking của bên thứ ba định vị và cài đặt người dùng được lưu trữ xác định xem nó có được hiển thị hay không. con "của cửa sổ chính. 2) Việc kiểm soát với HwndHost có thể không được hiển thị ban đầu (tùy thuộc vào cài đặt người dùng được lưu trữ), nhưng khi khởi động mã kế thừa cần hWnd. –

+0

Bạn sẽ có thể móc vào sự kiện được tải trên điều khiển của bạn và thực hiện tất cả khởi tạo tại đó: http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.loaded.aspx. Nếu mã di sản cần hwnd thì bạn chỉ cần giữ nguyên làm bất cứ điều gì với mã di sản cho đến khi hwnd đã sẵn sàng (đó là những gì tôi phải làm). –

+0

Trích dẫn từ liên kết: "Xảy ra khi phần tử được đặt ra, được hiển thị và sẵn sàng cho tương tác". Nếu tôi không hiển thị điều khiển, Đã tải sẽ không kích hoạt. –

0

Một chút trễ, nhưng bạn đã thử gọi UpdateLayout() về kiểm soát lưu trữ chưa? Điều đó phù hợp với tôi

+0

Thật không may, điều đó chỉ hoạt động nếu điều khiển lưu trữ hiển thị. Đó chính là vấn đề mà EnsureHandle() giải quyết, nhưng nó không tồn tại cho HwndHosts. –

0

tôi đã thêm sự kiện OnHandleCreated để HwndHost lớp -inherited của tôi, trong đó có tay cầm IntPtr. Sự kiện này được gọi bên trong BuildWindowCore().

Vì vậy, nó nắm tới:

public class Win32WindowHost : HwndHost { ... }

var host = new Win32WindowHost(); 
host.OnHandleCreated += (sender, e) => 
{ 
    var handle = e.Handle; 
    // Do stuff. 
}; 

Làm việc một điều trị.

+0

Thật không may, điều này không hoạt động trong trường hợp của tôi. BuildWindowCore() chỉ được gọi khi HwndHost được hiển thị. Không có cách nào khác để buộc việc tạo ra hwnd bản địa (tức là buộc WPF gọi BuildWindowCore()). Điều này chỉ có thể cho một cái gì đó có nguồn gốc từ Window, thông qua WindowInteropHelper.EnsureHandle(). –

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