Значения дочернего ComboBox меняются в зависимости от значения родительского ComboBox - PullRequest
0 голосов
/ 18 января 2020

Я работаю над приложением Windows Form C#, и у меня есть эти два комбинированных списка, один - родитель (штат), а другой - ребенок (город). Теперь у меня это работало раньше, потому что у меня было несколько дочерних блоков (City1, City2, City3, City4), и значения менялись в зависимости от того, какое состояние вы выбрали в родительском поле со списком, и у меня было свойство Visible, назначенное для дочернего поля со списком, но когда я соединяя весь проект с базой данных, база данных могла принимать значения только от одного дочернего comboBox, а не от других, и выдавала ошибку.

Я видел учебники, в которых люди создают отдельную таблицу и сохраняют и отображают значения для \ из комбинированного списка «Родитель и ребенок».

Я также искал фрагмент кода и нашел решение, которое заключалось в использовании ChildcomboBox.items.add (). работает, но не позволяет выбрать любое значение при запуске приложения.

Есть ли другой способ, кроме хранения и отображения значений из базы данных в comboBox? Я хочу иметь что-то похожее на ChildBox.items.add ()

Заранее спасибо!

РЕДАКТИРОВАТЬ: Вот как это выглядит

sql_com.Parameters.Add("@Image", image);

sql_com.Parameters.AddWithValue("@PrisonerName", PrisonerName_TextBox.Text);

sql_com.Parameters.AddWithValue("@FatherName", FatherName_TextBox.Text);

sql_com.Parameters.AddWithValue("@MotherName", MotherName_TextBox.Text);

sql_com.Parameters.AddWithValue("@Gender", Gender_ComboBox.SelectedItem);

sql_com.Parameters.AddWithValue("@DOB", DateOfBirth_DateTimePicker.Value);

sql_com.Parameters.AddWithValue("@CivilStatus",CivilStatus_ComboBox.SelectedItem);

sql_com.Parameters.AddWithValue("@SpouseName", SpouseName_TextBox.Text);

sql_com.Parameters.AddWithValue("@Province", Province_ComboBox.SelectedItem);

sql_com.Parameters.AddWithValue("@City", City_ComboBox.SelectedItem);

sql_com.Parameters.AddWithValue("@CNIC", CNIC_TextBox.Text);

и событие это:

private void Province_ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    City_ComboBox.Items.Clear();

    if (Province_ComboBox.SelectedItem == "Value1")
    {
        City_ComboBox.Items.Add("Value1");
        City_ComboBox.Items.Add("Value2");
        City_ComboBox.Items.Add("Value3");
    }
    if (Province_ComboBox.SelectedItem == "Value2")
    {
        City_ComboBox.Items.Add("Value4");
        City_ComboBox.Items.Add("Value5");
        City_ComboBox.Items.Add("Value6");
    }
    if (Province_ComboBox.SelectedItem == "Value3")
    {
        City_ComboBox.Items.Add("Value7");
        City_ComboBox.Items.Add("Value8");
        City_ComboBox.Items.Add("Value9");
    }
    if (Province_ComboBox.SelectedItem == "Value4")
    {
        City_ComboBox.Items.Add("Value10");
        City_ComboBox.Items.Add("Value11");
        City_ComboBox.Items.Add("Value12");
    }
}

1 Ответ

0 голосов
/ 19 января 2020

Из описания видно, что вы хотите создать отношения типа «родители-дети» (от 1 до многих) между двумя полями со списком. IE выбирает Провидение / Штат из поля со списком 1 и заполняет поле со списком 2 Городами в этом выбранном Провидении / Штате.

Из опубликованного кода в событии SelectedIndexChanged кажется, что комбо «город» Поле очищается, затем заполняется в зависимости от того, какое значение выбрано в поле со списком «Providence». Это должно / будет работать, однако у этого подхода есть как минимум один недостаток. Один поддерживает код. Даже с довольно «статичными» данными ... если вам когда-нибудь понадобится добавить новый провиденс или город ... вам придется вносить изменения в код и перекомпилировать. Это не очень гибко.

Лучше было бы получить данные из вашей базы данных или файла и поместить их в таблицу (таблицы) или, возможно, List<T>. Затем используйте эти таблицы / List в качестве источника данных для комбинированных списков. Переключение DataSource для поля со списком «города» должно быть относительно простым путем простого изменения источника данных с помощью нескольких строк кода…

City_ComboBox.DataSource = SelectedState.Cities;

Это уменьшит отправленный код SelectedIndexChanged до несколько строк кода, и мы можем легко добавлять новые провинции / штаты или города без изменения кода.

Учитывая это, ниже представлены два различных подхода… 1) использование «Класса» с List<T> 2) использование DataSet с двумя (2) DataTables.

классом и списком

