简体   繁体   English

WPF / XAML绑定:使用实际的DataContext

[英]WPF/XAML Binding: Work with real DataContext

This is my XAML code: 这是我的XAML代码:

<ComboBox Grid.Row="0" Margin="5" ItemsSource="{Binding Path=Clvm.Categories}">
</ComboBox>

<GridSplitter Grid.Row="0" Height="3" />

<DataGrid Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="Email" Binding="{Binding Email}" />
        <DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
                                ItemsSource="{Binding Path=Clvm.Categories}" />
    </DataGrid.Columns>
</DataGrid>

The ComboBox above is just for testing - I want to see if I can display all my Categories and it works well. 上面的ComboBox仅用于测试-我想看看是否可以显示所有类别,并且效果很好。

The DataGrid below is the control I really want. 下面的DataGrid是我真正想要的控件。 I want that Categories ComboBox as a column of the DataGrid. 我希望将该Category ComboBox作为DataGrid的一列。 But the way I do it in the snippet obviously doesn't work as the data bindings for the columns are relative to the DataGrid's ItemsSource and not to the old DataContext. 但是我在代码段中执行此操作的方式显然不起作用,因为列的数据绑定是相对于DataGrid的ItemsSource而不是相对于旧DataContext的。 How can I switch it back for that binding? 如何将其切换回该绑定?

The Control class: 控件类:

public partial class ArtistManagementControl : UserControl {
    public ArtistManagementControl() {
        InitializeComponent();

        IDatabase db = new MYSQLDatabase("Server = localhost; Database = ufo; Uid = root;");
        SharedServices.Init(db);
        Alvm = new ArtistListViewModel(SharedServices.GetInstance().ArtistService, SharedServices.GetInstance().CategoryService);
        Clvm = new CategoryListViewModel(SharedServices.GetInstance().CategoryService);
        this.DataContext = this;
    }

    public ArtistListViewModel Alvm {
        get; private set;
    }

    public CategoryListViewModel Clvm {
        get; private set;
    }
}

The ViewModel for the categories: 类别的ViewModel:

public class CategoryListViewModel {
    private ICategoryService categoryService;

    public CategoryListViewModel(ICategoryService categoryService) {
        this.categoryService = categoryService;
        Categories = new ObservableCollection<CategoryViewModel>();
        UpdateCategories();
    }

    public ObservableCollection<CategoryViewModel> Categories {
        get; set;
    }

    public void UpdateCategories() {
        Categories.Clear();
        foreach(var cat in categoryService.GetAllCategories()) {
            Categories.Add(new CategoryViewModel(cat));
        }
    }
}

Since DataGridComboBoxColumn or any other supported data grid columns are not part of visual tree of datagrid so they don't inherit the DataContext of datagrid . 由于DataGridComboBoxColumn或任何其他受支持的数据网格列都不是datagrid可视树的一部分,因此它们不会继承datagridDataContext Since, they don't lie in visual tree so any try to get DataContext using RelativeSource won't work. 由于它们不位于可视树中,因此使用RelativeSource获取DataContext任何尝试都将无效。

Solution - You can create a proxy element to bind the data context of window; 解决方案-您可以创建一个proxy元素来绑定window的数据上下文; use that proxy element to bind the ItemsSource of DataGridComboBoxColumn . 使用该代理元素绑定DataGridComboBoxColumnItemsSource

<Grid>
  <Grid.Resources>
           <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
   </Grid.Resources>
   <ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"></ContentControl>
    <DataGrid x:Name="datagrid" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
      <DataGrid.Columns>
        <DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="Email" Binding="{Binding Email}" />
        <DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
                                ItemsSource="{Binding DataContext.Clvm.Categories, Source={StaticResource ProxyElement}}" />
      </DataGrid.Columns>
    </DataGrid>     
</Grid>

You can bind to the DataGrids DataContext by using an ElementName Binding: 您可以使用ElementName绑定绑定到DataGrids DataContext

<DataGrid x:Name="datagrid" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding Alvm.Artists}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="ID" IsReadOnly="True" Binding="{Binding Id}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="Email" Binding="{Binding Email}" />
        <DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
                                ItemsSource="{Binding Path=DataContext.Clvm.Categories, ElementName=datagrid}" />
    </DataGrid.Columns>
</DataGrid>

Note that you have to give the DataGrid a name for that to work. 请注意,必须为DataGrid命名才能使用。


Another option is to bind to an Ancestor . 另一个选择是绑定到一个Ancestor With this solution you dont have to give the DataGrid a name, but it is more complex: 使用此解决方案,您不必给DataGrid命名,但是更复杂:

<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
      ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.Clvm.Categories}" />

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM