1

这个有点诡异。从 wpf 的主窗口(单击按钮),我正在创建另一个 STA 线程,其中我正在显示一个自定义窗口。此自定义窗口应用了一种使用 shell 中的 WindowChrome 类的样式。调用 Show() 方法时出现异常。

无法跨线程访问可冻结的“System.Windows.Shell.WindowChrome”,因为它无法被冻结。

如果我删除 WindowChrome 设置器,一切正常。我错过了什么?

我已经尝试将窗口镶边标记为冻结,但徒劳无功!

可在此处获得源的副本。

更新: 忘了提到在样式上添加 x:Shared="False" 似乎可以解决问题,但我不知道为什么!这会导致任何性能瓶颈吗?

MainWindow.xaml:

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication7"
        Title="MainWindow"
        Height="350"
        Width="525" Style="{StaticResource ResourceKey=WindowStyle}">
    <Grid>
        <Button Content="Open another window please..."
                Click="Button_Click" />
    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Threading;
using System.Windows;

namespace WpfApplication7
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Thread windowThread = new Thread(new ThreadStart(() =>
            {
                Window customWindow = new BackgroundWindow();
                customWindow.Closed += (s, a) => System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvokeShutdown(System.Windows.Threading.DispatcherPriority.Background);
                customWindow.Show();
                System.Windows.Threading.Dispatcher.Run();
            }));
            windowThread.IsBackground = true;
            windowThread.SetApartmentState(ApartmentState.STA);
            windowThread.Start();
        }
    }
}

背景窗口.xaml:

<Window x:Class="WpfApplication7.BackgroundWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication7"
        Title="BackgroundWindow"
        WindowStartupLocation="CenterScreen" 
        Style="{StaticResource ResourceKey=WindowStyle}">
</Window>

