Вот конкретный пример того, что я пытаюсь сделать с помощью элемента управления Telerik GridView. Допустим, у меня есть приложение, которое будет читать файл с разделителями (скажем, CSV), который будет иметь n столбцов. Значение n может варьироваться от файла к файлу, но является постоянным внутри файла. Приложение запускается с пустой сеткой и добавляет столбцы по мере необходимости, чтобы соответствовать входному файлу. Сетка отображает все данные в виде строк во всех ячейках. Это работает либо с привязкой к BindingList, либо с помещением записи (объектов) в список GridView.Items.
Теперь я хочу поместить одну строку в верхнюю часть сетки (строку, которая не будет прокручиваться), которая содержит комбинированные списки. То есть в верхней части каждого столбца первый ряд содержит поле со списком. На первом проходе комбинированный список будет только выпадающим списком, но на следующем проходе я добавлю еще одну строку с набором комбинированных списков, которые будут доступны для редактирования. А пока давайте рассмотрим только выпадающие списки.
Конкретная проблема, с которой я сталкиваюсь, заключается в том, что я не вижу, как установить определенный тип элемента управления для конкретной ячейки. Telerik предоставляет класс GridViewComboBoxColumn, который будет определять поведение всего столбца, но это не то, что мне нужно.
Из-за переменного количества столбцов, я думаю, что код-место будет местом, где можно применить это волшебство. Возможно, мне придется что-то делать в xaml, но, поскольку я работаю в WPF всего несколько месяцев, у меня ничего не выходит.
Я сделал что-то подобное с DataGridView и XtraGrid, но это меня озадачило. Указатели будут высоко оценены.
В ответ на ответ Джонатана Д. я взял предоставленный код и изменил его, чтобы распознавать, когда редактируется ячейка в 0-й строке. В этом случае выпадающий список отображается, когда пользователь начинает операцию редактирования.
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.GridView;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace LLamasoft.DataGuru.Plugin.Internal.ConfigurationUI
{
public class RadGridViewComboboxHeaderColumn : GridViewBoundColumnBase
{
public static readonly DependencyProperty SelectedStringProperty =
DependencyProperty.Register("SelectedString", typeof(string), typeof(RadGridViewComboboxHeaderColumn), new PropertyMetadata(null));
public string SelectedString
{
get { return (string) GetValue(SelectedStringProperty); }
set { SetValue(SelectedStringProperty, value); }
}
public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
{
// we need the row on which this cell lives
GridViewDataControl gridViewDataControl = cell.ParentRow.GridViewDataControl;
object currentEditItem = gridViewDataControl.Items.CurrentEditItem;
int index = gridViewDataControl.Items.IndexOf(currentEditItem);
FrameworkElement frameworkElement = null;
if (index == 0)
{
BindingTarget = ComboBox.SelectedValueProperty;
ComboBox comboBox = new ComboBox();
// seed some values,
// this list should be set right after construction if static, otherwise via callback if dynamic
comboBox.Items.Add(string.Empty);
comboBox.Items.Add("apples");
comboBox.Items.Add("oranges");
if (!comboBox.Items.Contains(cell.Value))
{
comboBox.Items.Add(cell.Value);
}
comboBox.SelectedValue = SelectedString;
frameworkElement = comboBox;
}
else
{
BindingTarget = TextBox.TextProperty;
TextBox textBox = new TextBox();
textBox.Text = SelectedString;
frameworkElement = textBox;
}
frameworkElement.SetBinding(BindingTarget, CreateValueBinding());
return frameworkElement;
}
public override object GetNewValueFromEditor(object editor)
{
// ensure that the control will return the correct value when queried for it
ComboBox comboBox = editor as ComboBox;
if (comboBox != null)
{
// bound to comboBox.SelectedValue which carries the correct value
}
TextBox textBox = editor as TextBox;
if (textBox != null)
{
// bound to textBox.Text which carries the correct value
}
return base.GetNewValueFromEditor(editor);
}
private Binding CreateValueBinding()
{
Binding valueBinding = new Binding();
valueBinding.Mode = BindingMode.TwoWay;
valueBinding.NotifyOnValidationError = true;
valueBinding.ValidatesOnExceptions = true;
valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
return valueBinding;
}
}
}
Хорошая новость заключается в том, что это показывает, что любой элемент управления для редактирования может использоваться в любой ячейке в зависимости от требований.
Плохие части: 1) фиктивная запись должна быть вставлена в 0-ю позицию списка и должна храниться там, 2) данные сохраняются обратно в поле в 0-й записи и могут требовать другого типа данных чем в эквивалентных полях других записей, и 3) поле со списком отображается только тогда, когда ячейка находится в режиме редактирования.
Последняя проблема для меня не может быть проблемой в другом месте. Мне нужна визуальная подсказка о том, что пользователь должен взаимодействовать с ячейками в верхней части столбцов. При использовании этого метода не существует дифференцирующего коэффициента между верхней строкой и остальными строками, пока не начнется операция редактирования. Моим идеальным решением было бы, чтобы клетки всегда показывали свои комбинированные списки.
Еще одна проблема, с которой мне трудно поверить, это то, что я не могу легко закрепить / заморозить самые верхние ряды. Я хочу, чтобы эта строка всегда оставалась наверху после прокрутки. _Grid.Rows [0] .IsPinned = true не существует.
Telerik ответил на мой запрос о предоставлении информации и предлагает использовать селектор шаблонов, чтобы определить, как отображается ячейка. (http://www.telerik.com/community/forums/wpf/gridview/need-just-first-row-in-grid-to-be-all-comboboxes.aspx#1820310). На этом этапе я переключаю свое внимание на тестирование этого метода.