Как распространить стили на гиперссылки внутри DataTemplate? - PullRequest
7 голосов
/ 03 марта 2011

Я пытаюсь установить цвет Foreground для Hyperlink, используя Style объект в Resources предка, но это не имеет никакого эффекта.Я даже использовал подсказку BasedOn из Изменение переднего плана гиперссылки без потери цвета при наведении , но это не имело никакого значения - я по-прежнему получаю синюю гиперссылку, которая красного цвета при наведении.

ВотXAML для моих элементов управления, включая ItemsControl, элементы которого отображаются с помощью гиперссылки:

<StackPanel Background="Red" TextElement.Foreground="White">
  <StackPanel.Resources>
    <Style TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}">
      <Setter Property="Foreground" Value="Yellow"/>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="White"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>
  <TextBlock>Data import errors</TextBlock>
  <ItemsControl ItemsSource="{Binding Errors}"/>
</StackPanel>

А элементы в ItemsControl собирают следующие DataTemplate:

<DataTemplate DataType="{x:Type Importer:ConversionDetailsMessage}">
  <TextBlock>
    <Run Text="{Binding Message, Mode=OneTime}"/>
    <Hyperlink Command="Common:ImportDataCommands.ExplainConversionMessage" CommandParameter="{Binding}">
      <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/>
    </Hyperlink>
  </TextBlock>
</DataTemplate>

Стоит также отметить, что я не хочу просто устанавливать разные цвета непосредственно на Hyperlink в DataTemplate.Это связано с тем, что шаблон будет использоваться числом различных ItemsControl объектов, большинство из которых будут на белом фоне и поэтому могут использовать стандартные цвета гиперссылок.(Обратите внимание, что тот, что в XAML выше, имеет красный фон.)

Короче говоря, я не хочу, чтобы DataTemplate знал что-либо об элементе управления, в котором он используется.Стили для элементов управления шаблона должны просто фильтроваться.

Итак ... кто-нибудь может сказать мне, почему стиль не фильтруется и что я могу сделать, чтобы исправить это?

Спасибо.

Обновление:
Поскольку я не мог получить ответ Павло для работы в моем производственном приложении, я с тех пор попробовал его в отдельном тестовом приложении.Это приложение WinForms, основная форма которого не содержит ничего, кроме ElementHost, которое само по себе содержит простой пользовательский контроль WPF.Вот его XAML:

<UserControl x:Class="DataTemplateStyling.StylingView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:DataTemplateStyling="clr-namespace:DataTemplateStyling"
             x:Name="root" Loaded="StylingViewLoaded">

  <UserControl.Resources>
    <Style x:Key="MyDefaultHyperlinkStyle" BasedOn="{StaticResource {x:Type Hyperlink}}"/>

    <DataTemplate DataType="{x:Type DataTemplateStyling:ImportMessage}">
      <DataTemplate.Resources>
        <Style TargetType="{x:Type Hyperlink}"
               BasedOn="{StaticResource MyDefaultHyperlinkStyle}"/>
      </DataTemplate.Resources>
      <TextBlock>
        <Run Text="{Binding Message, Mode=OneTime}"/>
        <Hyperlink NavigateUri="{Binding HelpLink.Item1}">
          <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/>
        </Hyperlink>
      </TextBlock>
    </DataTemplate>
  </UserControl.Resources>

  <Grid DataContext="{Binding ElementName=root}">
    <StackPanel Background="Red" TextElement.Foreground="White">
      <StackPanel.Resources>
        <Style x:Key="MyDefaultHyperlinkStyle" TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}">
          <Setter Property="Foreground" Value="Yellow"/>
          <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
              <Setter Property="Foreground" Value="White"/>
            </Trigger>
          </Style.Triggers>
        </Style>
      </StackPanel.Resources>
      <TextBlock>Data import errors</TextBlock>
      <ItemsControl ItemsSource="{Binding Messages}"/>
    </StackPanel>
  </Grid>
</UserControl>

В том виде, в котором он стоит выше, генерируется InvalidOperationException с указанием «Может основываться только на стиле с целевым типом, который является базовым типом« IFrameworkInputElement »."

Это можно исправить, поместив TargetType="Hyperlink" в определение Style непосредственно внутри элемента UserControl.Resources.Тем не менее, пока сообщения отображаются, их часть ссылки по-прежнему имеет синий стиль гиперссылки по умолчанию:

Blue hyperlinks persist

Короче говоря, он не работает, поэтому я приветствую любыедругие предложения / исправления.: (

Обновление 2:
Благодаря альтернативному решению от Pavlo, теперь работает.:)

1 Ответ

7 голосов
/ 03 марта 2011

После некоторого поиска в Google я наткнулся на этот пост: http://www.11011.net/archives/000692.html.

Как там описано, оказывается, что элементы, которые не являются производными от Control (например, TextBlock и Hyperlink) не ищите неявные стили за пределами DataTemplate.

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

