【问题标题】:WPF Trigger on TextBlock Inlines文本块内联上的 WPF 触发器
【发布时间】:2015-08-20 05:12:41
【问题描述】:

我有一个要显示用户名的 TextBlock,以及这样的电子邮件:

Firstname Lastname (Email)

但是,如果用户没有电子邮件存档,我不想放入 (Email) 部分。我还想将电子邮件斜体。通常,我会使用 TextBlock 并为文本的各个部分添加 Runs,但我找不到从 XAML 动态更改 TextBlock 内联的方法。

我试过了:

<TextBlock.Triggers>
<DataTrigger Binding="{Binding Path=HasEmail}" Value="True">

  <Setter Property="Inlines" TargetName="contactTagNameEmailTextBlock">
    <Setter.Value>
     <Run Text="{Binding Path=Firstname}" />
     <Run Text="{Binding Path=Lastname}" />
     <Run Text="(" />
     <Run Text="{Binding Path=Email}" />
     <Run Text=")" />
  </Setter.Value>

</Setter>
</DataTrigger>
</TextBlock.Triggers>

但是 VS 抱怨该值被设置了不止一次(由于多次运行)。我怎样才能解决这个问题?或者,如果我可以在整个 FrameworkElement 上设置绑定,那将非常方便。例如,如果我可以在我的 Grid 中放置一个占位符,我想在此绑定对象的代码中放置我在代码中构建的自定义控件,那将是最好的。

谢谢。

【问题讨论】:

  • Tangential:您可能可以使用&lt;Run Text="{Binding Path=Email, StringFormat='({0})'}" /&gt; 而不是使用 3 次运行。

标签: c# wpf


【解决方案1】:

类似的东西应该可以工作:

<Window.Resources>
    <BooleanToVisibility x:Key="visibilityConverter"/>
</Window.Resources>

...

<TextBlock>
    <Run Text="{Binding Path=Firstname}" />
    <Run Text="{Binding Path=Lastname}" />
    <TextBlock Visibility="{Binding HasEmail, Converter={StaticResource visibilityConverter}}">
        <Run Text="(" />
        <Run Text="{Binding Path=Email}" />
        <Run Text=")" />
    </TextBlock>
</TextBlock>

【讨论】:

  • 这行得通,谢谢! WPF 绝对是一种不同的做事方式。
  • 注意:您可以将 TextBlock 放在 中,然后根据需要更改基线对齐方式。 Blend 会自动执行此操作(也许 VS 也是,但不确定)
  • 投了反对票,因为这不是正确的方法。这破坏了布局引擎确定 TextTrimming 等指标的能力。它可能看起来正确,但您会发现问题。
  • @MarqueIV,到底是什么破坏了它?你认为正确的方法是什么?
  • 如上所述,TextTrimming 之类的东西会中断。您可以在 ListBox 中使用此模板看到这一点,将其 TextTrimming 属性设置为 CharacterEllipses,然后禁用 ListBox 的水平滚动条。运行并使宽度变小。布局被破坏。在 DockPanel 中使用它时更糟糕,因为事情开始重叠并在其他区域下滑动。我添加了一个答案,展示了使用通过触发器更改的运行的正确方法。
【解决方案2】:

研究多重绑定和字符串格式

<TextBlock>
  <TextBlock.Text>
    <MultiBinding StringFormat="{}{0}, {1}">
      <Binding Path="LastName" />
      <Binding Path="FirstName" />
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>

如果电子邮件不存在,您应该能够隐藏 ()。

【讨论】:

  • 如果其中一个参数为空,我不知道如何隐藏格式字符串的一部分。有可能吗?
【解决方案3】:

这里说嵌套 TextBlock 元素的答案会导致布局问题。例如,外部 TextBlock 将不再正确遵守 TextTrimming 设置。

这是一种更好的方法,它可以让您通过触发器*灵活地设置运行,这仍然允许正确的布局。

注意:我添加了格式属性(前景、粗体)来说明为什么您会在此处使用 Runs 而不是简单的多重绑定。使用 Runs(或任何内联)可让您格式化 TextBlock 的文本,但仍使其尊重布局(即 TextTrimming 按预期工作等)

<ContentControl x:Name="TextBlockPresenter">
    <TextBlock TextTrimming="CharacterEllipsis" IsHitTestVisible="False">
        <Run Text="{Binding Path=Firstname}" />
        <Run Text="{Binding Path=Lastname}" FontWeight="Bold" />
        <Run Text="(" Foreground="Gray" />
        <Run Text="{Binding Path=Email}" Foreground="Gray" />
        <Run Text=")" Foreground="Gray" />
    </TextBlock>
</ContentControl>

[SomeTriggerArea]

    <DataTrigger Binding="{Binding Path=HasEmail}" Value="False">
        <Setter TargetName="TextBlockPresenter" Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ContentControl}">
                    <TextBlock TextTrimming="CharacterEllipsis" IsHitTestVisible="False">
                        <Run Text="{Binding Path=Firstname}" />
                        <Run Text="{Binding Path=Lastname}" FontWeight="Bold" />
                    </TextBlock>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </DataTrigger>

[/SomeTriggerArea]

* 从技术上讲,您不是通过触发器设置运行。您正在设置一个全新的模板,其中包含一个定义了正确运行的 TextBox。

如果您在可能想要响应鼠标点击的控件中使用这样的 Runs,则将 IsHitTestVisible 设置为 False 也是一个好主意,然后遍历可视化树(即聆听鼠标右键的 TreeView -向下,遍历树找到 TreeViewItem 并将 IsSelected 设置为 true。)这是因为运行将响应鼠标,但它们不是 FrameworkElement,因此当您使用 e.OriginalSource 调用 VisualTreeHelper.GetParent 时,您将得到一个例外。只需禁用 TextBlock 参与鼠标事件即可避免这种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多