WPF主窗口标题栏能否随显示器DPI缩放自动调整?官方示例无效
解决WPF主窗口标题栏随DPI缩放自动调整的问题
核心原因
WPF默认的系统标题栏由操作系统负责渲染,不会自动响应Per-Monitor DPI变化,只有WPF自身绘制的元素会根据DPI缩放,这就是官方PerMonitorDPI示例中标题栏尺寸偏小的原因。
解决方案一:自定义标题栏(推荐)
替换系统标题栏为WPF自定义绘制的组件,确保其随DPI自动缩放,步骤如下:
- 禁用系统标题栏
在窗口XAML中设置相关属性,移除默认标题栏并保留窗口调整功能:
<Window x:Class="YourApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowStyle="None" ResizeMode="CanResize" AllowsTransparency="True">
- 构建自定义标题栏布局
在窗口内部添加包含标题文本、控制按钮的标题栏区域,以及主内容区:
<Grid> <!-- 自定义标题栏 --> <Grid x:Name="TitleBar" Height="32" Background="#FF2D2D30" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown"> <TextBlock Text="应用标题" Margin="10,0" VerticalAlignment="Center" Foreground="White"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="−" Width="46" Height="32" Click="Minimize_Click"/> <Button Content="□" Width="46" Height="32" Click="Maximize_Click"/> <Button Content="×" Width="46" Height="32" Click="Close_Click"/> </StackPanel> </Grid> <!-- 主内容区域 --> <Grid x:Name="MainContent" Margin="0,32,0,0"> <!-- 你的应用内容 --> </Grid> </Grid>
- 添加窗口拖动逻辑
在标题栏的MouseDown事件中实现窗口拖动:
private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e) { if (e.LeftButton == MouseButtonState.Pressed) { DragMove(); } }
- 响应DPI变化自动缩放
在窗口的DpiChanged事件中,基于新的DPI比例调整标题栏尺寸(.NET 5+可省略手动计算,元素会自动跟随DPI缩放):
protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) { base.OnDpiChanged(oldDpi, newDpi); double scale = newDpi.DpiScaleX; TitleBar.Height = 32 * scale; MainContent.Margin = new Thickness(0, 32 * scale, 0, 0); foreach (Button btn in TitleBar.FindVisualChildren<Button>()) { btn.Width = 46 * scale; btn.Height = 32 * scale; } }
解决方案二:强制系统标题栏重绘(兼容性一般)
通过Windows API触发系统标题栏重绘,使其适配当前DPI,步骤如下:
- 引入Windows API
[DllImport("user32.dll")] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); private const int GWL_STYLE = -16; private const int WS_CAPTION = 0x00C00000;
- 在DPI变化时重置窗口样式
protected override void OnDpiChanged(DpiScale oldDpi, DpiScale newDpi) { base.OnDpiChanged(oldDpi, newDpi); IntPtr hwnd = new WindowInteropHelper(this).Handle; int style = GetWindowLong(hwnd, GWL_STYLE); // 先移除标题栏样式再重新添加,触发系统重绘 SetWindowLong(hwnd, GWL_STYLE, style & ~WS_CAPTION); SetWindowLong(hwnd, GWL_STYLE, style | WS_CAPTION); }
内容的提问来源于stack exchange,提问作者ironsight
相关产品推荐
相关产品推荐

