开发者

WPF - TextBlock Text + Hyperlink

How would one generate this xaml from C# code instead:

Currently:

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

What I want:

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

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


And yes, I have a valid reason to do it this way instead of in xaml. :)

UPDATE: This is why I want to return the String, because IDataError returns a string...

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

        ret开发者_JAVA百科urn null;
    }
}


Unfortunately there's no easy way to do that... As far as I know, the best you can do is return a XAML string and use a converter to parse it.

Warning: ugly code ahead...

XAML

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

Converter

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
}

ViewModel

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

Note the use of a ContentControl rather than a TextBlock: that's because the TextBlock.Text property can only contain plain text, not a formatted document, and the Inlines property cannot be bound because it's not a dependency property (and anyway it's readonly). Instead we manually create a TextBlock in the converter and assign it to the content of the ContentControl.

It's definitely not a very elegant solution, but it works...


So you're trying to dictate the visual tree from your view model? Bad idea.

Instead, why don't you simply set a state property based on the validation and trigger the visual tree based on that? You could do so using a trigger, or using the visual state manager.


This control works are a replacement for TextBlock. It has bindable MarkupText property that understands same syntax you use when specifying TextBlock content.

Usage:

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

in C#

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

Control source code:

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);
                }
            }
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