Как сделать ListBox.ItemTemplate многоразовым/общим

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

У меня есть настраиваемый объект для привязки данных:

public class MyDataItem {
    public bool Checked { get; set; }
    public string DisplayName { get; set; }

    public MyDataItem(bool isChecked, string displayName) {
        Checked = isChecked;
        DisplayName = displayName;
    }
}

(Я создаю список этих и устанавливаю ListBox.ItemsSource в этот список.) И мой XAML выглядит так:

    <ListBox Name="listBox1">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

Это работает. Но я хочу сделать этот шаблон многоразовым, т.е. Мне захочется привязать к другим объектам свойства, отличные от "Проверено" и "DisplayName". Как изменить шаблон, чтобы я мог сделать его ресурсом, повторно использовать его в нескольких экземплярах ListBox и для каждого экземпляра привязать IsChecked и Content к произвольным именам свойств?

Ответы

Ответ 1

Самый простой способ - разместить DataTemplate как ресурс где-нибудь в вашем приложении с TargetType of MyDataItem, как этот

<DataTemplate DataType="{x:Type MyDataItem}">
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
</DataTemplate>

Вероятно, вам также придется включить xmlns в свою локальную сборку и ссылаться на нее через это. Затем всякий раз, когда вы используете ListBox (или что-нибудь еще, использующее MyDataItem в ContentPresenter или ItemsPresenter), он будет использовать этот DataTemplate для его отображения.

Ответ 2

Создайте свой DataTemplate в качестве ресурса, а затем ссылайтесь на него с помощью свойства ItemTemplate в ListBox. MSDN имеет хороший пример

<Windows.Resources>
  <DataTemplate x:Key="yourTemplate">
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" />
  </DataTemplate>
...
</Windows.Resources>

...
<ListBox Name="listBox1"
         ItemTemplate="{StaticResource yourTemplate}"/>

Ответ 3

Если вам нужен один способ отображения, вы можете использовать конвертер:

class ListConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((IList<MyDataItem>)value).Select(i => new { Checked = i.Checked2, DisplayName = i.DisplayName2 });
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Тогда xaml будет выглядеть примерно так:

<Window.Resources>
    <this:ListConverter x:Key="ListConverter" />
</Window.Resources>
<ListBox ItemsSource="{Binding Path=Items, Converter={StaticResource ListConverter}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Path=Checked, Mode=OneWay}" Content="{Binding Path=DisplayName, Mode=OneWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Этот шаблон данных вы можете сделать общим, как указано выше. Двусторонняя привязка будет более сложной.

Я думаю, вам лучше сделать, чтобы ваши базовые классы реализовали интерфейс ICheckedItem, который предоставляет общие свойства, которые вы хотите привязать к datatemplates?