自定义镀铬的WPF窗口在右侧和底部有不需要的轮廓

自定义镀铬的WPF窗口在右侧和底部有不需要的轮廓,wpf,xaml,winapi,Wpf,Xaml,Winapi,我已经使用Microsoft.Windows.Shell dll创建了一个带有自定义chrome的WPF窗口。 以下是代码: <Style TargetType="Window" x:Key="ChromeLessWindowStyle"> <Setter Property="shell:WindowChrome.WindowChrome"> <Setter.Value> <shel

我已经使用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>
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();
注意这一行

SizeToContent = SizeToContent.WidthAndHeight
这需要调整窗口的大小以适应用户控件的宽度和高度。 由此产生的模态窗口在窗口的右侧和底部有一个厚厚的黑色轮廓。像这样:

窗口应如下所示(调整大小后变为):

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

    window.SourceInitialized += Window_SourceInitialized;
}

有几点值得注意:

  • 调整窗口大小后,此黑色轮廓将立即消失

  • 如果SizeToContent设置为SizeToContent.Height或SizeToContent.Width,则不会显示此大纲。但是,它会分别取消模式窗口的宽度或高度

  • 我想重新绘制窗口可能会有问题。因此,我尝试使用以下代码重新绘制窗口:

    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);
    
    这没有效果

  • 如果为填充窗口的用户控件指定了固定高度和宽度属性,则不会出现此问题。然而,我不能总是这样做

  • 消息服务自很久以前就已经存在了,这个幻影轮廓最近在定制chrome更改后出现


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

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

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

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

    为什么会发生这种情况?

    如果我们使用的是一个包含静态内容的窗口,那么在初始化时窗口可以知道包含其子元素所需的最大宽度/高度

    如果我们希望使用具有自动缩放功能的动态元素,例如使用MVVM模式的视图,我们需要请求窗口在其所有绑定(例如视图)都已解析后更新其视觉状态

    解决方案

    要强制执行我们在上面推理的行为,我们需要使用窗口的ContentRendered事件,并使用它使EvalidateSual()失效

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

    ContentRendered="Window_OnContentRendered"
    
    在代码隐藏中:

    private void Window_OnContentRendered(object sender, EventArgs e)
    {
        InvalidateVisual();
    }
    

    祝你好运。

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

    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;
    }
    
    让我们看看这段代码的作用。我们为
    sourceinitialized
    LayoutUpdated
    事件创建两个处理程序。
    sourceinitialized
    事件处理程序执行窗口重新测量(删除窗口右边缘和下边缘的黑色条纹)。您可以停在这里,代码如下:

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

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

    您可以在窗口标记中设置AllowTransparency=“True”,并且可以添加OpacityMask。如果你有时间看这段视频,下面就是一个例子

    <Window ..... 
        xmlns:local="clr-namespace:Your.Project"
        AllowsTransparency="True">
    <FrameworkElement.Resources>
        <!-- Set the template style of the HolderForm -->
        <Style TargetType="{x:Type local:YourForm}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Window}">
    
                        <!-- Outer border with the drop shadow margin -->
                        <Border  Padding="{Binding OuterMarginSize, FallbackValue=0}" BorderBrush="{StaticResource BackgroundDarkBrush}" BorderThickness="{Binding FlatBorderThickness}">
    
                            <!-- Main window outline -->
                            <Grid>
                                <!-- Corner clipping -->
                                <Grid.OpacityMask>
                                    <VisualBrush Visual="{Binding ElementName=OpacityContainer}" />
                                </Grid.OpacityMask>
    
                                <!-- Opacity mask for corners on grid -->
                                <Border x:Name="OpacityContainer"
                                        Background="Black"
                                        CornerRadius="{Binding WindowCornerRadius, FallbackValue=10}" />
    
                                <!-- The main window content -->
                                <!-- Page Content -->
                                <Border Padding="{Binding InnerContentPadding}" ClipToBounds="True">
                                    <ContentPresenter Content="{TemplateBinding Content}" />
                                </Border>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </FrameworkElement.Resources>
    
    <Grid>
        <!--Your Content-->
    </Grid>
    
    </Window>
    

    为了完整起见,这个问题的解决方法似乎也很有效(但这是一种黑客行为,似乎更好)

    var window=newwindow1();
    window.SourceInitialized+=(s,e)=>
    {
    //如果没有这种变通办法,
    //黑色条显示在窗口的右边缘和下边缘。
    var sizeToContent=window.sizeToContent;
    var snapsToDevicePixels=window.snapsToDevicePixels;
    window.SnapsToDevicePixels=true;
    window.SizeToContent=SizeToContent==SizeToContent.widthandhight?SizeToContent.Height:SizeToContent.Manual;
    window.SizeToContent=SizeToContent;
    window.SnapsToDevicePixels=SnapsToDevicePixels;
    };
    
    它显然与调用
    InvalidateMeasure()
    具有相同的效果,就像Paviel的回答一样

    仅在
    ContentRendered
    上调用
    InvalidateVisual()
    (就像在另一个答案中一样)会在窗口中留下某种错误的填充


    编辑:我发现Paviel对窗口未居中的修复在我的情况下不起作用。事实上,这让情况变得更糟

    以下改编版本完成了此任务:

    publicstaticvoidfixlayout(窗口)
    {
    bool arrangeRequired=假;
    双宽度beforefix=0;
    双高度beforefix=0;
    无效窗口\u源已初始化(objec
    
    var modalWindow = new ModalCustomMessageDialog(viewModel_.Width, viewModel_.Height)
    {
      // 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
    };