web-dev-qa-db-fra.com

WPF RibbonWindow + Ribbon = Titre en dehors de l'écran?

J'essaie le contrôle Ribbon en combinaison avec RibbonWindow, mais ils échouent même dans des expériences triviales.

  1. Créé nouveau Application WPF
  2. Code modifié en exemple de MSDN
  3. Ajout de la référence à System.Windows.Controls.Ribbon et suppression du préfixe ribbon: (pourquoi les exemples sont-ils obsolètes?).
  4. Ajout de deux icônes (16x16 et 32x32).
  5. Exécuté l'application et vu ceci (Notepad for reference):

Je peux déjà voir de nombreux problèmes:

  1. La frontière est minuscule. Une fenêtre normale a une grande bordure, l’application WPF Ribbon en a une minuscule. La hauteur du titre est aussi plus petite.
  2. La frontière est floue. Lorsqu'une fenêtre normale est mise au point, sa bordure est noire. La bordure de l'application WPF est grisâtre (on peut voir du noir dans les coins; quelque chose est tracé au-dessus des frontières?).
  3. L'icône de l'application est mal placée. C'est collé au coin en haut à gauche.
  4. Le titre de l'application est mal placé. C'est collé au sommet.

Déplaçons la barre d'outils vers le bas. Maintenant nous voyons ceci:

Les boutons sont en dehors de la barre d'outils.

Et enfin, maximisons la fenêtre:

La moitié de l'en-tête a disparu en dehors de l'écran (techniquement, la fenêtre correspond à en dehors de l'écran de 8 pixels de chaque côté, mais les autres applications ne sont pas gênées par cela).

J'utilise Windows 7, Aero, un seul moniteur, rien de spécial. J'ai peur de tester l'application sur Windows 8 ...

Une chance de résoudre ce problème?

22
Athari

Le vrai problème

Sous le capot, la classe WindowChrome lie sa ResizeBorderThickness à SystemParameters.WindowResizeBorderThickness qui utilise à son tour l'API Win32 GetSystemMetrics pour déterminer la taille de la bordure système.

Toutefois, le comportement de cette méthode change en fonction de la version du sous-système définie dans l'en-tête PE exécutable. Si elle est compilée uniquement pour Windows Vista et versions ultérieures (version> = 6.0), les bordures seront plus fines que si elles étaient compilées pour des systèmes d'exploitation plus anciens. Plus d'informations à ce sujet dans cette SO réponse.

Lors de la compilation avec .NET 4.5, le compilateur C # définit cette version sur 6.0 car .NET 4.5 ne peut pas être utilisé sous XP. Toutefois, la classe WindowChrome semble s'appuyer sur le comportement hérité et ne parvient donc pas à calculer correctement la taille du verre sous Windows Vista et 7.

Solutions

Utilisez .NET 4

Vous pouvez compiler avec .NET 4 pour forcer le compilateur à utiliser 4.0 comme valeur de version de sous-système. Le ruban est disponible pour WPF 4 en tant que téléchargement séparé _. Notez que même avec cette solution, vous devez décocher "Activer le processus d'hébergement Visual Studio" dans les propriétés du projet à des fins de débogage. Sinon, le processus vshost.exe sera utilisé, ce qui est signalé par une version de sous-système de 6.0.

Changer la version du sous-système

Edit: Olly a fourni un moyen de le faire dans les commentaires:

Ajoutez une propriété dans le fichier de projet <subsystemversion>5.01</subsystemversion> qui indique faussement que Le code peut être exécuté sur Windows XP.

Ignorer le système

Vous pouvez modifier la propriété attachée WindowChrome.WindowChrome dans votre fenêtre et utiliser les valeurs souhaitées, ignorant ainsi complètement les valeurs système. Vous ne devriez jamais faire cela, mais vous le pouvez.

Remplir un bug

Il y a un bogue existant sur Connect concernant le changement de comportement de GetSystemMetrics , mais tout se résume à la version du sous-système, il s'agit donc plutôt d'une fonctionnalité du point de vue de Microsoft. Cependant, la classe WindowChrome devrait vraiment être corrigée pour fonctionner correctement sous Vista/7, d’autant plus qu’elle est maintenant intégrée à .NET 4.5.

28
Julien Lebosquain

Voici un autre moyen de contourner le problème, très simple et très simple. Ajoutez simplement une marge négative dans la barre d’outils. Vous devez conserver la classe de fenêtre d'origine et non la fenêtre Ribbon!

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        Title="Application Name" Height="350" Width="525" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged">

Ajoutez simplement cette marge au titre du ruban

<Ribbon Title="" Foreground="#333333" Margin="0,-22,0,0">

Maintenant, lorsque vous maximisez la fenêtre, tout reste bien

8
Yannick Turbang

Pour quiconque lit cette question, je réponds moi-même. Oubliez l'horrible contrôle de ruban empaqueté et utilisez autre chose. Recherchez quelques-unes des alternatives ici: Quelle est la meilleure suite de contrôles de ruban WPF? (comme toutes les bonnes questions, il est fermé cependant).

Jusqu'à présent, Fluent Ribbon Control Suite me semble être la meilleure option gratuite. Les fonctionnalités de base fonctionnent simplement (pas de problèmes de bordures et de maximisation, redimensionnement de la fenêtre n’est pas si lent, etc.) Il possède des styles Office et les préserve si Glass est désactivé (cela signifie que vous ne verrez pas la fenêtre Windows9x-ish dans Metro). Son interface (backstage, QAT) ressemble plus à Office 2010.

Peut-être que dans un futur lointain, Microsoft va réparer son ruban, mais pour le moment, cherche des alternatives.

6
Athari

J'ai eu le même problème avec le titre dans la RibbonWindow. Je l'ai résolu en définissant le style global du TextBlock dans RibbonTitlePanel.

    <Style TargetType="{x:Type TextBlock}"> 
    <Style.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type primitives:RibbonTitlePanel}},Path=Visibility}" Value="Visible"></Condition>
                <Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type RibbonWindow}},Path=WindowState}" Value="Maximized"></Condition>
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.Setters>
                <Setter Property="VerticalAlignment" Value="Center"></Setter>
            </MultiDataTrigger.Setters>
        </MultiDataTrigger>
    </Style.Triggers>
