颜色编码文本在FlowDocument中运行

时间:2013-08-23 06:38:48

标签: wpf xaml binding converter dependency-properties

我正在尝试生成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不起作用。

•转换器甚至没有被调用(我在它上面放了一个断点)所以看起来绑定/ datatrigger就是没有触发。

之前有人做过这样的事吗?如果是这样,你有没有设法让它工作?在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();
    }
};

2 个答案:

答案 0 :(得分: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。它会被调用每个BindableRun,但如果在设置DataContext之前加载XAML,则可以使用null调用它。
  4. 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;
    }
    

    进行这些更改后,您会看到DataTrigger都正常工作。

答案 1 :(得分:0)

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

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

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

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

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

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

  3. 所以,我的完整例子:

    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

    enter image description here

    Output #2

    enter image description here