[WPF自定义控件库]使用TextBlockHighlightSource强化高亮的功能,以及使用TypeConverter简化调用

1. 强化高亮的功能

上一篇文章介绍了使用附加属性实现TextBlock的高亮功能,但也留下了问题:不能定义高亮(或者低亮)的颜色。为了解决这个问题,我创建了TextBlockHighlightSource这个类,比单纯的字符串存储更多的信息,这个类的定义如下:

[WPF自定义控件库]使用TextBlockHighlightSource强化高亮的功能,以及使用TypeConverter简化调用

相应地,附加属性的类型也改变为这个类,并且属性值改变事件改成这样:

private static void OnHighlightTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) { var oldValue = (TextBlockHighlightSource)args.OldValue; var newValue = (TextBlockHighlightSource)args.NewValue; if (oldValue == newValue) return; void OnPropertyChanged(object sender,EventArgs e) { if (obj is TextBlock target) { MarkHighlight(target, newValue); } }; if(oldValue!=null) newValue.PropertyChanged -= OnPropertyChanged; if (newValue != null) newValue.PropertyChanged += OnPropertyChanged; OnPropertyChanged(null, null); }

MarkHighlight的关键代码修改为这样:

if (highlightSource.LowlightForeground != null) run.Foreground = highlightSource.LowlightForeground; if (highlightSource.HighlightForeground != null) run.Foreground = highlightSource.HighlightForeground; if (highlightSource.HighlightBackground != null) run.Background = highlightSource.HighlightBackground;

使用起来就是这样:

<TextBlock Text="Git hub" TextWrapping="Wrap"> <kino:TextBlockService.HighlightText> <kino:TextBlockHighlightSource Text="hub" LowlightForeground="Black" HighlightBackground="#FFF37D33" /> </kino:TextBlockService.HighlightText> </TextBlock>

[WPF自定义控件库]使用TextBlockHighlightSource强化高亮的功能,以及使用TypeConverter简化调用

2. 使用TypeConverter简化调用

TextBlockHighlightSource提供了很多功能,但和直接使用字符串比起来,创建一个TextBlockHighlightSource要复杂多。为了可以简化调用可以使用自定义的TypeConverter。

首先来了解一下TypeConverter的概念。XAML本质上是XML,其中的属性内容全部都是字符串。如果对应属性的类型是XAML内置类型(即Boolea,Char,String,Decimal,Single,Double,Int16,Int32,Int64,TimeSpan,Uri,Byte,Array等类型),XAML解析器直接将字符串转换成对应值赋给属性;对于其它类型,XAML解析器需做更多工作。

<Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions>

如上面这段XAML中的"Auto"和"*",XAML解析器将其分别解析成GridLength.Auto和new GridLength(1, GridUnitType.Star)再赋值给Height,它相当于这段代码:

grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });

为了完成这个工作,XAML解析器需要TypeConverter的协助。XAML解析器通过两个步骤查找TypeConverter:
1. 检查属性声明上的TypeConverterAttribute。
2. 如果属性声明中没有TypeConverterAttribute,检查类型声明中的TypeConverterAttribute。

属性声明上TypeConverterAttribute的优先级高于类型声明。如果以上两步都找不到类型对应的TypeConverterAttribute,XAML解析器将会报错:属性"*"的值无效。找到TypeConverterAttribute指定的TypeConverter后,XAML解析器调用它的object ConvertFromString(string text)函数将字符串转换成属性的值。

WPF内置的TypeConverter十分十分多,但有时还是需要自定义TypeConverter,自定义TypeConverter的基本步骤如下:

创建一个继承自TypeConverter的类;

重写virtual bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType);

重写virtual bool CanConvertTo(ITypeDescriptorContext context, Type destinationType);

重写virtual object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value);

重写virtual object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType);

使用TypeConverterAttribute 指示XAML解析器可用的TypeConverter;

到这里我想TypeConverter的概念已经介绍得够详细了。回到本来话题,要简化TextBlockHighlightSource的调用我创建了TextBlockHighlightSourceConverter这个类,它继承自TypeConverter,里面的关键代码如下:

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) { return true; } return base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { switch (value) { case null: throw GetConvertFromException(null); case string source: return new TextBlockHighlightSource { Text = value.ToString() }; } return base.ConvertFrom(context, culture, value); }

然后在TextBlockHighlightSource上使用TypeConverterAttribute:

[TypeConverter(typeof(TextBlockHighlightSourceConverter))] public class TextBlockHighlightSource : FrameworkElement

这样在XAML中TextBlockHighlightSource的调用方式就可以和使用字符串一样简单了。

<TextBlock Text="Github" kino:TextBlockService.HighlightText="hub" /> 3. 使用Style

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfswd.html