Как сделать ListBox.ItemTemplate многоразовым / универсальным - PullRequest
15 голосов
/ 24 марта 2009

Я пытаюсь понять, как лучше расширить элемент управления ListBox. Как опыт обучения, я хотел построить ListBox, чей ListBoxItem отображает 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>

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

Ответы [ 3 ]

17 голосов
/ 24 марта 2009

Создайте свой 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}"/>
16 голосов
/ 24 марта 2009

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

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

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

2 голосов
/ 24 марта 2009

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

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, который предоставляет общие свойства, с которыми вы хотите связать шаблоны данных?

...