Silverlight TextBlock:将文本转换为 InlineCollection

发布于 2024-10-13 09:21:01 字数 583 浏览 0 评论 0原文

考虑以下标记声明:

<TextBlock>
 <Run>abcdefghijklmnopqrstuvwxyz</Run>
 <LineBreak/>
 <Run>0123456789</Run>
</TextBlock>

我想将任何数据绑定到 TextBlock 并将该数据转换为 InlineCollection。如果使用数据绑定来做到这一点会非常优雅。另一种方法是观察我的数据源并使用代码隐藏中 TextBlock 类的 Inlines-Property。

我尝试了以下方法,但没有成功:

<TextBlock>
 <Binding Path="MyDataSource" Converter="{StaticResource MyTextConverter}"/>
</TextBlock>

我想要做的是通过将 TextBlock 封装在 ViewBox 中来实现自动字体缩放,而且在任意字母计数后生成 LineBreaks。

预先感谢您的任何帮助。 此致。

Consider the following markup declaration:

<TextBlock>
 <Run>abcdefghijklmnopqrstuvwxyz</Run>
 <LineBreak/>
 <Run>0123456789</Run>
</TextBlock>

I want to bind any data to a TextBlock and convert this data to an InlineCollection. It would be very elegant to do this with Data Binding. The other way is to observe my Data Source and use the Inlines-Property from the TextBlock class in Code Behind.

I tried the following but it didn't work:

<TextBlock>
 <Binding Path="MyDataSource" Converter="{StaticResource MyTextConverter}"/>
</TextBlock>

What I want to do is achieve auto font-scaling by encapsulating my TextBlock in a ViewBox, but also generating LineBreaks after an arbitrary letter count.

Thanks in advance for any help.
Best regards.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心清如水 2024-10-20 09:21:02

我用了这个 回答 WPF 相关问题,提出一个 Silverlight 行为,该行为可以向 Silverlight 中的 TextBlock。格式化是通过 Xml 元素完成的,其工作方式类似于在元素本身内格式化 TextBlock。可以使用粗体、斜体、下划线和换行元素。

首先,行为定义如下:


public static class FormattedTextBlockBehavior
{
    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 (FormattedTextBlockBehavior),
                                                new PropertyMetadata("", FormattedTextChanged));

    private 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>":
                        var b = new Bold();
                        b.Inlines.Add(Traverse(content));
                        return b;
                    case "<Italic>":
                        var i = new Italic();
                        i.Inlines.Add(Traverse(content));
                        return i;
                    case "<Underline>":
                        var u = new Underline();
                        u.Inlines.Add(Traverse(content));
                        return u;
                    case "<LineBreak/>":
                        return new LineBreak();
                    case "<LineBreak />":
                        return new LineBreak();
                    default:
                        return new Run
                                   {
                                       Text = section
                                   };
                }
            }
            return new Run
                       {
                           Text = section
                       };
        }
        var 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>
    private 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 pos = 0;
        do
        {
            int tempStartTokenIndex = value.IndexOf(token, pos);
            int tempEndTokenIndex = value.IndexOf(endToken, pos);

            if (tempStartTokenIndex >= 0 && tempStartTokenIndex < tempEndTokenIndex)
            {
                nesting++;
                pos = tempStartTokenIndex + token.Length;
            }
            else if (tempEndTokenIndex >= 0 && nesting > 0)
            {
                nesting--;
                pos = tempEndTokenIndex + 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>
    private static string[] SplitIntoSections(string value)
    {
        var 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)
    {
        var value = e.NewValue as string;

        var textBlock = sender as TextBlock;

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

然后,从xaml本身,可以如下引用行为:

  xmlns:Helpers="clr-namespace:Namespace.Helpers" 

  <TextBlock Grid.Row="0" Margin="10,10,10,10" TextWrapping="Wrap" FontSize="14" Helpers:FormattedTextBlockBehavior.FormattedText="{Binding ViewModel.Text}" />

I used this answer for a WPF related question to come up with a Silverlight Behavior that can add simple formatting to a TextBlock in Silverlight. Formatting is done through Xml elements that work similar to how a TextBlock can be formatted within the element itself. Bold, Italic, Underline, and LineBreak elements can be used.

First, the behavior is defined as follows:


public static class FormattedTextBlockBehavior
{
    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 (FormattedTextBlockBehavior),
                                                new PropertyMetadata("", FormattedTextChanged));

    private 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>":
                        var b = new Bold();
                        b.Inlines.Add(Traverse(content));
                        return b;
                    case "<Italic>":
                        var i = new Italic();
                        i.Inlines.Add(Traverse(content));
                        return i;
                    case "<Underline>":
                        var u = new Underline();
                        u.Inlines.Add(Traverse(content));
                        return u;
                    case "<LineBreak/>":
                        return new LineBreak();
                    case "<LineBreak />":
                        return new LineBreak();
                    default:
                        return new Run
                                   {
                                       Text = section
                                   };
                }
            }
            return new Run
                       {
                           Text = section
                       };
        }
        var 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>
    private 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 pos = 0;
        do
        {
            int tempStartTokenIndex = value.IndexOf(token, pos);
            int tempEndTokenIndex = value.IndexOf(endToken, pos);

            if (tempStartTokenIndex >= 0 && tempStartTokenIndex < tempEndTokenIndex)
            {
                nesting++;
                pos = tempStartTokenIndex + token.Length;
            }
            else if (tempEndTokenIndex >= 0 && nesting > 0)
            {
                nesting--;
                pos = tempEndTokenIndex + 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>
    private static string[] SplitIntoSections(string value)
    {
        var 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)
    {
        var value = e.NewValue as string;

        var textBlock = sender as TextBlock;

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

Then, from the xaml itself, the behavior can be referenced as follows:

  xmlns:Helpers="clr-namespace:Namespace.Helpers" 

  <TextBlock Grid.Row="0" Margin="10,10,10,10" TextWrapping="Wrap" FontSize="14" Helpers:FormattedTextBlockBehavior.FormattedText="{Binding ViewModel.Text}" />
旧故 2024-10-20 09:21:02

我想您只需将转换器分配给 TextBlock 的 TextProperty 即可。
然后,您的转换器添加 \r\n 以在需要时获得换行符。

I guess you just have to assign yout converter to the TextProperty of your TextBlock.
Then your converter add \r\n to get a linebreak when you need it.

遥远的绿洲 2024-10-20 09:21:02

我想我通过对 TextBlock 进行子类化以使 InlineCollection 可绑定并在 xaml 标记字符串和 InlineCollection(或内联的通用列表)之间编写转换器来回答这个问题]

堆栈详细信息

正如 infografnet 所指出的, Silverlight 版本的 TextBlock 类已被密封,这使得我的WPF 子类建议无效。

I think I answered this by sub-classing the TextBlock to make the InlineCollection bindable and writing a Convertor between a String of xaml markup and an InlineCollection(or generic list of Inlines)]

Details here on the Stack

As pointed out by infografnet, the Silverlight version of the TextBlock class is sealed which makes my WPF sub-classing suggestion invalid.

梦归所梦 2024-10-20 09:21:01

这太容易让人难以置信了... "\r\n" 在正确的位置就可以完成这项工作。

It's too easy to be true ... "\r\n" at the right position does the job.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文