2017-01-04 3 views
4

У меня есть список текстовых блоков, которые могут включать в себя URLs внутри, как чему-л:Условный рендеринг шаблона данных в XAML

  • Строить не удалось, смотрите здесь: Http: // ...
  • Сложение удалось
  • App http://myapp/ не может быть запущен, больше здесь: Http: // ...

Мне нужно, чтобы отобразить это (бесконечный) список в UWP приложение. Учитывая этот список может быть использован в различных взглядов внутри приложения, я сделал это общий шаблон:

<ResourceDictionary> 
    <ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="50"/> 
     <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <Image Grid.Column="0" Source="{Binding image_url}"/> 
     <TextBlock Grid.Column="1" Text="{Binding status}"/> 
    </Grid> 
    </ControlTemplate> 
</ResourceDictionary> 

В этом шаблоне ссылки рассматриваются как обычный текст (который, как ожидается). Насколько я понимаю, чтобы сделать работу ссылок, мне нужно обернуть их в тег <HyperLink>, но я не могу сделать это в шаблоне, потому что я не знаю, где именно будут ссылки и сколько из них будет отображаться.

Есть ли способ реализовать некоторый метод рендеринга, который может генерировать тело элемента (<TextBlock>) в коде, обрабатывая переданное значение?

Вероятно, конвертер может мне помочь, но если я правильно понимаю, он принимает значение только от привязки, и мне нужно ссылаться на весь экземпляр.

UPD: Расширение решения от принятого ответа:

словарь ресурсов:

<ResourceDictionary xmlns:resources="using:NamespaceWithTextBlockExt"> 
    <ControlTemplate x:Key="ListItemTemplate" TargetType="ItemsControl"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="50"/> 
     <ColumnDefinition Width="*"/> 
     </Grid.ColumnDefinitions> 
     <Image Grid.Column="0" Source="{Binding image_url}"/> 
     <TextBlock Grid.Column="1" resources:TextBlockExt.XAMLText="{Binding Text}"/> 
    </Grid> 
    </ControlTemplate> 
</ResourceDictionary> 

процессор где-то в вашем проекте:

public static class TextBlockExt 
{ 
    public static String GetXAMLText(TextBlock obj) 
    { 
     return (String)obj.GetValue(XAMLTextProperty); 
    } 

    public static void SetXAMLText(TextBlock obj, String value) 
    { 
     obj.SetValue(XAMLTextProperty, value); 
    } 

    /// <summary> 
    /// Convert raw string from ViewModel into formatted text in a TextBlock: 
    /// 
    /// @"This <Bold>is a test <Italic>of the</Italic></Bold> text." 
    /// 
    /// Text will be parsed as XAML TextBlock content. 
    /// 
    /// See WPF TextBlock documentation for full formatting. It supports spans and all kinds of things. 
    /// 
    /// </summary> 
    public static readonly DependencyProperty XAMLTextProperty = 
     DependencyProperty.RegisterAttached("XAMLText", typeof(String), typeof(TextBlockExt), 
              new PropertyMetadata("", XAMLText_PropertyChanged)); 

    private static void XAMLText_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     if (d is TextBlock) 
     { 
      var ctl = d as TextBlock; 

      try 
      { 
       // XAML needs a containing tag with a default namespace. We're parsing 
       // TextBlock content, so make the parent a TextBlock to keep the schema happy. 
       // TODO: If you want any content not in the default schema, you're out of luck. 
       var value = e.NewValue; 

       var strText = String.Format(@"<TextBlock xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">{0}</TextBlock>", e.NewValue); 

       TextBlock parsedContent = Windows.UI.Xaml.Markup.XamlReader.Load(strText) as TextBlock; 

       // The Inlines collection contains the structured XAML content of a TextBlock 
       ctl.Inlines.Clear(); 

       var inlines = parsedContent.Inlines.ToList(); 
       parsedContent.Inlines.Clear(); 

       // UI elements are removed from the source collection when the new parent 
       // acquires them, so pass in a copy of the collection to iterate over. 
       ctl.Inlines.Concat(inlines); 
       inlines.ForEach(x => ctl.Inlines.Add(x)); 
      } 
      catch (Exception ex) 
      { 
       System.Diagnostics.Debug.WriteLine(String.Format("Error in Ability.CAPS.WPF.UIExtensions.TextBlock.XAMLText_PropertyChanged: {0}", ex.Message)); 
       throw; 
      } 
     } 
    } 
} 

Я не уверен, что это лучший способ, bu Это работает. Мне просто нужно Preprocess связанного значения и завернуть все адреса в теги гиперссылка:

"App <Hyperlink NavigateUri=\"http://app/\">myapp</Hyperlink>"

Я предполагаю, что это должно работать с любым другим контентом, как <InlineUIContainer>

+0

Он должен работать с любым контентом, который может быть дочерним элементом TextBlock. –

ответ

1

Вы можете написать присоединенное поведение, которое разбирает строку как XAML с использованием XamlReader.Load(Stream), и добавляет результирующий элемент управления к целевому элементу управления. Here's one I wrote, который делает это с TextBlock контентом, который может включать Hyperlink. Это для WPF не UWP; могут быть некоторые различия.

Вам нужно будет сделать небольшую дополнительную работу: для нее потребуется строка, отличная от XAML, и до разбора в качестве XAML ему придется найти URL-адреса и заменить их элементами XAML Hyperlink в строке. Тогда вы проанализируете это.

Было бы чище поставить эту вторую часть в преобразователь ценности. Звоните HyperLinksToXAMLConverter:

<TextBlock 
    local:XAMLText="{Binding status, Converter={StaticResource HyperLinksToXAML}}" 
    /> 
+0

Большое спасибо! Я протестировал это решение, и он позволяет мне изменять контент и показывать гиперссылки. Как уже упоминалось, код должен быть слегка изменен. – ragzovskii

+0

@ragzovskii Удивительный. Можете ли вы добавить версию кода UWP для своего Q или изменить ее в моем A? Я одобрю редактирование. –

Смежные вопросы