带有自定义镶边的WPF窗口在右侧和底部有不需要的轮廓

Jam*_*mes 9 wpf winapi xaml

我使用Microsoft.Windows.Shell dll创建了一个带自定义chrome的WPF窗口.这是代码:

<Style TargetType="Window" x:Key="ChromeLessWindowStyle">
        <Setter Property="shell:WindowChrome.WindowChrome">
            <Setter.Value>
                <shell:WindowChrome
           GlassFrameThickness="0"
          ResizeBorderThickness="5"          
          CornerRadius="5"
          CaptionHeight="30">
                </shell:WindowChrome>
            </Setter.Value>
        </Setter>
        <Setter Property="WindowStyle" Value="None"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Grid>
                            <Grid Background="#FF595959" >
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <Border Grid.Row="0" Height="30" Background="#FF393939">
                                    <DockPanel LastChildFill="False" Margin="0,1,5,0">
                                        <TextBlock DockPanel.Dock="Left" Style="{DynamicResource {x:Static coreKeys:TextBlockKeys.Default}}" FontWeight="Bold" Text="{TemplateBinding Title}" Margin="10,0,0,0" VerticalAlignment="Center"/>
                                        <!--Buttons-->
                                        <Button DockPanel.Dock="Right" behaviors:WindowCommandBehaviors.IsCloseButton="True" Style="{DynamicResource {x:Static coreKeys:ButtonKeys.Close}}" shell:WindowChrome.IsHitTestVisibleInChrome="True"/>
                                        <Button DockPanel.Dock="Right" behaviors:WindowCommandBehaviors.IsMaximizeButton="True" Style="{DynamicResource {x:Static coreKeys:ButtonKeys.Maximize}}" Visibility="{TemplateBinding WindowState,Converter={StaticResource WindowStateToVisibilityConverter},ConverterParameter=MaximizeButton }" shell:WindowChrome.IsHitTestVisibleInChrome="True" />
                                        <Button DockPanel.Dock="Right" behaviors:WindowCommandBehaviors.IsMaximizeButton="True" Style="{DynamicResource {x:Static coreKeys:ButtonKeys.Restore}}"  Visibility="{TemplateBinding WindowState,Converter={StaticResource WindowStateToVisibilityConverter}, ConverterParameter=RestoreButton }" shell:WindowChrome.IsHitTestVisibleInChrome="True" />
                                        <Button DockPanel.Dock="Right" behaviors:WindowCommandBehaviors.IsMinimizeButton="True" Style="{DynamicResource {x:Static coreKeys:ButtonKeys.Minimize}}" shell:WindowChrome.IsHitTestVisibleInChrome="True"/>
                                    </DockPanel>
                                </Border>
                                <ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}"/>
                            </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Run Code Online (Sandbox Code Playgroud)

这在正常情况下完美无缺,在我需要使用C#代码使用窗口之前,我无法发现问题.我有一个消息服务:

  1. 创建一个模态窗口.
  2. 使用WPF用户控件填充其内容.
  3. 将窗口的数据上下文设置为适当的ViewModel.
  4. 显示窗口

这是代码:

var userControl = viewRegistry.GetViewByKey(viewKey_); // Get the UserControl.
var modalWindow = new ModalCustomMessageDialog
{
    // Set the content of the window as the user control
    DataContext = viewModel_,
    // Set the data context of the window as the ViewModel
    Owner = Util.AppMainWindow,
    // Set the owner of the modal window to the app window.
    WindowStartupLocation = WindowStartupLocation.CenterOwner,
    //Title = viewModel.TitleText ?? "",
    ShowInTaskbar = false,
    Content = userControl,
    SizeToContent = SizeToContent.WidthAndHeight
};
if (showAsToolWindow_)
{
    modalWindow.ResizeMode = ResizeMode.NoResize;
    modalWindow.WindowStyle = WindowStyle.ToolWindow;
}
modalWindow.Loaded += modalWindow_Loaded;
modalWindow.Closed += CleanModalWindow;
modalWindow.Show();
Run Code Online (Sandbox Code Playgroud)

注意这一行

SizeToContent = SizeToContent.WidthAndHeight
Run Code Online (Sandbox Code Playgroud)

这需要调整窗口大小以符合用户控件的宽度和高度.由此产生的模态窗口在窗口的右侧和底部具有粗黑色轮廓.像这样:

窗口与大纲

窗口应该像(并在调整大小后)像这样:

好窗口

