Announcement

Collapse
No announcement yet.

Einbettung eines fremden Fensters (Re-parenting) in eine CustomTaskPane

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Einbettung eines fremden Fensters (Re-parenting) in eine CustomTaskPane

    Hallo,

    ich habe eine Custom Task Pane, das mit einem UserControl erstellt wird.
    Das UserControl enthällt u.a. ein Panel, in das ich ein Window einer anderen Applikation integrieren will (Re-parenting).

    Mit Hilfe der Win32 Api und "SetParent(hndl, handl) gelingt mir das auch.

    Leider lässt sich das Fenster nicht so einpassen wie ich das gerne hätte. Folgende Probleme treten aktuell noch auf.

    a) Das Window soll innerhalb des Panel maximiert werden. Es findet zwar eine maximierung statt, aber das Panel wird nur in der Breite voll ausgefüllt, nicht aber in der Höhe.

    b) Ich möchte, dass wenn die CustomTaskPane verbreitert wird, das Window wieder passend in das Panel der UserControl eingepasst wird. Wenn ich aber versuche dies innerhalb des Resize-EventListerners zu machen, "vibriert" die ganze CustomTaskPane und lässt sich kaum noch bedienen. Evtl. ein Fokus-Problem?! Oder es entsteht eine Art Schleife, weil der Event während des Ziehens ständig ausgelöst wird.

    Ein Resize-End Event gibt es nur bei einer Form, aber das UserControl wird ja direkt in die CTP eingebettet.

    c) Wie kann ich verhindern, dass der User das Window innerhalb des Panel verschiebt?

    d) Obwohl ich für die CTP bzw das UserControl mindest-Werte für Breite und Höhe gesetzt habe, wird dies von der CTP scheinbar ignoriert.

    Viele Fragen, ich hoffe ihr könnt die eine oder andere lösen oder mir ne Anregung geben.

    Gruß
    RZ

  • #2
    Probleme gelöst...

    Hallo,

    konnte sämtliche Probleme lösen.

    Für die, die es interessiert:

    a) Nutze die Win32 Funktion: "SetWindowPos". Dazu hole ich mir das Handle des einzubettenden Windows und dann die Größe (Rectangle) des Panel, in die das Fenster eingebettet werden soll.

    Weiterhin nutze ich die Funktion Win32 'SetWindowLong', um dem Fenster die Titelleiste, Maximize-Box, usw. zu entfernen. Dann setze ich für das Fenster noch die Child Eigenschaften. (sämtliche Win32 Funktionen und Flags sind unten deklariert)

    Code:
    //...
    ctrlPanel = myUserControl1.Controls.Find("panelCenter", false)[0];
    IntPtr panelHandle = ctrlPanel.Handle;
    //...
    childHandle = FindWindow(null, "Window_name"); // MainWindowHandle;
    myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl1, "TaskPane_name");
    myCustomTaskPane.Width = 290;
    ctpWidth = myCustomTaskPane.Width;
    myUserControl1.Width = ctpWidth;
    ctrlPanel.Width = ctpWidth;
    oldFocus=SetFocus(childHandle);
    //*********** Store current window styles for restoration ************
    originalStyle = GetWindowLong(childHandle, WS_Styles);
    originalExStyle = GetWindowLong(childHandle, GWL_EXSTYLE);
    //*********** set new window styles **********************************
    SetWindowLong(childHandle, GWL_EXSTYLE, GetWindowLong(childHandle, GWL_EXSTYLE) | WS_EX_MDICHILD);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) | WS_CHILD);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) & ~WS_POPUP);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) & ~WS_CAPTION);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) & ~WS_SYSMENU);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) & ~WS_BORDER);
    SetWindowLong(childHandle, WS_Styles, GetWindowLong(childHandle, WS_Styles) & ~WS_SIZEBOX);
    //!!! Do not use: SetWindowLongA((int)childHandle, WS_Styles, GetWindowLongA(childHandle, WS_Styles) | WS_MAXIMIZE);
    // SetWindowLong needs SetWindowPos to take effekt of the changes
    SetWindowPos(childHandle, IntPtr.Zero, panelRect.X, panelRect.Y, panelRect.Width, panelRect.Height, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
    System.Windows.Forms.Application.DoEvents();
    oldParentForChild = SetParent(childHandle, panelHandle);
    //*********** maximize inner window (child window)****************
    panelRect = ctrlPanel.ClientRectangle;
    SetWindowPos(childHandle, IntPtr.Zero, panelRect.X, panelRect.Y, panelRect.Width, panelRect.Height, SWP_FRAMECHANGED | SWP_ASYNCWINDOWPOS | SWP_NOACTIVATE); //
    ctrlPanel.Refresh();
    b + c + d)
    Probleme machte der resize Event. For allem die eingestellte Mindestgröße für das Panel/UserControl verhinderte, dass resize Events gefeuert wurden, wenn das Fenster(die CTP) schmaler war (die CTP übergeht sämtliche Mindesteinstellungen, so dass das größere UserControl in der kleineren CTP steckte).

    Also habe ich im resize-Event eine Abfrage, die beim Unterschreiten der Mindest-breite die CTP auf das definierte Minimum zurück setzt. Um Probleme mit der Benutzbarkeit des einzubettenden Fensters zu lösen, wird vor dem re-parenting der Fokus (s.o.) auf das einzubettende Fenster gesetzt.

    Ein weiteres größeres Problem bereitete der Fokus-Verslust beim resizen. Das Flag 'NoActivate' in SetWindowPos verhindert, dass während des resize das UserControl oder das Panel den Fokus bekommt und der Resize-Event alle paar Pixel abbricht (verursachte das 'vibrieren').

    Das der User das Fenster nachträglich verändert/verschiebt wird durch die Flag-Einstellungen aus a) verhindert.

    Hier die Event-Methode(Das abschliessende Refresh sorgt dafür, dass Artefakte oder Grafikfehler, die durch das resizing entstehen können korrigiert werden):

    Code:
    // entweder für den resize-Event oder den Size-Changed
    void myUserControl1_SizeChanged(object sender, EventArgs e) {
         if(myCustomTaskPane.Width<235){
              myCustomTaskPane.Width=235;
              myUserControl1.Refresh();
              return;
         }
             
         panelRect = ctrlPanel.ClientRectangle;
         SetWindowPos(childHandle, IntPtr.Zero, panelRect.X, panelRect.Y, panelRect.Width, panelRect.Height, SWP_FRAMECHANGED | SWP_NOACTIVATE); //SWP_NOZORDER | SWP_FRAMECHANGED |
         myUserControl1.Refresh();
    }
    Abschließend hier noch die eingebundenen Win32-Funktionen und Flag-Werte (Diese müssen Bitweise verknüpft werden!):

    Code:
    #region DLL-Import (win32)
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, ExactSpelling = true)]
            public static extern IntPtr SetParent(IntPtr child, IntPtr newParent);
    
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "SetWindowLong")]
            public static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
    
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "GetWindowLong")]
            public static extern long GetWindowLong(IntPtr hwnd, int nIndex);
    
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "FindWindow")]
            public static extern IntPtr FindWindow(string className, string windowTitle);
    
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "SetWindowPos")]
            public static extern bool SetWindowPos(IntPtr handle, IntPtr handleAfter, int x, int y, int width, int height, uint flags);
    
            [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, EntryPoint = "SetFocus")]
            public static extern IntPtr SetFocus(IntPtr hwnd);
    
    
    #endregion DLL-Import
    
    #region Window Styles (win32 api)
            //Declaration of Constants for Window Styles (Win32Api, see: http://msdn.microsoft.com/en-us/library/ms632600%28v=VS.85%29.aspx):
            private const int WS_Styles = -16;
            private const int GWL_EXSTYLE = -20;
            //styles:
            private const long WS_BORDER = 0x00800000L;
            private const long WS_CAPTION = 0x00C00000L;
            private const long WS_CHILD = 0x40000000L;
            private const long WS_MAXIMIZE = 0x01000000L;
            private const long WS_MAXIMIZEBOX = 0x00010000L;
            private const long WS_POPUP = 0x80000000L;
            private const long WS_SYSMENU = 0x00080000L;
            private const long WS_SIZEBOX = 0x00040000L;
            //ext styles:
            private const long WS_EX_MDICHILD = 0x00000040L;
            private const long WS_EX_CLIENTEDGE = 0x00000200L;
    
            /* If you have changed certain window data using SetWindowLong,
             * you must call SetWindowPos for the changes to take effect. 
             * Use the following combination for uFlags:
             */
            private const uint SWP_NOMOVE = 0x0002;
            private const uint SWP_NOSIZE = 0x0001;
            private const uint SWP_NOZORDER = 0x0004;
            private const uint SWP_FRAMECHANGED = 0x0020;
            private const uint SWP_ASYNCWINDOWPOS = 0x4000;
            private const uint SWP_NOACTIVATE = 0x0010;
    
            private static long originalStyle = 0L;
            private static long originalExStyle = 0L;
    #endregion Window Styles
    Viele Grüße
    ralfz
    Zuletzt editiert von ralfz; 23.11.2010, 18:22.

    Comment

    Working...
    X