Привязка ItemsSource ComboBoxColumn в WPF DataGrid



у меня есть два простых класса модели и ViewModel...



public class GridItem
{
public string Name { get; set; }
public int CompanyID { get; set; }
}

public class CompanyItem
{
public int ID { get; set; }
public string Name { get; set; }
}

public class ViewModel
{
public ViewModel()
{
GridItems = new ObservableCollection<GridItem>() {
new GridItem() { Name = "Jim", CompanyID = 1 } };

CompanyItems = new ObservableCollection<CompanyItem>() {
new CompanyItem() { ID = 1, Name = "Company 1" },
new CompanyItem() { ID = 2, Name = "Company 2" } };
}

public ObservableCollection<GridItem> GridItems { get; set; }
public ObservableCollection<CompanyItem> CompanyItems { get; set; }
}


...и простое окно:



<Window x:Class="DataGridComboBoxColumnApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" />
<DataGridComboBoxColumn ItemsSource="{Binding CompanyItems}"
DisplayMemberPath="Name"
SelectedValuePath="ID"
SelectedValueBinding="{Binding CompanyID}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>


в ViewModel находится в окне MainWindow в DataContext в приложение.код XAML.cs:



public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

MainWindow window = new MainWindow();
ViewModel viewModel = new ViewModel();

window.DataContext = viewModel;
window.Show();
}
}


как вы можете видеть, я поставил ItemsSource из DataGrid в GridItems коллекция ViewModel. Эта часть работает, отображается одна линия сетки с именем "Jim".



Я также хочу установить ItemsSource из выпадающего списка в каждой строке в CompanyItems коллекция ViewModel. Эта часть не работает: поле со списком остается пустым и в окне вывода отладчика я вижу сообщение об ошибке:




1987   7  

7 ответов:

Pls, проверьте, если DataGridComboBoxColumn xaml ниже будет работать для вас:

<DataGridComboBoxColumn 
    SelectedValueBinding="{Binding CompanyID}" 
    DisplayMemberPath="Name" 
    SelectedValuePath="ID">

    <DataGridComboBoxColumn.ElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
        </Style>
    </DataGridComboBoxColumn.ElementStyle>
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
        </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

здесь вы можете найти другое решение проблемы вы столкнулись: использование полей со списком с WPF DataGrid

The документация на MSDN о ItemsSource на DataGridComboBoxColumn говорит, что только статические ресурсы, статический код или встроенные коллекции элементов combobox могут быть привязаны к ItemsSource:

чтобы заполнить выпадающий список, сначала установить свойство ItemsSource для ComboBox с помощью одного из следующих способов опции:

  • статический ресурс. Дополнительные сведения см. В разделе разметка StaticResource Расширение.
  • An x: статический код объекта. Дополнительные сведения см. В разделе X: статическая разметка Расширение.
  • встроенная коллекция типов ComboBoxItem.

привязка к свойству DataContext невозможна, если я правильно это понимаю.

и действительно: когда я делаю CompanyItems a статический свойство в ViewModel ...

public static ObservableCollection<CompanyItem> CompanyItems { get; set; }

... добавьте в окно пространство имен, в котором находится ViewModel ...

xmlns:vm="clr-namespace:DataGridComboBoxColumnApp"

... и измените привязку на ...

<DataGridComboBoxColumn
    ItemsSource="{Binding Source={x:Static vm:ViewModel.CompanyItems}}" 
    DisplayMemberPath="Name"
    SelectedValuePath="ID"
    SelectedValueBinding="{Binding CompanyID}" />

... тогда это работает. Но наличие ItemsSource в качестве статического свойства может быть иногда в порядке, но это не всегда то, что я хочу.

правильное решение, кажется:

<Window.Resources>
    <CollectionViewSource x:Key="ItemsCVS" Source="{Binding MyItems}" />
</Window.Resources>
<!-- ... -->
<DataGrid ItemsSource="{Binding MyRecords}">
    <DataGridComboBoxColumn Header="Column With Predefined Values"
                            ItemsSource="{Binding Source={StaticResource ItemsCVS}}"
                            SelectedValueBinding="{Binding MyItemId}"
                            SelectedValuePath="Id"
                            DisplayMemberPath="StatusCode" />
</DataGrid>

