TransWikia.com

WPF шаблон ItemsControl в зависимости от данных

Stack Overflow на русском Asked by axmed2004 on August 30, 2021

список может состоять из элементов 2 типов: текст и ссылка. ObservableCollection может содержать соот-но разные объекты с type="url" или type="text"

class Item
{
    string type;
    string title;
    string titleshort;
    string time;
}

collection.Add(new Item{type="url",title="http://url",time=datetime});
collection.Add(new Item{type="text",title="text text text ....", titleshort="text ..." ,time=datetime});

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel IsItemsHost="True"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border>
                <TextBlock Tag="{Binding title}"
                           Cursor="Hand"
                           TextWrapping="Wrap">
                    <Run Text="{Binding titleshort}" PreviewMouseDown="openTextFromHistory"/>
                    <Run Text="{Binding timeblock}"/>
                </TextBlock>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

В случае url шаблон должен быть таким:  

<Border>
    <TextBlock>
        <Hyperlink NavigateUri="{Binding title}">
            <Run Text="{Binding title}"/>
        </Hyperlink>
        <Run Text="{Binding time}"/>
    </TextBlock>
</Border>

В случае text таким:  

<Border>
    <TextBlock Tag="title" PreviewMouseLeftButtonDown="somemethod">
        <Run Text="{Binding titleshort}"/>
        <Run Text="{Binding time}"/>
    </TextBlock>
</Border>

как вставлять нужный шаблон в зависимости от type?

UPD
Пространства имен в MainWindow.xaml:

x:Class="RemoteControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RemoteControl"
mc:Ignorable="d"

One Answer

Обычно это делается при помощи разных классов, наследуемые от одного "предка".

Возьмем к примеру два ваших типа text и url:

  • URL - этот класс пусть имеет команду открытия ссылки, а в дизайне это будет кликабельная кнопка.
  • Text - будет обычным текстом с возможностью задать для примера размер шрифта.

И так, думаем для начала что у них общего, а общего у них простой текст, либо он http://site.ru, либо он Это обычный текст, а значит мы можем сделать интерфейс (если не нужна общая логика), либо класс, от них мы будем наследоваться.

public interface ITextProperty
{
    public string Text { get; set; }
}

Все, нам этого достаточно для дальнейшей работы.
Дальше нам нужны классы, которые будут содержать в себе все необходимое для текста или ссылки:

class TextProperty : ITextProperty
{
    public TextProperty(string text, double fontSize)
        => (Text, FontSize) = (text, fontSize);
    public string Text { get; set; }
    public double FontSize { get; set; }
}

class UrlProperty : ITextProperty
{
    public UrlProperty(string url) 
        => (Text, OpenCommand) = (url, new RelayCommand(Open)); 

    public string Text { get; set; }

    public ICommand OpenCommand { get; }
    public void Open() => Process.Start(Text);
}

Как видите это два простейших класса, где в тексте мы можем задать дополнительно размер, у ссылки имеется простая команда открытия этой самой ссылки.

Теперь сделаем текстовые данные. Тут нам нужна коллекция, содержащая в себе именно тот интерфейс/класс, от которого мы наследуемся, в нашем случае это ITextProperty.

 public ObservableCollection<ITextProperty> Items { get; } 

Добавим пару тестовых данных:

Items = new ObservableCollection<ITextProperty>
{
    new TextProperty("Привет мир!", 17),
    new UrlProperty("http://google.ru"),
    new TextProperty("Привет мир!", 8)
};

Осталось дело за малым - UI, нам надо заставить XAML понимать это и отдавать нужный нам дизайн под каждый объект. Для таких целей отлично подойдет DataTemplate, который позволяет задать напрямую нужный тип, к которому будет применен указанный стиль. Обычно он задается в ресурсах, я сделаю это в ресурсах Grid, в котором расположено отображение коллекции. Получаем в итоге такое:

<Grid>
    <Grid.Resources>
        <DataTemplate DataType="{x:Type local:TextProperty}">
            <TextBlock Text="{Binding Text}" FontSize="{Binding FontSize}" Margin="5"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:UrlProperty}">
            <Button Background="Transparent" 
                    BorderThickness="0 0 0 1"
                    Content="{Binding Text}" 
                    Command="{Binding OpenCommand}"
                    Cursor="Hand"/>
        </DataTemplate>
    </Grid.Resources>
    <ItemsControl ItemsSource="{Binding Items}" HorizontalAlignment="Center"/>
</Grid>

Запускаем и любуемся результатом

Result

Программа успешно отобразила два текста с разным размером и одну кнопку, которая кликабельна и открывает через браузер указанную ссылку.

Это самой оптимальный и удобный (как по мне) вариант, как такое можно сделать. Конечно, он не единственный и можно взять прямиком ваш код, где один объект, отличие которого в одном свойстве, делается это через триггеры, например:

<DataTrigger Binding="{Binding Type}" Value="Text">
    <!-- Дизайн -->                     
</DataTrigger>

В общем, удачи в изучении.
Кстати, если вам надо в тексте выделить все ссылки, то это делается вовсе другими методами.

Correct answer by EvgeniyZ on August 30, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP