2009-03-23 4 views
13

Я довольно новичок в WPF и пытаюсь понять, как лучше всего расширить элемент управления ListBox. В качестве учебного опыта я хотел построить ListBox, где ListBoxItem s отображает CheckBox вместо простого текста. Я получил базовую работу, используя ListBox.ItemTemplate, явно задав имена свойств, которые мне нужны для привязки данных. Пример стоит тысячи слов, так что ...Как сделать 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 к произвольным именам свойств?

+0

Я не думаю, что это возможно. Чтобы привязка «Проверено» была гибкой, вам придется подклассифицировать ListBox и добавить CheckedMemberPath, как DisplayMemberPath. Нельзя делать это только с шаблоном. –

+0

Вот чего я боялся. Я начал изучать подклассы ListBox, но я продолжал читать вещи о том, как невероятные шаблоны и как вам следует избегать подклассов, если вообще возможно выполнить то, что вы хотите с помощью шаблона. Я думаю, это может быть хорошим примером для подкласса. Благодаря! –

ответ

14

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

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

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

+0

Увлекательный! Я не понял, что вы можете применить шаблон к самому объекту данных. (BTW, я обнаружил, что «TargetType» на самом деле должен быть «DataType» на DataTemplate.) Недостатком этого является то, что я должен был бы определить шаблоны для каждого типа источников данных ... которые я пытался избежать. –

+0

Спасибо, я исправил ошибку. Независимо от того, что вам нужно будет сообщить пользовательскому интерфейсу, в котором находится IsChecked, и который является содержимым. Вам просто нужно решить, где это происходит. Я предпочитаю это на уровне DataTemplate, потому что он, как правило, является самым легким местом для сохранения в моем опыте. –

16

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

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

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

Я думаю, что задающий вопрос спрашивает, как настроить параметры «Проверено» и «Отображать», которые поставляются в шаблон, чтобы тот же внешний вид можно было использовать в другом месте, но, возможно, с другой привязкой. –

+0

Хм, да, я вижу, что ты прав, я не уверен, что это возможно, я буду ждать лучшего ответа – MrTelly

+0

Право Джонатана; Я хотел бы иметь возможность привязывать другие типы объектов к одному и тому же шаблону. Спасибо за попытку, хотя! –

2

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

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?

+0

Интересный подход - спасибо! Мне понадобится двусторонняя привязка, поэтому я, вероятно, избегу конвертер. Однако ICheckedItem открывает новые возможности, о которых я не думал. Интерфейсы могут оказаться в пути. –

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