如果在 WinRT 中设置为绑定,则不会设置自定义模板控件的依赖项 属性
Dependency property of Custom Templated Control won't be set if it is set to binding in WinRT
我编写了一个自定义模板化 RichTextBlock,以便将表情符号和链接分别表示为图像和超链接按钮。它基本上只有一个 "Text" 依赖项 属性,因为当它收到普通 text/string 时,所有转换工作都将在控件内部处理。
问题是如果 Text 属性 带有一些特定的字符串,如 Text="asdasdsa",控件将正确表示 "asdasdas"。如果 属性 设置为绑定类似 Text="{Binding something}" 的内容,控件将无法工作并且 setter 文本依赖项 属性 将永远不会触发。
控件的样式基本是这样
<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>
基本上,它转换纯文本并替换 XAML 中的那些表情符号和超链接,最后将 XAML 代码块添加到 RichTextBlock 中作为段落。
控件代码
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);
}
}
以及我如何使用此控件的方式
<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>
知道问题是什么吗?解决方案是什么?
setter 只是为了完成 依赖性 属性。您需要为您的 Text
属性 定义一个 属性 更改回调 ,就像这样 -
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());
}
我编写了一个自定义模板化 RichTextBlock,以便将表情符号和链接分别表示为图像和超链接按钮。它基本上只有一个 "Text" 依赖项 属性,因为当它收到普通 text/string 时,所有转换工作都将在控件内部处理。
问题是如果 Text 属性 带有一些特定的字符串,如 Text="asdasdsa",控件将正确表示 "asdasdas"。如果 属性 设置为绑定类似 Text="{Binding something}" 的内容,控件将无法工作并且 setter 文本依赖项 属性 将永远不会触发。
控件的样式基本是这样
<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>
基本上,它转换纯文本并替换 XAML 中的那些表情符号和超链接,最后将 XAML 代码块添加到 RichTextBlock 中作为段落。
控件代码
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);
}
}
以及我如何使用此控件的方式
<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>
知道问题是什么吗?解决方案是什么?
setter 只是为了完成 依赖性 属性。您需要为您的 Text
属性 定义一个 属性 更改回调 ,就像这样 -
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());
}