XAML 中数据模板的条件渲染
Conditional rendering of the data template in XAML
我有一个文本块列表,其中可能包含网址,例如:
- 构建失败,查看更多信息:http://...
- 构建成功
- App http://myapp/ 无法启动,更多信息请看这里:http://...
我需要在 UWP 应用程序中显示这个(无穷无尽的)列表。考虑到此列表可以在应用程序内的多个视图中使用,我将其设为通用模板:
<ResourceDictionary>
<ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding image_url}"/>
<TextBlock Grid.Column="1" Text="{Binding status}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
在此模板中,链接被视为常规文本(这是预期的)。据我了解,要使链接正常工作,我需要将它们包装到 <HyperLink>
标记中,但我不能在模板中执行此操作,因为我不知道链接的确切位置以及链接的数量。
有没有什么方法可以实现一些渲染器方法,可以在代码中生成项目的主体(<TextBlock>
),处理传递的值?
可能转换器可以帮助我,但如果我理解正确,它只接受绑定的值,我需要引用整个实例。
UPD:根据已接受的答案扩展解决方案:
资源字典:
<ResourceDictionary xmlns:resources="using:NamespaceWithTextBlockExt">
<ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding image_url}"/>
<TextBlock Grid.Column="1" resources:TextBlockExt.XAMLText="{Binding Text}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
您项目中某处的处理器:
public static class TextBlockExt
{
public static String GetXAMLText(TextBlock obj)
{
return (String)obj.GetValue(XAMLTextProperty);
}
public static void SetXAMLText(TextBlock obj, String value)
{
obj.SetValue(XAMLTextProperty, value);
}
/// <summary>
/// Convert raw string from ViewModel into formatted text in a TextBlock:
///
/// @"This <Bold>is a test <Italic>of the</Italic></Bold> text."
///
/// Text will be parsed as XAML TextBlock content.
///
/// See WPF TextBlock documentation for full formatting. It supports spans and all kinds of things.
///
/// </summary>
public static readonly DependencyProperty XAMLTextProperty =
DependencyProperty.RegisterAttached("XAMLText", typeof(String), typeof(TextBlockExt),
new PropertyMetadata("", XAMLText_PropertyChanged));
private static void XAMLText_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBlock)
{
var ctl = d as TextBlock;
try
{
// XAML needs a containing tag with a default namespace. We're parsing
// TextBlock content, so make the parent a TextBlock to keep the schema happy.
// TODO: If you want any content not in the default schema, you're out of luck.
var value = e.NewValue;
var strText = String.Format(@"<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">{0}</TextBlock>", e.NewValue);
TextBlock parsedContent = Windows.UI.Xaml.Markup.XamlReader.Load(strText) as TextBlock;
// The Inlines collection contains the structured XAML content of a TextBlock
ctl.Inlines.Clear();
var inlines = parsedContent.Inlines.ToList();
parsedContent.Inlines.Clear();
// UI elements are removed from the source collection when the new parent
// acquires them, so pass in a copy of the collection to iterate over.
ctl.Inlines.Concat(inlines);
inlines.ForEach(x => ctl.Inlines.Add(x));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(String.Format("Error in Ability.CAPS.WPF.UIExtensions.TextBlock.XAMLText_PropertyChanged: {0}", ex.Message));
throw;
}
}
}
}
我不确定这是最好的方法,但它确实有效。我只需要预处理绑定值并将所有 URL 包装到超链接标签中:
"App <Hyperlink NavigateUri=\"http://app/\">myapp</Hyperlink>"
我认为这应该适用于任何其他内容,例如 <InlineUIContainer>
您可以编写一个附加行为,使用 XamlReader.Load(Stream)
, and adds the resulting control to the target control. 将字符串解析为 XAML,它使用 TextBlock
内容进行解析,其中可以包含 Hyperlink
。那是针对 WPF 而不是 UWP;可能会有一些差异。
你必须做一些额外的工作:它需要一个非 XAML 字符串,并且在解析为 XAML 之前它必须找到 URL 并将它们替换为 XAML Hyperlink
字符串中的元素。然后你会解析它。
将第二部分放在值转换器中会更清晰。称之为 HyperLinksToXAMLConverter
:
<TextBlock
local:XAMLText="{Binding status, Converter={StaticResource HyperLinksToXAML}}"
/>
我有一个文本块列表,其中可能包含网址,例如:
- 构建失败,查看更多信息:http://...
- 构建成功
- App http://myapp/ 无法启动,更多信息请看这里:http://...
我需要在 UWP 应用程序中显示这个(无穷无尽的)列表。考虑到此列表可以在应用程序内的多个视图中使用,我将其设为通用模板:
<ResourceDictionary>
<ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding image_url}"/>
<TextBlock Grid.Column="1" Text="{Binding status}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
在此模板中,链接被视为常规文本(这是预期的)。据我了解,要使链接正常工作,我需要将它们包装到 <HyperLink>
标记中,但我不能在模板中执行此操作,因为我不知道链接的确切位置以及链接的数量。
有没有什么方法可以实现一些渲染器方法,可以在代码中生成项目的主体(<TextBlock>
),处理传递的值?
可能转换器可以帮助我,但如果我理解正确,它只接受绑定的值,我需要引用整个实例。
UPD:根据已接受的答案扩展解决方案:
资源字典:
<ResourceDictionary xmlns:resources="using:NamespaceWithTextBlockExt">
<ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding image_url}"/>
<TextBlock Grid.Column="1" resources:TextBlockExt.XAMLText="{Binding Text}"/>
</Grid>
</ControlTemplate>
</ResourceDictionary>
您项目中某处的处理器:
public static class TextBlockExt
{
public static String GetXAMLText(TextBlock obj)
{
return (String)obj.GetValue(XAMLTextProperty);
}
public static void SetXAMLText(TextBlock obj, String value)
{
obj.SetValue(XAMLTextProperty, value);
}
/// <summary>
/// Convert raw string from ViewModel into formatted text in a TextBlock:
///
/// @"This <Bold>is a test <Italic>of the</Italic></Bold> text."
///
/// Text will be parsed as XAML TextBlock content.
///
/// See WPF TextBlock documentation for full formatting. It supports spans and all kinds of things.
///
/// </summary>
public static readonly DependencyProperty XAMLTextProperty =
DependencyProperty.RegisterAttached("XAMLText", typeof(String), typeof(TextBlockExt),
new PropertyMetadata("", XAMLText_PropertyChanged));
private static void XAMLText_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TextBlock)
{
var ctl = d as TextBlock;
try
{
// XAML needs a containing tag with a default namespace. We're parsing
// TextBlock content, so make the parent a TextBlock to keep the schema happy.
// TODO: If you want any content not in the default schema, you're out of luck.
var value = e.NewValue;
var strText = String.Format(@"<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">{0}</TextBlock>", e.NewValue);
TextBlock parsedContent = Windows.UI.Xaml.Markup.XamlReader.Load(strText) as TextBlock;
// The Inlines collection contains the structured XAML content of a TextBlock
ctl.Inlines.Clear();
var inlines = parsedContent.Inlines.ToList();
parsedContent.Inlines.Clear();
// UI elements are removed from the source collection when the new parent
// acquires them, so pass in a copy of the collection to iterate over.
ctl.Inlines.Concat(inlines);
inlines.ForEach(x => ctl.Inlines.Add(x));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(String.Format("Error in Ability.CAPS.WPF.UIExtensions.TextBlock.XAMLText_PropertyChanged: {0}", ex.Message));
throw;
}
}
}
}
我不确定这是最好的方法,但它确实有效。我只需要预处理绑定值并将所有 URL 包装到超链接标签中:
"App <Hyperlink NavigateUri=\"http://app/\">myapp</Hyperlink>"
我认为这应该适用于任何其他内容,例如 <InlineUIContainer>
您可以编写一个附加行为,使用 XamlReader.Load(Stream)
, and adds the resulting control to the target control. TextBlock
内容进行解析,其中可以包含 Hyperlink
。那是针对 WPF 而不是 UWP;可能会有一些差异。
你必须做一些额外的工作:它需要一个非 XAML 字符串,并且在解析为 XAML 之前它必须找到 URL 并将它们替换为 XAML Hyperlink
字符串中的元素。然后你会解析它。
将第二部分放在值转换器中会更清晰。称之为 HyperLinksToXAMLConverter
:
<TextBlock
local:XAMLText="{Binding status, Converter={StaticResource HyperLinksToXAML}}"
/>