Laden...

In Aero Glass zeichnen

Erstellt von LonelyPixel vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.106 Views
L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren
In Aero Glass zeichnen

Hallo,

ich hab heute mal probiert, was in den Aero-Glass-Bereich eines Fensters zu zeichnen. (Windows 7, VS 2008 SP1, C#) Dazu habe ich die Funktion DwmExtendFrameIntoClientArea aufgerufen und mir oben etwas Glass hinmontiert. Das klappt ja ganz gut, aber das Zeichnen dadrin geht schief. Ich kann mir jetzt aussuchen, ob der Hintergrund das übliche Grau sein soll (also das Glas vollständig übermalt), oder ob alles was in schwarzer Farbe gezeichnet wird, durchsichtig sein soll, oder ob alles schwarz wird. Es haut einfach nicht hin. Einfache Codebeispiele hab ich keine gefunden.

Konkret möchte ich einen Tab-Header in die erste Zeile des Fensters setzen. Den zeichne ich komplett selbst. Die Umrandung der einzelnen Tabs ist teilweise transparent (leichter Schatten), der Text im Inneren wird mit TextRenderer.DrawText angezeigt. Im Prinzip sieht das so aus wie in Google Chrome. Ohne Glas funktioniert das einwandfrei, nur mit nicht.

Kann mir da jemand erklären, worauf ich alles achten muss? Insbesondere würde mich interessieren, was ich in OnPaintBackground machen muss und wie ich schwarzen Text auch zu sehen bekomme.

5.742 Beiträge seit 2007
vor 13 Jahren

Hallo LonelyPixel,

verwende mal ein


Application.SetCompatibleTextRendering(true);

in der Main.

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Das hat gar keinen Unterschied gemacht. Ich hab davon gelesen, aber nur im Kontext mit Windows-Forms-Controls, die ich ja nicht verwende. Ich hab mal 2 Screenshots gemacht. Im ersten wird in OnPaintBackground nichts gemacht und im 2. wird die Methode gar nicht überschrieben.

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Ach ja, und so sieht es ohne den ganzen DWM-Schnörkel aus:

V
352 Beiträge seit 2008
vor 13 Jahren

Hier das Zauberwort:


[DllImport("uxtheme.dll", CharSet = CharSet.Unicode)]
        private static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref RECT pRect, ref DTTOPTS pOptions);


        private const int DTT_TEXTCOLOR       = 1 << 0;        // crText has been specified
        private const int DTT_BORDERCOLOR     = 1 << 1;        // crBorder has been specified
        private const int DTT_SHADOWCOLOR     = 1 << 2;        // crShadow has been specified
        private const int DTT_SHADOWTYPE      = 1 << 3;        // iTextShadowType has been specified
        private const int DTT_SHADOWOFFSET    = 1 << 4;        // ptShadowOffset has been specified
        private const int DTT_BORDERSIZE      = 1 << 5;        // iBorderSize has been specified
        private const int DTT_FONTPROP        = 1 << 6;        // iFontPropId has been specified
        private const int DTT_COLORPROP       = 1 << 7;        // iColorPropId has been specified
        private const int DTT_STATEID         = 1 << 8;        // IStateId has been specified
        private const int DTT_CALCRECT        = 1 << 9;        // Use pRect as and in/out parameter
        private const int DTT_APPLYOVERLAY    = 1 << 10;       // fApplyOverlay has been specified
        private const int DTT_GLOWSIZE        = 1 << 11;       // iGlowSize has been specified
        private const int DTT_CALLBACK        = 1 << 12;       // pfnDrawTextCallback has been specified
        private const int DTT_COMPOSITED      = 1 << 13;       // Draws text with antialiased alpha (needs a DIB section)
        private const int DTT_VALIDBITS       = DTT_TEXTCOLOR | DTT_BORDERCOLOR | DTT_SHADOWCOLOR | DTT_SHADOWTYPE | DTT_SHADOWOFFSET |
                                                DTT_BORDERSIZE | DTT_FONTPROP | DTT_COLORPROP | DTT_STATEID | DTT_CALCRECT |
                                                DTT_APPLYOVERLAY | DTT_GLOWSIZE | DTT_COMPOSITED;


