Отфильтруйте список представлений таблицы данных по значению из текстового поля поиска: «Ссылка на объект не установлена ​​для экземпляра объекта». - PullRequest
0 голосов
/ 08 сентября 2018

Я настроил текстовое поле для поиска имен в своей сетке данных, но получаю сообщение об ошибке: 'Object reference not set to an instance of an object.'

List<Member> members = new List<Member>();

public class Member
{
    public int id { get; set; }
    public string name { get; set; }
    public int age { get; set; }
    public Image image_url { get; set; }
}

// In a keyup event of the text box
(memberGrid.DataSource as DataTable).DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);

Я попытался изменить DataTable на List или Member. Я также пробовал кастовать, используя List/Member перед DataTable, но, похоже, это не работает.

Какой лучший подход к этому?

Edit:

Так я добавляю в список каждую строку данных из моего SQL-выбора.

members.Add(new Member
{
    id = Convert.ToInt32(reader["id"]),
    name = reader["name"].ToString(),
    age = Convert.ToInt32(reader["age"]),
    image_url = (Image)Properties.Resources.ResourceManager.GetObject(reader["image_url"].ToString())
});

Чтобы добавить в сетку, я перебираю список и добавляю строку для каждого члена:

for (int i = 0; i < members.Count; i++)
{
    memberGrid.Rows.Add(new object[]
    {
        members[i].image_url,
        members[i].name,
        members[i].age
    });
}

Ответы [ 2 ]

0 голосов
/ 11 сентября 2018

Реза Агаи объяснил, как решить проблему, но в случае, если кому-то в будущем понадобится еще один способ решения этих ситуаций, вот как я это делаю.

Итак, как уже упоминалось, проблема заключается в (memberGrid.DataSource as DataTable).DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);

Есть две причины, по которым это может произойти.

  • Вы DataSource не назначены, поэтому is null
  • Вы присвоили объект с другим типом, чем DataTable, а затем пытаетесь привести его «обратно» к DataTable, чтобы он возвратил ноль

Для безупречного потока ВСЕГДА связывайте данные с источником данных и манипулируйте ими через него. Вот как я это делаю в своих обзорах данных.

  • Сначала я пытаюсь загрузить данные непосредственно в DataTable

Так что, если вы загружаете его из SQL, просто используйте DataAdapter и заполните DataTable

using(SqlConnetion....)
{
    con.Open();
    using(SqlDataAdapter da ....)
    {
        DataTable dt = new DataTable();
        da.Fill(dt);

        dataGridView.DataSource = dt;

        //I use this method often because every change i am doing is directly into database and then i just refresh dgv if i need it by loading data again.
    }
}

Если загрузка данных осуществляется напрямую, по какой-то причине вам трудно DataTable (вам нужно создать таблицу данных самостоятельно и заполнить ее foreach, но слишком большим количеством столбцов или что-то в этом роде) или вы хотите манипулировать данными внутри кода, но не в DataTable вы можете использовать List<T>

Так просто создайте объект

public class Car
{
    public string Model { get; set; }
    public Color Color { get; set; }
    public string LicencePlate { get; set; }
    public doube HP { get; set; }

    public Car()
    {
    }
}

тогда, скажем, нам нужно загрузить машины с jsonString у нас List<Car> myCars = JsonConverter.Deserialize<List<Car>>(stringFromWebForExample);

и теперь у нас есть список, в котором мы можем манипулировать, но нужно поместить его в сетку данных. Опять же, мы можем сделать это просто с помощью dataGridView.DataSource = myCars, но DataTable более функционально, чем List, когда дело доходит до отображения в datagridview, так что я делаю, чтобы преобразовать этот список в datatable с помощью этого кода:

public static DataTable ConvertToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection properties =
       TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
            row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    return table;
}

и просто назовите это dataGridView.DataSource = myCars.ConvertToDataTable();

Итак, это два практических способа обработки данных и их отображения в DataGridView

0 голосов
/ 11 сентября 2018

Поскольку вы не установили DataSource из DataGridView и оно равно null, то вы получили исключение, когда использовали его в этом выражении: (memberGrid.DataSource as DataTable).DefaultView....

