Как вставить пустое поле в ComboBox, привязанное к DataTable

У меня есть поле со списком в приложении 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 ответа). Я пытаюсь заставить работать шаблон итератора, прежде чем я выберу «ответ»

Обновлять: Я думаю, что это правка помещает меня в область Вики Сообщества, я решил не указывать ни одного ответа, поскольку все они имеют достоинства в контексте своей области. Спасибо за ваш коллективный вклад.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
19
0
66 649
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Не можете ли вы добавить новый DataRow в DataTable перед тем, как привязать его к своему DataSource?

Для этого можно использовать функцию NewRow DataTable:

http://msdn.microsoft.com/en-us/library/system.data.datatable.newrow.aspx

вставьте пустую строку в свою таблицу данных и проверьте ее в проверке / обновлении / создании

Лучше было бы «выбрать» пустую строку и ОБЪЕДИНЕНИЕ ее с вашим реальным запросом. Это оставит вашу базу данных нетронутой.

BoltBait 14.10.2008 05:08

@ [BoltBait]: за исключением того, что он уже сказал, что не хочет изменять свою хранимую процедуру

Steven A. Lowe 14.10.2008 05:09

Я предпочитаю сохранять требования к пользовательскому интерфейсу за пределами БД.

Billy Coover 25.08.2009 22:46

@ [Coov]: я тоже, но пробег OP может отличаться; очевидно, что это взлом!

Steven A. Lowe 26.08.2009 18:58

Обычно я создаю итератор для такого рода вещей. Это позволяет избежать загрязнения ваших данных и хорошо работает с привязкой данных:

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)

johnc 14.10.2008 05:29

Некоторое время я был на земле ASP.Net, но помню, как использовал этот шаблон в WPF. Я не работал с WinForms около 3 лет. Если для этого нужен список, я бы предложил аналогичную стратегию, просто создайте список вместо использования итератора.

Jason Jackson 14.10.2008 05:33

ASP.NET имеет смысл. Хотя мне нравится узор

johnc 14.10.2008 08:19

как насчет GetDisplayTable (theDataTable) .ToList ()?

geofftnz 28.04.2009 11:59

Я думаю, вам нужно поместить "cmbHierarchies.DataSource = list;" после установщиков значений и отображаемых элементов, тогда все будет хорошо. Хороший пример. +1

Billy Coover 16.02.2010 18:27
Ответ принят как подходящий

Вы можете сделать две вещи:

  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.

  2. Если вы действительно не хотите добавлять новую строку, как указано выше. Вы можете разрешить пользователю устанавливать значение ComboBox равным нулю, просто обрабатывая событие нажатия клавиши «Удалить». Когда пользователь нажимает клавишу Delete, установите SelectedIndex на -1. Вы также должны установить ComboBox.DropDownStyle на DropDownList. Поскольку это не позволит пользователю редактировать значения в ComboBox.

Немного поздно в игре, но первый вариант мне понравился. Единственное, что я сделал иначе, так это то, что я использовал InsertAt, чтобы моя пустая опция («None») отображалась как первый элемент в выпадающем списке: hierarchies.Rows.InsertAt (emptyRow, 0);

Jerome Viveiros 18.08.2016 12:45

Забыл упомянуть ... Я не использовал DataView и привязан непосредственно к таблице, возвращаемой методом, поэтому она уже была правильно отсортирована. InsertAt полезен в этом случае.

Jerome Viveiros 18.08.2016 13:02

Э, не могли бы вы просто добавить элемент по умолчанию в ComboBox после привязки данных?

Установка значения по умолчанию (через свойство Text) в поле со списком отменяется, когда источник данных привязан к таблице данных.

John M 11.06.2010 18:11

Затем добавьте значение в DataTable.

flipdoubt 11.06.2010 18:17

Я бы связал данные, а затем вставил пустой элемент в позицию 0 с помощью метода ComboxBox.Items.Insert. Подобно тому, что предлагал flipdoubt, но он добавляет элемент наверх.

Как только источник данных поля со списком привязан к таблице данных, при попытке вставить возникает ошибка. В частности, коллекцию элементов нельзя изменить, если задано свойство Datasource.

John M 11.06.2010 18:13

cmbHierarchies.SelectedIndex = -1;

Это будет показывать пустой элемент, пока вы не сделаете выбор, он не будет вставлять пустой элемент

johnc 29.04.2009 04:01

Я написал этот метод на основе предложений Джейсона Джексона:

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» или что-то еще, что мне нужно в данных обстоятельствах.

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