在 Loaded 事件中调用 Close() 时 WPF 自定义 WindowChrome 异常
WPF Custom WindowChrome exception when invoke Close() in Loaded event
场景:
- 带有自定义模板的 WPF window (
CustomWindow
)/WindowChrome.
- 问题: 尝试在
Loaded
事件中关闭 window 抛出 NullReferenceException
.
- 解决方法:
- 如果我注释掉
WindowChrome
setter,那么它就起作用了。
- 但失去风俗chrome
- 如果我在关闭 window 之前延迟任意时间,那么它会起作用。
- 使用延迟感觉不对
问题:
只有延迟解决方法允许我 1) 保留我的自定义 window chrome 和 2) 不抛出异常。
但是延迟解决方案感觉像是一个 hack。
有更好的方法吗?
自定义窗口xaml
<Style TargetType="v:CustomWindow">
<!-- Workaround 1: Works when commented out -->
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome UseAeroCaptionButtons="False" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type v:CustomWindow}">
<DockPanel Background="White">
<!-- Caption bar -->
<Grid Background="Cyan" DockPanel.Dock="Top">
<Grid Margin="5">
<!-- Title -->
<TextBlock FontSize="20" Text="{TemplateBinding Title}" />
<!-- Close button -->
<Button Width="30" Command="SystemCommands.CloseWindowCommand"
HorizontalAlignment="Right"
WindowChrome.IsHitTestVisibleInChrome="True"
Content="Close" />
</Grid>
</Grid>
<!-- Window content -->
<ContentPresenter Content="{TemplateBinding Content}" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
用法
var dialog = new CustomWindow();
dialog.Loaded += async delegate
{
// On loaded, do something, then close immediately
// await Task.Delay(10); // <-- Workaround 2: Uncomment this, and it seems to work
dialog.Close();
};
dialog.ShowDialog();
堆栈跟踪:
at System.Windows.Shell.WindowChromeWorker._ExtendGlassFrame()
at System.Windows.Shell.WindowChromeWorker._UpdateFrameState(Boolean force)
at System.Windows.Shell.WindowChromeWorker._ApplyNewCustomChrome()
at System.Windows.Shell.WindowChromeWorker._WindowSourceInitialized(Object sender, EventArgs e)
at System.Windows.Window.OnSourceInitialized(EventArgs e)
at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
at System.Windows.Window.CreateSourceWindowDuringShow()
at System.Windows.Window.SafeCreateWindowDuringShow()
at System.Windows.Window.ShowHelper(Object booleanBox)
at System.Windows.Window.Show()
at System.Windows.Window.ShowDialog()
环境:
- Windows 10 专业版
- Visual Studio 2017
- .NET 4.6.1
解决方案
将代码更改为以下工作:
var dialog = new CustomWindow();
dialog.Loaded += delegate
{
Dispatcher.BeginInvoke(new Action(
delegate
{
// On loaded, do something, then close immediately
dialog.Close();
})
);
};
dialog.ShowDialog();
考虑使用 Dispatcher 延迟关闭,直到所有典型工作完成:
Dispatcher.BeginInvoke(new Action(ShutMeDown), DispatcherPriority.Background);
我不完全确定 DispatcherPriority.Background
是否正确,但基本思想是您的 window chrome 将使用一些工作优先级自行初始化,并且您的关机应该发生的优先级较低,因此将被推迟。
场景:
- 带有自定义模板的 WPF window (
CustomWindow
)/WindowChrome. - 问题: 尝试在
Loaded
事件中关闭 window 抛出NullReferenceException
. - 解决方法:
- 如果我注释掉
WindowChrome
setter,那么它就起作用了。- 但失去风俗chrome
- 如果我在关闭 window 之前延迟任意时间,那么它会起作用。
- 使用延迟感觉不对
- 如果我注释掉
问题:
只有延迟解决方法允许我 1) 保留我的自定义 window chrome 和 2) 不抛出异常。
但是延迟解决方案感觉像是一个 hack。
有更好的方法吗?
自定义窗口xaml
<Style TargetType="v:CustomWindow">
<!-- Workaround 1: Works when commented out -->
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome UseAeroCaptionButtons="False" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type v:CustomWindow}">
<DockPanel Background="White">
<!-- Caption bar -->
<Grid Background="Cyan" DockPanel.Dock="Top">
<Grid Margin="5">
<!-- Title -->
<TextBlock FontSize="20" Text="{TemplateBinding Title}" />
<!-- Close button -->
<Button Width="30" Command="SystemCommands.CloseWindowCommand"
HorizontalAlignment="Right"
WindowChrome.IsHitTestVisibleInChrome="True"
Content="Close" />
</Grid>
</Grid>
<!-- Window content -->
<ContentPresenter Content="{TemplateBinding Content}" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
用法
var dialog = new CustomWindow();
dialog.Loaded += async delegate
{
// On loaded, do something, then close immediately
// await Task.Delay(10); // <-- Workaround 2: Uncomment this, and it seems to work
dialog.Close();
};
dialog.ShowDialog();
堆栈跟踪:
at System.Windows.Shell.WindowChromeWorker._ExtendGlassFrame()
at System.Windows.Shell.WindowChromeWorker._UpdateFrameState(Boolean force)
at System.Windows.Shell.WindowChromeWorker._ApplyNewCustomChrome()
at System.Windows.Shell.WindowChromeWorker._WindowSourceInitialized(Object sender, EventArgs e)
at System.Windows.Window.OnSourceInitialized(EventArgs e)
at System.Windows.Window.CreateSourceWindow(Boolean duringShow)
at System.Windows.Window.CreateSourceWindowDuringShow()
at System.Windows.Window.SafeCreateWindowDuringShow()
at System.Windows.Window.ShowHelper(Object booleanBox)
at System.Windows.Window.Show()
at System.Windows.Window.ShowDialog()
环境:
- Windows 10 专业版
- Visual Studio 2017
- .NET 4.6.1
解决方案
将代码更改为以下工作:
var dialog = new CustomWindow();
dialog.Loaded += delegate
{
Dispatcher.BeginInvoke(new Action(
delegate
{
// On loaded, do something, then close immediately
dialog.Close();
})
);
};
dialog.ShowDialog();
考虑使用 Dispatcher 延迟关闭,直到所有典型工作完成:
Dispatcher.BeginInvoke(new Action(ShutMeDown), DispatcherPriority.Background);
我不完全确定 DispatcherPriority.Background
是否正确,但基本思想是您的 window chrome 将使用一些工作优先级自行初始化,并且您的关机应该发生的优先级较低,因此将被推迟。