Я пытаюсь реализовать окно 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 также не работает во время разработки — предок во время разработки может на самом деле не быть окном. Должен быть какой-то способ обойти это, верно?