Значения DataGridComboBoxColumn из пользовательского класса

У меня есть класс CommentsData, и он используется для загрузки, управления и сохранения значений в DataGrid. Я хочу, чтобы поле «Статус» в классе отображалось в виде раскрывающегося списка в сетке. Значения комментария необходимо заполнить только один раз. Я пробовал много вариантов, но это не работает. Комбо пустое. Мне нужно иметь возможность заполнять значения в комбо, и когда выбор изменяется, значение должно оставаться там и не исчезать.

Вот Xaml для сетки (обновлено 2)

<DataGrid Grid.Row="2" AutoGenerateColumns="False"  Height="Auto" HorizontalAlignment="Stretch" Name="grdComments" VerticalAlignment="Stretch" CanUserAddRows="True" CanUserDeleteRows="True" BeginningEdit="grdComments_BeginningEdit" InitializingNewItem="grdComments_InitializingNewItem" PreviewKeyDown="grdComments_PreviewKeyDown" SizeChanged="grdComments_SizeChanged">
  <DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding Path=Author}" Header="Author" />
    <DataGridTemplateColumn Header="Status" >
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <ComboBox  ItemsSource="{Binding UserValues}" DisplayMemberPath="UserStatus" SelectedValuePath="UserStatus" SelectedValue="{Binding Status, UpdateSourceTrigger=PropertyChanged}" />
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
    <DataGridTextColumn Binding="{Binding Path=Comment}" Header="Comment" Width="570" />
  </DataGrid.Columns>

Here is the code for the CommentData class (Updated 2)

public class CommentsData 
{

    public string Author { get; set; }
    public string Status { get; set; }
    public string Comment { get; set; }
    public string Username { get; set; }

    public ObservableCollection<StatusValue> UserValues { get; set; }

    public CommentsData()
    {
        UserValues = new ObservableCollection<StatusValue>();

        UserValues.Add(new StatusValue("New"));
        UserValues.Add(new StatusValue("Open"));
        UserValues.Add(new StatusValue("ReOpen"));
        UserValues.Add(new StatusValue("Closed"));

    }

}

public class StatusValue
{
    public string UserStatus { get; set; }

    public StatusValue (string value)
    {
        UserStatus = value;
    }
}

Вот код, в котором инициализируется список комментариев

private List<CommentsData> _commentsList;

private void InitializeObjects()
{
        _commentsList = new List<CommentsData>();
        grdComments.ItemsSource = _commentsList;

}

Приведенный выше код работает. Спасибо всем за отзывы.


person CodeMe    schedule 21.12.2011    source источник


Ответы (3)


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

  • Статический ресурс.
  • Объект x:Static code.
  • Встроенная коллекция типов ComboBoxItem.

Если вы хотите привязать ComboBox.ItemsSource к свойству объекта, проще использовать DataGridTemplateColumn следующим образом:

<DataGridTemplateColumn Header="Status">
     <DataGridTemplateColumn.CellTemplate>
         <DataTemplate>
              <ComboBox ItemsSource="{Binding UserValues}" DisplayMemberPath="UserStatus" SelectedValuePath="UserStatus" SelectedValue="{Binding Status, UpdateSourceTrigger=PropertyChanged}" />
         </DataTemplate>
     </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
person icebat    schedule 21.12.2011
comment
Список заполняется, и выбор также сохраняется, однако, когда я пытаюсь получить данные из DataGrid, чтобы сохранить их, значение для статуса всегда остается новым и не отражает измененное значение - person CodeMe; 22.12.2011
comment
@CodeMe, я забыл добавить UpdateSourceTrigger в привязку SelectedValue. Я обновляю код, теперь должно работать. - person icebat; 22.12.2011
comment
требует ли UpdateSourceTrigger INotifyPropertyChanged реализации? - person CodeMe; 22.12.2011
comment
@CodeMe, реализация INotifyPropertyChanged требуется, если вы хотите изменить свойство из кода, чтобы ваш графический интерфейс мог обнаруживать и отображать изменения. Если вы просто хотите выбрать значение из ComboBox и все, вы можете сделать это без этого интерфейса. - person icebat; 22.12.2011
comment
Ваше решение работает для меня, и я также удалил реализацию INotifyPropertyChanged, поскольку она мне не нужна. Я обновлю код позже. Любая идея, как переместить ObservableCollection в другой класс и привязать к нему? Как в текущем коде. - person CodeMe; 22.12.2011
comment
@CodeMe, Рэйчел написала об этом. Вы можете использовать привязку RelativeSource для доступа к этой коллекции в DataContext самой DataGrid. Обратитесь к ее ответу для примера кода. - person icebat; 22.12.2011
comment
Да, я пробовал ее пример, но он не работает, комбо пусто. - person CodeMe; 22.12.2011
comment
@CodeMe, тогда вы должны указать, как вы устанавливаете DataGrid.ItemsSource. Этот подход будет работать только в том случае, если класс с UserValues ​​​​является DataContext DataGrid. - person icebat; 22.12.2011
comment
@rachel Я обновил пост тем, что работает для меня. Это немного неэффективно, но основная проблема решена. Комбо заполняется, и значения не исчезают при выборе какого-либо элемента. Спасибо - person CodeMe; 23.12.2011