有几点值得注意:

  1. 一旦调整窗口大小,此黑色轮廓就会消失.

  2. 如果SizeToContent设置为SizeToContent.Height或SizeToContent.Width,则不会出现此大纲.但它会分别吹掉模态窗口的宽度或高度.

  3. 我认为重新绘制窗口可能存在一些问题.所以我尝试了以下代码来重绘窗口:

    private const int WmPaint = 0x000F;
    
    [DllImport("User32.dll")]
    public static extern Int64 SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
     ......................
    //Inside the Loaded event handler of the modalWindow
    var windowHandle = new WindowInteropHelper(modalWindow).Handle;
    SendMessage(windowHandle, WmPaint, IntPtr.Zero, IntPtr.Zero);
    
    Run Code Online (Sandbox Code Playgroud)

    这没有效果.

  4. 如果我已将固定的高度和宽度属性赋予填充窗口的用户控件,则不会出现此问题.但是,我不能永远这样做.

  5. 消息服务已经存在了很久以来,这个鬼轮廓最近在自定义chrome更改后出现了.

有人遇到过类似的情况吗?任何帮助将不胜感激.

Pav*_*ski 7

我遇到了同样的问题,并为Window类创建了以下扩展:

public static void FixLayout(this Window window)
{
    bool arrangeRequired = false;
    double deltaWidth = 0;
    double deltaHeight = 0;

    void Window_SourceInitialized(object sender, EventArgs e)
    {
        window.InvalidateMeasure();
        arrangeRequired = true;
        window.SourceInitialized -= Window_SourceInitialized;
    }

    void CalculateDeltaSize()
    {
        deltaWidth = window.ActualWidth - deltaWidth;
        deltaHeight = window.ActualHeight - deltaHeight;
    }

    void Window_LayoutUpdated(object sender, EventArgs e)
    {
        if (arrangeRequired)
        {
            if (window.SizeToContent == SizeToContent.WidthAndHeight)
            {
                CalculateDeltaSize();
            }
            window.Left -= deltaWidth * 0.5;
            window.Top -= deltaHeight * 0.5;
            window.LayoutUpdated -= Window_LayoutUpdated;
        }
        else
        {
            CalculateDeltaSize();
        }
    }

    window.SourceInitialized += Window_SourceInitialized;
    window.LayoutUpdated += Window_LayoutUpdated;
}
Run Code Online (Sandbox Code Playgroud)

让我们看看这段代码做了什么。我们为SourceIntializedLayoutUpdated事件创建了两个处理程序。的SourceIntialized事件处理程序执行窗口remeasurment(除去在窗口的右边缘和下边缘的黑条纹)。你可以停在这里,代码将是这样的:

public static void FixLayout(this Window window)
{    
    void Window_SourceInitialized(object sender, EventArgs e)
    {
        window.InvalidateMeasure();
        window.SourceInitialized -= Window_SourceInitialized;
    }

    window.SourceInitialized += Window_SourceInitialized;
}
Run Code Online (Sandbox Code Playgroud)

代码的剩余部分负责窗口重新排列。我注意到我的窗口与屏幕的理想中心有一些偏移。这是因为 WPF 在计算窗口位置时使用了错误的窗口大小。LayoutUpdated事件在SourceInitialized事件发生之前触发多次(计数取决于SizeToContent属性)。首先,我们计算正确和错误的窗口大小之间的差异。该SourceInitialized事件触发后,执行窗口重新测量并arrangeRequired为即将LayoutUpdated发生的事件设置标志以执行窗口重新排列。然后LayoutUpdated事件处理程序计算最终的偏移量(如果SizeToContent属性是WidthAndHeight) 并将窗口移动到正确的位置。之后窗口不再有黑色条纹,它位于屏幕或所有者的中心。这个方法应该在InitializeComponent方法之后的窗口构造函数中调用。


小智 5

我最近在包含动态生成的元素的窗口上使用自定义窗口镶边时遇到了这个问题.

为什么会这样?

如果我们使用具有静态内容的窗口,则窗口可以在初始化时知道​​包含其子元素所需的最大宽度/高度.

在我们想要使用具有自动缩放的动态元素的情况下,例如如果使用MVVM模式的视图,我们需要在所有bindngs(例如,视图)已经解析后请求窗口更新其视觉状态.

解决方案

为了强制执行我们上面提到的行为,我们需要使用窗口的ContentRendered事件,并将其用于InvalidateVisual().

在窗口的XAML中,您遇到了问题:

ContentRendered="Window_OnContentRendered"
Run Code Online (Sandbox Code Playgroud)

在代码隐藏中:

private void Window_OnContentRendered(object sender, EventArgs e)
{
    InvalidateVisual();
}
Run Code Online (Sandbox Code Playgroud)

祝你好运.


小智 2

有同样的问题。作为解决方法,我自己在 window_loaded-Method 中执行了“SizeToContent”:

void Window_Loaded(object sender, RoutedEventArgs e)
{
    Height = outerpanel.DesiredSize.Height + 
             WindowChrome.GetWindowChrome(this).CaptionHeight;
    Width = outerpanel.DesiredSize.Width;
}
Run Code Online (Sandbox Code Playgroud)