</Style>
1
troYman

Ce n'est pas une solution, peut-être même pas une solution de contournement, mais plutôt un bidouillage que j'espère ne pas utiliser que pendant une courte période, le temps que le problème soit résolu dans le cadre.

Le code est principalement copier-coller de cette question https://stackoverflow.com/a/8082816/44726

J'ai changé la position autorisée de l'écran, ce qui semble résoudre le problème, pas le résoudre.

Call est comme ça dans le code derrière

        InitializeComponent();
        RibbonWindowService.FixMaximizedWindowTitle(this);


public static class RibbonWindowService
{
    public static void FixMaximizedWindowTitle(Window window)
    {
        window.SourceInitialized += WinSourceInitialized;
    }

    [DllImport("user32")]
    internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);

    [DllImport("User32")]
    internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);

    private static IntPtr WindowProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        switch (msg)
        {
            case 0x0024:
                WmGetMinMaxInfo(hwnd, lParam);
                handled = true;
                break;
        }

        return (IntPtr)0;
    }

    private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    {
        MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Adjust the maximized size and position to fit the work area of the correct monitor
        int MONITOR_DEFAULTTONEAREST = 0x00000002;
        IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);

        if (monitor != IntPtr.Zero)
        {
            MONITORINFO monitorInfo = new MONITORINFO();
            GetMonitorInfo(monitor, monitorInfo);
            RECT rcWorkArea = monitorInfo.rcWork;
            RECT rcMonitorArea = monitorInfo.rcMonitor;

            // Offset top and left 1 pixel improves the situation
            rcMonitorArea.top += 1;
            rcMonitorArea.left += 1;

            mmi.ptMaxPosition.x = Math.Abs(rcWorkArea.left - rcMonitorArea.left);
            mmi.ptMaxPosition.y = Math.Abs(rcWorkArea.top - rcMonitorArea.top);
            mmi.ptMaxSize.x = Math.Abs(rcWorkArea.right - rcWorkArea.left);
            mmi.ptMaxSize.y = Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
        }

        Marshal.StructureToPtr(mmi, lParam, true);
    }

    private static void WinSourceInitialized(object sender, EventArgs e)
    {
        IntPtr handle = (new WinInterop.WindowInteropHelper((Window)sender)).Handle;
        WinInterop.HwndSource.FromHwnd(handle).AddHook(WindowProc);
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MINMAXINFO
    {
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        /// <summary>
        /// x coordinate of point.
        /// </summary>
        public int x;

        /// <summary>
        /// y coordinate of point.
        /// </summary>
        public int y;

        /// <summary>
        /// Construct a point of coordinates (x,y).
        /// </summary>
        public POINT(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 0)]
    public struct RECT
    {
        /// <summary> Win32 </summary>
        public int left;

        /// <summary> Win32 </summary>
        public int top;

        /// <summary> Win32 </summary>
        public int right;

        /// <summary> Win32 </summary>
        public int bottom;

        /// <summary> Win32 </summary>
        public static readonly RECT Empty = new RECT();

        /// <summary> Win32 </summary>
        public int Width
        {
            get { return Math.Abs(right - left); } // Abs needed for BIDI OS
        }

        /// <summary> Win32 </summary>
        public int Height
        {
            get { return bottom - top; }
        }

        /// <summary> Win32 </summary>
        public RECT(int left, int top, int right, int bottom)
        {
            this.left = left;
            this.top = top;
            this.right = right;
            this.bottom = bottom;
        }

        /// <summary> Win32 </summary>
        public RECT(RECT rcSrc)
        {
            left = rcSrc.left;
            top = rcSrc.top;
            right = rcSrc.right;
            bottom = rcSrc.bottom;
        }

        /// <summary> Win32 </summary>
        public bool IsEmpty
        {
            get
            {
                // BUGBUG : On Bidi OS (hebrew arabic) left > right
                return left >= right || top >= bottom;
            }
        }

        /// <summary> Return a user friendly representation of this struct </summary>
        public override string ToString()
        {
            if (this == Empty)
            {
                return "RECT {Empty}";
            }
            return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
        }

        /// <summary> Determine if 2 RECT are equal (deep compare) </summary>
        public override bool Equals(object obj)
        {
            if (!(obj is Rect))
            {
                return false;
            }
            return (this == (RECT)obj);
        }

        /// <summary>Return the HashCode for this struct (not garanteed to be unique)</summary>
        public override int GetHashCode()
        {
            return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
        }

        /// <summary> Determine if 2 RECT are equal (deep compare)</summary>
        public static bool operator ==(RECT rect1, RECT rect2)
        {
            return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
        }

        /// <summary> Determine if 2 RECT are different(deep compare)</summary>
        public static bool operator !=(RECT rect1, RECT rect2)
        {
            return !(rect1 == rect2);
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class MONITORINFO
    {
        /// <summary>
        /// </summary>            
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));

        /// <summary>
        /// </summary>            
        public RECT rcMonitor = new RECT();

        /// <summary>
        /// </summary>            
        public RECT rcWork = new RECT();

        /// <summary>
        /// </summary>            
        public int dwFlags = 0;
    }
}
1
Karsten