none
Windows Aero Glas Effekte nutzen mit korrekter Darstellung der Steuerelemente RRS feed

  • Frage

  • Hallo zusammen,

    ich hofe jmand kann mir helfen.
    Ich möchte gern ein Fenster in Aero Glass Optik erstrahlen lassen, was eigentlich ja auch kein Problem ist mit der DWMAPI, das bekomme ich ja auch hin.
    Das Problem ist, dass Windows mir alle Teile im Fenster die Schwarz sind transparent gestaltet, da es denkt, dass es zu den Bereichen der Glass Oberfläche gehört.

    Ich war auf der Suche nach Lösungen und bin auch fündig geworden. Eine war zb, die Steuerelemente abzuleiten und deren Darstellung selber zeichnen (Subclassing, Ownerdrawing) - dies ist aber keine gute Lösung für mich. (auch wenn es einigermaßen funktioniert)

    Ich habe dann folgenden Artikel von Kenny Kerr gefunden, der beschreibt wie man die Farbe, die transparent dargestellt werden soll ändern kann.

    Leider sind die Beispiele in C++ geschrieben und ich versuche diese in  C# zu übersetzen. Bisher mit mäßigem Erfolg. Führe ich meinen übersetzten Code aus sieht mein Fenster so aus: klick -> Das Fenster zeichnet das was hinter ihm ist auf die Oberfläche, Glaß wird aber nicht dargestellt.

    Es geht um folgenden Code von Kenny Kerr (C++):

     

    class
     SampleDialog :
     Public CDialogImpl<SampleDialog>
    {
    Public:
     
     enum
     { IDD = IDD_SAMPLE };
     
     BEGIN_MSG_MAP(MainWindow)
      MSG_WM_INITDIALOG(OnInitDialog)
      MSG_WM_ERASEBKGND(OnEraseBackground)
      COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
     END_MSG_MAP()
     
     SampleDialog() :
      m_transparencyKey(RGB(200, 201, 202))
     {
      // Do Nothing
    
     }
     
    Private:
     
     LRESULT OnInitDialog(HWND /*Control*/
    ,
           LPARAM /*lParam*/
    )
     {
      SetWindowLong(GWL_EXSTYLE,
          GetExStyle() | WS_EX_LAYERED);
     
      VERIFY(::SetLayeredWindowAttributes(m_hWnd,
               m_transparencyKey,
               0,
               LWA_COLORKEY));
     
      Const MARGINS margins = { -1 };
     
      COM_VERIFY(::DwmExtendFrameIntoClientArea(m_hWnd,
                 &margins));
     
      Return TRUE; // Yes, go ahead And Set the keyboard focus.
    
     }
     
     bool
     OnEraseBackground(CDCHandle dc)
     {
      CRect rect;
      VERIFY(GetClientRect(&rect));
     
      dc.FillSolidRect(&rect,
           m_transparencyKey);
     
      Return True; // Yes, I erased the background.
    
     }
     
     LRESULT OnCancel(WORD /*notifyCode*/
    ,
          WORD identifier,
          HWND /*window*/
    ,
          BOOL& /*handled*/
    )
     {
      VERIFY(EndDialog(identifier));
      Return 0;
     }
     
     Const COLORREF m_transparencyKey;
    
    };
    

    ____________________________________________________________________________

    Ich habe das so umgesetzt:

      public partial class Form1 : Form
      {
    	[DllImport("user32.dll")]
        static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
    
        [DllImport("user32.dll")]
        static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
    
        [DllImport("dwmapi.dll")]
        public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
    
        [StructLayout(LayoutKind.Sequential)]
        public struct MARGINS
        {
          public int cxLeftWidth;
          public int cxRightWidth;
          public int cyTopHeight;
          public int cyBottomHeight;
        }
        public const int GWL_EXSTYLE = -20;
        public const int WS_EX_LAYERED = 0x80000;
        public const int LWA_ALPHA = 0x2;
        public const int LWA_COLORKEY = 0x1;
        public const int WS_EX_TRANSPARENT = 0x20;
    
        public Form1()
        {
          InitializeComponent();
        }
    
        System.IntPtr hr;
        MARGINS m;
        private void Form1_Load(object sender, EventArgs e)
        {
    
          m = new MARGINS();
          m.cxLeftWidth = -1;
          m.cxRightWidth = 0;
          m.cyBottomHeight = 0;
          m.cyTopHeight = 0;
    
          SetWindowLong(this.Handle, GWL_EXSTYLE, GetWindowLong(this.Handle, GWL_EXSTYLE) | WS_EX_LAYERED );
          SetLayeredWindowAttributes(this.Handle, 0x00112233, 0, LWA_COLORKEY);
    
          DwmExtendFrameIntoClientArea(this.Handle, ref m);
    
        }
    
        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
        {
          e.Graphics.FillRectangle(
           new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(0x00112233)),
           this.ClientRectangle);
          this.Text += "!";
        }
        
      }

    Wie oben beschrieben funktioniert das ganze leider nciht so wirklich. Ich würde mich freuen, wenn mit jemand meinen Fehler aufzeigen könnte oder einen link zu einem Beispiel hat.

    Danke!

    Freitag, 16. Juli 2010 11:52

