You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的原生实现方式,从根源避免兼容性问题:

  1. 创建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>
  1. 修改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>
  1. 删除原有的SetParent相关代码,WPF会自动处理尺寸同步。

方案二:修复Win32互操作逻辑

若必须保留两个独立Window的结构,需通过Win32 API精准同步子窗口的位置和尺寸:

  1. 修改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
  1. 添加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
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 11:07:00