WPF - TextBlock文本+超链接

时间:2011-05-10 16:49:27

标签: wpf string data-binding text hyperlink

如何从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。 :)

UPDATE:这就是我想返回String的原因,因为IDataError返回一个字符串......

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

        return null;
    }
}

3 个答案:

答案 0 :(得分:3)

不幸的是,没有简单的方法可以做到这一点......据我所知,你能做的最好的事情就是返回一个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属性不能绑定,因为它不是依赖属性(无论如何它只是readonly)。相反,我们在转换器中手动创建TextBlock并将其分配给ContentControl的内容。

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

答案 1 :(得分:2)

此控件可替代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);
                }
            }
        }
    }
}

答案 2 :(得分:1)

所以你试图从你的视图模型中指定可视树?不好的主意。

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

相关问题