【问题标题】:Colour-coding text runs in a FlowDocument彩色编码文本在 FlowDocument 中运行
【发布时间】:2013-08-26 03:01:34
【问题描述】:

我正在尝试生成一个 FlowDocument,并希望在某些文本运行包含特定字符串时突出显示它们。我使用BindableRun 类来确保我可以更新 Run 文本值(虽然我只在构造时设置它们,所以 Run 的 vanilla 实例应该没问题)。

基本上,我想这样做:

 <FlowDocument Name="FlowDoc" FontFamily="{x:Static SystemFonts.CaptionFontFamily}" 
                  Background="{x:Static SystemColors.ControlBrush}" 
                  FontSize="{x:Static SystemFonts.SmallCaptionFontSize}"
                  TextAlignment="Left">
      <FlowDocument.Resources>
        <Style TargetType="{x:Type local:BindableRun}">
          <!-- 1. Style all BindableRuns with a pink background by default -->
          <Setter Property="Background" Value="HotPink"/>

          <Style.Triggers>

            <!-- 2. Style BindableRuns where Text=Hello to have a green background-->
            <DataTrigger Binding="{Binding BoundText}" Value="Hello">
              <Setter Property="Background" Value="GreenYellow"/>
            </DataTrigger>
            <!-- 3. Fire a Datatrigger with parameterised converter to change 
                  text to Orange if the text contains 'StackOverflow'-->
            <DataTrigger Binding="{Binding BoundText, 
                           Converter={StaticResource ContainsBoolConverter},
                           ConverterParameter=StackOverflow}" Value="False">
              <Setter Property="Foreground" Value="Orange"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </FlowDocument.Resources>
    </FlowDocument>

现在,当我尝试使用上面的 Xaml 时:

• 更改 BindableRun 对象(即 #1)背景的样式效果很好 - 所以 FlowDocument 的内容是我所期望的

• 针对 BindableRun 对象的 BountText 属性的 DataTriggers 不起作用。

• 转换器甚至没有被调用(我在它上面设置了一个断点),所以看起来绑定/数据触发器只是没有触发。

以前有人做过这样的事吗?如果是这样,你有没有设法让它工作?关于 SO 等的 FlowDocument 示例并不多,因此我没有设法追踪任何其他尝试(无论成功与否)在 Run 的 Text 属性上使用 DataTriggers 的人。

作为参考,这是我正在使用的 Contains 转换器(虽然它根本不会触发,所以它是否正确并不重要;))。

public class ContainsTextConverter : IValueConverter
{
    public object Convert( object value, Type t, object parameter, CultureInfo culture )
    {
        String input = value.ToString();
        string param = parameter.ToString();

        return input.IndexOf( param, StringComparison.OrdinalIgnoreCase ) != -1;
    }

    public object ConvertBack( object value, Type t, object parameter, CultureInfo culture )
    {
        // Don't care about this
        throw new NotSupportedException();
    }
};

