Как реализовать DisplayMemberPath для моего Wpf UserControl?

Я пишу Пользовательский элемент управления WPF для своего приложения, перенося ListBox и несколько других объектов.

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

Однако я хочу, чтобы мой UserControl был немного более гибким.

На ListBox и ComboBox там свойство DisplayMemberPath (наследованный от ItemsControl), который, кажется, "вводит" соответствующую привязку свойства в стандартный ItemTemplate.

Как я достигаю того же результата со своим пользовательским элементом управления?

Я хотел бы настроить четыре новых свойства для разрешения конфигурации отображенной информации:

public string LabelDisplayPath { get; set; } 
public string MetricDisplayPath { get; set; }
public string TitleDisplayPath { get; set; }
public string SubtitleDisplayPath { get; set; }

Рассмотрение ItemsControl. DisplayMemberPath с Отражателем, кажется, спускается по кроличьей норе, я не смог понять, как он работает.

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

Обновление

Вот разъяснение того, чего я пытаюсь достигнуть.

ListBox в моем пользовательском элементе управления отображает четыре сведения на объект: Маркировка, Заголовок, Подзаголовок и Метрика

В одном месте я хочу использовать этот Пользовательский элемент управления для отображения списка проблем. Каждая проблема похожа на это:

public class Issue {
    public string Code { get; set; }
    public string Description { get; set; }
    public string Priority { get; set; }
    public string Reporter { get; set; }
}

При отображении проблем я хочу использовать следующие отображения:

Code --> Label
Description --> Title
Reporter --> Subtitle
Priority --> Metric

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

public class Post {
    public DateTime PostedOn { get; set; }
    public string Title { get; set; }
    public string Teaser { get; set; }
    public int CommentCount { get; set; }
}

При показывании сообщений я хочу использовать следующие отображения:

PostedOn --> Label
Title --> Title
Teaser --> Subtitle
CommentCount --> Metric

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

5
задан Bevan 5 May 2010 в 04:41
поделиться

4 ответа

Я нашел решение своей проблемы - это немного сложнее, чем хотелось бы ...

В любом элементе ItemsControl, когда вы устанавливаете свойство DisplayMemberPath , экземпляр внутренний класс DisplayMemberTemplateSelector назначен ItemTemplateSelector . Фактически, этот класс генерирует настраиваемый шаблон DataTemplate с соответствующей привязкой в ​​соответствии с текущей конфигурацией DisplayMemberPath. Новый экземпляр этого класса создается и используется каждый раз, когда изменяется DisplayMemberPath .

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

2
ответ дан 15 December 2019 в 06:19
поделиться

Я бы использовал ViewModels в качестве оберток в этом случае, чтобы объединить два случая: Вы можете создать (абстрактный) класс ItemViewModelBase, который определяет свойства Label, Title, Subtitle и Metric. Затем вы создаете конкретные классы, производные от этого базового VM, для всех элементов, которые вы хотите отображать с помощью одного элемента управления. Каждый из этих классов возвращает что-то свое в свойствах.
Таким образом, вы можете определить один DataTemplate для класса ItemViewModelBase, и он будет применяться к обоим типам элементов.

Некоторый код может сделать это более понятным:

public abstract class ItemViewModelBase
{
    public abstract string Label { get; }
    public abstract string Title { get; }
    public abstract string Subtitle { get; }
    public abstract string Metric { get; }
}

public class IssueViewModel : ItemViewModelBase
{
    private Issue _issue;

    public override string Label { get { return _issue.Code; } }
    public override string Title { get { return _issue.Description; } }
    public override string Subtitle { get { return _issue.Reporter; } }
    public override string Metric { get { return _issue.Priority; } }

    public IssueViewModel(Issue issue)
    {
        _issue = issue;
    }
}

public class PostViewModel : ItemViewModelBase
{
    private Post _post;

    public override string Label { get { return _post.PostedOn.ToString(); } }
    public override string Title { get { return _post.Title; } }
    public override string Subtitle { get { return _post.Teaser; } }
    public override string Metric { get { return _post.CommentCount.ToString(); } }

    public PostViewModel(Issue post)
    {
        _post= post;
    }
}

В вашем ListBox вы будете отображать коллекцию экземпляров ItemViewModelBase, а не Issue и Post, которые будут отображаться с помощью одного общего DataTemplate, вот так:

<DataTemplate DataType="{x:Type local:ItemViewModelBase}">
    <StackPanel>
        <TextBlock x:Name="txtLabel" Text="{Binding Label}"/>
        <TextBlock x:Name="txtTitle" Text="{Binding Title}"/>
        <TextBlock x:Name="txtSubtitle" Text="{Binding Subtitle}"/>
        <TextBlock x:Name="txtMetric" Text="{Binding Metric}"/>
    </StackPanel>
</DataTemplate>

Конечно, ваш DataTemplate будет выглядеть по-другому, приведенный выше пример просто демонстрирует принцип. Кроме того, вы можете определить другие типы возврата для свойств - я сделал их все строками, хотя в ваших классах данных есть DateTime и int. Еще одна вещь, которую я бы не стал делать в производственном коде: DateTime.ToString(), как я сделал в PostViewModel.Label - лучше замените его каким-нибудь локализованным строковым представлением.

1
ответ дан 15 December 2019 в 06:19
поделиться

Думаю, лучше всего использовать шаблоны данных.
Если вы хотите сделать его гибким, то определите шаблон данных по умолчанию в вашей библиотеке ресурсов (это можно сделать, просто не определяя x:Key или x:Name для элемента). Создайте один шаблон данных для каждого класса, который вы хотите отобразить. Затем просто привяжите/заполните список классами, и они будут отображаться с использованием шаблона данных по умолчанию :)

0
ответ дан 15 December 2019 в 06:19
поделиться

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

Позвольте мне обрисовать идею

XAML:

<UserControl x:Name="myControl">
    <StackPanel>
        <TextBlock x:Name="textBlock" 
                   Text="{Binding ElementName=myControl, Path=LabelDisplayPath}" />
        <TextBlock x:Name="textBlock" 
                   Text="{Binding ElementName=myControl, Path=MetricDisplayPath}" />
        <TextBlock x:Name="textBlock" 
                   Text="{Binding ElementName=myControl, Path=TitleDisplayPath}" />
        <TextBlock x:Name="textBlock" 
                   Text="{Binding ElementName=myControl, Path=SubtitleDisplayPath}" />
    </StackPanel>
</UserControl>

где DisplayPaths - это зависимость properties

Теперь создайте эти Свойства зависимостей в коде UserControl позади: LabelDisplayPathProperty, MetricDisplayPathProperty ...

Теперь вы можете использовать их как встроенное свойство DisplayMemberPath, например:

<ListView ItemsSource="{Binding IssueList}"> 
    <ListView.ItemTemplate>
        <DataTemplate>
            <myUC:Control1 LabelDisplayPath = "{Binding Path=Issue.Code}"
                           MetricDisplayPath = "{Binding Path=Issue.Priority}"
                           TitleDisplayPath = "{Binding Path=Issue.Description}"
                           SubtitleDisplayPath = "{Binding Path=Issue.Reporter}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
0
ответ дан 15 December 2019 в 06:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: