Привязка к свойству зависимости, которое, в свою очередь, связано с другим источником привязки - PullRequest
3 голосов
/ 13 января 2011

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

-

<!-- ActionButtons.xaml -->

<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Button Name="btnNew" Content="New" Command="{Binding Path=NewCommand}" />
        <Button Name="btnEdit" Content="Edit" Command="{Binding Path=EditCommand, Mode=OneWay}" />
        <Button Name="btnDelete" Content="Delete" Command="{Binding Path=DeleteCommand, Mode=OneWay}" />
    </StackPanel>

-

Затем в приведенном ниже коде у меня есть объявления dpprops:

// ActionButtons.xaml.cs

public uscActionButtons()
{
            InitializeComponent();
            this.DataContext = this;
        }

        public ICommand NewCommand
        {
            get { return (ICommand)GetValue(NewCommandProperty); }
            set { SetValue(NewCommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for NewCommand.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NewCommandProperty =
            DependencyProperty.Register("NewCommand", typeof(ICommand), typeof(uscActionButtons), new UIPropertyMetadata(null, new PropertyChangedCallback(OnCommandChanged)));

Я хотел связать свойство NewCommand с его конкретной реализацией в другом элементе управления.Пример предполагаемого использования:

<!-- SomeControl.xaml -->
<common:uscActionButtons Grid.Row="0" HorizontalAlignment="Left" 
                                 NewCommand="{Binding NewItemCommand}"
                                 />

И

// SomeControlViewModel.cs
// Note: SomeControlViewModel IS in the DataContext of SomeControl.
public ICommand NewItemCommand
        {
            get
            {
                if (mNewItemCommand == null)
                {
                    mNewItemCommand = new RelayCommand(x => this.CreateItem());
                }

                return mNewItemGroupCommand;
            }
        }

Проблема заключается в том, что повторно используемый элемент управления (ActionButtons) не видит команду NewItemCommand.Если я использую простую кнопку, она видит это нормально.Кажется, проблема в том, что эта «цепочка» обязательна.Но я знаю, что это возможно, кнопка WPF имеет свойство зависимости Command, к которому вы привязываете свои команды, поэтому не должно быть так сложно создать свой собственный многократно используемый элемент управления, который предоставляет свойство зависимости ICommand.

Есть идеи?

Спасибо


edit: вот решение, все, что мне нужно было сделать, это использовать RelativeSource с FindAncestor.

<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Button Name="btnNew" Content="New" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=NewCommand, Mode=OneWay}" />
        <Button Name="btnEdit" Content="Edit" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=EditCommand, Mode=OneWay}" />
        <Button Name="btnDelete" Content="Delete" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=DeleteCommand, Mode=OneWay}" />
    </StackPanel>

Ответы [ 3 ]

4 голосов
/ 14 января 2011

Проблема, которую вы видите, заключается в том, что вы изменяете DataContext элемента управления ActionButtons. Когда вы устанавливаете DataContext в конструкторе, все Bindings, которые вы укажете на него (даже из внешнего XAML, который его создает), будут указывать на новый DataContext. Таким образом, когда вы применяете Binding в SomeControl, это связывание пытается связать с DataContext, который является экземпляром ActionButtons.

Я не думаю, что Control должен когда-либо устанавливать свой собственный DataContext, так как это вызывает ошибки, которые вы видите. Если вы хотите использовать UserControl (и я, вероятно, сам буду использовать Control, чтобы я мог TemplateBindings), тогда вы можете использовать привязку RelativeSource (как вы упоминали в своем комментарии к Snowbear) и Bindings должен работать.

3 голосов
/ 14 января 2011

Вот чего не хватало в моем коде:

<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Top">
        <Button Name="btnNew" Content="New" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=NewCommand, Mode=OneWay}" />
        <Button Name="btnEdit" Content="Edit" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=EditCommand, Mode=OneWay}" />
        <Button Name="btnDelete" Content="Delete" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:uscActionButtons}, Path=DeleteCommand, Mode=OneWay}" />
    </StackPanel>

Мне пришлось использовать RelativeSource с FindAncestor.

Спасибо за все ваши ответы!

1 голос
/ 13 января 2011
this.DataContext = this;

это кажется проблемой, потому что в этом XAML:

 NewCommand="{Binding NewItemCommand}"

вы привязываетесь к NewItemCommand самого ActionButtons.Вот почему установка собственного DataContext внутри элемента управления, на мой взгляд, является плохой моделью.Если вам действительно нужен этот DataContext (вам), тогда установите для него внутренний элемент верхнего уровня (в вашем случае StackPanel)

Также отладчик Visual Studio должен помочь вам при отладке привязки.Если вы запустите свое приложение с подключенным отладчиком, то в окне вывода Visual Studio вы увидите ошибки привязки, и они, как правило, просты для понимания, ошибка даст вам знать, что для этой конкретной привязки она ищет свойство NewItemCommand в экземпляре ActionButtons вместокласс, который вы ожидали.

ОБНОВЛЕНИЕ Протестировал ваш код в VS.Ошибка в окне вывода:

System.Windows.Data Error: 40 : BindingExpression path error: 'NewItemCommand' property not found on 'object' ''uscActionButtons' (Name='')'. BindingExpression:Path=NewItemCommand; DataItem='uscActionButtons' (Name=''); target element is 'uscActionButtons' (Name=''); target property is 'NewCommand' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'EditCommand' property not found on 'object' ''uscActionButtons' (Name='')'. BindingExpression:Path=EditCommand; DataItem='uscActionButtons' (Name=''); target element is 'Button' (Name='btnEdit'); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'DeleteCommand' property not found on 'object' ''uscActionButtons' (Name='')'. BindingExpression:Path=DeleteCommand; DataItem='uscActionButtons' (Name=''); target element is 'Button' (Name='btnDelete'); target property is 'Command' (type 'ICommand')

Не могли бы вы пропустить эти ошибки, например, из-за слишком позднего открытия окна вывода?

Обновление 2:

Исправлено с заменой вашего:

this.DataContext = this;

с моим:

root.DataContext = this; //root - name for stackpanel
...