WPF - TextBlock Text + Гиперссылка - PullRequest
1 голос
/ 10 мая 2011

Как можно вместо этого сгенерировать этот xaml из кода C #:

В настоящее время:

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

Ответы [ 3 ]

3 голосов
/ 10 мая 2011

К сожалению, нет простого способа сделать это ... Насколько я знаю, лучшее, что вы можете сделать, это вернуть строку 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
}

ViewModel

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

Обратите внимание на использование ContentControl вместо TextBlock: это потому, что свойство TextBlock.Text может содержать только простой текст, а не форматированный документ и Inlinesсвойство не может быть связано, потому что это не свойство зависимости (и в любом случае оно доступно только для чтения).Вместо этого мы вручную создаем TextBlock в конвертере и присваиваем его содержимому ContentControl.

Это определенно не очень элегантное решение, но оно работает ...

2 голосов
/ 11 мая 2011

Этот элемент управления работает вместо 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);
                }
            }
        }
    }
}
1 голос
/ 10 мая 2011

То есть вы пытаетесь продиктовать визуальное дерево из вашей модели представления?Плохая идея.

Вместо этого, почему бы вам просто не установить свойство состояния на основе проверки и не запустить визуальное дерево на основе этого?Вы можете сделать это с помощью триггера или с помощью диспетчера визуальных состояний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...