【问题标题】:Display same content twice in WPF Button在 WPF 按钮中显示相同的内容两次
【发布时间】:2013-03-26 10:54:19
【问题描述】:

我们希望设置 wpf 按钮的样式,以便按钮中设置的内容显示两次。这样做的原因是我们要实现按钮内容的投影效果。当时的想法是在 Button 样式中有两个 ContentControl,如下所示:

<ContentControl x:Name="ContentControl" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<ContentControl Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="White" Margin="0,1,0,0" />

所以,一个 ContentControl 用于显示真实的内容,一个 ContentControl 用于显示相同的内容并留有一点边距,这样它就可以产生投影效果。问题是它没有在两个内容控件中显示内容。其中只有一个显示内容。如何在两个内容控件中成功显示内容?

此外,阴影效果也不是一个选项,因为按钮的内容变得模糊。

感谢您的帮助!

【问题讨论】:

  • 第二个 ContentControl 没有 ContentTemplate 绑定,您应该始终绑定 ContentTemplateSelector,相信我,这将为您省去很多烦人的 bug 搜索。您还应该阅读 ContentPresenter 及其在 ContentControls 中的使用。
  • 感谢您的回复。我知道在模板中使用 ContentPresenter 和 ContentControl 是有区别的。我在这里使用 contentControl 的原因是能够设置 contentControl 的 Foreground 属性。这样我可以根据 VisualState(按下按钮等)控制前景。 ContentPresenter 没有前景属性。 ContentTemplate 没有任何区别,使用 2 个 ContentPresenters 也无济于事。

标签: wpf button styles contentcontrol


【解决方案1】:

带有嵌套内容的按钮

<Style x:Key="ShadowButton"
        TargetType="{x:Type Button}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid>
          <Rectangle Width="{Binding ActualWidth,
                                      ElementName=presenter}"
                      Height="{Binding ActualHeight,
                                      ElementName=presenter}">
            <Rectangle.Fill>
              <VisualBrush AlignmentX="Left"
                            Stretch="None"
                            Visual="{Binding ElementName=presenter}" />
            </Rectangle.Fill>
            <Rectangle.RenderTransform>
              <TranslateTransform X="3"
                                  Y="3" />
            </Rectangle.RenderTransform>
          </Rectangle>
          <!-- You can replace the following line to a ContentControl if you absolutely have to -->
          <ContentPresenter x:Name="presenter"
                            ContentSource="Content" />
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

然后使用可以是动态的,例如:

<Button HorizontalAlignment="Center"
        VerticalAlignment="Center"
        FontSize="36"
        Style="{StaticResource ShadowButton}">
  <StackPanel>
    <Button Margin="2"
            Content="A" />
    <Button Margin="2"
            Content="B" />
    <TextBox Margin="2"
             Text="Blah" />
  </StackPanel>
</Button>

通过使用 VisualBrush,您的样式中没有 2 个 ContentControl / ContentPresenter,只是将一个渲染到 Brush 以填充矩形并获得您的效果。

使用模板复制可视化树

首先尝试让UserControl 执行此操作,而不是Button。如果您想以自己的风格复制可视化树,则需要使用模板。

<Style x:Key="ShadowButton"
        TargetType="{x:Type Button}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type Button}">
        <Grid>
          <ContentControl x:Name="shadow"
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          Foreground="SpringGreen">
            <ContentControl.RenderTransform>
              <TranslateTransform X="50"
                                  Y="50" />
            </ContentControl.RenderTransform>
          </ContentControl>
          <ContentControl x:Name="presenter"
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          Foreground="SlateBlue" />
        </Grid>
        <ControlTemplate.Triggers>
          <Trigger SourceName="presenter"
                    Property="IsMouseOver"
                    Value="True">
            <Setter TargetName="shadow"
                    Property="Foreground"
                    Value="Teal" />
            <Setter TargetName="presenter"
                    Property="Foreground"
                    Value="Red" />
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

及用法:

<Button HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Style="{StaticResource ShadowButton}">
  <Button.ContentTemplate>
    <DataTemplate>
      <StackPanel>
        <Button Margin="2"
                Content="A"
                Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
                                                                    AncestorType={x:Type ContentControl}},
                                      Path=Foreground}" />
        <TextBox Margin="2"
                  Foreground="{Binding RelativeSource={RelativeSource FindAncestor,
                                                                      AncestorType={x:Type ContentControl}},
                                      Path=Foreground}"
                  Text="Blah" />
      </StackPanel>
    </DataTemplate>
  </Button.ContentTemplate>
</Button>

【讨论】:

  • 像这样更改模板将删除将其与自定义数据一起使用的选项,因为现在缺少 ContentPresenter。虽然我理解这一点是为了解释,但应该注意这不是好的做法。
  • 我使用 ContentControl 而不是 ContentPresenter 因为我需要根据视觉状态更改按钮内容的前景。如果 Button 的内容仅为文本,您的解决方案将有效。但是,如果您在按钮的内容中包含自定义内容(例如具有多个子元素的堆栈面板),则它将不起作用
  • @dowhilefor 是的,发布的此样式将特定于仅具有阴影效果文本内容的按钮。它不缺少 ContentPresenter (它仍然在样式中使用它),只有效果绑定到 ContentPresenter 的 Text 属性,如果按钮持有其他子元素并且具有此样式,则该属性将不起作用。正在更新答案以适应任何嵌套的子控件。
  • 感谢您的更新回复!我实际上之前尝试过这个解决方案,问题是 VisualBrush 总是被渲染为内容的精确副本,所以我不能用另一种颜色得到它。我需要白色的阴影和另一种颜色的内容,这也需要根据视觉状态进行更改:-)
  • 嗯,你需要明白,当你设置内容时,你有“1”个实际的视觉树。因此,您最终无法将其添加到您样式中的 2 个不同元素中,您需要使用模板来执行此操作。但是,您实际上应该使整个控件成为单独的 UserControl tbh。我将通过变通方法更新我的答案,但请考虑将控件变为 UserControl 而不是在按钮上执行此操作。
【解决方案2】:

我在一个小的虚拟应用程序中尝试了这个,它工作得很好。看看这是不是你想要的。

     <Window.Resources>
        <Style x:Key="test" TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Grid>
                            <ContentControl Content="{TemplateBinding Content}" 
                                            ContentTemplate="{TemplateBinding ContentTemplate}" 
                                            ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"/>
                            <ContentControl Foreground="DarkGray" 
                                            Content="{TemplateBinding Content}" 
                                            ContentTemplate="{TemplateBinding ContentTemplate}" 
                                            ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}">
                                <ContentControl.RenderTransform>
                                    <TranslateTransform Y="2" X="2"/>
                                </ContentControl.RenderTransform>
                            </ContentControl>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Button Style="{StaticResource test}">
            Test
        </Button>
    </Grid>

【讨论】:

  • 感谢您的回复。不,它不起作用。它仅适用于字符串内容,但不适用于复杂内容,例如
  • 那么没有简单的方法可以解决这个问题,现在最好的办法是使用 VisualBrush 来实现阴影效果,同时只保留一个内容。
猜你喜欢
  • 2017-03-22
  • 1970-01-01
  • 1970-01-01
  • 2013-03-25
  • 2021-06-27
  • 2020-12-03
  • 1970-01-01
  • 2021-08-01
  • 2011-01-15
相关资源
最近更新 更多