C# WPF:如何绑定到位于相邻控件的可视树下的属性?

C# WPF:如何绑定到位于相邻控件的可视树下的属性?,c#,wpf,xaml,data-binding,C#,Wpf,Xaml,Data Binding,我有一个WPF窗口和用户控件,我在窗口中使用它 窗口: <Window x:Class="AdjacentControlVisualTree.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-na

我有一个WPF
窗口
用户控件
,我在
窗口中使用它

窗口:

<Window x:Class="AdjacentControlVisualTree.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:AdjacentControlVisualTree"
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <StackPanel Orientation="Vertical">
        <local:AdjacentControl x:Name="AdjacentControl"/>
        <Button Content="Foo"
            CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl.BarDataGrid}"
            Command="{Binding FooCommand}"/>
    </StackPanel>
</Window>
<UserControl x:Class="AdjacentControlVisualTree.AdjacentControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:AdjacentControlVisualTree">

    <UserControl.DataContext>
        <local:AdjacentViewModel/>
    </UserControl.DataContext>

    <Grid>
        <DataGrid x:Name="BarDataGrid"
            ItemsSource="{Binding Collection}"
            IsReadOnly="True"
            AutoGenerateColumns="False"
            SelectionUnit="FullRow"
            SelectionMode="Single">
            <DataGrid.Columns>
                <DataGridTextColumn Header="String" Binding="{Binding}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

有没有一种方法可以使绑定工作而不必更改控件的结构?

最简单的方法可能是将
BarDataGrid
作为属性公开

public static readonly DependencyProperty SelectedItemProperty =
    System.Windows.Controls.Primitives.Selector.SelectedItemProperty.AddOwner(
        typeof(AdjacentControl));

public object SelectedItem
{
    get { return GetValue(SelectedItemProperty); }
    set { SetValue(SelectedItemProperty, value); }
}
更改XAML名称(例如,更改为
x:name=“barDataGrid”
),并将此属性添加到UserControl的代码中:

public DataGrid BarDataGrid { get { return barDataGrid; } }
然后绑定到DataGrid的SelectedItem属性,如下所示:

CommandParameter="{Binding BarDataGrid.SelectedItem, ElementName=AdjacentControl}"
CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl}"

一个更干净的解决方案是不公开DataGrid子级,而只通过一个专用的依赖项属性公开其SelectedItem

public static readonly DependencyProperty SelectedItemProperty =
    System.Windows.Controls.Primitives.Selector.SelectedItemProperty.AddOwner(
        typeof(AdjacentControl));

public object SelectedItem
{
    get { return GetValue(SelectedItemProperty); }
    set { SetValue(SelectedItemProperty, value); }
}
您可以通过RelativeSource绑定将DataGrid的SelectedItem绑定到此属性

<DataGrid SelectedItem="{Binding SelectedItem,
                         RelativeSource={RelativeSource AncestorType=UserControl}}"

最简单的方法可能是将
BarDataGrid
作为属性公开

public static readonly DependencyProperty SelectedItemProperty =
    System.Windows.Controls.Primitives.Selector.SelectedItemProperty.AddOwner(
        typeof(AdjacentControl));

public object SelectedItem
{
    get { return GetValue(SelectedItemProperty); }
    set { SetValue(SelectedItemProperty, value); }
}
更改XAML名称(例如,更改为
x:name=“barDataGrid”
),并将此属性添加到UserControl的代码中:

public DataGrid BarDataGrid { get { return barDataGrid; } }
然后绑定到DataGrid的SelectedItem属性,如下所示:

CommandParameter="{Binding BarDataGrid.SelectedItem, ElementName=AdjacentControl}"
CommandParameter="{Binding SelectedItem, ElementName=AdjacentControl}"

一个更干净的解决方案是不公开DataGrid子级,而只通过一个专用的依赖项属性公开其SelectedItem

public static readonly DependencyProperty SelectedItemProperty =
    System.Windows.Controls.Primitives.Selector.SelectedItemProperty.AddOwner(
        typeof(AdjacentControl));

public object SelectedItem
{
    get { return GetValue(SelectedItemProperty); }
    set { SetValue(SelectedItemProperty, value); }
}
您可以通过RelativeSource绑定将DataGrid的SelectedItem绑定到此属性

<DataGrid SelectedItem="{Binding SelectedItem,
                         RelativeSource={RelativeSource AncestorType=UserControl}}"

UserControl必须公开您将绑定到的专用属性。值得注意的是,UserControl永远不应该像在其XAML中那样显式设置自己的DataContext。如果这样做,您将无法在其属性上使用基于标准DataContext的绑定。有没有什么特别的原因让您使用此UserControl,不是直接在MainWindow中使用DataGrid?这是一个非常轻量级的示例,我把它放在一起是为了向自己证明它确实没有像我预期的那样工作,并且它不是由我在实际解决方案中可能忽略的标记堆中的某个东西引起的。我假设如果我可以绑定到一个祖先控件的
DataContext
的属性,那么也可以绑定到另一个控件中的子控件……UserControl必须公开一个您将绑定到的专用属性。值得注意的是,UserControl永远不应该像在其XAML中那样显式设置自己的DataContext。如果这样做,您将无法在其属性上使用基于标准DataContext的绑定。有没有什么特别的原因让您使用此UserControl,不是直接在MainWindow中使用DataGrid?这是一个非常轻量级的示例,我把它放在一起是为了向自己证明它确实没有像我预期的那样工作,并且它不是由我在实际解决方案中可能忽略的标记堆中的某个东西引起的。我假设如果我可以绑定到一个祖先控件的
DataContext
属性,那么也可以绑定到另一个控件中的子控件……非常感谢您的帮助!然而,为了完全公平,我不得不承认我完全忘记了提及我非常希望有一个符合MVVM的解决方案,如果我理解正确的话,代码中的任何东西都肯定不符合MVVM原则。现在,我有点困惑,因为有时候,如果不严格遵守规则,遵循所谓的最佳实践可能会导致人们不会遇到的陷阱。无论如何,考虑到我的情况,这似乎是一个很好的解决方案,所以再次感谢…:)@Valachovidom“代码背后有任何东西肯定不符合MVVM原则”是一个常见的误解。特别是当涉及到自定义控件时。它们从来没有自己的视图模型,而是公开属性。这当然是在他们的代码背后声明的。您可以通过查看WPF框架控件的源代码轻松地验证这一点。后面有很多代码,但没有一个单一的视图模型。非常感谢您的帮助!然而,为了完全公平,我不得不承认我完全忘记了提及我非常希望有一个符合MVVM的解决方案,如果我理解正确的话,代码中的任何东西都肯定不符合MVVM原则。现在,我有点困惑,因为有时候,如果不严格遵守规则,遵循所谓的最佳实践可能会导致人们不会遇到的陷阱。无论如何,考虑到我的情况,这似乎是一个很好的解决方案,所以再次感谢…:)@Valachovidom“代码背后有任何东西肯定不符合MVVM原则”是一个常见的误解。特别是当涉及到自定义控件时。它们从来没有自己的视图模型,而是公开属性。这当然是在他们的代码背后声明的。您可以通过查看WPF框架控件的源代码轻松地验证这一点。后面有很多代码,但没有一个视图模型。