Как создать присоединенное свойство коллекции?
У меня есть WebBrowser, взаимодействовать с которыми я могу только через ObjectForScripting и задача состоит в том что нужно рендерить коллекцию в браузере в виде графических елементов. WebBrowser - sealed и особо изменять его не получается а нужен функционал как ItemsControl.ItemsSource. Конечно можно создать attached property List<T> и делать биндинг к нему, но я посмотрел на ItemsSource свойство и мне интересно как коллекция IEnumerable<T> биндиться к IEnumerable и как потом делается биндинг к свойствам?
[System.ComponentModel.Bindable(true)]
public System.Collections.IEnumerable ItemsSource { get; set; }
Ответы (1 шт):
Смотрите.
Для начала, если вы создаёте attached property, вы его можете создавать где угодно. Например, давайте создадим attached property NumberOfColumns для Grid, чтобы не вбивать его вручную. Заодно оно будет делать что-то полезное: создавать нужное колиество колонок, чтобы не возиться с <ColumnDefinition .../>.
Мы кладём определение в статический класс, нам даже не обязательно наследоваться от DependencyObject. Создавать attached property проще всего при помощи сниппета propa.
// этот класс будет содержать наше attached property
public static class GridExt
{
// к геттеру присоединили атрибут, чтобы отображалось только у Grid
[AttachedPropertyBrowsableForType(typeof(Grid))]
// стандартный геттер...
public static int GetNumberOfRows(DependencyObject obj) =>
(int)obj.GetValue(NumberOfRowsProperty);
// ... и сеттер
public static void SetNumberOfRows(DependencyObject obj, int value) =>
obj.SetValue(NumberOfRowsProperty, value);
// объявляет attached property
public static readonly DependencyProperty NumberOfRowsProperty =
DependencyProperty.RegisterAttached("NumberOfRows", typeof(int), typeof(GridExt),
new PropertyMetadata(0, (o, args) => OnNumberOfRowsChanged((Grid)o)));
// это будет вызываться при изменении значения
static void OnNumberOfRowsChanged(Grid g)
{
var numberOfRows = GetNumberOfRows(g);
// приведём реальное количество колонок к запрошенному
while (g.RowDefinitions.Count > numberOfRows)
g.RowDefinitions.RemoveAt(g.RowDefinitions.Count - 1);
while (g.RowDefinitions.Count < numberOfRows)
g.RowDefinitions.Add(new RowDefinition()
{
Height = new GridLength(1, GridUnitType.Star)
});
}
}
Можно использовать в XAML, чтобы сослаться на наше attached property, нужно указать имя класса и namespace:
<Grid local:GridExt.NumberOfRows="3">
<Border Background="Black" Grid.Row="0"/>
<Border Background="Red" Grid.Row="1"/>
<Border Background="Gold" Grid.Row="2"/>
</Grid>
Получается вот такая картинка:
Attached property можно использовать и по-другому. Например, их можно использовать как просто хранилище значений, и привязываться к этим значениям из стилей.
Дополнение:
Если речь идёт не о View-объектах, а об объектах VM и модели, там правильным решением является агрегация: создайте объект-обёртку UserWithRelatedUsers (или как там правильно должен называться такой класс), который содержит User, а также подчинённый ему List<User> (или, возможно, List<UserWithRelatedUsers>), и привязывайтесь к нему.
