我用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>
这在正常情况下工作得很好,直到我有使用C#代码的窗口的要求,我才发现问题。我有一个消息服务:
1.创建模式窗口。
1.使用WPF用户控件填充其内容。
1.将窗口的数据上下文设置为适当的ViewModel。
1.显示窗口
下面是代码:
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
这会调整窗口的大小以适应用户控件的宽度和高度。这样生成的模态窗口在窗口的右下角有一个黑色的粗轮廓。如下所示:
窗口应该像这样(调整大小后变成):
有几点值得注意:
1.调整窗口大小后,此黑色轮廓将立即消失。
1.如果将SizeToContent设置为SizeToContent.Height或SizeToContent.Width,则不会显示此轮廓。但是,它会分别取消模态窗口的Width或Height。
1.我想窗口重绘可能会有问题,所以我尝试了下面的代码来重绘窗口:
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);
这没有效果。
1.这个问题不会出现,如果我有固定的高度和宽度属性给用户控件,填充窗口。但是,我不能总是这样做。
1.消息服务已经存在了很长时间,这个幽灵轮廓最近在自定义镀 chrome 后出现了。
有人遇到过类似的情况吗?任何帮助都将不胜感激。
7条答案
按热度按时间kninwzqo1#
我最近在一个包含动态生成元素的窗口上使用自定义窗口镶边时遇到了这个问题。
"为什么会这样“
如果我们使用的是一个静态内容的窗口,窗口可以在初始化时知道包含其子元素所需的最大宽度/高度。
在我们想要使用自动缩放的动态元素的情况下,例如使用MVVM模式的视图,我们需要请求窗口在所有绑定(例如视图)被解析后更新其视觉状态。
解决方案
为了执行我们上面推理的行为,我们需要使用窗口的ContentRendered事件,并将其用于InvalidateVisual()。
在窗口的XAML中,您遇到了问题:
在代码背后:
mwkjh3gx2#
我也遇到了同样的问题,并为
Window
类创建了以下扩展:让我们看看这段代码的作用。我们为
SourceIntialized
和LayoutUpdated
事件创建了两个处理程序。SourceIntialized
事件处理程序执行窗口重新测量(删除窗口右边缘和下边缘的黑色条纹)。您可以在此停止,代码如下所示:代码的剩余部分负责窗口重新排列。我注意到我的窗口与理想的屏幕中心有一些偏移。这是因为WPF在计算窗口位置时使用了错误的窗口大小。
LayoutUpdated
事件在SourceInitialized
事件发生之前触发了几次(计数取决于SizeToContent
属性)。首先,我们计算正确和错误窗口大小之间的差异。在SourceInitialized
事件触发之后,执行窗口重新测量并为即将发生的LayoutUpdated
事件设置arrangeRequired
标志以执行窗口重新排列。然后LayoutUpdated
事件处理程序计算最终偏移量(如果SizeToContent
属性为WidthAndHeight
),并将窗口移到正确的位置,之后窗口不再有黑条。并且它位于屏幕或所有者的中心。此方法应在窗口构造函数中InitializeComponent
方法之后调用。6uxekuva3#
遇到了同样的问题。作为一种解决方案,我自己在window_loaded-Method中执行了“SizeToContent”:
7hiiyaii4#
你可以在window标签中设置AllowsTransparency=“True”,你可以添加一个OpacityMask。如果你看这个视频的时间是https://www.youtube.com/watch?v=TDOxHx-AMqQ&t=1s,这是下面的一个例子。
ndh0cuux5#
出于完整性的考虑,MahApps解决这个问题的方法似乎也有效(但它是一个黑客。Paviel's answer似乎更好)。
它显然与调用
InvalidateMeasure()
具有相同的效果,就像Paviel的回答一样。只有在
ContentRendered
上调用InvalidateVisual()
(就像在另一个答案中)才会在窗口中留下某种错误的填充。**编辑:**我发现Paviel对窗口不居中的修复在我的例子中不起作用,事实上,它使情况变得更糟。
下面的改编版本完成了这项工作:
在调用
InvalidateMeasure()
之后,只需计算一次正确的窗口位置,这非常简单:给定固定之前的窗口大小和固定之后的窗口大小,简单地减去差值。egdjgwm86#
有点晚了,但也许有人可以用这个。
我遇到过同样的问题,我通过在ModalCustomMessageDialog中添加宽度和高度作为参数来解决这个问题(我假设它继承了System.Windows.Window)。
就像这样:
然后在您的对话框服务更改为:
您需要定义一个视图模型可以实现的接口,它包含Width和Height。
x8diyxa77#
如果您希望使用JC a的OnContentRendered解决方案,但由于您使用的窗口是由资源字典中的样式主题化的,并且不希望在使用此样式的每个窗口中实现OnContentRendered处理程序而遇到困难,你可以为资源字典建立一个代码隐藏文件,并在其中放置处理程序。我发现这方面的一个困难是,在该样式中,您不能为RoutedEvents以外的事件设置处理程序,而OnContentRendered事件不是RoutedEvents。
解决方案
在窗口模板上为路由事件“Loaded”设置一个处理程序,如果需要,可以在其中设置OnContentRendered处理程序。
XAML:
背后的资源字典代码: