WPF DataGrid: DataGridComboxBox ItemsSource Привязка к коллекции коллекций

Ситуация:

Я создал DataGrid в XAML, и ItemsSource привязан к ObservableCollection определенного класса, который содержит свойства. Затем в C# я создаю DataGridTextColumn и DataGridComboBoxColumn и связываю их со свойствами объектов внутри ObservableCollection. Я могу связать DataGridComboBoxColumn с простой коллекцией, но я хочу связать его с коллекцией коллекций строк, чтобы для каждой строки ComboBox внутри DataGrid имел свою коллекцию строк. Я не смог сделать это...

Вопрос:

Как я могу связать DataGridCombBoxColumn так, чтобы я мог иметь различную коллекцию строк для каждой строки этого типа столбца?

Пример кода:

XAML:

<Window>
  <!-- ... -->
  WPFToolkit:DataGrid
           x:Name="DG_Operations"
           Margin="10,5,10,5" 
           Height="100" 
           HorizontalAlignment="Stretch" 
           FontWeight="Normal" 
           ItemsSource="{Binding Path=OperationsStats}"
           AlternatingRowBackground="{DynamicResource SpecialColor}" 
           HorizontalScrollBarVisibility="Auto" 
           VerticalScrollBarVisibility="Visible" 
           SelectionMode="Extended"
           CanUserAddRows="False" 
           CanUserDeleteRows="False"
           CanUserResizeRows="True" 
           CanUserSortColumns="True"
           AutoGenerateColumns="False" 
           IsReadOnly="False" 
           IsEnabled="True"
           BorderThickness="1,1,1,1" 
           VerticalAlignment="Stretch"/>
  <!-- ... -->
</Window>

C#:

public class DataModelStatsOperations
{
   public ObservableCollection<IStatsOperation> OperationsStats { get; set; }
}

public interface IStatsOperation
{
   string Operation { get; set; }
   Collection<string> Data{ get; set; }
}

public class StatsOperation : IStatsOperation
{
    public StatsOperation(string operation, Collection<string> data)
    {
        Operation = operation;
        Data = data;
    }
    public string Operation { get; set; }
    public Collection<string> Data{ get; set; }
}

private ObservableCollection<IStatsOperation> dataOperations_ =
        new ObservableCollection<IStatsOperation>();

//...
 Binding items = new Binding();
 PropertyPath path = new PropertyPath("Operation");
 items.Path = path;
 DG_Operations.Columns.Add(new DataGridTextColumn()
 {
     Header = "Operations",
     Width = 133,
     Binding = items
  });
  DG_Operations.Columns.Add(new DataGridComboBoxColumn()
  {
     Header = "Data",
     Width = 190,
     ItemsSource = /*???*/,
     SelectedValueBinding = new Binding("Data"),
     TextBinding = new Binding("Data")
  });
dataOperations_.Add(new StatsOperation(CB_Operation.SelectedItem.ToString(),
                                                           dataCollection));
DG_Operations.DataContext = new DataModelStatsOperations
{
    OperationsStats = dataOperations_
};
//...

Любая помощь будет принята с благодарностью!

Заметки:

Итак, прочитав два первых ответа, я кое-что заметил. Мое связывание действительно не правильно! Теперь я хочу сделать нечто похожее на то, что предложил AndyG:

DG_Operations.Columns.Add(new DataGridComboBoxColumn()
{
    Header = "Data",
    Width = 190,
    ItemsSource = new Binding("Data"), //notice this here does not work (have a look at the following error)
    SelectedValueBinding = new Binding("Operation"),
    TextBinding = new Binding("Operation")
});

Ошибка: "Не удается неявно преобразовать тип" System.Windows.Data.Binding "в" System.Collections.IEnumerable "."

Как можно связать ItemSource с данными?

6 ответов

Решение

Во-первых, это должно быть легко... во-вторых, почему вы строите (и привязываете) столбцы в C#? Ик.

XAML (я использую обычную сетку, потому что я ленивый):

<ListView Name="MyListView">
    <ListView.View>
        <GridView>

            <GridView.Columns>

                <GridViewColumn DisplayMemberBinding="{Binding Operation}" />

                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Choices}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

            </GridView.Columns>

        </GridView>
    </ListView.View>
</ListView>

C#:

void Window1_Loaded(object sender, RoutedEventArgs e)
{
    var dahList = new List<StatsOperation>();

    dahList.Add(new StatsOperation
    {
        Operation = "Op A",
        Choices = new string[] { "One", "Two", "Three" },
    });

    dahList.Add(new StatsOperation
    {
        Operation = "Op B",
        Choices = new string[] { "4", "5", "6" },
    });

    this.MyListView.ItemsSource = dahList;
}

Результаты, достижения:

http://www.singingeels.com/Articles/Articles/UserImage.aspx?ImageID=b1e3f880-c278-4d2b-bcc2-8ad390591200

Я думаю, что ошибка в том, как вы сделали свою привязку. Когда вы определяете столбец, привязка связана с объектом, который представлен определенной строкой. Итак, насколько я понимаю, у вас есть StatsOperation для каждой строки, поэтому столбец TextBox привязан к операции, как и у вас, а столбец ComboBox ItemsSource должен быть привязан к коллекции. Прямо сейчас это выглядит так, как будто Collection<Collection<string>>,

Я не определял столбцы в коде позади, так что вот пример в XAML. Я обнаружил, что ComboBoxColumn иногда может быть сложным, поэтому я показал, как можно создать комбинированный список в столбце, используя либо TemplateColumn, либо ComboBoxColumn. Я скопировал копию из моего собственного кода, поэтому просто замените 'dg' на 'WPFToolkit' в вашем случае:

<dg:DataGrid
      ...
      ...>
      <dg:DataGrid.Columns>
            <dg:DataGridTextColumn Binding="{Binding Operation}" CanUserReorder="True" CanUserResize="True" Header="Operation" />
            <dg:DataGridTemplateColumn CanUserReorder="True" CanUserResize="True" Header="Template Column">
                <dg:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox ItemsSource="{Binding Data}" SelectedItem="{Binding Operation}" />
                    </DataTemplate>
                </dg:DataGridTemplateColumn.CellTemplate>
            </dg:DataGridTemplateColumn>
            <dg:DataGridComboBoxColumn
                Header="ComboBox Column"                                                                                    
                 SelectedValueBinding="{Binding Operation}"                     
                 SelectedItemBinding="{Binding Operation}">
                <dg:DataGridComboBoxColumn.ElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
                        <Setter Property="ItemsSource" Value="{Binding Data}" />
                    </Style>
                </dg:DataGridComboBoxColumn.ElementStyle>
                <dg:DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding Data}" />
                        <Setter Property="IsDropDownOpen" Value="True" />
                    </Style>
                </dg:DataGridComboBoxColumn.EditingElementStyle>
            </dg:DataGridComboBoxColumn>
      </dg:DataGrid.Columns>

</dg:DataGrid>

Я предполагаю, что Operation - это выбранный элемент, Data - это элементы для выбора, и что ваш DataGrid связан с коллекцией StatsOperation. Удачи!

Чтобы исправить ошибку ItemsSource Binding Error, используйте форму:

BindingOperations.SetBinding(new DataGridComboBoxColumn(), DataGridComboBoxColumn.ItemsSourceProperty, new Binding("Data"));

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

РЕДАКТИРОВАТЬ Извините, я немного медленно в полдень:). Вот обновленный ответ. Похоже, отличная статья от Винсента Сибала WPF DataGrid - DataGridComboBoxColumn v1 Intro отвечает на ваш вопрос. Является ли?

Давайте свяжем ItemsSource вашего ComboBox со свойством, например CurrentChoices вашей модели просмотра. Это свойство должно возвращать отфильтрованный список на основе текущего выбора строки таблицы данных. CompareWithSelectedRow - это просто псевдофункция для иллюстрации примера. Каждый раз, когда вы нажимаете ComboBox для заполнения списка выбора, это свойство будет вызываться и возвращать правильный список выбора. См. Пример ниже, CompleteChoiceList - это список кортежей строки и списка. Есть место для улучшений, например, с помощью словаря или поиска.

          List<(string Key, List<string> Choices)> EntireChoiceList= new List<(string, List<string>)>();

    public List<string> CurrentChoices
    {
      get => CurrentChoices.Where(q => CompareWithSelectedRow(q.Key, CurrentRow )).FirstOrDefault().Choices;
    }

public object CurrentRow { get => ItemsView.CurrentItem; }

bool CompareWithSelectedRow(string key, object row)
{
    return key == row; // here you should define compare expression
}

Используйте CollectionViewSource, чтобы получить выбранную строку в сетке данных. Items - это список в модели представления, который был привязан к сетке данных.

            ItemsView = (CollectionView)CollectionViewSource.GetDefaultView(Items);
      ItemsView.CurrentItem

Частично - я думаю, что в том, что вы говорите, есть путаница. Вы сказали, что вам нужна коллекция наборов строк в каждой строке, чтобы в комбинированном окне могли отображаться разные строки для разных строк. Однако для того, чтобы в комбинированном окне отображался набор строк, вам нужен только набор строк для каждой строки, а не набор строк.

Теперь, когда вам нужна коллекция строк в строке, вы можете подумать, что вам понадобится коллекция строк.

Правильно ли я понимаю ваш вопрос? Если так, то ваше упоминание о коллекции строк неверно.

Что вам на самом деле нужно, так это коллекция StatOperations, в которой каждая StatOperation должна иметь коллекцию строк. Это именно то, что вы делали, как показано в ваших классах выше.

Чтобы добиться прогресса, я предлагаю вам отредактировать свой вопрос и указать, где именно вы застряли после исправления привязки, как предложено AndyG.

Другие вопросы по тегам