WPF中子窗口与父窗口同步尺寸异常的解决方法咨询
WPF子窗口随父窗口同步尺寸异常问题解决
问题描述
通过user32.dll的SetParent方法将Window1设为MainWindow的子窗口时,出现以下异常:
- MainWindow位于(0,0)位置时,调整父窗口大小,子Window1尺寸能正常匹配
- MainWindow位置改为非原点(如(10,0))时,调整父窗口大小会导致Window1意外向右偏移
- 仅在MainWindow和Window1的
AllowsTransparency设为True时触发异常,设为False则行为正常
需要实现子窗口与父窗口同步尺寸的正确方法。
相关代码
MainWindow VB代码
Class MainWindow Dim Window1 As Window1 Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded Me.Left = 0 Me.Top = 0 Window1 = New Window1(New Interop.WindowInteropHelper(Me).Handle) Window1.Show() End Sub Private Sub Window_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged If Window1 IsNot Nothing Then Window1.Width = Me.ActualWidth Window1.Height = Me.ActualHeight End If End Sub End Class
MainWindow XAML
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:_01" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" Background="Cyan" AllowsTransparency="True" WindowStyle="None" ResizeMode="CanResizeWithGrip" > </Window>
Window1 VB代码
Public Class Window1 Sub New(parentWindowHandle As IntPtr) InitializeComponent() Dim interop = New Interop.WindowInteropHelper(Me) interop.EnsureHandle() Me.WindowStartupLocation = WindowStartupLocation.Manual Me.Left = 0 Me.Top = 0 SetParent(interop.Handle, parentWindowHandle) End Sub Public Declare Function SetParent Lib "user32" (hWndChild As IntPtr, hWndNewParent As IntPtr) As IntPtr End Class
Window1 XAML
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:_01" mc:Ignorable="d" Title="Window1" Height="300" Width="300" Background="Magenta" WindowStyle="None" AllowsTransparency="True" > </Window>
补充信息
- 项目基于.NET Framework 4.6.2
- 无标题栏拖动的WindowChrome代码:
<WindowChrome.WindowChrome> <WindowChrome CaptionHeight="10" ResizeBorderThickness="4" > </WindowChrome> </WindowChrome.WindowChrome>
解决方案
方案一:改用WPF原生布局(推荐)
放弃Win32互操作,将Window1改为UserControl嵌入MainWindow,这是WPF的原生实现方式,从根源避免兼容性问题:
- 创建UserControl1替代Window1:
<UserControl x:Class="UserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Background="Magenta"> </UserControl>
- 修改MainWindow的XAML,直接嵌入UserControl:
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:_01" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525" Background="Cyan" AllowsTransparency="True" WindowStyle="None" ResizeMode="CanResizeWithGrip"> <local:UserControl1 /> </Window>
- 删除原有的
SetParent相关代码,WPF会自动处理尺寸同步。
方案二:修复Win32互操作逻辑
若必须保留两个独立Window的结构,需通过Win32 API精准同步子窗口的位置和尺寸:
- 修改MainWindow的
SizeChanged事件处理:
Private Sub Window_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged If Window1 IsNot Nothing Then Dim interopParent = New Interop.WindowInteropHelper(Me) Dim interopChild = New Interop.WindowInteropHelper(Window1) Dim parentRect As Interop.RECT Interop.User32.GetClientRect(interopParent.Handle, parentRect) Interop.User32.ClientToScreen(interopParent.Handle, parentRect) Interop.User32.SetWindowPos(interopChild.Handle, IntPtr.Zero, parentRect.Left, parentRect.Top, parentRect.Right - parentRect.Left, parentRect.Bottom - parentRect.Top, Interop.SetWindowPosFlags.SWP_NOZORDER Or Interop.SetWindowPosFlags.SWP_NOACTIVATE) End If End Sub
- 添加Win32 API声明到
Interop类:
Friend Class Interop <StructLayout(LayoutKind.Sequential)> Public Structure RECT Public Left As Integer Public Top As Integer Public Right As Integer Public Bottom As Integer End Structure <Flags> Public Enum SetWindowPosFlags As UInteger SWP_NOZORDER = &H4 SWP_NOACTIVATE = &H10 End Enum <DllImport("user32.dll", SetLastError:=True)> Public Shared Function GetClientRect(hWnd As IntPtr, ByRef lpRect As RECT) As Boolean End Function <DllImport("user32.dll")> Public Shared Function ClientToScreen(hWnd As IntPtr, ByRef lpPoint As RECT) As Boolean End Function <DllImport("user32.dll", SetLastError:=True)> Public Shared Function SetWindowPos(hWnd As IntPtr, hWndInsertAfter As IntPtr, X As Integer, Y As Integer, cx As Integer, cy As Integer, uFlags As SetWindowPosFlags) As Boolean End Function Public Const GWL_STYLE As Integer = -16 <Flags> Public Enum WindowStyles As UInteger WS_CAPTION = &HC00000 WS_THICKFRAME = &H40000 End Enum <DllImport("user32.dll", SetLastError:=True)> Public Shared Function GetWindowLongPtr(hWnd As IntPtr, nIndex As Integer) As IntPtr End Function <DllImport("user32.dll", SetLastError:=True)> Public Shared Function SetWindowLongPtr(hWnd As IntPtr, nIndex As Integer, dwNewLong As IntPtr) As IntPtr End Function End Class
- 修改Window1的构造函数,调整窗口样式避免布局偏差:
Sub New(parentWindowHandle As IntPtr) InitializeComponent() Dim interop = New Interop.WindowInteropHelper(Me) interop.EnsureHandle() Me.WindowStartupLocation = WindowStartupLocation.Manual Dim style = Interop.User32.GetWindowLongPtr(interop.Handle, Interop.GWL_STYLE) style = style And Not (Interop.WindowStyles.WS_CAPTION Or Interop.WindowStyles.WS_THICKFRAME) Interop.User32.SetWindowLongPtr(interop.Handle, Interop.GWL_STYLE, style) SetParent(interop.Handle, parentWindowHandle) End Sub
内容的提问来源于stack exchange,提问作者iTDy0lyMSN
相关产品推荐
相关产品推荐

