Привязка к DependencyProperty в UserControl - PullRequest
2 голосов
/ 28 февраля 2012

У меня есть форма с двумя различными пользовательскими элементами управления - одна, содержащая Telerik RadGridView, а другая, содержащая Telerik DataForm.

Пользовательский контроль сетки связан с ViewModel, включающей свойство, которое предоставляет элементы.коллекция, к которой привязана сетка.

Когда я связываю форму с этим свойством, все работает нормально.
Но мне нужно получить доступ к дополнительной информации в элементе управления формы, который на самом деле не принадлежит сетке.viewmodel элемента управления.

Поэтому я решил добавить свойство в форму usercontrol и привязать его к коллекции элементов:

<local:FormControl x:Name="formControl"
    ItemsSource="{Binding items}"
/>

В коде формы я добавилобычное свойство:

private object itemsSource;
public object ItemsSource
{
    get { return this.itemsSource; }
    set { this.itemsSource = value; }
}

И это, конечно, не сработало.Я получил ошибки об использовании DependencyProperty.То, что я думал, было обнадеживающим - страница фактически пыталась привязать к свойству, которое я думал, что должно.

Так что я преобразовал это в свойство DependencyProperty:

public static DependencyProperty ItemsSourceProperty =
    DependencyProperty.Register("ItemsSource", typeof(object), typeof(FormControl));

public object ItemsSource
{
    get { return GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

Это компилировалось и работало без ошибок,За исключением, конечно, что у модели элемента управления не было никаких элементов.Следующим шагом была попытка передать свойство ItemsSource в viewmodel элемента управления формы:

public FormControl()
{
    InitializeComponent();
    this.DataContext = new FormControlVM(this.ItemsSource);
    ...

И это не сработало.ItemsSource был нулевым, когда FormControl () был создан.Поэтому я добавил метод setItemsSource () в viewmodel и вызвал его в функции set свойства ItemsSource.Это тоже не сработало.Очевидно, xaml привязан к свойству, но, похоже, делает это без вызова функции set свойства.

Поэтому я решил прослушать событие ValueChanged в DependencyProperty:

public FormControl()
{
    InitializeComponent();
    this.DataContext = new FormControlVM();
    DependencyPropertyDescriptor prop =
            DependencyPropertyDescriptor.FromProperty(FormControl.ItemsSourceProperty, this.GetType());
    prop.AddValueChanged(this, delegate
    {
        FormControlVM formControlVM = (FormControlVM)this.DataContext;
        formControlVM.setItemsSource(this.ItemsSource);
    });
    ...

И это все еще не работает.Кажется, никогда не вызывается делегат ValueChanged.

Кажется, что это должно быть просто, но я не смог найти примеры в Интернете, и я попробовал каждую комбинацию, которую могу придумать.

Есть идеи, как мне с этим справиться?

============ Дополнительная информация о привязке ============

Уилл запрашивал информацию о xaml, который выполняет связывание.

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

<local:FormControl x:Name="formControl"
        Grid.Column="2"
        DataContext="{Binding}"
        />

И затем это в пользовательском элементе управления, связывающем форму в пользовательском элементе управления с itemsCollection модели представления страницы:

<telerik:RadDataForm
        ItemsSource="{Binding itemsCollection}"
        Header="View Item:"
        CommandButtonsVisibility="None"
        AutoGenerateFields="False"
        />

Тогда все работает нормально.

Но проблема в том, что я не могу связать пользовательский элемент управления с моделью представления страницы.Мне нужно, чтобы viewmodel пользовательского элемента управления предоставлял информацию, которую не должна видеть viewmodel страницы.

И я не могу, чтобы форма в пользовательском элементе управления выходила за пределы пользовательского элемента управления, привязывая к свойству свойствастраница просмотра модели.Это нарушение инкапсуляции, которое значительно усложнит использование пользовательского элемента управления на другой странице и сильно ограничит возможности изменения внутренних элементов элемента управления в будущем.(Страница не должна ничего знать об элементах управления в пользовательском элементе управления, элементы управления в пользовательском элементе управления не должны ничего знать о странице.)

Когда я включаю пользовательский элемент управления на страницу, я хочу связатьпользовательский элемент управления со свойством viewmodel страницы, и я хочу, чтобы любые элементы управления в пользовательском элементе управления связывались со свойствами пользовательского элемента управления или viewmodel пользовательского элемента управления.

Я думаю, это было довольнообычное явление.Но я не смог найти никаких примеров того, как это можно сделать.

1 Ответ

3 голосов
/ 01 марта 2012

Для повторного решения проблемы: у меня есть UserControl, который содержит встроенный элемент управления Telerik DataForm. Мне нужно связать свойство ItemsSource встроенной DataForm со свойством DataContext страницы, на которой размещен мой UserControl.

Если бы UserControl не установил свой DataContext, он бы унаследовал DataContext страницы, и я мог бы легко связать свойство ItemsSource встроенной DataForm с его свойством, но UserControl имеет свой собственный DataContext.

Если UserControl был написан для использования только на этой странице, я мог бы связать свойство ItemsSource встроенной DataForm со свойством страницы, используя привязку RelativeSource. Но этот UserControl предназначен для использования во многих местах и ​​не может иметь тихих зависимостей от свойств вне UserControl. Мне нужно сделать зависимость явной.

Создание DependencyProperty в UserControl является правильным подходом, но нет необходимости пытаться реплицировать свойство в модели представления UserControl.

Что мне нужно сделать, это

1: добавить свойство DependencyProperty в UserControl:

public QueryableCollectionView ItemsSource
{
    get { return (QueryableCollectionView)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
    DependencyProperty.Register("ItemsSource", typeof(QueryableCollectionView), typeof(FormControl));

Примечание: мне не нужно реализовывать функцию обратного вызова.

2: привяжите ItemsProperty встроенной DataForm к этому новому DependencyProperty UserControl, используя привязку RelativeSource:

<UserControl
        ...>
    <telerik:RadDataForm
        ItemsSource="{Binding Path=ItemsSource, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
        />
</UserControl>

3: сделать viewModel страницы видимым с правильным типом (DataContext имеет тип object):

public partial class MainWindow : Window
{
    public MainWIndow()
    {
        this.InitializeComponent();
        this.DataContext = new MainWindowVM();
    }
    public MainWindowVM viewModel
    { get { return this.DataContext as MainWindowVM; } }
}

4: На странице свяжите новый ItemsProperty DependencyProperty UserControl с соответствующим свойством модели представления страницы, снова используя привязку RelativeSource:

<Window
        ...>
    <local:FormControl
        ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type telerik:RadPane}},Path=viewModel.items}"
        />
</Window>
...