c# .net wpf windows ribbon

c# - WPF RibbonWindow+Ribbon=Título fuera de la pantalla?



.net windows (6)

Estoy probando Ribbon control en combinación con RibbonWindow , sin embargo, fallan incluso en experimentos triviales.

  1. Creada nueva aplicación WPF
  2. Código cambiado a ejemplo de MSDN
  3. Se agregó una referencia a System.Windows.Controls.Ribbon y se eliminó la ribbon: prefijo (¿por qué los ejemplos están desactualizados?).
  4. Añadido dos iconos (16x16 y 32x32).
  5. Ejecuté la aplicación y vi esto (Notepad para referencia):

Ya puedo ver numerosos problemas:

  1. La frontera es pequeña. Una ventana normal tiene un borde grande, la aplicación WPF Ribbon tiene un tamaño pequeño. La altura del título es más pequeña también.
  2. La frontera está borrosa. Cuando se enfoca una ventana normal, su borde es negro. El borde de la aplicación WPF es grisáceo (se puede ver el negro en las esquinas; ¿se dibuja algo sobre los bordes?).
  3. El icono de la aplicación está fuera de lugar. Está pegado a la esquina superior izquierda.
  4. El título de la aplicación está fuera de lugar. Está pegado a la parte superior.

Vamos a mover la barra de herramientas a la parte inferior. Ahora vemos esto:

Los botones están fuera de la barra de herramientas.

Y por último, vamos a maximizar la ventana:

La mitad del encabezado desapareció fuera de la pantalla (técnicamente, la ventana está fuera de la pantalla por 8 píxeles en cada lado, pero otras aplicaciones no se confunden con esto).

Estoy usando Windows 7, Aero, solo monitor, nada especial. Tengo miedo de probar la aplicación en Windows 8 ...

¿Alguna posibilidad de arreglar esto?


El problema real

Bajo el capó, la clase WindowChrome vincula su ResizeBorderThickness a SystemParameters.WindowResizeBorderThickness que a su vez usa la API Win32 GetSystemMetrics para determinar el tamaño del borde del sistema.

Sin embargo, el comportamiento de este método cambia según la versión del subsistema establecida en el encabezado PE ejecutable. Si se compila solo para Windows Vista y versiones posteriores (versión> = 6.0), devolverá bordes más finos que si se compilara para sistemas operativos más antiguos. Más información sobre esto en esta respuesta SO.

Al compilar contra .NET 4.5, el compilador de C # establece esta versión en 6.0 ya que .NET 4.5 no se puede usar en XP. Sin embargo, la clase WindowChrome parece depender del comportamiento heredado y, por lo tanto, no logra calcular correctamente el tamaño del vidrio en Windows Vista y 7.

Soluciones

Utilizar .NET 4

Puede compilar contra .NET 4 para forzar al compilador a usar 4.0 como su valor de versión del subsistema. La cinta está disponible para WPF 4 como una descarga separada . Tenga en cuenta que incluso con esta solución, debe desmarcar "Habilitar el proceso de alojamiento de Visual Studio" en las propiedades del proyecto para fines de depuración. De lo contrario, se utilizará el proceso vshost.exe, que se marca con una versión del subsistema 6.0.

Cambia la versión del subsistema

Edit: Olly proporcionó una manera de hacer esto en los comentarios:

Agregue una propiedad en el archivo de proyecto <subsystemversion>5.01</subsystemversion> que indica falsamente que el código puede ejecutarse en Windows XP.

Ignorar el sistema

Puede cambiar la propiedad adjunta WindowChrome.WindowChrome en su ventana y usar los valores que desee, por lo tanto, ignorando completamente los valores del sistema. Nunca debes hacer eso, pero puedes.

Llenar un error

Hay un error existente en Connect sobre el cambio en el comportamiento de GetSystemMetrics pero todo se reduce a la versión del subsistema, por lo que es más bien una característica desde el punto de vista de Microsoft. Sin embargo, la clase WindowChrome realmente debería arreglarse para que funcione correctamente en Vista / 7, especialmente porque ahora está integrada en .NET 4.5.


Aquí está mi solución alternativa. Utilizo el evento SizeChanged para detectar el estado maximizado, después de eso creo un margen para la cuadrícula principal.

private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { Thickness m = GridMain.Margin; if (WindowState == WindowState.Maximized) { m.Left = 3; m.Bottom = 3; m.Left = 3; } else { m.Left = 0; m.Bottom = 0; m.Left = 0; } GridMain.Margin = m; }


Aquí hay otra forma de WorkAround, muy fácil y simple. Simplemente agregue un margen negativo en la barra de herramientas. ¡Debes mantener la clase de ventana original y no la ventana de cinta de opciones!

<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">

Solo agrega este margen al título de la cinta

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

Ahora cuando maximizas la ventana, todo queda bien.


Esta no es una solución, tal vez ni siquiera una solución alternativa, sino un hackeo deficiente, que espero usar solo por un corto tiempo hasta que el problema se resuelva en el marco.

El código es principalmente copiar y pegar desde esta pregunta https://.com/a/8082816/44726

He cambiado la posición permitida de la pantalla, que parece ayudar con el problema, no a solucionarlo.

La llamada es así en el código de atrás.

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; } }


Para cualquiera que lea esta pregunta, la estoy respondiendo yo mismo. Olvídate del horrible control de la cinta y usa otra cosa. Busque algunas de las alternativas aquí: ¿Cuál es la mejor suite de control de la cinta de WPF? (Como todas las buenas preguntas, está cerrado).

Hasta ahora, Fluent Ribbon Control Suite me parece la mejor opción gratuita. La funcionalidad básica simplemente funciona (no hay problemas con los bordes y la maximización, el tamaño de la ventana no es tan lento como el infierno, etc.). Tiene estilos de Office y los conserva si el vidrio está deshabilitado (eso significa que no verá la ventana de Windows9x-ish en Metro). Su interfaz (backstage, QAT) se parece más a Office 2010.

Tal vez en un futuro lejano, Microsoft arregle su Cinta, pero por ahora, busque alternativas.


Tuve el mismo problema con el título en el RibbonWindow. Lo resolví configurando el estilo global de TextBlock dentro de 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>