В первом подходе используется Class с именем StateCityData, и этот класс может выглядеть примерно так ...

public class StateCityData {
  public string StateName { get; set; }
  public List<string> Cities { get; set; }
  public string ShortName { get; set; }

  public StateCityData () {
    StateName = "";
    Cities = new List<string>();
    ShortName = "";
  }

  public StateCityData(string state, string shortName) {
    StateName = state;
    Cities = new List<string>();
    ShortName = shortName;
  }

  public override bool Equals(object obj) {
    return obj is StateCityData data &&
           ShortName == data.ShortName;
  }

  public override int GetHashCode() {
    return -1319491066 + EqualityComparer<string>.Default.GetHashCode(ShortName);
  }

Объект StateCityData содержит основную c информацию о состоянии. Как показано, свойство Cities является List<string>, и эта информация может находиться в другом файле или таблице. При этом предполагается, что данные «Состояния» находятся в одной таблице / файле базы данных, а данные «Города» - в другой таблице / файле, где в каждом городе будет поле с именем «ShortName», соответствующее «какому» СОСТОЯНИЮ, к которому принадлежит город .

Идея состоит в том, чтобы сначала получить все свойства basi c StateCityData (StateName, ShortName), где список «Cities» каждого объекта пуст. Затем проведите l oop через данные «городов» и поместите каждый город в соответствующий объект StateCityData, сопоставив переменную ShortName. Переопределение равно используется, чтобы определить, к какому штату принадлежит город, сопоставив ShortName из города с ShortName штата.

Если мы создадим List<StateCityData>, мы могли бы использовать это List<StateCityData> как DataSource для поля со списком 1, используя StateName в качестве поля со списком DisplayMember. Это должно позаботиться о первом поле со списком «Providence / State».

Для второго поля со списком «Cities» мы хотим использовать данные из переменной «Cities» в «selected» StateCityData Объект в первом поле со списком DataSource для второго поля со списком «Города». Поэтому, когда первое поле со списком «Состояния» меняет значение, нам также необходимо изменить значения поля со списком городов. Это должно быть относительно легко, если просто взять «выбранный» объект StateCityData из первого поля со списком и установить во втором поле со списком «Города» DataSource значение Cities List<string> из выбранного объекта StateCityData. Первое поле со списком «State» SelectedIndexChanged событие может выглядеть примерно так: *

private void State_ComboBox_SelectedIndexChanged(object sender, EventArgs e) {
  int selectedIndex = State_ComboBox.SelectedIndex;
  if (StatesData[selectedIndex].Cities.Count > 0) {
    City_ComboBox.DataSource = StatesData[selectedIndex].Cities;
  }
  else {
    City_ComboBox.DataSource = null;
  }
}

Соединение всего этого может выглядеть примерно так, как показано ниже. Метод GetListFromCSV получает данные из файла с разделителями-запятыми, однако не имеет значения, «как» вы получаете данные. Этот List<StateCityData> (StateData) используется как DataSource для первого поля со списком «States», используя «StateName» в качестве DisplayMember. Поскольку это происходит в событии загрузки форм, мы знаем, что в поле со списком будет выбрано «первое» состояние, поэтому мы можем установить второе поле со списком «Города» DataSource, используя первое состояние в списке… StatesData[0].Cities .

private List<StateCityData> StatesData;

private void Form1_Load(object sender, EventArgs e) {
  StatesData = GetListFromCSV();
  State_ComboBox.DisplayMember = "StateName";
  State_ComboBox.DataSource = StatesData;
  City_ComboBox.DataSource = StatesData[0].Cities;
}

Ниже приведен метод для чтения данных из CSV-файла и возврата заполненного List<StateCityData> - StatesData.

private List<StateCityData> GetListFromCSV() {
  List<string> curCities = new List<string>();
  List<StateCityData> statesData = new List<StateCityData>();
  StreamReader sr = new StreamReader(@"D:\Test\States.csv");
  string curLine;
  StateCityData curSCD;
  string[] splitArray;
  while ((curLine = sr.ReadLine()) != null) {
    splitArray = curLine.Split(',');
    if (splitArray.Length >= 2) {
      curSCD = new StateCityData {
        StateName = splitArray[0],
        ShortName = splitArray[1]
      };
      statesData.Add(curSCD);
    }
    else {
      MessageBox.Show("array less than 2: " + curLine);
    }
  }
  sr.Close();
  sr = new StreamReader(@"D:\Test\Cities.csv");
  int targetIndex;
  while ((curLine = sr.ReadLine()) != null) {
    splitArray = curLine.Split(',');
    if (splitArray.Length >= 2) {
      curSCD = new StateCityData("", splitArray[1]);
      targetIndex = statesData.IndexOf(curSCD);
      if (targetIndex >= 0) {
        curSCD = statesData[targetIndex];
        curSCD.Cities.Add(splitArray[0]);
      }
      else {
        MessageBox.Show("Target not found: " + curLine);
      }
    }
    else {
      MessageBox.Show("Less than 2: " + curLine);
    }
  }
  return statesData;
}

DataSet с двумя таблицами DataTable.

Во втором подходе используется DataSet, который содержит два простых DataTables. Первые DataTable «Штаты» могут выглядеть примерно так:

DataTable dt = new DataTable("States");
dt.Columns.Add("StateName", typeof(string));
dt.Columns.Add("ShortName", typeof(string));

Вторые DataTable «Города» имеют город и сокращенное название штата, которому оно принадлежит. Это может выглядеть примерно так: *

dt = new DataTable("Cities");
dt.Columns.Add("City", typeof(string));
dt.Columns.Add("ShortName", typeof(string));

В отличие от использования подхода «Класс», где каждый объект StateCityData содержал свой «собственный» список городов, этот подход имеет одну таблицу со ВСЕМИ городами и должен будет «Отфильтруйте» таблицу, чтобы она содержала только города, соответствующие выбранному состоянию ShortName. Есть возможность полностью исключить таблицу «Города» и просто запросить базу данных для вейлов, однако я не рекомендую этого. В этом случае «все» города находятся в одной таблице, и мы будем «фильтровать» таблицу на основе выбранного состояния, а затем использовать «отфильтрованные» результаты в качестве DataSource для поля со списком «Города».

Поэтому поле со списком «States» SelectedIndexChanged может выглядеть примерно так, как показано ниже. Сначала нам нужно получить короткое имя для выбранных состояний (target). Затем создайте новый DataView из таблицы «Города». Отфильтруйте это представление на основе выбранного target, а затем используйте это представление в качестве DataSource в поле со списком «Города». Поскольку мы заменяем источник данных, нам также необходимо сбросить поля со списком DisplayMember.

private void State_ComboBox_SelectedIndexChanged(object sender, EventArgs e) {
  string target = ((DataRow)StatesDataSet.Tables["States"].Rows[State_ComboBox.SelectedIndex])["ShortName"].ToString();
  dv = new DataView(StatesDataSet.Tables["Cities"]);
  dv.RowFilter = String.Format("ShortName = '{0}'", target);
  if (dv.Count > 0)
    City_ComboBox.DataSource = dv;
  else
    City_ComboBox.DataSource = null;
  City_ComboBox.DisplayMember = "City";
}

Объединение этого в событии загрузки формы может выглядеть примерно так, как показано ниже. Подобно первому подходу, я использую простой CSV-файл для получения данных. Метод GetDataSetFromCSV просто заполняет таблицы данных данными. Опять же, неважно, как вы получаете данные.

private DataSet StatesDataSet;
private DataView dv;

private void Form1_Load(object sender, EventArgs e) {
  GetDataSetFromCSV();
  State_ComboBox.DataSource = StatesDataSet.Tables["States"];
  State_ComboBox.DisplayMember = "StateName";
  dv = new DataView(StatesDataSet.Tables["Cities"]);
  string targetState = ((DataRow)StatesDataSet.Tables["States"].Rows[0])["ShortName"].ToString();
  dv.RowFilter = String.Format("ShortName = '{0}'", targetState);
  City_ComboBox.DataSource = dv;
  City_ComboBox.DisplayMember = "City";
}

Метод получения данных из файла CSV.

private void GetDataSetFromCSV() {
  StatesDataSet = new DataSet("States");
  DataTable dt = new DataTable("States");
  dt.Columns.Add("StateName", typeof(string));
  dt.Columns.Add("ShortName", typeof(string));
  StreamReader sr = new StreamReader(@"D:\Test\States.txt");
  string curLine = "";
  string[] splitArray;
  while ((curLine = sr.ReadLine()) != null) {
    splitArray = curLine.Split(',');
    if (splitArray.Length >= 2) {
      dt.Rows.Add(splitArray[0], splitArray[1]);
    }
    else {
      MessageBox.Show("array less than 3: " + curLine);
    }
  }
  StatesDataSet.Tables.Add(dt);
  dt = new DataTable("Cities");
  dt.Columns.Add("City", typeof(string));
  dt.Columns.Add("ShortName", typeof(string));
  sr.Close();
  sr = new StreamReader(@"D:\Test\Cities.csv");
  while ((curLine = sr.ReadLine()) != null) {
    splitArray = curLine.Split(',');
    if (splitArray.Length >= 2) {
      dt.Rows.Add(splitArray[0], splitArray[1]);
    }
    else {
      MessageBox.Show("Less than 3: " + curLine);
    }
  }
  sr.Close();
  StatesDataSet.Tables.Add(dt);
}

Надеюсь, это имеет смысл и поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...