gpt4 book ai didi

wpf - 如何将 TextBlock 绑定(bind)到包含格式化文本的资源?

转载 作者:行者123 更新时间:2023-12-03 10:55:28 24 4
gpt4 key购买 nike

我的 WPF 窗口中有一个 TextBlock。

 <TextBlock>
Some <Bold>formatted</Bold> text.
</TextBlock>

当它被渲染时,它看起来像这样,

Some formatted text.



我的问题是,我可以将此内联“内容”绑定(bind)到我的应用程序中的资源吗?

我做到了:

制作应用程序资源字符串,
myText="Some <Bold>formatted</Bold> text."

和以下 xaml(为简洁起见省略了一些代码)
 <Window xmlns:props="clr-namespace:MyApp.Properties">
<Window.Resources>
<props:Resources x:Key="Resources"/>
</Window.Resources>
<TextBlock x:Name="Try1"
Text="{Binding Source={StaticResource Resources} Path=myText}"/>
<TextBlock x:Name="Try2">
<Binding Source="{StaticResource Resources}" Path="myText" />
</TextBlock>
</Window>

Try1 使用适当的标签呈现并且不影响格式。

Some <Bold>formatted<Bold> text.



Try2 不会编译或呈现,因为资源“myText”不是 Inline 类型,而是一个字符串。

这个看似简单的任务是否可行?如果可以,怎么做?

最佳答案

这是我修改后的用于递归格式化文本的代码。它处理粗体、斜体、下划线和换行符,但可以轻松扩展以支持更多(修改 开关 语句)。

public static class MyBehavior
{
public static string GetFormattedText(DependencyObject obj)
{
return (string)obj.GetValue(FormattedTextProperty);
}

public static void SetFormattedText(DependencyObject obj, string value)
{
obj.SetValue(FormattedTextProperty, value);
}

public static readonly DependencyProperty FormattedTextProperty =
DependencyProperty.RegisterAttached("FormattedText",
typeof(string),
typeof(MyBehavior),
new UIPropertyMetadata("", FormattedTextChanged));

static Inline Traverse(string value)
{
// Get the sections/inlines
string[] sections = SplitIntoSections(value);

// Check for grouping
if (sections.Length.Equals(1))
{
string section = sections[0];
string token; // E.g <Bold>
int tokenStart, tokenEnd; // Where the token/section starts and ends.

// Check for token
if (GetTokenInfo(section, out token, out tokenStart, out tokenEnd))
{
// Get the content to further examination
string content = token.Length.Equals(tokenEnd - tokenStart) ?
null :
section.Substring(token.Length, section.Length - 1 - token.Length * 2);

switch (token)
{
case "<Bold>":
return new Bold(Traverse(content));
case "<Italic>":
return new Italic(Traverse(content));
case "<Underline>":
return new Underline(Traverse(content));
case "<LineBreak/>":
return new LineBreak();
default:
return new Run(section);
}
}
else return new Run(section);
}
else // Group together
{
Span span = new Span();

foreach (string section in sections)
span.Inlines.Add(Traverse(section));

return span;
}
}

/// <summary>
/// Examines the passed string and find the first token, where it begins and where it ends.
/// </summary>
/// <param name="value">The string to examine.</param>
/// <param name="token">The found token.</param>
/// <param name="startIndex">Where the token begins.</param>
/// <param name="endIndex">Where the end-token ends.</param>
/// <returns>True if a token was found.</returns>
static bool GetTokenInfo(string value, out string token, out int startIndex, out int endIndex)
{
token = null;
endIndex = -1;

startIndex = value.IndexOf("<");
int startTokenEndIndex = value.IndexOf(">");

// No token here
if (startIndex < 0)
return false;

// No token here
if (startTokenEndIndex < 0)
return false;

token = value.Substring(startIndex, startTokenEndIndex - startIndex + 1);

// Check for closed token. E.g. <LineBreak/>
if (token.EndsWith("/>"))
{
endIndex = startIndex + token.Length;
return true;
}

string endToken = token.Insert(1, "/");

// Detect nesting;
int nesting = 0;
int temp_startTokenIndex = -1;
int temp_endTokenIndex = -1;
int pos = 0;
do
{
temp_startTokenIndex = value.IndexOf(token, pos);
temp_endTokenIndex = value.IndexOf(endToken, pos);

if (temp_startTokenIndex >= 0 && temp_startTokenIndex < temp_endTokenIndex)
{
nesting++;
pos = temp_startTokenIndex + token.Length;
}
else if (temp_endTokenIndex >= 0 && nesting > 0)
{
nesting--;
pos = temp_endTokenIndex + endToken.Length;
}
else // Invalid tokenized string
return false;

} while (nesting > 0);

endIndex = pos;

return true;
}

/// <summary>
/// Splits the string into sections of tokens and regular text.
/// </summary>
/// <param name="value">The string to split.</param>
/// <returns>An array with the sections.</returns>
static string[] SplitIntoSections(string value)
{
List<string> sections = new List<string>();

while (!string.IsNullOrEmpty(value))
{
string token;
int tokenStartIndex, tokenEndIndex;

// Check if this is a token section
if (GetTokenInfo(value, out token, out tokenStartIndex, out tokenEndIndex))
{
// Add pretext if the token isn't from the start
if (tokenStartIndex > 0)
sections.Add(value.Substring(0, tokenStartIndex));

sections.Add(value.Substring(tokenStartIndex, tokenEndIndex - tokenStartIndex));
value = value.Substring(tokenEndIndex); // Trim away
}
else
{ // No tokens, just add the text
sections.Add(value);
value = null;
}
}

return sections.ToArray();
}

private static void FormattedTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
string value = e.NewValue as string;

TextBlock textBlock = sender as TextBlock;

if (textBlock != null)
textBlock.Inlines.Add(Traverse(value));
}
}