Вместо добавления строк по одной к DataGridView, присвойте данные его DataSource. Затем в зависимости от используемой структуры данных вы можете фильтровать данные.

Вы можете использовать любое из следующих решений:

  • Как вариант, чтобы решить проблему, вы можете загрузить данные в DataTable и установить его как DataSource из DataGridView. Тогда фильтрация может быть выполнена так же, как вы пытаетесь.

  • Также, если по какой-либо причине вы предпочитаете иметь List<Member> и применить к нему фильтр, после загрузки данных и преобразования в List<Member> сохраните список в поле члена формы, например List<Member> members;, затем для фильтрация с использованием linq:

    dataGridView1.DataSource = members.Where(x=>x.Name ==searchBox.Text).ToList();
    

Подробнее читайте в следующих разделах поста.

Почему я получаю исключение NullReferenceException

Следующая строка кода выдаст NullReferenceException, если DataSource равно нулю или DataSource не равно DataTable:

(memberGrid.DataSource as DataTable).DefaultView.RowFilter = ...

В вашем случае вы заполняете базу данных путем добавления строк и не используете ее источник данных, поэтому DataSource имеет значение null и код генерирует исключение.

Для получения дополнительной информации о том, как отладить NullReferenceException, взгляните на Что такое исключение NullReferenceException и как его исправить? .

Загрузка данных

Вы упомянули:

Так я добавляю в список каждую строку данных из моего SQL-выбора.

members.Add(new Member {
      id = Convert.ToInt32(reader["id"]),
      name = reader["name"].ToString(), 
      ...

Кажется, вы используете запрос SQL и SqlDataReader. Тогда вам не нужно использовать List<Member>, для вас будет достаточно DataTable. Вы можете изменить свой код на следующий код для загрузки данных:

public DataTable GetData()
{
    var dt = new DataTable();
    var cn = @"Your Connection String";
    var cmd = @"Your Select Command";
    using (var da = new SqlDataAdapter(cmd, cn))
        da.Fill(dt);
    return dt;
}

Примечание: Если по какой-либо причине вы заинтересованы продолжать работать с List<Member>, выполните рефакторинг кода, чтобы получить List<Member> in GetData:

public List<Member> GetData()
{
    var dt = new DataTable();
    var cn = @"Your Connection String";
    var cmd = @"Your Select Command";
    using (var da = new SqlDataAdapter(cmd, cn))
        da.Fill(dt);
    return dt.AsEnumerable().AsEnumerable().Select(r=>{
        id = r.Field<int>("id"),
        name = r.Field<string>("name"),
        age = r.Field<int>("age")
    }).ToList();
}

Показать данные в DataGridView

Вы упомянули:

Чтобы добавить в сетку, я перебираю список и добавляю строку для каждого члена:

for (int i = 0; i < members.Count; i++) {
    memberGrid.Rows.Add(new object[]
      ...

Когда вы загружаете данные в DataTable (или даже в List<Member>), вам не нужно добавлять строки одну за другой к DataGridView, просто присвойте данные свойству DataSource свойства DataGridView.

Для этого вы можете переопределить метод OnLoad формы или обработать событие Load, загрузить данные в таблицу данных и установить их в качестве источника данных DataGridView:

DataTable dt;
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    dt = LoadData();
    dataGridView1.DataSource = dt;
}

Примечание: Если по какой-либо причине вы предпочли вернуть List<Member> из GetData, код будет:

List<Member> members;
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    members = LoadData();
    dataGridView1.DataSource = members;
}

Фильтр данных

Затем для фильтрации данных достаточно использовать dt.DefaultView.RowFilter:

dt.DefaultView.RowFilter = string.Format("name = '{0}'", searchBox.Text);

Примечание: Если по какой-либо причине вы предпочитаете иметь List<Member> и применить к нему фильтр, после загрузки и сохранения списка в элементе формы, как я делал в предыдущих разделах, используйте linq для фильтрации данных:

dataGridView1.DataSource = members.Where(x=>x.Name ==searchBox.Text).ToList();
...