WPF RibbonWindow + Ribbon =标题外屏?

我正在试用Ribbon控制与RibbonWindow结合使用,但是即使在微不足道的实验中也是如此。

  1. 创build了新的WPF应用程序
  2. 将代码更改为MSDN中的示例
  3. 添加了对System.Windows.Controls.Ribbon引用,并删除了ribbon:前缀(为什么示例过时?)。
  4. 增加了两个图标(16×16和32×32)。
  5. 执行了应用程序,看到了这个(记事本供参考):

我已经可以看到很多问题了:

  1. 边界很小。 一个正常的窗口有一个大的边框,WPF Ribbon应用程序很小。 标题高度也较小。
  2. 边框模糊。 当一个正常的窗口被聚焦时,它的边界是黑色的。 WPF应用程序的边框是灰色的(黑色可以在angular落看到;有东西是在边界上绘制的)。
  3. 应用程序图标放错了位置。 它粘在左上angular。
  4. 应用程序标题错位。 它粘在顶部。

让我们把工具栏移到底部。 现在我们看到这个:

button在工具栏之外。

最后,让我们最大化的窗口:

一半的标题消失在屏幕之外(技术上,窗口在屏幕的外侧每边8像素,但其他应用程序不会被这个混淆)。

我正在使用Windows 7,Aero,单个显示器,没有什么特别的。 我害怕在Windows 8上testing应用程序…

有什么机会解决这个问题?

Solutions Collecting From Web of "WPF RibbonWindow + Ribbon =标题外屏?"

真正的问题

WindowChromeWindowChrome类将其ResizeBorderThickness绑定到SystemParameters.WindowResizeBorderThickness ,后者轮流使用Win32 API GetSystemMetrics来确定系统边界大小。

但是,此方法的行为会根据可执行PE标头中设置的子系统版本而变化。 如果仅针对Windows Vista及更高版本(版本> = 6.0)进行编译,则它将返回比为旧版操作系统编译的更细的边界。 在这个答案更多的信息。

在针对.NET 4.5进行编译时,C#编译器将此版本设置为6.0,因为.NET 4.5无法在XP上使用。 但是, WindowChrome类似乎依赖于传统行为,因此无法在Windows Vista和7上正确计算玻璃大小。

解决方案

使用.NET 4

您可以使用.NET 4进行编译,以强制编译器使用4.0作为子系统版本值。 功能区可用于WPF 4作为单独的下载 。 请注意,即使使用此解决方案,也应该取消选中项目属性中的“启用Visual Studio宿主进程”以进行调试。 否则,将使用vshost.exe进程,该进程将使用子系统版本6.0进行标记。

更改子系统版本

编辑:奥利提供了一个在评论中做到这一点的方法:

在项目文件<subsystemversion>5.01</subsystemversion>中添加一个属性,错误地表示代码可以在Windows XP上运行。

忽略系统

您可以更改窗口上的WindowChrome.WindowChrome附加属性,并使用所需的值,从而完全忽略系统值。 你不应该这样做,但你可以。

填写一个错误

有关Connect的一个现有的错误, GetSystemMetrics行为发生了变化,但是这一切都归结为子系统版本,所以这是Microsoft的一个特点。 然而, WindowChrome类应该被固定在Vista / 7下正确工作,尤其是因为它现在已经构建在.NET 4.5中。

对于任何读这个问题的人,我都会自己回答。 忘掉可怕的捆绑带控制和使用别的东西。 在这里寻找一些替代方案: 什么是最好的WPF功能区控制套件? (就像所有的好问题一样,虽然封闭了)。

到目前为止, 流利的色带控制套件看起来像是我最好的免费选项。 基本的功能只是工作(没有边界的问题和最大化,resize窗口不像地狱等缓慢)。 它具有Office样式并在禁用玻璃的情况下保留它们(这意味着您不会在Metro中看到Windows9x-ish窗口)。 它的界面(后台,QAT)更像Office 2010。

也许在遥远的将来,微软将修复其功能区,但现在,寻找替代品。

这是另一个WorkAround,非常简单和简单的方法。 只需在工具栏上添加一个负边距即可。 您需要保留原始的Window类而不是RibbonWindow!

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

只需将此边距添加到功能区标题即可

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

现在,当你最大化的窗口,一切都保持正确

我在RibbonWindow中的标题有同样的问题。 我通过在RibbonTitlePanel中设置TextBlock的全局样式来解决这个问题。

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

这不是一个解决方案,可能甚至不是一个解决方法,而是一个很糟糕的做法,我希望只能使用很短的时间,直到问题在框架中得到解决。

代码主要是从这个问题复制+粘贴https://stackoverflow.com/a/8082816/44726

我已经改变了允许的屏幕位置,这似乎有助于解决问题,而不是解决问题。

在后面的代码中调用就像这样

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

这是我的解决方法。 我使用SizeChanged事件来检测最大化的状态,之后,我为主网格创建边缘。

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