public static void drawGlowingText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags, bool drawGlow, int glowSize)
        {
            IntPtr hdc = graphics.GetHdc();
            IntPtr hDC = CreateCompatibleDC(hdc);

            BITMAPINFO structure = new BITMAPINFO();
            structure.biSize = Marshal.SizeOf(structure);
            structure.biWidth = bounds.Width;
            structure.biHeight = -bounds.Height;
            structure.biPlanes = 1;
            structure.biBitCount = 0x20;
            structure.biCompression = 0;

            IntPtr hObject = CreateDIBSection(hdc, structure, 0, 0, IntPtr.Zero, 0);
            SelectObject(hDC, hObject);
            IntPtr ptr4 = font.ToHfont();
            SelectObject(hDC, ptr4);

            VisualStyleRenderer renderer = new VisualStyleRenderer(VisualStyleElement.Window.Caption.Active);

            DTTOPTS pOptions = new DTTOPTS();
            pOptions.dwSize = Marshal.SizeOf(typeof(DTTOPTS));
            if (drawGlow)
                pOptions.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR | DTT_GLOWSIZE;
            else
                pOptions.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR;
            pOptions.iGlowSize = glowSize;
            pOptions.crText = ColorTranslator.ToWin32(color);

            RECT pRect = new RECT(0, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
            DrawThemeTextEx(renderer.Handle, hDC, 0, 0, text, -1, (int)flags, ref pRect, ref pOptions);
            BitBlt(hdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, hDC, 0, 0, 0xcc0020);
            DeleteObject(ptr4);
            DeleteObject(hObject);
            DeleteDC(hDC);
            graphics.ReleaseHdc(hdc);
        }

Das verwende ich um schwarzen Text mit weißem Glühen zu erstellen wie die Titeltexte der Fenster. Bau dir einfach das Glühen raus. Ansonsten gibts über Google genug zu dem Thema 😉

GRüße
ViperNeo

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Mhm, gleich mal ausprobieren...

und was mach ich mit dem Hintergrund? Der ist ja noch alles andere als ordentlich. Wo der Button-Rest ganz oben links herkommt, hab ich absolut keine Ahnung. Da ist gar kein Button.

Update: Das Zeichnen der Linien haut in meinem Beispiel aber auch noch nicht hin. Da sind total hässliche dunkle Ränder rum. Wo kommen die denn her?

V
352 Beiträge seit 2008
vor 13 Jahren

hm malst du die linien wirklich selbst? ich habs noch nie hinbekommen so schöne ränder links und rechts zu malen. wie machst du das?

ansonsten ist es grundsätzlich etwas schwerer auf der glasfläche zu malen. transparent werden schwarze flächen und eigentlich sollten normale linien kein problem darstellen. der button rest ist wirklich seltsam. hast du irgendwo noch eine paint methode überschrieben oder irgendwas? wird vllt dein vorhandener button dort am anfang gezeichnet und später verschoben... kann ihc mir auch nicht so richtig erklären..

edit... deine buttons sind doch grafiken oder? wenn ich das richtig sehe haben die einen leichten schatten deine linien. dieser schatten benötigt natürlich auch einen schwarzen hintergrund. wenn das transparente bilder sind, dann wird das so nicht klappen, da dieser alpha kanal im bild nicht unterstützt wird.

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Ne, keine Bilder. Das sind Linien, die ich aus einem Point-Array als GraphicsPath zusammensetze. Die genaue Position der Punkte (pro Tab-Header sind das 14 Stück, 7 links und 7 rechts) ist etwas Tüftelei, aber mit etwas geometrischem Geschick schon machbar. Dann setze ich SmoothingMode auf AntiAlias und fülle erst den Hintergrund mit FillPath, zeichne dann den Schatten (beim aktiven Tab) bzw. das Highlight (beim inaktiven Tab) mit einer leicht veränderten Punktmenge und zum Schluss kommt der eigentliche Rand drüber mit DrawPath.

Wenn das alles gemalt ist, setze ich halt mit TextRenderer noch den Text oben drauf.

Der Inhalt der einzelnen Tab-Seiten sind UserControls. In diesem Test hab ich nur eins davon, das halt für alle Seiten jeweils einmal instantiiert wird. Deshalb sehen die alle gleich aus (kommt auf den Screenshots nicht so raus... 😉). Der Button ist aber immer so weit weg von der oberen linken Ecke.

Ich bin mir im Übrigen auch nicht sicher, ob mir das DrawThemeText was nutzt, da ich ja gar nicht (mit Glow und so) direkt auf Glas schreiben möchte, sondern ganz normal auf eine Fläche, die ich vorher gefüllt habe. Später kommen dann noch Icons und ein Close-Button (den zeichne ich wohl auch mit Graphics-Methoden dazu) dazu. In diesen Icons kann auch schwarze Farbe vorkommen, das sollte dann auch sichtbar bleiben. Aber so haut das ja noch überhaupt nicht hin.

V
352 Beiträge seit 2008
vor 13 Jahren

alles was du selbst malst und schwarz ist wird transparent werden, daher gibt es ja diese methode.

ansonsten würde mich die sache mit den tabs echt interessieren. hatte da auch mal echt lange dran getüftelt bin aber gescheitert... vllt bist du ja so lieb und hilfst mir 😃

mehr als bislang kann ich dir leider nicht mehr helfen. bei drawthemetext kannst du ja auch das glow ignorieren. ist einfach ein verfügbarer bereich dieser methode. warum die schatten so komisch aussehen ist wirklich komisch. ich werde auch mal etwas tüfteln. vllt finde ich eine lösung für dich 😃

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Ich weiß nicht, ob das so gut rübergekommen ist, aber so soll es mal aussehen:

Edit: Dass die Tabs hier in der Caption Bar drinstecken, brauche ich nicht. Das wird wohl nochmal ne Ecke komplizierter und unter XP ist dann eh Schluss. Aber wenn die Tabs bei mir so aussehen würden, wär das toll.

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Ich hab nochmal weiter gesucht, aber ich find einfach keinen Beispielcode, der irgendwas in Client-Glas zeichnet. Ich finde nur Artikel darüber, wie man dort WinForms-Controls reinsetzen kann, aber nichts selbstgezeichnetes. Davon sehe ich immer nur Screenshots, aber keinen Code.

Meine Probleme sind jetzt konkret folgende:

* Was muss ich im Hintergrund zeichnen (OnPaintBackground)? Wenn ich in der Methode gar nichts mache, wird großer Müll angezeigt aber es ist zumindest stellenweise das Glas zu sehen. Beim Neuzeichnen bleiben natürlich Reste des vorherigen Inhalts stehen. Wenn ich schwarz oder BackColor fülle, ist genau diese Farbe zu sehen aber kein Glas. Wenn ich Transparent fülle, hat das den gleichen Effekt, wie wenn ich gar nichts in der Methode mache. Wenn ich die Methode nicht überschreibe, ist es als ob ich in BackColor füllen würde.

* Wie bekomme ich schwarzen Text sichtbar? Ich möchte wirklich sehr gerne den TextRenderer verwenden, weil das die einzige Möglichkeit ist, Text so zu zeichnen, wie das der Rest von Windows auch tut. Mit Graphics.DrawString sieht es immer ein kleines bisschen anders aus. Und mit Label-Controls wollte ich da jetzt nicht anfangen, zumal die intern auch nur den TextRenderer verwenden (laut Reflector).

V
352 Beiträge seit 2008
vor 13 Jahren

leider scheint das nicht so einfach zu sein... weiter infos für dich:
Aero Glass: Erstellen von Spezialeffekten mit dem Desktopfenster-Manager
http://blog.bigbasti.com/windows-aero-glas-in-eigenen-projekten-nurzen-teil-3/
http://www.welt-held.de/1609-howto-aero-glass-in-c-forms-nutzen.html

aber die schaffen das auch irgendwie... schau dir doch mal den quellcode an. da kann man sicher was rausziehen:
http://www.codeproject.com/KB/toolbars/WinFormsRibbon.aspx

grüße
viperneo

L
LonelyPixel Themenstarter:in
333 Beiträge seit 2007
vor 13 Jahren

Also, danke für die Links, aber da war leider nichts neues dabei.

Ich hab nun noch etwas weiter gefrickelt und hab zumindest rausgefunden, wie man richtig zeichnen kann. Dazu braucht man folgendes:


protected override void OnPaintBackground(PaintEventArgs pevent)
{
	pevent.Graphics.Clear(Color.FromArgb(0));
}

Danach hat man eine saubere leere Glass-Fläche, auf die man dann mit den gewohnten Zeichenmethoden zeichnen kann (Linien, Flächen, Bilder, alles kein Problem, auch schwarz).

Übrig bleibt nur noch der Text. Mit TextRenderer sieht man sehr wenig bis gar nichts, je nach eingestellter Farbe. In keinem Fall aber das, was man will. In folgendem Screenshot sind jetzt untereinander folgende Text-Darstellungsmethoden gezeigt:

* DrawThemeTextEx (ohne Glow; mit käme das raus, was man erwarten würde)
* Graphics.DrawString
* TextRenderer.DrawText

Im 2. Bild passt der Text nicht rein, weil ich mit TextRenderer.MeasureText ausmesse und nicht mit Graphics.sonstwas. Da sieht man auch, dass der Text größer wird.