3 votes

Redessiner le titre de la fenêtre lors de l'utilisation de chrome personnalisé et de DWM

J'utilise la bibliothèque d'intégration WPF Shell ( http://archive.msdn.microsoft.com/WPFShell ) mais lorsque j'utilise le chrome personnalisé avec Aero, la barre de titre est supprimée.

Je comprends que je dois utiliser la fonction DrawThemeTextEx pour redessiner le titre de la fenêtre, mais je ne trouve aucun exemple en C# qui le fasse. J'ai trouvé un guide à l'adresse Cadre de fenêtre personnalisé à l'aide de DWM (Windows) qui détaille la peinture le titre de la légende.

Je ne sais pas vraiment (j'ai peu d'expérience avec pinvoke) comment faire cela en C# pour que les polices système correctes soient utilisées, etc. Quelqu'un peut-il me fournir un exemple en C# que je pourrais intégrer dans la bibliothèque d'intégration WPF Shell ?

Mise à jour n° 1 : J'ai essayé ce code dans un projet Windows Form et il fonctionne correctement. Je remarque que le formulaire Windows perd le texte du titre si je déplace le formulaire hors de l'écran. Je pense donc que le problème est peut-être lié à cela. J'ai essayé de dessiner le texte du titre dans l'événement OnRender, mais cela ne résout pas le problème.

J'ai ajouté le code suivant au fichier WindowChromeWorker.cs :

    private void _DrawCustomTitle(IntPtr hwnd)
    {
        if (NativeMethods.DwmIsCompositionEnabled())
        {

            Standard.RECT rcClient = new Standard.RECT();
            NativeMethods.GetClientRect(hwnd, ref rcClient);

            Standard.RECT rcPaint = rcClient;
            rcPaint.Top += 8;
            rcPaint.Right -= 125;
            rcPaint.Left += 8;
            rcPaint.Bottom = 50;

            IntPtr destdc = NativeMethods.GetDC(hwnd);
            IntPtr Memdc = NativeMethods.CreateCompatibleDC(destdc); // Set up a memory DC where we'll draw the text.
            IntPtr bitmap;
            IntPtr bitmapOld = IntPtr.Zero;
            IntPtr logFont;

            uint uFormat = NativeMethods.DT_SINGLELINE | NativeMethods.DT_TOP | NativeMethods.DT_LEFT | NativeMethods.DT_WORD_ELLIPSIS;

            BITMAPINFO dib = new BITMAPINFO();
            dib.bmiHeader.biHeight = -(rcClient.Bottom - rcClient.Top); // negative because DrawThemeTextEx() uses a top-down DIB
            dib.bmiHeader.biWidth = rcClient.Right - rcClient.Left;
            dib.bmiHeader.biPlanes = 1;
            dib.bmiHeader.biSize = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
            dib.bmiHeader.biBitCount = 32;
            dib.bmiHeader.biCompression = NativeMethods.BI_RGB;

            if (!(NativeMethods.SaveDC(Memdc) == 0))
            {
                bitmap = NativeMethods.CreateDIBSection(Memdc, ref dib, NativeMethods.DIB_RGB_COLORS, 0, IntPtr.Zero, 0);   // Create a 32-bit bmp for use in offscreen drawing when glass is on
                if (!(bitmap == IntPtr.Zero))
                {
                    bitmapOld = NativeMethods.SelectObject(Memdc, bitmap);

                    System.Drawing.Font font = new System.Drawing.Font("Segoe UI", 9f);
                    IntPtr hFont = font.ToHfont();
                    logFont = NativeMethods.SelectObject(Memdc, hFont);
                    try
                    {

                        System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active);

                        NativeMethods.DTTOPTS dttOpts = new NativeMethods.DTTOPTS();
                        dttOpts.dwSize = (int)Marshal.SizeOf(typeof(NativeMethods.DTTOPTS));
                        dttOpts.dwFlags = NativeMethods.DTT_COMPOSITED | NativeMethods.DTT_GLOWSIZE;
                        dttOpts.iGlowSize = 15;

                        string title = "Windows Title";
                        NativeMethods.DrawThemeTextEx(renderer.Handle, Memdc, 0, 0, title, -1, uFormat, ref rcPaint, ref dttOpts);

                        NativeMethods.BitBlt(destdc, rcClient.Left, rcClient.Top, rcClient.Right - rcClient.Left, rcClient.Bottom - rcClient.Top, Memdc, 0, 0, NativeMethods.SRCCOPY);

                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine(e.Message);
                    }

                    // Clean Up
                    NativeMethods.SelectObject(Memdc, bitmapOld);
                    NativeMethods.SelectObject(Memdc, logFont);
                    NativeMethods.DeleteObject(bitmap);
                    NativeMethods.DeleteObject(hFont);

                    NativeMethods.ReleaseDC(Memdc, -1);
                    NativeMethods.DeleteDC(Memdc);
                }

            }
        }

    }