макет выше отлично работает для меня, и должен работать для других. Этот выбор дизайна также имеет смысл, хотя он не очень хорошо объяснен нигде. Но если у вас есть столбец данных с предопределенными значениями, эти значения обычно не меняются во время выполнения. Так что создание CollectionViewSource и инициализация данных один раз имеет смысл. Он также избавляется от более длинных Привязок, чтобы найти предка и привязать его к контексту данных (который всегда чувствовал себя неправильно для меня).

Я оставляю это здесь для всех, кто боролся с этой привязкой, и задавался вопросом, есть ли лучший способ (поскольку эта страница, очевидно, все еще появляется в результатах поиска, Вот как я попал сюда).

Я понимаю, что этот вопрос старше года, но я просто наткнулся на него в решении аналогичной проблемы и подумал, что поделюсь другим потенциальным решением, если это может помочь будущему путешественнику (или себе, когда я забуду об этом позже и окажусь на StackOverflow между криками и бросками ближайшего объекта на моем столе).

в моем случае я смог получить эффект, который я хотел, используя DataGridTemplateColumn вместо DataGridComboBoxColumn, а-ля следующий фрагмент. [предостережение: я использую .NET 4.0, и то, что я читал, заставляет меня поверить, что DataGrid сделал много эволюции, поэтому YMMV при использовании более ранней версии]

<DataGridTemplateColumn Header="Identifier_TEMPLATED">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox IsEditable="False" 
                Text="{Binding ComponentIdentifier,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                ItemsSource="{Binding Path=ApplicableIdentifiers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ComponentIdentifier}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

RookieRick прав, используя DataGridTemplateColumn вместо DataGridComboBoxColumn дает гораздо более простой XAML.

кроме того, поставив CompanyItem список рядом с GridItem позволяет избавиться от RelativeSource.

ИМХО, это дает вам очень чистое решение.

XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
    <DataGrid.Resources>
        <DataTemplate x:Key="CompanyDisplayTemplate" DataType="vm:GridItem">
            <TextBlock Text="{Binding Company}" />
        </DataTemplate>
        <DataTemplate x:Key="CompanyEditingTemplate" DataType="vm:GridItem">
            <ComboBox SelectedItem="{Binding Company}" ItemsSource="{Binding CompanyList}" />
        </DataTemplate>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn CellTemplate="{StaticResource CompanyDisplayTemplate}"
                                CellEditingTemplate="{StaticResource CompanyEditingTemplate}" />
    </DataGrid.Columns>
</DataGrid>

Модель Вид:

public class GridItem
{
    public string Name { get; set; }
    public CompanyItem Company { get; set; }
    public IEnumerable<CompanyItem> CompanyList { get; set; }
}

public class CompanyItem
{
    public int ID { get; set; }
    public string Name { get; set; }

    public override string ToString() { return Name; }
}

public class ViewModel
{
    readonly ObservableCollection<CompanyItem> companies;

    public ViewModel()
    {
        companies = new ObservableCollection<CompanyItem>{
            new CompanyItem { ID = 1, Name = "Company 1" },
            new CompanyItem { ID = 2, Name = "Company 2" }
        };

        GridItems = new ObservableCollection<GridItem> {
            new GridItem { Name = "Jim", Company = companies[0], CompanyList = companies}
        };
    }

    public ObservableCollection<GridItem> GridItems { get; set; }
}

ваш ComboBox пытается привязать к привязке к GridItem[x].CompanyItems, которого не существует.

ваш RelativeBinding близок, однако он должен привязаться к DataContext.CompanyItems потому что окно.CompanyItems не существует

способ bast, который я использую, я привязываю textblock и combobox к одному и тому же свойству, и это свойство должно поддерживать notifyPropertyChanged.

я использовал relativeresource для привязки к родительскому представлению datacontext, который является usercontrol, чтобы подняться на уровень datagrid в привязке, потому что в этом случае datagrid будет искать объект, который вы использовали в datagrid.itemsource

<DataGridTemplateColumn Header="your_columnName">
     <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
             <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit.Name, Mode=TwoWay}" />
           </DataTemplate>
     </DataGridTemplateColumn.CellTemplate>
     <DataGridTemplateColumn.CellEditingTemplate>
           <DataTemplate>
            <ComboBox DisplayMemberPath="Name"
                      IsEditable="True"
                      ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.UnitLookupCollection}"
                       SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                      SelectedValue="{Binding UnitId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                      SelectedValuePath="Id" />
            </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

Comments

    Ничего не найдено.