Наличие базового класса Window приносит критический недостаток, а именно то, что связывание со свойствами в вашем базовом классе сделать намного сложнее (и принятый в настоящее время ответ не решает эту проблему) . Какой смысл наследовать, если вы не можете ссылаться на базовые свойства? Я выяснил, как это настроить после нескольких долгих часов, и хотел поделиться надеждой, что другие избавятся от этой боли.
Возможно, вам потребуется использовать такие вещи, как преобразователи значений, на которые можно ссылаться только через статическое связывание, которое в моем случае имело смысл иметь в классе WindowBase. Я включил пример, потому что мне было трудно использовать эти преобразователи последовательно как в режиме разработки, так и в режиме работы.
Вы не можете установить свойство x: Name этого унаследованного окна с помощью XAML, но вам может не потребоваться это делать, если используется описанный ниже подход. Я включил пример того, как установить имя, потому что наследование от Window не позволит вам установить имя во время разработки в подклассе. Я не рекомендую полагаться на имя окна во время разработки, но настройка d: DataContext должна позаботиться о любых связывающих вас потребностях.
Имейте в виду, что в режиме разработки, но не в режиме выполнения, экземпляр WindowBase (или класс, указанный в d: DataContext) будет создан в режиме разработки и использован в качестве контекста привязки. Таким образом, в очень специфических случаях вы можете увидеть несоответствия данных, но в подавляющем большинстве случаев использования этого подхода должно быть достаточно.
WindowBase.cs
`` ``
public class WindowBase : Window
{
//User-Defined UI Configuration class containing System.Drawing.Color
//and Brush properties (platform-agnostic styling in your Project.Core.dll assembly)
public UIStyle UIStyle => Core.UIStyle.Current;
//IValueConverter that converts System.Drawing.Color properties
//into WPF-equivalent Colors and Brushes
//You can skip this if you do not need or did not implement your own ValueConverter
public static IValueConverter UniversalValueConverter { get; } = new UniversalValueConverter();
public WindowBase()
{
//Add window name to scope so that runtime properties can be referenced from XAML
//(Name setting must be done here and not in xaml because this is a base class)
//You probably won't need to, but working example is here in case you do.
var ns = new NameScope();
NameScope.SetNameScope(this, ns);
ns["window"] = this;
//Call Initialize Component via Reflection, so you do not need
//to call InitializeComponent() every time in your base class
this.GetType()
.GetMethod("InitializeComponent",
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance)
.Invoke(this, null);
//Set runtime DataContext - Designer mode will not run this code
this.DataContext = this;
}
//Stub method here so that the above code can find it via reflection
void InitializeComponent() { }
}
SubClassWindow.xaml
<local:WindowBase
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:local="clr-namespace:YourProjectNamespace"
x:Class="YourProjectNamespace.SubClassWindow"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type= {x:Type local:WindowBase}, IsDesignTimeCreatable=True}"
Title="SubClassWindow" Height="100" Width="300">
<!--Design-time DataContext is set in d:DataContext. That option does not affect runtime data binding
Replace local:WindowBase with local:SubClassWindow if you need to access properties in SubClassWindow-->
<Grid Background="{Binding UIStyle.BackgroundColor, Converter={x:Static local:WindowBase.UniversalValueConverter}}"></Grid>
</local:WindowBase>
Ничего не требуется в коде SubClassWindow (даже в конструкторе).