<StackPanel Background="Red" TextElement.Foreground="White">
  <StackPanel.Resources>
    <Style x:Key="MyDefaultHyperlinkStyle" TargetType="Hyperlink" BasedOn="{StaticResource {x:Type Hyperlink}}">
      <Setter Property="Foreground" Value="Yellow"/>
      <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
          <Setter Property="Foreground" Value="White"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Resources>
  <TextBlock>Data import errors</TextBlock>
  <ItemsControl ItemsSource="{Binding Errors}"/>
</StackPanel>

Затем вы можете добавить неявный стиль для Hyperlink, который просто ссылается на наш именованный стиль в ресурсах DataTemplate:

<DataTemplate DataType="{x:Type Importer:ConversionDetailsMessage}">
  <DataTemplate.Resources>
    <Style TargetType="{x:Type Hyperlink}"
           BasedOn="{StaticResource MyDefaultHyperlinkStyle}"/>
  </DataTemplate.Resources>
  <TextBlock>
    <Run Text="{Binding Message, Mode=OneTime}"/>
    <Hyperlink Command="Common:ImportDataCommands.ExplainConversionMessage" CommandParameter="{Binding}">
      <Run Text="{Binding HelpLink.Item2, Mode=OneTime}"/>
    </Hyperlink>
  </TextBlock>
</DataTemplate>

И поскольку шаблон данных можно использовать в разных местах, существует вероятность того, что родительский контейнер не определяет стиль с ключом «MyDefaultHyperlinkStyle».В этом случае будет сгенерировано исключение о том, что ресурс "MyDefaultHyperlinkStyle" не найден.Чтобы решить эту проблему, вы можете определить стиль с таким ключом, который будет наследовать стиль по умолчанию только где-то в App.xaml:

<Style x:Key="MyDefaultHyperlinkStyle"
       BasedOn="{StaticResource {x:Type Hyperlink}}/>

Обновление:

Код, который вывключение в ваше обновление не будет работать из-за природы статических ресурсов, что означает, что следующая ссылка на ресурс в шаблоне даты ...

BasedOn="{StaticResource MyDefaultHyperlinkStyle}"

... всегда будет указывать на следующий ресурс (которыйстиль по умолчанию):

<Style x:Key="MyDefaultHyperlinkStyle" BasedOn="{StaticResource {x:Type Hyperlink}}"/>

Статические ссылки на ресурсы разрешаются во время компиляции, поэтому используется ближайший ресурс в дереве.

Возможно, вы захотите использовать DynamicResource, нок сожалению, это не поддерживается свойством BasedOn.

НО, свойство Foreground поддерживает динамические ресурсы, поэтому мы можем использовать тот же трюк с кистями, которые мы используем внутри нашего стиля.Вот ваш тестовый пользовательский элемент управления, модифицированный для использования динамических кистей:

<UserControl x:Class="DataTemplateStyling.StylingView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:DataTemplateStyling="clr-namespace:DataTemplateStyling"
             x:Name="root"
             Loaded="StylingViewLoaded">

    <UserControl.Resources>
        <SolidColorBrush x:Key="HyperlinkForeground"
                         Color="Blue" />

        <SolidColorBrush x:Key="HyperlinkHoverForeground"
                         Color="Gray" />

        <Style x:Key="MyDefaultHyperlinkStyle"
               TargetType="Hyperlink"
               BasedOn="{StaticResource {x:Type Hyperlink}}">
            <Setter Property="Foreground"
                    Value="{DynamicResource HyperlinkForeground}" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver"
                         Value="True">
                    <Setter Property="Foreground"
                            Value="{DynamicResource HyperlinkHoverForeground}" />
                </Trigger>
            </Style.Triggers>
        </Style>

        <DataTemplate DataType="{x:Type DataTemplateStyling:ImportMessage}">
            <DataTemplate.Resources>
                <Style TargetType="{x:Type Hyperlink}"
                       BasedOn="{StaticResource MyDefaultHyperlinkStyle}" />
            </DataTemplate.Resources>
            <TextBlock>
                <Run Text="{Binding Message, Mode=OneTime}" />
                <Hyperlink NavigateUri="{Binding HelpLink.Item1}">
                    <Run Text="{Binding HelpLink.Item2, Mode=OneTime}" />
                </Hyperlink>
            </TextBlock>
        </DataTemplate>
    </UserControl.Resources>

    <Grid DataContext="{Binding ElementName=root}">
        <StackPanel Background="Red"
                    TextElement.Foreground="White">
            <StackPanel.Resources>
                <SolidColorBrush x:Key="HyperlinkForeground"
                                 Color="Yellow" />

                <SolidColorBrush x:Key="HyperlinkHoverForeground"
                                 Color="White" />
            </StackPanel.Resources>
            <TextBlock>Data import errors</TextBlock>
            <ItemsControl ItemsSource="{Binding Messages}" />
        </StackPanel>
    </Grid>
</UserControl>

Он работает как положено, то есть все ссылки внутри StackPanel будут желтыми / белыми, а снаружи они будут синими.

Надеюсь, это поможет.

...