绑定到依赖属性,该属性反过来又绑定到另一个绑定源。

3

虽然有点难以解释,但我会尽力。我想要一个可重复使用的控件,其中包含三个按钮,一个用于创建实体,另一个用于编辑,第三个用于删除。以下是相关部分的缩写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按钮具有一个命令依赖属性,您可以将其绑定到自己的命令,因此创建自己的可重用控件并公开ICommand依赖属性不应该那么难。

有什么想法吗?

谢谢


编辑:这是解决方案,我所要做的就是使用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
您看到的问题是您正在更改ActionButtons控件的DataContext。当您在构造函数中设置其DataContext时,所有指定在其中的绑定(甚至来自实例化它的外部XAML)都将指向新的DataContext。因此,当您在SomeControl中应用绑定时,该绑定试图绑定到DataContext,而这恰好是ActionButtons实例。
我认为Control永远不应该设置自己的DataContext,因为它会导致像您所看到的那样的错误。如果您想使用UserControl(我可能会自己使用Control以便可以执行TemplateBindings),则可以使用RelativeSource绑定(如您在对Snowbear的评论中提到的),然后Bindings应该可以正常工作。

我改用了RelativeSource(我确信我已经尝试过了),现在至少它会调用SomeControlViewModel.cs中的NewItemCommand属性上的GET方法,但不幸的是它不会调用注入到RelayCommand构造函数中的委托(RelayCommand已经广泛使用,所以我确定它没问题)。有什么想法吗?我已经删除了“this.DataContext = this”,你说服我不使用它的缺点 :) - SoManyGoblins

3

以下是我代码中缺少的部分:

<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
this.DataContext = this;

这似乎是一个问题,因为在这个XAML中:

 NewCommand="{Binding NewItemCommand}"

你正在绑定到ActionButtons自身的NewItemCommand。这就是为什么在控件内设置self DataContext是不好的模式,这是我的意见。如果你真的需要这个DataContext(你需要吗?),那么将它设置为你的顶层内部元素(在你的情况下是StackPanel)。

此外,Visual Studio调试器应该帮助你调试绑定问题。如果你使用附加了调试器的方式运行应用程序,那么在Visual Studio的输出窗口中,你将看到绑定错误,它们通常很容易理解,错误会告诉你,对于这个特定的绑定,它在ActionButtons实例中查找NewItemCommand属性,而不是你期望的类。

更新 在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

你正在混合使用两个不同的控件。NewCommand属性在ActionButtons中,NewItemCommand属性在SomeControl中。我使用了"this.DataContext = this"将"new"按钮的Command属性绑定到"NewCommand"属性。或者我可以使用RelativeSource、ElementName并给ActionButtons命名,但对我来说更或多或少都一样。VS调试器没有显示任何绑定错误。注意:所有数据上下文都在它需要的地方。 - SoManyGoblins
更新了我的答案,回复评论太麻烦了 :( - Snowbear
根据Abe Heidebrecht的评论,我移除了"this.DataContext = this"。现在它可以调用SomeControlViewModel.cs的NewItemCommand属性上的GET方法,但是当我点击按钮时不会调用执行委托 :( - SoManyGoblins
在你的第一个代码块中,你有这个:"Binding Path=NewCommand,"。也许是因为这个逗号引起了一些问题? - Snowbear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接