【问题标题】:WPF - TextBlock Text + HyperlinkWPF - TextBlock 文本 + 超链接
【发布时间】:2011-08-22 15:34:13
【问题描述】:

如何从 C# 代码生成此 xaml:

目前:

<TextBlock>
    Click <Hyperlink Command="{Binding MyCommand}">here</Hyperlink> to continue.
</TextBlock>

我想要什么:

<TextBlock Text="{Binding MyTextWithHyperlink, Mode=OneWay}" />

public string MyTextWithHyperlink
{
    get
    {
        return ""; //???
    }
}


是的,我有充分的理由这样做,而不是在 xaml 中。 :)

更新:这就是我要返回字符串的原因,因为 IDataError 返回一个字符串...

String IDataError.this[String columnName]
{
    get
    {
        if (columnName == "MyProperty")
        {
            if (something1) return ""; //????
            if (something2) return "some other string";
        }

        return null;
    }
}

【问题讨论】:

  • 我敢打赌,解决导致您认为您有正当理由在代码中生成这些对象的问题比在代码中生成这些对象更容易。
  • 我更新了我的问题以显示为什么我需要创建对象并返回字符串。
  • 对。您需要的是包含错误消息文本的超链接。因此,创建一个包含消息的属性,并将超链接的文本绑定到该属性。但是等等,你说Hyperlink 没有可绑定的Text 属性。正确:见stackoverflow.com/questions/140996/…
  • @Robert:到了一半……整个文本没有超链接,只有一部分……这肯定需要一些额外的逻辑。

标签: wpf string data-binding text hyperlink


【解决方案1】:

不幸的是,没有简单的方法可以做到这一点...据我所知,您能做的最好的事情就是返回一个 XAML 字符串并使用转换器对其进行解析。

警告:前面有丑陋的代码...

XAML

<Window.Resources>
    <local:XamlToTextBlockConverter x:Key="xamlConverter" />
</Window.Resources>
<Grid>
    <ContentControl Content="{Binding MyTextWithHyperlink, Converter={StaticResource xamlConverter}}" />
</Grid>

转换器

public class XamlToTextBlockConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string xaml = value as string;
        if (xaml == null)
            return Binding.DoNothing;

        const string textBlockFormat =
            @"<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">{0}</TextBlock>";
        string fullXaml = string.Format(textBlockFormat, xaml);

        return (TextBlock)XamlReader.Parse(fullXaml);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

视图模型

public string MyTextWithHyperlink
{
    get { return "Click <Hyperlink Command=\"{Binding MyCommand}\">here</Hyperlink> to continue"; }
}

注意使用ContentControl 而不是TextBlock:这是因为TextBlock.Text 属性只能包含纯文本,不能包含格式化文档,并且Inlines 属性不能绑定,因为它不是依赖项属性(无论如何它是只读的)。相反,我们在转换器中手动创建一个TextBlock,并将其分配给ContentControl 的内容。

这绝对不是一个非常优雅的解决方案,但它确实有效......

【讨论】:

  • 天哪,太丑了。我想知道 XAML 是如何将超链接直接内联到文本属性本身的?
  • @myermian,它没有。正如我所说,此处不能使用 Text 属性,因为它只支持纯文本。所以内容实际上是添加到TextBlock的Inlines属性中的。
  • 我的意思是实际的 xaml 行(它确实有效,因为这是我目前拥有的)......这有效并且链接是可点击的:&lt;TextBlock&gt;Click &lt;Hyperlink Command="{Binding MyCommand}"&gt;here&lt;/Hyperlink&gt;&lt;/TextBlock&gt;
  • 哦,我只是窥探它并看到 Text 属性设置为“单击”(它会切断)。然而......它仍然有效......非常奇怪的事情。
  • @myermian,&lt;TextBlock&gt;元素的内容不是Text属性而是Inlines属性,因为TextBlock类上的ContentProperty属性将Inlines定义为内容,而不是Text
【解决方案2】:

所以你想从你的视图模型中指定可视化树?坏主意。

相反,您为什么不简单地根据验证设置一个状态属性并基于它触发可视化树呢?您可以使用触发器或使用可视状态管理器来执行此操作。

【讨论】:

  • 所以不是返回错误字符串,而是返回一个错误代码并使用它来确定真正显示哪个错误消息?
  • @myermian:基本上,是的。您的验证逻辑可以将另一个属性设置为值(可能是枚举),然后用于指示 UI。
  • 你说得对,我给了你复选标记,因为重要的是要重申我希望打算做的事情会完全按照你所说的那样破坏 MV-VM 模式:我' m 从我的 VM 口述可视化树。相反,我走的是公开更多属性的路线,并使用System.ComponentModel.DataAnnotation(验证)加上一个ValidationHelper类来处理我所有的验证:)
【解决方案3】:

此控件的工作原理是 TextBlock 的替代品。它具有可绑定的 MarkupText 属性,可以理解您在指定 TextBlock 内容时使用的相同语法。

用法:

<local:MarkupTextBlock MarkupText="{Binding MyText}"/>

在 C# 中

public string MyText
{
    get
    {
        return "My <Bold>link</Bold> is <Hyperlink NavigateUri='http://search.msn.com'>MSN</Hyperlink>.";
    }
}

控制源码:

using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;

/// <summary>
/// The markup text block is a replacement for <see cref="TextBlock"/> 
/// that allows to specify markup content dynamically.
/// </summary>
[ContentProperty("MarkupText")]
[Localizability(LocalizationCategory.Text)]
public class MarkupTextBlock : TextBlock
{
    /// <summary>
    /// The markup text property.
    /// </summary>
    public static readonly DependencyProperty MarkupTextProperty = DependencyProperty.Register(
        "MarkupText", 
        typeof( string ), 
        typeof( MarkupTextBlock ), 
        new FrameworkPropertyMetadata(
            string.Empty, 
            FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, 
            OnTextMarkupChanged));

    private const string FlowDocumentPrefix =
        "<FlowDocument xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><Paragraph><Span>";

    private const string FlowDocumentSuffix = "</Span></Paragraph></FlowDocument>";

    /// <summary>
    /// Initializes a new instance of the <see cref="MarkupTextBlock"/> class.
    /// </summary>
    /// <param name="markupText">
    /// The markup text.
    /// </param>
    public MarkupTextBlock(string markupText)
    {
        MarkupText = markupText;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="MarkupTextBlock"/> class.
    /// </summary>
    public MarkupTextBlock()
    {
    }

    /// <summary>
    /// Gets or sets content of the <see cref="MarkupTextBlock"/>.
    /// </summary>
    [Localizability(LocalizationCategory.Text)]
    public string MarkupText
    {
        get { return Inlines.ToString(); }
        set { SetValue(MarkupTextProperty, value); }
    }

    private static void OnTextMarkupChanged(
        DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        var markupTextBlock = dependencyObject as MarkupTextBlock;
        if( markupTextBlock != null )
        {
            var flowDocument = new StringBuilder();
            flowDocument.Append(FlowDocumentPrefix);
            flowDocument.Append(dependencyPropertyChangedEventArgs.NewValue);
            flowDocument.Append(FlowDocumentSuffix);

            var document = (FlowDocument) XamlReader.Parse(flowDocument.ToString());
            var paragraph = document.Blocks.FirstBlock as Paragraph;
            if( paragraph != null )
            {
                var inline = paragraph.Inlines.FirstInline;
                if( inline != null )
                {
                    paragraph.Inlines.Remove(inline);
                    markupTextBlock.Inlines.Clear();
                    markupTextBlock.Inlines.Add(inline);
                }
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2010-10-23
    • 1970-01-01
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2013-01-15
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多