Как предложил Фуванадил, я бы раскрыл свойство DataSource DependencyProperty.Но помимо этого я бы также раскрыл еще 5 свойств зависимости строк.Потребитель элемента управления затем помещает имена свойств, которые он хочет использовать, в конкретные элементы управления.
Позвольте мне быть более точным:
Подумайте о том, как работает Combobox, где вы можетесвязать их DataSource, но вы также можете указать DisplayMemberPath
и SelectedValuePath
, которые определяют, какие свойства в источнике данных использовать.
Вы можете сделать то же самое с вашим элементом управления:
Предоставьте свойство ImagePathMember.Это будет Имя свойства, которое содержит путь к изображению, которое идет в элементе управления изображением
Предоставить свойство "LinkPathMember".Это свойство будет Name свойства, содержащего путь ссылки
Предоставить свойство LinkDisplayMember.Это свойство будет Name свойства, содержащего текст, на который будет похожа ссылка.
Предоставляет свойство "TopTextBlockMember".Это свойство будет Name свойства, содержащего текст для верхнего текстового блока
Предоставить свойство "BottomTextBlockMember".Это свойство будет Name свойства, содержащего текст для нижнего текстового блока
Затем вы просто используете отражение в элементе управления, чтобы определить значение для каждого элемента списка, а затем привязать его кэлемент управления изображением listboxitem, ссылка и 2 текстовых блока
Надеюсь, что это поможет
u_u
РЕДАКТИРОВАТЬ
Хорошо, вы попросили указать некоторый кодВы в правильном направлении.
Прежде всего: свойства зависимостей
public static DependencyProperty ImagePathMemberProperty = DependencyProperty.Register("ImagePathMember", typeof(string), typeof(MyCustomControl), new PropertyMetadata("",ImagePathMemberPropertyChanged));
public static DependencyProperty DataSourceProperty = DependencyProperty.Register("DataSource", typeof(string), typeof(MyCustomControl), new PropertyMetadata("",DataSourceChanged));
public string ImagePathMember
{
get
{
return (string)GetValue(ImagePathMemberProperty);
}
set
{
SetValue(ImagePathMemberProperty, value);
}
}
public string DataSource
{
get
{
return (string)GetValue(DataSourceProperty);
}
set
{
SetValue(DataSourceProperty, value);
}
}
Код позади
Как оказалось, нам даже не нужно отражение.Когда вы используете привязки в коде, вы фактически предоставляете строковое имя для свойства, что удобно, потому что созданные нами свойства зависимостей являются действительно строковыми именами свойств.К счастью для нас!
private void DataSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
RecreateBindings();
}
//Repeat this for all the dependencyproperties
private void ImagePathMemberPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
RecreateBindings();
}
private void RecreateBindings()
{
//Repeat this for all dependency properties
if(ImagePathMember!=null)
{
Binding ImagePathBinding= new Binding(ImagePathMember);
ImagePathBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
MyImageControl.SetBinding(ImageControl.ImagePathProperty, ImagePathBinding);
}
}
Я написал этот код от руки, так что, вероятно, все глючит.Также меня немного беспокоит привязка, я не установил Source
, потому что думал, что он будет привязан к элементу в коллекции, но я не уверен в этом поведении.
Я собираюсь опубликовать это сейчас, а затем провести тестовый прогон и адаптировать его там, где требуется.
Удачи
u_u
РЕДАКТИРОВАТЬ 2
Хорошо, так что все оказалось намного сложнее, чем я себе представлял.Когда TextBlocks находятся в DataTemplate, вы не можете получить к ним доступ, просто вызвав их имя в коде позади.
Что вам нужно сделать, это дождаться, пока ListBox / ListView сгенерирует контейнеры своих элементов, затемиспользуйте VisualTreeHelper, чтобы пройтись по всем дочерним элементам списка, чтобы найти конкретные элементы управления, которые вы ищете, и затем связать их.
Это заняло у меня так много времени, потому что яне удалось найти элементы управления, потому что я прикрепил обработчик события к событию ItemsSourceChanged в ListView, что означало, что я посмотрел, как изменилось свойство ItemsSource, но за до были созданы контейнеры для этих элементов.
В конце концов я нашел решение:
XAML:
В шаблоне вашего ListView / ListBox, где у вас есть элементы управления, вы должны назвать их так:
<ImageControl x:Name="MyImageControl" [...]></ImageControl>
Вам также нужно дать имя вашему списку / списку, например, (и связать его ItemsSource с вашим свойством DataSource):
<ListBox x:Name="listbox" ItemsSource="{Binding ElementName=me, Path=DataSource, UpdateSourceTrigger=PropertyChanged}" [...]></ListBox>
Вы увидите, что привязка имеет ElementName=me
.Это связано с тем, что я привязан к фактическому элементу управления , находящемуся в (т.е. MyCustomControl).У моего UserControl
* x:Name="me"
выше xmlns
, так как это облегчает мне привязку к свойствам в коде.
RecreateBindings:
В основном вам необходимо обновить метод RecreateBindings. Я допустил большую ошибку в своем первом посте, так как он должен быть статическим методом для запуска в PropertyChangedCallBack объекта DependencyProperty (я действительно не должен был делать код вручную).
Вот чем я закончил:
//Repeat this for all types of controls in your listbox.
private static void RecreateImageControlBindings(ListBox listbox, string controlName, string newPropertyName)
{
if (!string.IsNullOrEmpty(newPropertyName))
{
if (listbox.Items.Count > 0)
{
for (int i = 0; i < listbox.Items.Count; i++)
{
ListBoxItem item = listbox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
if (item != null)
{
Binding imageControlBinding = new Binding(newPropertyName);
imageControlBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ImageControl t = FindDescendant<ImageControl>(item, controlName);
if (t != null)
BindingOperations.SetBinding(t, ImageControl.ImagePath, imageControlBinding);
}
}
}
}
}
Как видите, теперь вам нужен метод RecreateBindings для всех различных типов элементов управления в вашем списке / списке. Существует более общий способ сделать это, но вы можете разобраться с этим самостоятельно. Я не могу делать всю работу: P
Что делает код, так это просматривает элементы в списке и получает их контейнер. После генерации ImageControl будет дочерним элементом этого контейнера. Итак, мы получаем ImageControl с помощью метода FindDescendants, который я адаптировал из этого поста .
Вот этот метод:
Метод FindDescendant
public static T FindDescendant<T>(DependencyObject obj,string objectName) where T : FrameworkElement
{
// Check if this object is the specified type
if (obj is T && ((T)obj).Name == objectName)
return obj as T;
// Check for children
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
// First check all the children
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T && ((T)child).Name == objectName)
return child as T;
}
// Then check the childrens children
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i), objectName);
if (child != null && child is T && ((T)child).Name == objectName)
return child as T;
}
return null;
}
Единственное приспособление, которое я сделал, это добавило проверку имени элемента управления. У нас есть 2 TextBlocks в нашем ListBoxItem, поэтому оригинальный метод будет возвращать только первый. Нам нужно проверить имена, чтобы мы могли связывать оба.
Методы PropertyCallBack:
Таким образом, поскольку метод RecreateBindings был разделен, нам нужно изменить PropertyChangedCallBacks для вызова методов RecreateBindings, специфичных для каждого свойства. Свойство источника данных будет содержать все методы RecreateBindings.
private static void DataSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//Put the RecreateBindings for all the properties here:
RecreateImageControlBindings(((MyCustomControl)sender).listbox, "MyImageControl", ((MyCustomControl)sender).ImagePathMember);
}
//Repeat this for all the dependencyproperties
private void ImagePathMemberPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
RecreateImageControlBindings(((MyCustomControl)sender).listbox, "MyImageControl", ((MyCustomControl)sender).ImagePathMember);
}
Обратите внимание, что MyCustomControl - это тип создаваемого вами элемента управления.
Конструктор и прикрепленный обработчик событий:
Наконец, нам нужно добавить строку в конструктор, который добавляет обработчик события в ItemContainerGenerator в ListBox, чтобы мы могли проверять, когда сгенерирован контейнер элемента, и можем прикреплять наши привязки .:
public MyCustomControl()
{
InitializeComponent();
listview.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (listview.ItemContainerGenerator.Status
== System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
//Do this for all the different bindings we want
RecreateImageControlBindings(listview, "MyImageControl", ImagePathMember);
}
}
Так и должно быть. Если вам нужна помощь / есть вопросы, дайте мне знать
u_u