Есть несколько вещей, которых я не вижу в вашем коде.

Во-первых, ваш класс не реализует INotifyPropertyChanged. Это означает, что если свойство изменяется в CommentData, оно не сообщает пользовательскому интерфейсу об изменении, поэтому пользовательский интерфейс не обновляется для отображения нового значения.

Во-вторых, вы сообщаете своему ComboBox, что у каждого элемента есть свойство с именем Status, и используете его как ComboBoxItem.Value, однако этого свойства нет у StatusValue. Измените его, чтобы он ссылался на UserStatus, что является допустимым свойством для StatusValue.

SelectedValuePath="UserStatus"

И, наконец, вам действительно не следует заново создавать элементы ComboBox для каждого элемента. Вместо этого создайте коллекцию где-нибудь выше в иерархии ViewModel или сделайте ее статическим ресурсом.

Например, если класс, который содержит вашу коллекцию CommentsData, также содержит вашу коллекцию StatusValues, вы можете использовать привязку RelativeSource для привязки к ней следующим образом:

ItemsSource="{Binding 
    RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
    Path=DataContext.UserValues}"
person Rachel    schedule 21.12.2011
comment
согласно предложению @Rachel INotifyPropertyChanged было реализовано, SelectedValuePath также обновлено, и коллекция была перемещена туда, где хранится список комментариев. Теперь список комбо пуст. Я обновил код, чтобы вы могли посмотреть. - person CodeMe; 22.12.2011
comment
@CodeMe Я не вижу, чтобы ваш DataGrid.ItemsSource был привязан к CommentList. Что такое DataContext из DataGrid? Это должен быть любой класс, содержащий как список CommentList, так и список UserValues, однако, если это тот случай, когда я увижу ItemsSource="{Binding CommentList}" в DataGrid - person Rachel; 22.12.2011
comment
извините, забыл упомянуть об этом, я обновил код, это grdComments.ItemsSource = _commentsList;. Класс, который содержит обе коллекции, является моим оконным классом. - person CodeMe; 22.12.2011
comment
@CodeMe Является ли DataContext вашего DataGrid равным классу, который содержит ваши CommentList и UserValues? В противном случае привязка RelativeSource не будет работать. Вы можете определить, является ли DataContext правильным или нет, привязав свойство ItemsSource DataGrid к CommentList в XAML (ItemsSource="{Binding _commentsList}". Если DataContext не является этим классом, вы всегда можете установить его, используя grdComments.DataContext = this; в коде позади - person Rachel; 22.12.2011
comment
Вещь DataContext у меня не работает, но, поскольку проблема с оригиналом решена, я опубликую ее для перемещения коллекции в класс окна. - person CodeMe; 23.12.2011

Вот что я бы использовал вместо вашей DataGridComboboxColumn:

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding UserValues}" SelectedItem="{Binding Status}" DisplayMemberPath="UserStatus" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Также CommentsData.Status должен быть типа StatusValue, а не string, чтобы можно было привязать к нему SelectedItem.

person ken2k    schedule 21.12.2011
comment
Совершенно нормально, когда CommentsData.Status является строкой. Обычно я рекомендую людям использовать SelectedValue для привязки выбранного элемента вместо SelectedItem, потому что SelectedItem используется по ссылке, а это означает, что если он не указывает на ту же самую ссылку в памяти, что и версия в ItemsSource, он не будет выбран. - person Rachel; 21.12.2011
comment
Я рекомендовал использовать CommentsData.Status типа StatusValue именно из-за SelectedItem. Я лично рекомендую использовать SelectedItem, чтобы выбранный элемент являлся одним из элементов списка, который привязан к источнику элемента, особенно при использовании шаблона MVVM (вы хотите, чтобы экземпляр Selected Item имел ту же ссылку, что и элемент из источника данных). - person ken2k; 21.12.2011
comment
Думаю, у каждого свои предпочтения. Обычно я делаю один вызов базы данных, чтобы получить список значений ComboBox, а затем второй вызов базы данных, чтобы получить записи. При привязке SelectedItem я должен быть уверен, что второй вызов базы данных для получения записей прикрепляет элемент ComboBox, полученный из 1-го вызова базы данных, и не создает и не загружает свою собственную копию из базы данных. Это особенно проблема с системами ORM, такими как Entity Framework, которые автоматически создают связанные элементы. - person Rachel; 21.12.2011
comment
Когда я делаю CommentsData.Status типом StatusValue, тогда он не показывает выбранный элемент, вероятно, это из-за вышеперечисленных вещей, которые вы обсуждали, но что мне теперь делать? - person CodeMe; 22.12.2011
comment
Если CommentsData.Status имеет тип StatusValue, то вам придется использовать для привязки SelectedItem, а не SelectedValue. См. код выше для правильных привязок. Я протестировал приведенный выше код, и он работает для меня. - person ken2k; 22.12.2011