Пользовательский элемент управления WPF: настройка стиля горизонтального выравнивания в окне просмотра инициализируется неправильно

#c# #wpf #.net-core

Вопрос:

У меня есть следующий пользовательский элемент управления в проекте .NET 5 WPF:

 <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SteelCloud.Controls"
    xmlns:converters="clr-namespace:My.Controls.Converters">
    <Style TargetType="{x:Type local:BorderedContainer}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:BorderedContainer}">
                    <ControlTemplate.Resources>
                        <converters:BorderClipConverter x:Key="BorderClipConverter"/>
                        <converters:InnerBorderRadiusConverter x:Key="InnerBorderRadiusConverter"/>
                        <converters:HeaderRadiusConverter x:Key="HeaderRadiusConverter"/>
                        <converters:GridLengthConverter x:Key="GridLengthConverter"/>
                    </ControlTemplate.Resources>
                    <Border CornerRadius="{TemplateBinding BorderRadius}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            Margin="{TemplateBinding Margin}"
                            Padding="{TemplateBinding Padding}">
                        <Border.Clip>
                            <MultiBinding Converter="{StaticResource BorderClipConverter}">
                                <Binding Path="ActualWidth"
                                         RelativeSource="{RelativeSource Self}" />
                                <Binding Path="ActualHeight"
                                         RelativeSource="{RelativeSource Self}" />
                                <Binding Path="CornerRadius"
                                         RelativeSource="{RelativeSource Self}" />
                            </MultiBinding>
                        </Border.Clip>
                        <Border CornerRadius="{TemplateBinding BorderRadius, Converter={StaticResource InnerBorderRadiusConverter}}">
                            <Border.Clip>
                                <MultiBinding Converter="{StaticResource BorderClipConverter}">
                                    <Binding Path="ActualWidth"
                                             RelativeSource="{RelativeSource Self}" />
                                    <Binding Path="ActualHeight"
                                             RelativeSource="{RelativeSource Self}" />
                                    <Binding Path="CornerRadius"
                                             RelativeSource="{RelativeSource Self}" />
                                </MultiBinding>
                            </Border.Clip>
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <Border CornerRadius="{TemplateBinding BorderRadius, Converter={StaticResource HeaderRadiusConverter}}"
                                        BorderBrush="{TemplateBinding BorderBrush}"
                                        MaxHeight="{TemplateBinding HeaderSize}"
                                        VerticalAlignment="Top"
                                        Visibility="{TemplateBinding HeaderVisibility}">
                                    <Border.Clip>
                                        <MultiBinding Converter="{StaticResource BorderClipConverter}">
                                            <Binding Path="ActualWidth"
                                                 RelativeSource="{RelativeSource Self}" />
                                            <Binding Path="ActualHeight"
                                                 RelativeSource="{RelativeSource Self}" />
                                            <Binding Path="CornerRadius"
                                                 RelativeSource="{RelativeSource Self}" />
                                        </MultiBinding>
                                    </Border.Clip>
                                    <Grid VerticalAlignment="Stretch"
                                            HorizontalAlignment="Stretch">
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="{TemplateBinding HeaderSize, Converter={StaticResource GridLengthConverter}}"/>
                                            <RowDefinition Height="{TemplateBinding HeaderSeparatorSize}"/>
                                        </Grid.RowDefinitions>
                                        <Border Padding="{TemplateBinding HeaderMargin}"
                                            Background="{TemplateBinding HeaderBackground}">
                                            <Grid HorizontalAlignment="Stretch"
                                                  VerticalAlignment="Stretch">
                                                <Viewbox HorizontalAlignment="{TemplateBinding HeaderHorizontalAlignment}">
                                                    <TextBlock Text="{TemplateBinding HeaderText}"
                                                               HorizontalAlignment="Left"/>
                                                </Viewbox>
                                            </Grid>
                                        </Border>
                                        <Rectangle Grid.Row="1"
                                               Height="{TemplateBinding HeaderSeparatorSize}"
                                               HorizontalAlignment="Stretch"
                                               Fill="{TemplateBinding BorderBrush}"/>
                                    </Grid>
                                </Border>
                                <ContentControl Grid.Row="1" 
                                                Content="{TemplateBinding Content}"
                                                VerticalAlignment="Stretch"/>
                            </Grid>
                        </Border>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
 

