C# WPF:将形状插入 TextBlock

C# WPF: Insert Shape Into TextBlock

我想在 WPF 中将彩色方块(带边框)插入到 TextBlock 中。正方形的颜色需要动态设置,因此理想情况下这应该发生在代码隐藏中,而不是 XAML.

我猜最好的方法是使用 InlineUIContainer,但我不知道如何放置 Rectangle 使其与文本对齐,并且大小适合字体大小。

到目前为止我有:

Color myColor = GetMyColor();

TextBlock textBlock = new TextBlock();
textBlock.Inlines.Add(new Run("My color: "));

// Attempt with a Canvas and Rectangle
Canvas canvas = new Canvas();
canvas.Children.Add(new Rectangle() { Height = 6, Width = 6, Fill = new SolidColorBrush(color.Value) });

textBlock.Inlines.Add(new InlineUIContainer(canvas));

// Hacky version that looks terrible
textBlock.Inlines.Add(new Run("  ") { Background = new SolidColorBrush(myColor) });

这里的问题是 Rectangle 是从文本基线创建的,垂下。我希望它相对于文本、正方形(即纵横比为 1)垂直居中,并且理想情况下自动调整为字体大小。

我想知道 Viewbox 是否有用,或者 VerticalAlignment 属性的某种组合,但我无法使它们起作用。任何建议将不胜感激。

根据您想要的方块大小,您可以尝试使用 unicode 字符 0x25A0 或 0x25AA。

这是在 Xaml 中定义的示例,但您也可以在代码隐藏中实现相同的效果。

<TextBlock FontFamily="Segoe UI">
    <Run Text="ABC" />
    <Run Foreground="Red" Text="&#x25a0;" />
    <Run Foreground="Green" Text="&#x25aa;" />
</TextBlock>

<TextBlock FontFamily="Tahoma">
    <Run Text="ABC" />
    <Run Foreground="Red" Text="&#x25a0;" />
    <Run Foreground="Green" Text="&#x25aa;" />
</TextBlock>

请注意,与常规字母的高度相比,不同的字体系列以不同的比例呈现这些字符。

您可以使用 ContentControlDataTemplate
UserControl 或自定义 ControlContentControl 也是一个很好的解决方案,特别是如果您想添加一个行为。

以下示例使用 ContentControlDataTemplate 在文本旁边显示一个居中的 Rectangle,其中形状的颜色和文本是动态值。形状的大小是相对于 FontSize 应用于 ContentControl.
可以通过在 Viewbox 上设置 Margin 或将 IValueConverter 附加到 ViewboxHeight 绑定来调整形状的最终大小:

MainWindow.xaml

<Window>
  <Window.Resources>
    <DataTemplate x:Key="DataModelTemplate" 
                  DataType="{x:Type DataModel}">
      <DockPanel HorizontalAlignment="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=HorizontalContentAlignment}">
        <TextBlock x:Name="TextLabel"
                   FontSize="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=FontSize}"
                   Text="{Binding TextValue}"
                   VerticalAlignment="Center" />
        <Viewbox Height="{Binding ElementName=TextLabel, Path=ActualHeight}"
                 Margin="8"
                 Stretch="Uniform">
          <Rectangle Width="10"
                     Height="10"
                     Fill="{Binding Color}" />
        </Viewbox>
      </DockPanel>
    </DataTemplate>
  </Window.Resources>

  <StackPanel>
    <ContentControl x:Name="TextControl1"
                    ContentTemplate="{StaticResource DataModelTemplate}"
                    FontSize="50" />
    <ContentControl x:Name="TextControl2"
                    ContentTemplate="{StaticResource DataModelTemplate}"
                    FontSize="20" />
  </StackPanel>
</Window>

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
  
    this.TextControl1.Content = new DataModel("@Test 1", Brushes.Yellow);
    this.TextControl2.Content = new DataModel("@Test 2", Brushes.Red);
  }
}

DataModel.cs

class DataModel : INotifyPropertyChanged
{
  public DataModel(string textValue, Brush color) 
  {
    this.TextValue = textValue;
    this.Color = color;
  }

  public string TextValue { get; }
  public Brush Color { get; }
}