WindowStyle.xaml(与 App.xaml 合并

 <!-- Setting x:Shared=False will solve the cross threaded exception -->
<Style x:Key="WindowStyle"
       TargetType="{x:Type Window}">
    <Setter Property="Padding"
            Value="5,5,5,5" />
    <Setter Property="BorderBrush"
            Value="Black" />
    <Setter Property="BorderThickness"
            Value="1" />
    <Setter Property="WindowChrome.WindowChrome">
        <Setter.Value>
            <WindowChrome CaptionHeight="44"
                          GlassFrameThickness="-1"
                          CornerRadius="0,0,0,0" />
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Window}">
                <Border Background="{TemplateBinding Property=Background}"
                        BorderBrush="{TemplateBinding Property=BorderBrush}"
                        BorderThickness="{TemplateBinding Property=BorderThickness}">
                    <Grid Background="{TemplateBinding Property=Background}"
                          UseLayoutRounding="True"
                          SnapsToDevicePixels="True">
                        <Grid.RowDefinitions>
                            <!-- Window Controls -->
                            <RowDefinition Height="44" />
                            <!-- Content -->
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <DockPanel x:Name="PART_DragPanel"
                                   Grid.Row="0"
                                   Background="Black">
                            <Button x:Name="PART_CloseButton"
                                    DockPanel.Dock="Right"
                                    HorizontalAlignment="Right"
                                    Margin="3,8,8,8"
                                    WindowChrome.IsHitTestVisibleInChrome="True"
                                    Width="20"
                                    Height="20" />
                            <Button x:Name="PART_RestoreButton"
                                    DockPanel.Dock="Right"
                                    HorizontalAlignment="Right"
                                    Margin="3,8,3,8"
                                    Visibility="Collapsed"
                                    WindowChrome.IsHitTestVisibleInChrome="True"
                                    Width="20"
                                    Height="20" />
                            <Button x:Name="PART_MinimizeButton"
                                    DockPanel.Dock="Right"
                                    HorizontalAlignment="Right"
                                    Margin="3,8,3,8"
                                    Visibility="Collapsed"
                                    WindowChrome.IsHitTestVisibleInChrome="True"
                                    Width="20"
                                    Height="20" />
                            <TextBlock x:Name="PART_Title"
                                       DockPanel.Dock="Left"
                                       Margin="8,8,8,8"
                                       Text="{TemplateBinding Property=Title}"
                                       IsHitTestVisible="False"
                                       WindowChrome.IsHitTestVisibleInChrome="True" />
                        </DockPanel>
                        <Border x:Name="contentBorder"
                                Grid.Row="1"
                                Padding="{TemplateBinding Property=Padding}">
                            <AdornerDecorator>
                                <ContentPresenter />
                            </AdornerDecorator>
                        </Border>
                    </Grid>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="ResizeMode"
                             Value="CanMinimize">
                        <Setter TargetName="PART_MinimizeButton"
                                Property="Visibility"
                                Value="Visible" />
                    </Trigger>
                    <Trigger Property="ResizeMode"
                             Value="NoResize">
                        <Setter TargetName="PART_RestoreButton"
                                Property="Visibility"
                                Value="Collapsed" />
                        <Setter TargetName="PART_MinimizeButton"
                                Property="Visibility"
                                Value="Collapsed" />
                    </Trigger>
                    <Trigger Property="ResizeMode"
                             Value="CanResize">
                        <Setter TargetName="PART_RestoreButton"
                                Property="Visibility"
                                Value="Visible" />
                        <Setter TargetName="PART_MinimizeButton"
                                Property="Visibility"
                                Value="Visible" />
                    </Trigger>
                    <Trigger Property="ResizeMode"
                             Value="CanResizeWithGrip">
                        <Setter TargetName="PART_RestoreButton"
                                Property="Visibility"
                                Value="Visible" />
                        <Setter TargetName="PART_MinimizeButton"
                                Property="Visibility"
                                Value="Visible" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="ResizeMode"
                 Value="CanResize">
            <Setter Property="WindowChrome.ResizeBorderThickness"
                    Value="5,5,5,5" />
        </Trigger>
        <Trigger Property="ResizeMode"
                 Value="CanResizeWithGrip">
            <Setter Property="WindowChrome.ResizeBorderThickness"
                    Value="5,5,5,5" />
        </Trigger>
        <Trigger Property="ResizeMode"
                 Value="NoResize">
            <Setter Property="WindowChrome.ResizeBorderThickness"
                    Value="0,0,0,0" />
        </Trigger>
    </Style.Triggers>
</Style>
4

1 回答 1

3

问题的根源是当你这样做时:

<Setter Property="WindowChrome.WindowChrome">
    <Setter.Value>
        <WindowChrome CaptionHeight="44"
                      GlassFrameThickness="-1"
                      CornerRadius="0,0,0,0" />
    </Setter.Value>
</Setter>

值(WindowChrome在这种情况下为实例)创建一次,并在构造时保留样式。每次将样式应用于控件时,它都不会创建新实例。首先,您将样式应用于主窗口。此时样式是从 xaml 创建的,因此创建了 WindowChrome 实例。当您从另一个线程将样式应用于背景窗口时 - 不会重新创建样式,Style而是使用已创建的实例。值得注意的是,样式设置器包含在另一个线程中创建的值,该值是可冻结但未冻结的 - 因此它会因异常而失败,因为它被认为是危险的(并且无论如何都不会工作,因为您无法分配在另一个线程中创建的这个 WindowChrome 实例到您的BackgroundWindow,也不能克隆它)。

当您申请x:Shared=False样式时 -Style会在每个请求上创建新实例,从而避免出现问题(因为也为每个样式实例创建了 WindowChrome 的新实例)。另一种强制新实例的方法是:

<WindowChrome x:Key="chrome" x:Shared="False"
              CaptionHeight="44"
              GlassFrameThickness="-1"
              CornerRadius="0,0,0,0" />

<Style ...>
    <Setter Property="WindowChrome.WindowChrome" Value="{DynamicResource chrome}"/>
</Style>

只有在没有其他方法时才使用多个 UI 线程,因为它们可能很棘手。

于 2017-06-27T19:17:18.833 回答