Привязка WPF / XAML: работа с реальным DataContext

Это мой код 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>

ComboBox выше предназначен только для тестирования - я хочу посмотреть, могу ли я отобразить все свои категории, и он работает хорошо.

DataGrid ниже - это элемент управления, который мне действительно нужен. Я хочу, чтобы ComboBox категорий был столбцом DataGrid. Но то, как я это делаю во фрагменте, очевидно, не работает, поскольку привязки данных для столбцов относятся к ItemsSource DataGrid, а не к старому DataContext. Как я могу переключить его обратно для этой привязки?

Класс Control:

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;
    }
}

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));
        }
    }
}

person Ethon    schedule 05.12.2015    source источник


Ответы (2)


Поскольку DataGridComboBoxColumn или любые другие поддерживаемые столбцы сетки данных не являются частью визуального дерева datagrid, поэтому они не наследуют DataContext datagrid. Поскольку они не лежат в визуальном дереве, любая попытка получить DataContext с помощью RelativeSource не сработает.

Решение. Вы можете создать прокси-элемент для привязки контекста данных окна; используйте этот прокси-элемент для привязки ItemsSource из DataGridComboBoxColumn.

<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>
person user1672994    schedule 05.12.2015

Вы можете привязать DataGrids DataContext с помощью ElementName Binding:

<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>

Обратите внимание, что вы должны дать DataGrid имя, чтобы это работало.


Другой вариант - привязать к Ancestor. С этим решением вам не нужно давать DataGrid имя, но это более сложно:

<DataGridComboBoxColumn Header="Category" SelectedItemBinding="{Binding Category}"
      ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.Clvm.Categories}" />
person Domysee    schedule 05.12.2015
comment
Поскольку DataGridComboBoxColumn не является частью визуального дерева DataGrid или окна, я не думаю, что RelativeSource или привязка к ElementName будут работать. - person user1672994; 05.12.2015