J'appelle ensuite la fonction DrawCustomTitle dans la fonction suivante après que le verre DWM a été étendu. Pourquoi cela ne fonctionne-t-il pas ?

    private void _ExtendGlassFrame()
    {
        Assert.IsNotNull(_window);

        // Expect that this might be called on OSes other than Vista.
        if (!Utility.IsOSVistaOrNewer)
        {
            // Not an error.  Just not on Vista so we're not going to get glass.
            return;
        }

        if (IntPtr.Zero == _hwnd)
        {
            // Can't do anything with this call until the Window has been shown.
            return;
        }

        // Ensure standard HWND background painting when DWM isn't enabled.
        if (!NativeMethods.DwmIsCompositionEnabled())
        {
            _hwndSource.CompositionTarget.BackgroundColor = SystemColors.WindowColor;
        }
        else
        {
            // This makes the glass visible at a Win32 level so long as nothing else is covering it.
            // The Window's Background needs to be changed independent of this.

            // Apply the transparent background to the HWND
            _hwndSource.CompositionTarget.BackgroundColor = Colors.Transparent;

            // Thickness is going to be DIPs, need to convert to system coordinates.
            Point deviceTopLeft = DpiHelper.LogicalPixelsToDevice(new Point(_chromeInfo.GlassFrameThickness.Left, _chromeInfo.GlassFrameThickness.Top));
            Point deviceBottomRight = DpiHelper.LogicalPixelsToDevice(new Point(_chromeInfo.GlassFrameThickness.Right, _chromeInfo.GlassFrameThickness.Bottom));

            var dwmMargin = new MARGINS
            {
                // err on the side of pushing in glass an extra pixel.
                cxLeftWidth = (int)Math.Ceiling(deviceTopLeft.X),
                cxRightWidth = (int)Math.Ceiling(deviceBottomRight.X),
                cyTopHeight = (int)Math.Ceiling(deviceTopLeft.Y),
                cyBottomHeight = (int)Math.Ceiling(deviceBottomRight.Y),
            };

            NativeMethods.DwmExtendFrameIntoClientArea(_hwnd, ref dwmMargin);

            this._DrawCustomTitle(_hwnd);
        }
    }

0voto

Mranz Points 866

Vous ne devriez pas avoir à subir tout cela. Jetez un coup d'œil à mon exemple de code ci-dessous. L'étiquette avec Text={TemplateBinding Title} fait ce que vous voulez.

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
    Title="MainWindow" Height="350" Width="525">
<Window.Style>
    <Style TargetType="Window">
        <Setter Property="shell:WindowChrome.WindowChrome">
            <Setter.Value>
                <shell:WindowChrome GlassFrameThickness="4,40,4,4" ResizeBorderThickness="5" CaptionHeight="30"/>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Window">
                    <Grid x:Name="PART_ComponentRoot">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="40"/>
                            <RowDefinition/>
                            <RowDefinition Height="4"/>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="4"/>
                            <ColumnDefinition/>
                            <ColumnDefinition Width="4"/>
                        </Grid.ColumnDefinitions>

                        <TextBlock Grid.Column="1" Text="{TemplateBinding Title}"
                                   HorizontalAlignment="Center" VerticalAlignment="Center"/>

                        <ContentPresenter Grid.Row="1" Grid.Column="1" Content="{TemplateBinding Content}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Style>

<Grid Background="White">

</Grid>

0voto

Joe Castro Points 1170

La raison pour laquelle cela ne fonctionne pas est probablement ce que l'on appelle communément les problèmes "Airspace". En fait, le code que vous utilisez rend les pixels avec GDI et WPF est implémenté avec DirectX, et les deux technologies de rendu ne savent pas comment partager les pixels. Il se peut qu'il y ait aussi quelque chose de plus subtil, mais à première vue, je pense que c'est cela.

La façon de procéder en WPF est similaire à ce que Mranz a suggéré précédemment. Il existe deux façons simples d'obtenir l'effet de flou avec WPF :

  1. Créez deux blocs de texte directement l'un sur l'autre (par exemple dans une grille occupant la même cellule) avec la même police et le même texte, etc., mais donnez au bloc du bas la couleur que vous voulez pour la lueur et appliquez-lui un effet de flou. Vous devez veiller à ce que, sans l'effet, les deux images soient rendues de la même manière, sinon l'effet de flou ne sera pas respecté.

  2. Créez une ellipse, ou un rectangle avec des ellipses sur les bords, et faites la même chose en le plaçant derrière le bloc de texte avec le titre et en lui appliquant un effet de flou.

C'est un peu délicat aussi parce que Windows a changé certains de ces comportements entre Vista et 7 (et je n'ai aucune idée de ce à quoi 8 va ressembler en fin de compte). Si je me souviens bien, dans Vista, quand on maximisait la fenêtre, l'effet de flou disparaissait et la couleur de la police devenait blanche. Dans 7, cela ne se produit plus. Dans au moins l'aperçu développeur de 8 (et Office 2010), le texte du titre est maintenant centré. Si vous le faites vous-même, vous devez veiller à ne pas masquer les boutons de légende, car le texte ne commencera probablement pas à être coupé à l'endroit prévu.

Si vous avez des problèmes avec l'une ou l'autre de ces approches, n'hésitez pas à m'envoyer un ping. Je peux probablement mettre en place un exemple de code mais je n'ai pas les styles sous la main. De plus, si vous résolvez le problème, merci de poster votre solution pour que d'autres puissent en profiter :)

J'espère que cela vous aidera,

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X