Окно WPF, реализованное как CustomControl, не применяет шаблон во время разработки

Я пытаюсь реализовать окно WPF как CustomControl, поэтому я могу переопределить WindowChrome по умолчанию и создать свой собственный многоразовый вид в наборе приложений. У меня есть работающий демонстрационный проект, который будет автоматически применять пользовательский шаблон стиля элемента управления к окну при его запуске, но мне трудно заставить его работать чисто во время разработки.

Я понимаю, что это дубликат WPF пользовательское окно не применяет свой общий шаблон во время разработки, но на этот вопрос не было применимого ответа, который подходит для моего варианта использования. В моей демо-версии все находится в одном проекте, поэтому нет проблем с несоответствием целевых процессоров. И мне нужно изменить стиль окна, поэтому простая замена части управления содержимым тоже не работает.

Вот мой базовый класс окна (Classes\WindowBase.cs) с дополнительным свойством зависимости для тестирования:

using System.Windows;

namespace WPFTestWindowSubclassing.Classes
{
    public class WindowBase : Window
    {
        public string ContentText
        {
            get { return (string)GetValue(ContentTextProperty); }
            set { SetValue(ContentTextProperty, value); }
        }

        public static readonly DependencyProperty ContentTextProperty =
            DependencyProperty.Register(
                "ContentText",
                typeof(string),
                typeof(WindowBase),
                new PropertyMetadata(string.Empty));
    }
}

И мой класс окна CustomControl (CustomContorls\CustomWindow.cs):

using System.Windows;
using System.Windows.Controls;
using WPFTestWindowSubclassing.Classes;
    
namespace WPFTestWindowSubclassing.CustomControls
{
    [TemplatePart(Name = "PART_CloseButton", Type = typeof(Button))]
    public class CustomWindow : WindowBase
    {
        static CustomWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(
                typeof(CustomWindow),
                new FrameworkPropertyMetadata(typeof(CustomWindow)));
        }
    
        public override void OnApplyTemplate()
        {
            ((Button)GetTemplateChild("PART_CloseButton")).Click += (s, e) => this.Close();
            ContentText += " Internal Template Applied.";
            base.OnApplyTemplate();
        }
    }
}

Ресурс Style для окна (ResourceDictionaries\CustomControls\CustomWindowStyle.xaml):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cc="clr-namespace:WPFTestWindowSubclassing.CustomControls">
    
    <Style
        x:Key="{x:Type cc:CustomWindow}"
        TargetType="{x:Type cc:CustomWindow}">
        <Setter Property="Background" Value="AliceBlue" />
        <Setter Property="AllowsTransparency" Value="True" />
        <Setter Property="WindowStyle" Value="None" />
        <Setter Property="SnapsToDevicePixels" Value="true" />
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome
                    CaptionHeight="30"
                    CornerRadius="0"
                    GlassFrameThickness="0"
                    NonClientFrameEdges="None"
                    ResizeBorderThickness="10"
                    UseAeroCaptionButtons="False" />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="{x:Type cc:CustomWindow}">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Button
                                x:Name="PART_CloseButton"
                                HorizontalAlignment="Right"
                                VerticalAlignment="Top"
                                Height="30" Width="30"
                                WindowChrome.IsHitTestVisibleInChrome="True"/>
                            <AdornerDecorator>
                                <ContentPresenter />
                            </AdornerDecorator>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

Моя тема\Generic.xaml:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary
            Source="../ResourceDictionaries/CustomControls/CustomWindowStyle.xaml" />
    </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

И, наконец, мои MainWindow.xaml и MainWindow.xaml.cs:

<cc:CustomWindow
    x:Class="WPFTestWindowSubclassing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:cc="clr-namespace:WPFTestWindowSubclassing.CustomControls"
    mc:Ignorable="d"
    Title="MainWindow"
    Height="300"
    Width="400"
    ContentText="Hello?"
    Loaded="Window_Loaded">
    <Grid>
        <TextBlock
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type cc:CustomWindow}}, Path=ContentText}"
            Grid.RowSpan="2" />
    </Grid>
</cc:CustomWindow>
using System.Windows;
using WPFTestWindowSubclassing.CustomControls;

namespace WPFTestWindowSubclassing
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : CustomWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ContentText += " Window Loaded!";
        }

        public override void OnApplyTemplate()
        {
            ContentText += " Template Applied!";
            base.OnApplyTemplate();
        }
    }
}

Это не полная реализация красивого стиля, но этого достаточно, чтобы воспроизвести проблему. Все это работает, когда я запускаю проект. Окно без рамки, ручки изменения размера все еще работают, и кнопка закрытия работает. Но в Visual Studio XAML Designer оно по-прежнему выглядит как обычное окно. Я могу заставить его работать, вручную применяя стиль к окну как таковому:

<cc:CustomWindow
    ...
    Style="{StaticResource {x:Type cc:CustomWindow}}">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary
                    Source="ResourceDictionaries/CustomControls/CustomWindowStyle.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    ...

Но зачем требуется это дополнительное прямое присвоение стиля, если оно работает во время выполнения? Есть ли более простой способ заставить окно работать только во время разработки, как и любую другую реализацию CustomControl?

Я наткнулся на этот ответ (https://stackoverflow.com/a/59297503/1236614) из аналогичного вопроса, который кажется чтобы указать, что VS XAML Designer делает какую-то странную закулисную ерунду вокруг отображения окна, но действительно ли это происходит? Это также может объяснить, почему привязка к заголовку окна или моему свойству CustomWindow ContentText также не работает во время разработки — предок во время разработки может на самом деле не быть окном. Должен быть какой-то способ обойти это, верно?


person nedmech    schedule 18.03.2021    source источник