-2

I have a class EmptyWindow which inherits from WPF Window. It has no XAML and is only used to set WindowStyle = None to remove Windows OS header.

I have bunch of windows that inherits from EmptyWindow. I want to emulate header without copy/pasting same code 50 times.

I want a dependency/attached property EmptyWindow.TitleContent that would place text on the top of the window.

So the usage in the children would be EmptyWindow.TitleContent="A Child Title" and the actual child content would go into EmptyWindow.Content

If it were a custom ContentControl I'd supply default template for it with TitleContent to be bound to ContentPresenter in a grid row 0 and Content in row 1.

I cant quite figure how to make the same in windows.

What have I done (and it is not working - the inherited window is supposed to display "A Child Title" string on the top and "A WINDOW" in the middle, I have empty black window)

App.xaml

<Application x:Class="WindowSubclassing.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WindowSubclassing"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <Style TargetType="local:SlimWindow" BasedOn="{StaticResource {x:Type Window}}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:SlimWindow}">
                        <Grid Background="Blue">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <ContentPresenter ContentSource="TitleContent" />
                            <AdornerDecorator Grid.Row="1">
                                <ContentPresenter/>
                            </AdornerDecorator>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>    
    </Application.Resources>
</Application>

the base window class:

    public class SlimWindow : Window
    {
        static SlimWindow()
        {
            var metaData = new FrameworkPropertyMetadata(typeof(SlimWindow));
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimWindow), metaData);
        }

        public object TitleContent
        {
            get => GetValue(TitleContentProperty);
            set => SetValue(TitleContentProperty, value);
        }
        public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(
            nameof(TitleContent), typeof(object), typeof(SlimWindow));
    }

the final window:

<local:SlimWindow x:Class="WindowSubclassing.AWindow"
        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:WindowSubclassing"
        mc:Ignorable="d"
        Title="AWindow" Height="450" Width="800" TitleContent="A Child Title">
    <Grid>
        <TextBlock Text="A WINDOW" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</local:SlimWindow>
Boppity Bop
  • 9,613
  • 13
  • 72
  • 151

2 Answers2

1

The custom window type should have a default style defined in Themes/Generic.xaml:

public class EmtpyWindow : Window
{
    static EmtpyWindow()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(EmtpyWindow),
            new FrameworkPropertyMetadata(typeof(EmtpyWindow)));
    }

    public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(
        nameof(TitleContent), typeof(object), typeof(EmtpyWindow));

    public object TitleContent
    {
        get => GetValue(TitleContentProperty);
        set => SetValue(TitleContentProperty, value);
    }
}

Themes/Generic.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp1">
    <Style TargetType="local:EmtpyWindow" BasedOn="{StaticResource {x:Type Window}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:EmtpyWindow">
                    <Border Background="{TemplateBinding Background}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <ContentPresenter ContentSource="TitleContent" />
                            <AdornerDecorator Grid.Row="1">
                                <ContentPresenter/>
                            </AdornerDecorator>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Window1.xaml:

<local:EmtpyWindow x:Class="WpfApp1.Window1"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800"
        TitleContent="A Child Title">
    <Grid>
        <TextBlock>contents...</TextBlock>
    </Grid>
</local:EmtpyWindow>
mm8
  • 163,881
  • 10
  • 57
  • 88
  • that what i tried first. but for some reason the generic template wasnt picked up so i decided a window cant be styled that way.. so i wrote this question.. let me do a clean prototype with your code. thank you for the effort. *PS - ah! i forgot `DefaultStyleKeyProperty` that must be it!* – Boppity Bop May 24 '23 at 11:41
  • 1
    @BoppityBop: Note the `static` constructor of the `EmptyWindow` class in my answer. This should cause the default style/template to be picked up. – mm8 May 24 '23 at 11:42
1

You can't override the default Style without adding the new default Style to the Generic.xaml ResourceDictionary:

static SlimWindow()
{
  var metaData = new FrameworkPropertyMetadata(typeof(SlimWindow));

  // The following line overrides the default template for the current control.
  // WPF will lookup the new Style in the Generic.xaml file,
  // and only in Gerneric.xaml.  
  DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimWindow), metaData);
}

Because you have added the default Style to App.xaml, WPF is not able to find it. Your SlimWindow ends up without a ControlTemplate and therefore without a ContentPresenter to host the Window.Content value.
The important point is that WPF will only lookup default styles in the Generic.xaml file which must be located in the "\Themes" folder

Solution 1

To fix your issue either remove the static constructor (and leave the Style in App.xaml):

SlimWindow.cs

public class SlimWindow : Window
{
  public object TitleContent
  {
    get => GetValue(TitleContentProperty);
    set => SetValue(TitleContentProperty, value);
  }

  public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(
            nameof(TitleContent), typeof(object), typeof(SlimWindow));}

App.xaml

<Application>
  <ResourceDictionary>
    <Style TargetType="SlimWindow">
    </Style>
  </ResourceDictionary>
</Application>

Solution 2

Or alternatively move the Style to Generic.xaml (and keep the static constructor):

SlimWindow.cs

public class SlimWindow : Window
{
  static SlimWindow()
  {
    var metaData = new FrameworkPropertyMetadata(typeof(SlimWindow));
    DefaultStyleKeyProperty.OverrideMetadata(typeof(SlimWindow), metaData);
  }

  public object TitleContent
  {
    get => GetValue(TitleContentProperty);
    set => SetValue(TitleContentProperty, value);
  }

  public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(
            nameof(TitleContent), typeof(object), typeof(SlimWindow));}

Generic.xaml

<ResourceDictionary>
  <Style TargetType="SlimWindow">
  </Style>
</ResourceDictionary>
BionicCode
  • 1
  • 4
  • 28
  • 44
  • solution1 doesnt work - I get black window. – Boppity Bop May 25 '23 at 08:58
  • But then you must be missing an important detail. I guess you have removed the static constructor? Solution 1 is definitely working (in general). When you inline the Style like (using solution 1) does the SlimWindow still renders nothing/black? I guess solution 2 works as expected? –  May 29 '23 at 21:05