Reputation: 113
I wrote a custom templated RichTextBlock in order to represent the emojis and links as images and hyperlink buttons respectivly. It basically has only one "Text" dependency property because all the conversion works will process inside the control when it recieve the plain text/string.
The problem is if the Text property is with some specific string like Text="asdasdsa", the control will represent "asdasdas" properly. If the property is set to bind something like Text="{Binding something}", the control won't work and the setter Text dependency property will never fire.
The style of the control is basically like this
<Style TargetType="local:MyRichTextBlock" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyRichTextBlock">
<Border HorizontalAlignment="Center" Width="auto" Height="auto">
<StackPanel>
<RichTextBlock x:Name="ChildRichTextBlock"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Basically, it converts the plain text and replace those emojis and hyperlinks in XAML, at the end add the block of XAML code into the RichTextBlock as a paragraph.
The code of the control
public sealed class MyRichTextBlock : Control
{
RichTextBlock _richTextBlock;
StringBuilder builder = new StringBuilder();
Regex urlRx = new Regex(@"(?<url>(http:[/][/]|www.)([a-z]|[A-Z]|[0-9]|[/.]|[~])*)", RegexOptions.IgnoreCase);
public MyRichTextBlock()
{
this.DefaultStyleKey = typeof(MyRichTextBlock);
foreach (var key in emojiDict.Keys)
{
builder.Append(key.Replace("[", @"\[").Replace("]", @"\]"));
builder.Append("|");
}
builder.Remove(builder.Length - 1, 1);
}
private readonly Dictionary<string, string> emojiDict = new Dictionary<string, string>
{
//Dictionary of emojis key
};
protected override void OnApplyTemplate()
{
_richTextBlock = GetTemplateChild("ChildRichTextBlock") as RichTextBlock;
SetRichTextBlock(Text);
}
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); } //This setter won't fire
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(MyRichTextBlock), new PropertyMetadata(""));
//Conversion work, it does not really related to the property I think
private void SetRichTextBlock(string value)
{
string abc = value;
MatchCollection matches = urlRx.Matches(value);
var r = new Regex(builder.ToString());
var mc = r.Matches(value);
foreach (Match m in mc)
{
value = value.Replace(m.Value, string.Format(@"<InlineUIContainer><Border><Image Source=""ms-appx:///Assets/Emoji/{0}.png"" Margin=""2,0,2,0"" Width=""30"" Height=""30""/></Border></InlineUIContainer>", emojiDict[m.Value]));
}
foreach (Match match in matches)
{
string url = match.Groups["url"].Value;
value = value.Replace(url,
string.Format("<InlineUIContainer><Border><HyperlinkButton Margin=\"0,0,0,-4\" Padding=\"0,2,0,0\" NavigateUri =\"{0}\"><StackPanel HorizontalAlignment=\"Center\" Height=\"25\" Width=\"90\" Background=\"#FFB8E9FF\" Orientation = \"Horizontal\"><Image Margin=\"5,0,0,0\" Source = \"/Assets/Link.png\" Width = \"15\" Height = \"15\"/><TextBlock Margin=\"4,2.5,0,0\" Text=\"网页链接\" Foreground=\"White\" FontFamily=\"Microsoft YaHei UI\" FontSize=\"14\" FontWeight=\"Bold\"/></StackPanel ></HyperlinkButton></Border></InlineUIContainer>", url));
}
value = value.Replace("\r\n", "<LineBreak/>");
var xaml = string.Format(@"<Paragraph
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<Paragraph.Inlines>
<Run></Run>
{0}
</Paragraph.Inlines>
</Paragraph>", value);
var p = (Paragraph)XamlReader.Load(xaml);
_richTextBlock.Blocks.Add(p);
}
}
And the way how I use this control
<DataTemplate x:Name="NormalTemplate">
<Grid Grid.Column="1"
Grid.Row="1"
d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin"
Margin="0,15,0,15"
Name="NormalTemplateGrid">
//Codes omit//
<local:MyRichTextBlock x:Name="WeiboTextTextblock"
Margin="20,35,0,0"
Text="{Binding Text}"
Grid.Column="2"
Grid.Row="1"
d:LayoutOverrides="HorizontalAlignment, TopMargin, BottomMargin, LeftPosition, RightPosition, TopPosition, BottomPosition"
HorizontalAlignment="Left"
FontFamily="Microsoft YaHei"/>
</Grid>
</DataTemplate>
Any ideas what is the problem and what is the solution for this?
Upvotes: 2
Views: 142
Reputation: 39006
The setter is there just for the sake of completing a dependency property. You need to define a property changed callback for your Text
property, like this -
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(String), typeof(MyRichTextBlock), new PropertyMetadata("", OnTextChange));
private static void OnTextChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var rtb = (MyRichTextBlock)d;
rtb.SetRichTextBlock(e.NewValue.ToString());
}
Upvotes: 3