WPF DataGrid — присваивает значение по умолчанию столбцу ComboBoxColumn при создании новой строки

#c# #wpf #xaml #datagrid #default-value

Вопрос:

Мое DataGrid связано с ObservableCollection моим в моем ViewModel .

Я добавляю новые строки с помощью команды, которая выглядит следующим образом:

 public void AddItem() {
    MyObservableCollection.Add(new MyClass());
}
 

Как я могу установить значение по умолчанию для a DataGridComboBoxColumn в моем DataGrid , когда я создаю новые строки, потому что как есть — он оказывается пустым, что портит мои привязки. Начальное X-количество строк при загрузке имеет значения, но не новые строки, которые добавляет пользователь.
Если бы я мог, например, привязать DefaultValue его к тому элементу , который находится в индексе 1 ComboBoxColumn , это было бы здорово.
Возможно ли это?

XAML для сетки данных и столбца DataGridComboBoxColumn выглядит следующим образом:

 <DataGrid
    AutoGenerateColumns="False"
    ItemsSource="{Binding MyObservableCollection}">

     <DataGridComboBoxColumn
         ItemsSource="{Binding Source={StaticResource MyObservableCollection}}"
         SelectedValueBinding="{Binding PropertyName1}"
         DisplayMemberPath="PropertyName1"
         SelectedValuePath="PropertyName1" />

</DataGrid>
 

Что я пробовал:

Я попытался установить TargetNullValue «и FallBackValue «, просто чтобы появилось значение, потому что это все, что мне нужно. Но это не работает. Я также попытался установить параметр DefaultValue при создании экземпляра новой строки, но не смог заставить его работать. Что-то подобное добавлено к моему AddItem() методу:

 MyObservableCollection.Add(new MyClass());
DataTable dt = new();
dt.Columns.Add("PropertyName1", typeof(string));
dt.Columns["PropertyName1"].DefaultValue = /* not sure what to put here */ ;
 

Я попытался добавить туда случайный текст, но он не отображается, значит, что-то не так…

Ответ №1:

Вы должны привязать SelectedItem ComboBox или DataGridComboBoxColumn.SelecetedItemBinding , если быть точным.
Если предположить, что такое SelectedMyClass свойство будет определено в текущем DataGrid.DataContext , привязка может выглядеть следующим образом:

 <DataGridComboBoxColumn Header="Value"
                        ItemsSource="{Binding Source={StaticResource MyObservableCollection}}"
                        DisplayMemberPath="PropertyName1"
                        SelectedItemBinding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.SelectedMyClassItem}" />
 

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


Обычно источником элементов ячейки ComboBox является коллекция, определенная в самой модели данных — в вашем случае MyClass . В этом сценарии SelectedItem свойство (источник привязки ComboBox.SelectedItem ) также будет определено в модели MyClass данных. Я переименовал MyClass DataGridItem , чтобы сделать назначение участвующих моделей более ясным). Это SelectedItem важно, когда вы хотите отобразить начальное значение в ComboBox ячейке:

 // The item model defines the source collection for the cell's ComboBox
class DataGridItem
{
  public ObservableCollection<DataItem> DataItems { get; }
  
  // TODO::Property must raise INotifyPropertyChanged.PropertyChanged
  public DataItem SelectedDataItem { get; set; }

  public string SomeProperty { get; set; }
}
 

Просто инициализируйте DataGridItem.SelectedDataItem , чтобы отобразить начальное значение в ComboBox ячейке:

 public void AddItem() 
{
  var newDataGridItem = new DataGridItem();
  newDataGridItem.SelectedDataItem = newDataGridItem.DataItems.FirstOrDefault();  
  this.DataGridItems.Add(newDataGridItem);
}
 

Затем отрегулируйте привязку данных DataGridComboBoxColumn . Поскольку определение DataGridComboBoxColumn не является частью визуального дерева DataContext , оно не является моделью данных текущей строки. Поэтому мы должны настроить привязку с ComboBox.ItemsSource помощью Style :

 <DataGrid ItemsSource="{Binding DataGridItems}">
  <DataGridComboBoxColumn Header="Value"
                          DisplayMemberPath="SomeProperty"
                          SelectedItemBinding="{Binding SelectedDataItem}">
    <DataGridComboBoxColumn.EditingElementStyle>
      <Style TargetType="ComboBox">
        <Setter Property="ItemsSource" Value="{Binding DataItems}" />
      </Style>
    </DataGridComboBoxColumn.EditingElementStyle>

    <DataGridComboBoxColumn.ElementStyle>
      <Style TargetType="ComboBox">
        <Setter Property="ItemsSource" Value="{Binding DataItems}" />
      </Style>
    </DataGridComboBoxColumn.ElementStyle>
  </DataGridComboBoxColumn>
</DataGrid>
 

Комментарии:

1. большое вам спасибо за информативный пост. Я работаю над реализацией вашего решения, но — моя наблюдаемая коллекция находится в моей модели просмотра. Как я мог обойти это? Существует автоматическая связь между моим представлением и моделью представления, и я бы предпочел не изменять это.

2. Это не проблема. В моем примере уже предполагается MyObservableCollection , что (или элементы DataGridItems) являются коллекцией ObservableCollection, определенной в модели представления. Вы привязываете эту коллекцию к сетке данных, как вы уже сделали в своем первоначальном сообщении. Затем каждая модель элемента в сетке данных. ItemsSource также должен иметь коллекцию и свойство SelectedItem, которые оба привязываются к фактическому столбцу DataGridComboBoxColumn (ячейка) — это вторая часть ответа..

3. спасибо за уточнение. Теперь у меня есть Система. Исключение ArgumentNullException: ‘Значение не может быть нулевым. (Параметр «источник») » когда я попытался добавить новую строку. Я трижды проверил и, по-моему, все устроил так, как вы велели. Хммм.. Я получил исключение во 2-й строке в методе AddItem (). Кроме того, при загрузке формы инициализируется X строк, раньше у них была одна строка для каждого экземпляра SomeProperty, но теперь все они имеют один и тот же индекс SomeProperty, и когда я изменяю одну из них, все они меняются.

4. Прежде всего, я показал только минимальную версию кода для отображения шаблона. Вам , конечно, придется инициализировать коллекцию DataItems в конструкторе DataGridItem , в противном случае, когда вы ссылаетесь на нее для добавления или получения элементов, коллекция будет null .

5. Что касается вашей проблемы с sencond: SomeProperty является строковым свойством DataGridItem . Если вы последуете моему примеру, у вас будет строка для каждого DataGridItem экземпляра внутри DataGridItems коллекции, когда DataGridItems коллекция будет привязана DataGrid.ItemsSource . Если у вас включена автогенерация столбцов, то каким-то свойством будет столбец. Если вы, как вы сказали, измените значение одной ячейки, и это приведет к изменению значений всех ячеек строки, то вы использовали один и тот же экземпляр для каждой строки. Убедитесь, что вы не добавляете один и тот же экземпляр несколько раз в DataGridItems коллекцию.