Почему для некоторых свойств требуется значение по умолчанию, определенное в стиле, прежде чем вступит в силу DataTrigger?

#wpf #xaml #dependency-properties

#wpf #xaml #зависимость-свойства

Вопрос:

Почему некоторым свойствам зависимостей необходимо иметь параметр по умолчанию в стиле, прежде чем сработавшие параметры вступят в силу?

Например,

 <ContentControl>

    <ContentControl.Resources>
        <DataTemplate x:Key="DefaultTemplate">
            <TextBlock Text="Default Template" />
        </DataTemplate>
        <DataTemplate x:Key="MouseOverTemplate">
            <TextBlock Text="MouseOver Template" />
        </DataTemplate>
    </ContentControl.Resources>

    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">

            <!-- Triggered setter will work without this default setter -->
            <!--<Setter Property="ContentTemplate" 
                        Value="{StaticResource DefaultTemplate}" />-->

            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="ContentTemplate" 
                            Value="{StaticResource MouseOverTemplate}" />
                </Trigger>
            </Style.Triggers>

        </Style>
    </ContentControl.Style>

</ContentControl>
  

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

Ответ №1:

Я был бы очень удивлен, если ваш первый Style не будет работать, если ваш ContentControl не определен как:

 <ContentControl ContentTemplate="{StaticResource DefaultTemplate}" />
  

Если это так, то это должно быть связано с приоритетом значений. По вашей ссылке настройка, подобная в моем примере, будет находиться на # 3. Таким образом, единственное, что могло бы переопределить это, была бы анимация или если бы значение было принудительно введено. Ни Style или ControlTemplate (если таковые имеются) не могут изменить ContentControl.ContentTemplate свойство.

С вашим вторым Style вы могли бы определить свой ContentControl подобный:

 <ContentControl />
  

В этом случае Setter for DefaultTemplate находится на # 8, поэтому триггеры могут переопределить его (как и на # 6).

Опять же, предполагая, что у вас есть:

 <ContentControl ContentTemplate="{StaticResource DefaultTemplate}" />
  

Тогда можно переопределить DataTemplate используемое в ControlTemplate , но вы не можете изменить значение ContentControl.ContentTemplate ‘s . Что-то вроде:

 <Style TargetType="{x:Type ContentControl}">
    <Setter Property="ControlTemplate">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ContentControl}">
                <ContentPresenter x:Name="presenter" />
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding SomeProperty}" Value="A">
                        <Setter TargetName="presenter" Property="ContentTemplate" Value="{StaticResource TemplateA}" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding SomeProperty}" Value="B">
                        <Setter TargetName="presenter" Property="ContentTemplate" Value="{StaticResource TemplateB}" />
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
  

Здесь мы переключаем DataTemplate используемый ContentPresenter, чтобы он эффективно игнорировал ContentControl.ContentTemplate свойство.

Однако, основываясь на ваших обновлениях, мышь не должна быть «над». ContentControl ничего не отобразил (даже прозрачный пиксель), поэтому он не получит никаких событий мыши. Вы можете сделать что-то вроде этого, чтобы исправить это:

 <Style TargetType="{x:Type ContentControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ContentControl}">
                <Border Background="{TemplateBinding Background}">
                    <ContentPresenter />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="ContentTemplate" Value="{StaticResource MouseOverTemplate}" />
        </Trigger>
    </Style.Triggers>
</Style>
  

И вам нужно будет установить Background=»Transparent» в ContentControl .

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

1. Первый стиль не работает. Попробуйте с помощью простого IsMouseOver триггера.

2. @Rachel — Как вы определяете этот триггер? Это не сработает {Binding IsMouseOver} , поскольку это относится к DataContext, но это должно сработать {Binding IsMouseOver, RelativeSource={RelativeSource Self}} .

3. Hrrm вы правы в том, что в этом случае это не работает из-за отсутствия содержимого, но я видел такое поведение с другими установщиками стилей. Давайте посмотрим, смогу ли я найти лучший пример.

4. Я начинаю думать, что я ошибаюсь. Я знаю, что несколько раз я сталкивался с триггерами, которые не работали, если я не добавлял установщик стиля по умолчанию, хотя прошло некоторое время с тех пор, как я в последний раз сталкивался с этой проблемой, и с тех пор всегда добавлял установщик по умолчанию в свои триггеры. Вполне возможно, что мой XAML был ошибочным или что это была ошибка в более старой версии . Net framework. Пока принимаю ваш ответ, поскольку более вероятно, что причиной моих проблем был некорректный XAML, но если я найду хороший пример этой проблемы, я снова открою вопрос с вознаграждением 🙂