【问题讨论】:

    标签: wpf xaml binding converter dependency-properties


    【解决方案1】:

    RelativeSource={RelativeSource Self} 添加到您的两个DataTrigger 绑定中。默认情况下,绑定会查找控件DataContext 的属性。要让它查找控件的属性,您需要 RelativeSource 参数。

        <!-- 2. Style BindableRuns where Text=Hello to have a green background-->
        <DataTrigger Binding="{Binding BoundText, RelativeSource={RelativeSource Self}}" Value="Hello">
            <Setter Property="Background" Value="GreenYellow"/>
        </DataTrigger>
    
        <!-- 3. Fire a Datatrigger with parameterised converter to change text to Orange if the text contains 'StackOverflow'-->
        <DataTrigger Binding="{Binding BoundText, RelativeSource={RelativeSource Self},
                Converter={StaticResource ContainsTextConverter},
                ConverterParameter=StackOverflow}" Value="True">
            <Setter Property="Foreground" Value="Orange"/>
        </DataTrigger>
    

    这将解决“Hello”没有绿色背景的问题。

    现在,您的转换器示例还有几个问题:

    1. 转换器名为ContainsTextConverter,但您引用{StaticResource ContainsBoolConverter}
    2. 当您说确实希望文本包含参数时,您为条件指定了Value="False"
    3. 您需要确保转换器中的值不是null。它会为您的每个 BindableRuns 调用,但如果在设置 DataContext 之前加载 XAML,则可以使用 null 调用它。
    public object Convert( object value, Type t, object parameter, CultureInfo culture )
    {
        if( value != null )
        {
            return (value as String).IndexOf( parameter as String, StringComparison.OrdinalIgnoreCase ) != -1;
        }
        else return false;
    }
    

    进行这些更改,您会发现您的 DataTriggers 都可以正常工作。

    【讨论】:

    • 谢谢,好地方。实际上,当我在代码隐藏中构造 Run 时,我通过设置 DataContext = this 解决了这个问题,但这与您建议的更改效果基本相同。我的代码中的转换器错误只是剪切'n'paste 人工制品,但无论如何感谢您的调试。 ;-)
    【解决方案2】:

    要获得一个单词的检查,您需要编写这样的触发器:

    <Trigger Property="BindableText" Value="text">
        <Setter Property="Background" Value="GreenYellow" />
    </Trigger>   
    

    现在,如果字符串是text,那么触发了我们的触发器。

    关于Converter,我尝试了不同的选项,但传递给Converter 的值始终是Null。然后我决定走另一条路。之后,您为属性 BindableText 创建了一个类,所以我决定再添加两个:

    1. 一个属性用于验证您传递给转换器的字符串(StackOverflow 字符串)。

    2. Brush 类型的一个属性,用于设置颜色。

    所以,我的完整示例:

    XAML

    <Grid>
        <FlowDocumentScrollViewer>
            <FlowDocument>
                <FlowDocument.Resources>
                    <Style TargetType="{x:Type local:BindableExtender}">
                        <Style.Triggers>
                            <Trigger Property="BindableText" Value="text">
                                <Setter Property="Background" Value="GreenYellow"/>
                            </Trigger>                                                        
                        </Style.Triggers>
                    </Style>
                </FlowDocument.Resources>
    
                <Paragraph>
                    Binding string:
    
                    <local:BindableExtender BindableText="{Binding ElementName=MyTextBox, Path=Text}"
                                            CompareText="StackOverflow"
                                            ForegroundBrush="OrangeRed" />
                </Paragraph>
    
                <BlockUIContainer>
                    <TextBox Name="MyTextBox" Text="text"/>
                </BlockUIContainer>
            </FlowDocument>
        </FlowDocumentScrollViewer>
    </Grid>
    

    Code behind

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    
    public class BindableExtender : Run
    {
        #region BindableText declaration
    
        public String BindableText
        {
            get 
            { 
                return (string)GetValue(BindableTextProperty); 
            }
    
            set 
            { 
                SetValue(BindableTextProperty, value); 
            }
        }
    
        public static readonly DependencyProperty BindableTextProperty =
            DependencyProperty.Register("BindableText",
                typeof(string),
                typeof(BindableExtender),
                new UIPropertyMetadata(null, BindableTextProperty_PropertyChanged));
    
        private static void BindableTextProperty_PropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            BindableExtender MyRun = dependencyObject as BindableExtender;
    
            if (dependencyObject is BindableExtender)
            {
                MyRun.Text = (string)e.NewValue;
                string bind = (string)e.NewValue;
    
                // Check the two strings
                if (bind.IndexOf(MyRun.CompareText, StringComparison.OrdinalIgnoreCase) != -1)
                {
                    MyRun.Foreground = MyRun.ForegroundBrush;
                }
                else
                {
                    MyRun.Foreground = Brushes.Black;
                }
            }
        }
    
        #endregion BindableText declaration
    
        #region CompareText declaration
    
        public String CompareText
        {
            get 
            { 
                return (string)GetValue(CompareProperty); 
            }
    
            set 
            {
                SetValue(CompareProperty, value); 
            }
        }
    
        public static readonly DependencyProperty CompareProperty =
            DependencyProperty.Register("CompareText",
                typeof(string),
                typeof(BindableExtender),
                new UIPropertyMetadata(null, CompareProperty_PropertyChanged));
    
        private static void CompareProperty_PropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            BindableExtender MyRun = dependencyObject as BindableExtender;
            string CompareString = e.NewValue as string;
    
            // Save the value in CompareText property
            MyRun.CompareText = CompareString;
        }
    
        #endregion
    
        #region ForegroundBrush declaration
    
        public Brush ForegroundBrush
        {
            get
            {
                return (Brush)GetValue(ForegroundBrushProperty);
            }
    
            set
            {
                SetValue(ForegroundBrushProperty, value);
            }
        }
    
        public static readonly DependencyProperty ForegroundBrushProperty =
            DependencyProperty.Register("ForegroundBrush",
                typeof(Brush),
                typeof(BindableExtender),
                new UIPropertyMetadata(null, ForegroundBrushProperty_PropertyChanged));
    
        private static void ForegroundBrushProperty_PropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            BindableExtender MyRun = dependencyObject as BindableExtender;
            Brush NewForegroundBrush = e.NewValue as Brush;
    
            // Save the value in ForegroundBrush property
            MyRun.ForegroundBrush = NewForegroundBrush;
        }
    
        #endregion
    }
    

    Output #1

    Output #2

    【讨论】:

    • 谢谢。您不需要画笔 - 这一切都可以在 XAML 中的触发器中完成。但感谢您的时间。
    猜你喜欢
    • 1970-01-01
    • 2021-12-20
    • 2019-03-29
    • 1970-01-01
    • 2014-06-05
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多