У меня есть поле со списком в приложении WinForms, в котором можно выбрать элемент, но это не обязательно. Поэтому мне нужен первый элемент «Пустой», чтобы указать, что значение не было установлено.
Поле со списком привязано к DataTable, возвращаемому из хранимой процедуры (я не приношу извинения за венгерскую нотацию в моих элементах управления пользовательского интерфейса: p):
DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
cmbHierarchies.DataSource = hierarchies;
cmbHierarchies.ValueMember = "guid";
cmbHierarchies.DisplayMember = "ObjectLogicalName";
Как мне вставить такой пустой элемент?
У меня есть доступ к изменению SP, но я бы предпочел не «загрязнять» его логикой пользовательского интерфейса.
Обновлять: Это был DataTable.NewRow (), который я отключил, спасибо. Я вас всех обновил (все равно пока все 3 ответа). Я пытаюсь заставить работать шаблон итератора, прежде чем я выберу «ответ»
Обновлять: Я думаю, что это правка помещает меня в область Вики Сообщества, я решил не указывать ни одного ответа, поскольку все они имеют достоинства в контексте своей области. Спасибо за ваш коллективный вклад.





Не можете ли вы добавить новый DataRow в DataTable перед тем, как привязать его к своему DataSource?
Для этого можно использовать функцию NewRow DataTable:
http://msdn.microsoft.com/en-us/library/system.data.datatable.newrow.aspx
вставьте пустую строку в свою таблицу данных и проверьте ее в проверке / обновлении / создании
@ [BoltBait]: за исключением того, что он уже сказал, что не хочет изменять свою хранимую процедуру
Я предпочитаю сохранять требования к пользовательскому интерфейсу за пределами БД.
@ [Coov]: я тоже, но пробег OP может отличаться; очевидно, что это взлом!
Обычно я создаю итератор для такого рода вещей. Это позволяет избежать загрязнения ваших данных и хорошо работает с привязкой данных:
DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();//Calls SP
cmbHierarchies.DataSource = GetDisplayTable(hierarchies);
cmbHierarchies.ValueMember = "guid";
cmbHierarchies.DisplayMember = "ObjectLogicalName";
...
private IEnumerable GetDisplayTable(DataTable tbl)
{
yield return new { ObjectLogicalName = string.Empty, guid = Guid.Empty };
foreach (DataRow row in tbl.Rows)
yield return new { ObjectLogicalName = row["ObjectLogicalName"].ToString(), guid = (Guid)row["guid"] };
}
Отказ от ответственности: я не компилировал этот код, но использовал этот шаблон много раз.
Примечание: Я был в WPF и ASP.Net последние пару лет. По-видимому, комбинированному списку Winforms нужен IList, а не IEnumerable. Более затратной операцией было бы создание списка. Этот код действительно лаконичен, и я действительно, действительно не скомпилировал его:
DataTable hierarchies = _database.GetAvailableHierarchies(cmbDataDefinition.SelectedValue.ToString()).Copy();
List<KeyValuePair<string, Guid>> list = new List<KeyValuePair<string, Guid>>(hierarchies.Rows.Cast<DataRow>().Select(row => new KeyValuePair<string, Guid>(row["Name"].ToString(), (Guid)row["Guid"])));
list.Insert(0, new KeyValuePair<string,Guid>(string.Empty, Guid.Empty));
cmbHierarchies.DataSource = list;
cmbHierarchies.ValueMember = "Value";
cmbHierarchies.DisplayMember = "Key";
Я получаю сообщение об ошибке «Комплексная привязка данных принимает в качестве источника данных либо IList, либо IListSource», и мне не удается найти преобразование между IEnumerable и IList (я использую WinForms, если вы привыкли делать это в WPF)
Некоторое время я был на земле ASP.Net, но помню, как использовал этот шаблон в WPF. Я не работал с WinForms около 3 лет. Если для этого нужен список, я бы предложил аналогичную стратегию, просто создайте список вместо использования итератора.
ASP.NET имеет смысл. Хотя мне нравится узор
как насчет GetDisplayTable (theDataTable) .ToList ()?
Я думаю, вам нужно поместить "cmbHierarchies.DataSource = list;" после установщиков значений и отображаемых элементов, тогда все будет хорошо. Хороший пример. +1
Вы можете сделать две вещи:
Добавьте пустую строку в DataTable, возвращаемую хранимой процедурой.
DataRow emptyRow = hierarchies.NewRow();
emptyRow["guid"] = "";
emptyRow["ObjectLogicalName"] = "";
hierarchies.Rows.Add(emptyRow);
Создайте DataView и отсортируйте его, используя столбец ObjectLogicalName. Это сделает вновь добавленную строку первой строкой в DataView.
DataView newView =
new DataView(hierarchies, // source table
"", // filter
"ObjectLogicalName", // sort by column
DataViewRowState.CurrentRows); // rows with state to display
Затем установите представление данных DataSource для ComboBox.
Если вы действительно не хотите добавлять новую строку, как указано выше. Вы можете разрешить пользователю устанавливать значение ComboBox равным нулю, просто обрабатывая событие нажатия клавиши «Удалить». Когда пользователь нажимает клавишу Delete, установите SelectedIndex на -1. Вы также должны установить ComboBox.DropDownStyle на DropDownList. Поскольку это не позволит пользователю редактировать значения в ComboBox.
Немного поздно в игре, но первый вариант мне понравился. Единственное, что я сделал иначе, так это то, что я использовал InsertAt, чтобы моя пустая опция («None») отображалась как первый элемент в выпадающем списке: hierarchies.Rows.InsertAt (emptyRow, 0);
Забыл упомянуть ... Я не использовал DataView и привязан непосредственно к таблице, возвращаемой методом, поэтому она уже была правильно отсортирована. InsertAt полезен в этом случае.
Э, не могли бы вы просто добавить элемент по умолчанию в ComboBox после привязки данных?
Установка значения по умолчанию (через свойство Text) в поле со списком отменяется, когда источник данных привязан к таблице данных.
Затем добавьте значение в DataTable.
Я бы связал данные, а затем вставил пустой элемент в позицию 0 с помощью метода ComboxBox.Items.Insert. Подобно тому, что предлагал flipdoubt, но он добавляет элемент наверх.
Как только источник данных поля со списком привязан к таблице данных, при попытке вставить возникает ошибка. В частности, коллекцию элементов нельзя изменить, если задано свойство Datasource.
cmbHierarchies.SelectedIndex = -1;
Это будет показывать пустой элемент, пока вы не сделаете выбор, он не будет вставлять пустой элемент
Я написал этот метод на основе предложений Джейсона Джексона:
private IEnumerable<KeyValuePair<object,object>> GetDisplayTable(DataTable dataTable, DataColumn ValueMember, string sep,params DataColumn[] DisplayMembers)
{
yield return new KeyValuePair<object,object>("<ALL>",null);
if (DisplayMembers.Length < 1)
throw new ArgumentException("At least 1 DisplayMember column is required");
foreach (DataRow r in dataTable.Rows)
{
StringBuilder sbDisplayMember = new StringBuilder();
foreach(DataColumn col in DisplayMembers)
{
if (sbDisplayMember.Length > 0) sbDisplayMember.Append(sep);
sbDisplayMember.Append(r[col]);
}
yield return new KeyValuePair<object, object>(sbDisplayMember.ToString(), r[ValueMember]);
}
}
Использование:
bindingSource1.DataSource = GetDisplayTable(
/*DataTable*/typedDataTable,
/*ValueMember*/typedDataTable.IDColumn,
/*DisplayColumn Seperator*/" - ",
/*List of Display Columns*/
typedDataTable.DB_CODEColumn,
typedDataTable.DB_NAMEColumn);
comboBox1.DataSource = bindingSource1;
comboBox1.DisplayMember = "Key";
comboBox1.ValueMember = "Value";
//another example without multiple display data columns:
bindingSource2.DataSource = GetDisplayTable(
/*DataTable*/typedDataTable,
/*ValueMember*/typedDataTable.IDColumn,
/*DisplayColumn Seperator*/null,
/*List of Display Columns*/
typedDataTable.DESCColumn );
далее вниз, где расходуется выбранное значение:
if (comboBox1.SelectedValue != null)
// Do Something with SelectedValue
else
// All was selected (all is my 'empty')
Это позволит отображать несколько столбцов, объединенных в ComboBox, сохраняя при этом член Value для одного идентификатора + он использует блок итератора с BindingSource, BindingSource может быть излишним для вашей ситуации.
Комментарии и предложения приветствуются.
Нашел другое решение:
Сразу после создания таблицы данных (перед использованием заполнения) добавьте новую строку и используйте метод AcceptChanges для таблицы. Новая запись получит RowState = Unchanged и не будет добавлена в базу данных, но будет видна в вашей таблице данных и поле со списком.
DataTable dt = new DataTable();
dt.Rows.Add();
dt.AcceptChanges();
...
dt.Fill("your query");
У меня была похожая проблема. Как часть события загрузки формы я установил SelectedIndex элемента управления на -1
т.е.
private void Form1_Load(object sender, EventArgs e)
{
this.TableAdapter.Fill(this.dsListOfCampaigns.EvolveCampaignTargetListMasterInfo);
this.comboCampaignID.SelectedIndex = -1;
}
Фактически, поле со списком заполняется и выбирается первый элемент. Затем элемент не выбран. Не может быть жизнеспособным решением для всех случаев.
this.recieptDateTimePicker.SelectedIndex = -1;
this.dateCompoBox.SelectedIndex = -1;
Я нашел такой способ:
DataTable hierarchies = new DataTable();
cmbHierarchies.BeginUpdate();
cmbHierarchies.ValueMember = this.Value;
cmbHierarchies.DisplayMember = this.Display;
hierarchies = DataView.ToTable();
cmbHierarchies.DataSource = table;
cmbHierarchies.EndUpdate();
//Add empty row
DataRow row = table.NewRow();
table.Rows.InsertAt(row, 0);
cmbHierarchies.SelectedIndex = 0;
Вместо добавления новой строки в таблицу данных просто привяжите данные к полю со списком и при загрузке установите для SelectedIndex значение -1. Это приведет к тому, что выбор будет нулевым, пока пользователь не выберет элемент.
Я вырезал это из одного из моих текущих проектов.
Attorney_List_CB.DataSource = DA_Attorney_List.BS.DataSource;
Attorney_List_CB.DisplayMember = "Attorney Name";
Attorney_List_CB.SelectedIndex = -1;
Чтобы очистить выделение, я обычно вставляю кнопку, которая устанавливает для SelectedIndex значение -1.
private void Clear_Selection_BTN_Click(object sender, EventArgs e)
{
Attorney_List_CB.SelectedIndex = -1; // Clears user selection
}
Наконец, как только я проверю данные в своей форме, если SelectedIndex любого поля со списком равно -1, тогда он будет пропущен, или я сгенерирую какое-то значение по умолчанию, такое как «N / A» или что-то еще, что мне нужно в данных обстоятельствах.
Лучше было бы «выбрать» пустую строку и ОБЪЕДИНЕНИЕ ее с вашим реальным запросом. Это оставит вашу базу данных нетронутой.