编辑:(由 Spook 提出)

一个较短的版本,但要求文本是 XML 有效的:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Xml;

// (...)

public static class TextBlockHelper
{
#region FormattedText Attached dependency property

public static string GetFormattedText(DependencyObject obj)
{
return (string)obj.GetValue(FormattedTextProperty);
}

public static void SetFormattedText(DependencyObject obj, string value)
{
obj.SetValue(FormattedTextProperty, value);
}

public static readonly DependencyProperty FormattedTextProperty =
DependencyProperty.RegisterAttached("FormattedText",
typeof(string),
typeof(TextBlockHelper),
new UIPropertyMetadata("", FormattedTextChanged));

private static void FormattedTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
string value = e.NewValue as string;

TextBlock textBlock = sender as TextBlock;

if (textBlock != null)
{
textBlock.Inlines.Clear();
textBlock.Inlines.Add(Process(value));
}
}

#endregion

static Inline Process(string value)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(value);

Span span = new Span();
InternalProcess(span, doc.ChildNodes[0]);

return span;
}

private static void InternalProcess(Span span, XmlNode xmlNode)
{
foreach (XmlNode child in xmlNode)
{
if (child is XmlText)
{
span.Inlines.Add(new Run(child.InnerText));
}
else if (child is XmlElement)
{
Span spanItem = new Span();
InternalProcess(spanItem, child);
switch (child.Name.ToUpper())
{
case "B":
case "BOLD":
Bold bold = new Bold(spanItem);
span.Inlines.Add(bold);
break;
case "I":
case "ITALIC":
Italic italic = new Italic(spanItem);
span.Inlines.Add(italic);
break;
case "U":
case "UNDERLINE":
Underline underline = new Underline(spanItem);
span.Inlines.Add(underline);
break;
}
}
}
}
}

以及一个使用示例:
<RootItem xmlns:u="clr-namespace:MyApp.Helpers">
<TextBlock u:TextBlockHelper.FormattedText="{Binding SomeProperty}" />
</RootItem>

关于wpf - 如何将 TextBlock 绑定(bind)到包含格式化文本的资源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5565885/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com