Со следующим определением:

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

        public object Content
        {
            get => GetValue(ContentProperty);
            set => SetValue(ContentProperty, value);
        }

        public new static readonly DependencyProperty ContentProperty =
            DependencyProperty.Register("Content", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        public CornerRadius BorderRadius
        {
            get => (CornerRadius) GetValue(BorderRadiusProperty);
            set => SetValue(BorderRadiusProperty, value);
        }

        public static readonly DependencyProperty BorderRadiusProperty =
            DependencyProperty.Register("BorderRadius", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        public Brush HeaderBackground
        {
            get => (Brush) GetValue(HeaderBackgroundProperty);
            set => SetValue(HeaderBackgroundProperty, value);
        }

        public static readonly DependencyProperty HeaderBackgroundProperty =
            DependencyProperty.Register("HeaderBackground", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        public GridLength HeaderSeparatorSize
        {
            get => (GridLength) GetValue(HeaderSeparatorSizeProperty);
            set => SetValue(HeaderSeparatorSizeProperty, value);
        }

        public static readonly DependencyProperty HeaderSeparatorSizeProperty =
            DependencyProperty.Register("HeaderSeparatorSize", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));
        public int HeaderSize
        {
            get => (int)GetValue(HeaderSizeProperty);
            set => SetValue(HeaderSizeProperty, value);
        }

        public static readonly DependencyProperty HeaderSizeProperty =
            DependencyProperty.Register("HeaderSize", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        
        public string HeaderText
        {
            get => (string) GetValue(HeaderTextProperty);
            set => SetValue(HeaderTextProperty, value);
        }

        public static readonly DependencyProperty HeaderTextProperty =
            DependencyProperty.Register("HeaderText", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        public Thickness HeaderMargin
        {
            get => (Thickness)GetValue(HeaderMarginProperty);
            set => SetValue(HeaderMarginProperty, value);
        }

        public static readonly DependencyProperty HeaderMarginProperty =
            DependencyProperty.Register("HeaderMargin", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));
        
        public Visibility HeaderVisibility
        {
            get => (Visibility)GetValue(HeaderVisibilityProperty);
            set => SetValue(HeaderVisibilityProperty, value);
        }

        public static readonly DependencyProperty HeaderVisibilityProperty =
            DependencyProperty.Register("HeaderVisibility", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(null,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

        public HorizontalAlignment HeaderHorizontalAlignment
        {
            get => (HorizontalAlignment)GetValue(HeaderHorizontalAlignmentProperty);
            set => SetValue(HeaderHorizontalAlignmentProperty, value);
        }

        public static readonly DependencyProperty HeaderHorizontalAlignmentProperty =
            DependencyProperty.Register("HeaderHorizontalAlignment", typeof(object), typeof(BorderedContainer),
                                        new FrameworkPropertyMetadata(HorizontalAlignment.Left,
                                                                      FrameworkPropertyMetadataOptions.AffectsRender |
                                                                      FrameworkPropertyMetadataOptions.AffectsParentMeasure));

    }
 

Если я установлю свойство HeaderHorizontalAlignment в поле просмотра при включении этого элемента управления в другой проект (либо с помощью стиля, либо непосредственно в свойстве xaml), он не инициализируется в правильном положении. Однако, если я изменю значение во время работы приложения, произойдет горячая перезагрузка и оно будет установлено правильно. Аналогично, если я установлю его непосредственно в generic.xaml, а не с помощью привязки шаблонов, он будет работать так, как ожидалось. Что здесь происходит?

Комментарии:

1. Тип недвижимости должен быть typeof(HorizontalAlignment) указан вместо typeof(object) того, когда вы регистрируете недвижимость.

2. @mm8 Это сделало свое дело. Можете ли вы объяснить, почему это имеет значение? Я бы ожидал, что актерского состава будет достаточно. Это не проблема ни для одного из моих других свойств, и когда я устанавливаю тип по умолчанию для некоторых из них на ожидаемый тип, я получаю исключения, связанные с несоответствиями типов.

Ответ №1:

Тип свойства должен быть typeof(HorizontalAlignment) вместо typeof(object) того, когда вы его регистрируете:

 public static readonly DependencyProperty HeaderHorizontalAlignmentProperty =
    DependencyProperty.Register("HeaderHorizontalAlignment", typeof(HorizontalAlignment), typeof(BorderedContainer),
                                new FrameworkPropertyMetadata(HorizontalAlignment.Left,
                                                              FrameworkPropertyMetadataOptions.AffectsRender |
                                                              FrameworkPropertyMetadataOptions.AffectsParentMeasure));
                                                                  FrameworkPropertyMetadataOptions.AffectsParentMeasure));