Antworten

Alle Antworten

  • Das Problem ist, dass Windows mir alle Teile im Fenster die Schwarz sind transparent gestaltet, da es denkt, dass es zu den Bereichen der Glass Oberfläche gehört.

    Hallo Sebastian

    nur zur Präzisierung  (wie der Artikel schon erwähnt)  das eigentliche 'Übel' ist wohl nicht die schwarze Farbe, sondern Elemente die per classic-GDI (und somit ohne Alpha) gezeichnet wurden.
    Da einfach eine andere (spekulativ 'seltenere')  Farbe für die Transparenz zu wählen ist immer nur ein 'Würgaround'...
    Insbesondere wenn jemand gerade diese Farbe in den Windows-Farbeinstellungen irgendwo selektiert hat, werden deine Fenster/Steuerelemente hässlicher den je aussehen.
    (Betroffen sind ggf auch entsprechende Pixels in Fotos + Farbverläufen)

    Freitag, 16. Juli 2010 12:19
  • Ups, lies bitte späteren Kommentar des Autors:

    The transparency key approach (using SetLayeredWindowAttributes) no longer works on RC1 and presumably won’t work in the RTM build.

    und neuerer Artikel:

    http://weblogs.asp.net/kennykerr/archive/2007/01/23/controls-and-the-desktop-window-manager.aspx

    Unfortunately, when Microsoft finally released Windows Vista this "hack" no longer worked leaving developers to wonder how it could be done

     

    Freitag, 16. Juli 2010 13:08
  • Hallo Sebastian,

    es gibt ein:

    [Windows® API Code Pack for Microsoft® .NET Framework - Home]
    http://code.msdn.microsoft.com/WindowsAPICodePack

    dort sind gute Glass-Beispiele enthalten und vor allen Dingen managed Wrapper für diese DWM-API-Calls, die Du momentan ja noch manuell aufrufst. Bzgl. Vista 64 Bit haben wir auch gerade einen (noch ungelösten) Thread laufen:

    http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/6a76720b-6d7c-4d39-8eb0-9bff3c5110d4

     


    ciao Frank
    Freitag, 16. Juli 2010 14:14
  • The transparency key approach (using SetLayeredWindowAttributes) no longer works on RC1 and presumably won’t work in the RTM build.

    ...

    Hallo Thomas danke für den Hinweis, habe das total übersehen! Ohh mann, und ich saß da so lange dran und habe mich gewundert wo der Fehler ist. :(

    Werde mir mal den weiteren Artikel angucken.

    @Frank:Das Code Pack hatte ich mir auch angeschaut, wollte aber gerne eine schlanke Version haben die ich bei Bedarf anpassen kann, aber da führt wohl kein Weg dran vorbei...

    "leaving developers to wonder how it could be done" - sehr schön! :)

    Freitag, 16. Juli 2010 19:41
  • Hallo Sebastian,

    Du schriebst:
       "Das Code Pack hatte ich mir auch angeschaut, wollte aber 
         gerne eine schlanke Version haben die ich bei Bedarf anpassen kann, ... "

    Dann hast Du wohl nicht so richtig reingeschaut ;-) Das liegt als C# Code vor, und ist deswegen ja beliebig anpassbar. Deswegen habe ich es Dir ja empfohlen, weil Du daran ggf. Fehler entdecken kannst, denn die haben die Umsetzungen da gut nach best practice gemacht.
    Aber trotzdem, wenn Du jetzt Deinen Fehler finden konntest und es funktioniert, besteht ja erstmal keine Notwendigkeit. Da kannst Du dann später drin schmökern.

     


    ciao Frank
    Freitag, 16. Juli 2010 19:57
  • Hallo Frank,

    ich stöber gerade durch das Aero Sample aus dem Windows API Code Pack, und mir fällt auf, dass ich daraus einfach nicht schlau werde!

    In dem Beispiel wird das Korrekte Darstellen der Steuerlemente dadurch erreicht, dass ein Panal benutzt wird, dass von der Glass-Oberfläche ausgeschlossen wird.

    Hier der Ausschnitt aus der GlassForm Klasse:

        public void ExcludeControlFromAeroGlass( Control control )
        {
          if( AeroGlassCompositionEnabled )
          {
            Rectangle clientScreen = this.RectangleToScreen( this.ClientRectangle );
            Rectangle controlScreen = control.RectangleToScreen( control.ClientRectangle );
    
            MARGINS margins = new MARGINS( );
            margins.cxLeftWidth = controlScreen.Left - clientScreen.Left;
            margins.cxRightWidth = clientScreen.Right - controlScreen.Right;
            margins.cyTopHeight = controlScreen.Top - clientScreen.Top;
            margins.cyBottomHeight = clientScreen.Bottom - controlScreen.Bottom;
    
            // Extend the Frame into client area
            DesktopWindowManagerNativeMethods.DwmExtendFrameIntoClientArea( Handle, ref margins );
          }
        }
    

    Ich weiß ja nicht, aber in meinen Augen ist das keine Lösung! Denn so wird immer nur ein Control ausgeschlossen, dass dann alle anderen Controls beinhalten muss...

    Und was ist, wenn ich die Controls direkt auf dem Glas plazieren will? Dann hilft mir das ganze überhaupt nicht weiter...

    Gibts denn hier keine vernünftige Lösung, mit der man eine "normale" Darstellung erreichen kann, ohne auf WPF zu wechseln?

    MfG, Basti

    Samstag, 17. Juli 2010 15:00
  • Hallo Basti,

    • Du schriebst: "Gibts denn hier keine vernünftige Lösung, mit der man eine "normale" Darstellung erreichen kann, ohne auf WPF zu wechseln?"

    Das Beispiel, was Du gepostet hast ist doch genau das für ein Windows Forms-Control (ohne WPF). Das, mit dem panel1 als Parameter ist die WPF Implementierung.

    • Du schriebst: "Und was ist, wenn ich die Controls direkt auf dem Glas plazieren will? Dann hilft mir das ganze überhaupt nicht weiter..."

    Hier dazu ggf. ein Beispiel:

    [The Moth - Vista: Glass in C#]
    http://www.danielmoth.com/Blog/Vista-Glass-In-C.aspx

    In der MSDN sind natürlich auch Artikel vorhanden:

    [Windows Vista for Developers – Part 3 – The Desktop Window Manager - Kenny Kerr]
    http://weblogs.asp.net/kennykerr/archive/2006/08/10/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager.aspx

    [Erweitern von Glasframe in eine WPF-Anwendung]
    http://msdn.microsoft.com/de-de/library/ms748975.aspx

     


    ciao Frank
    Sonntag, 18. Juli 2010 10:52