Запрет на срабатывание события listBox
В общем, такая проблема. Есть ListBox1, в Item которого отображаются поле объектов, при выборе одного Item в ListBox2 отображаются остальные поля объекта. Происходит это через событие listBox1_SelectedIndexChanged. Также нужно оформить возможность удаления объекта, через нажатие клавиши "Delete". Это сделал в listBox1_KeyUp. Но при удалении объекта выбрасывается NullReferenceExcaption. Срабатывает событие KeyUp, а после него SelectedIndexChanged, и во втором получается, что он пытает отобразить не существующий объект (я так понял). Теперь сижу и думаю, как это исправить. Буду благодарен за подсказку.
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var newlist = list.questionList;
if ((newQuestion.questions == "")&&(newQuestion.rightAnswer=="")&&(newQuestion.answer.Count==0))
{
listBox2.Items.Clear();
foreach (var obj in newlist)
{
if (obj.questions == listBox1.SelectedItem.ToString())
{
foreach (var obj2 in obj.answer)
{
listBox2.Items.Add(obj2);
}
listBox2.Items.Add(obj.rightAnswer);
}
}
private void listBox1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyValue.ToString() == "46")
{
var newlist = list.questionList;
foreach (var obj in newlist)
{
if (listBox1.SelectedItem.ToString() == obj.questions)
{
listBox1.Items.Remove(listBox1.SelectedItem);
list.questionList.Remove(obj);
MessageBox.Show("Sucsess");
}
}
}
}
public class Question
{
public string questions;
public List<string> answer;
public string rightAnswer;
public Question()
{
this.questions = "";
this.answer = new List<string>();
this.rightAnswer = "";
}
public override string ToString()
{
string result = "";
foreach (var obj in answer)
{
result += "\n"+obj+"\n";
}
return $"Question\n{questions}\nanswer\n{result}\nright answer\n{rightAnswer}";
}
}
[Serializable]
public class QuestionList
{
public string TestName { get; set; }
public int TestTime { get; set; }
public int PassMark { get; set; }
public List<Question> questionList;
public QuestionList()
{
questionList = new List<Question>();
TestName = "";
TestTime = 0;
PassMark = 0;
}
public void AddQuestion(Question obj)
{
questionList.Add(obj);
}
Ответы (1 шт):
Отключить возникновение события у вас не получится, это не предусмотрено.
Но у вашей проблемы есть вполне очевидное решение: добавьте необходимую проверку в обработчик listBox1_SelectedIndexChanged и, если объект был удален, просто завершите обработчик не пытаясь получить данные из несуществующего объекта.
Но вообще вы идете не тем путем в построении приложения на WinForms. Я понимаю. что все примеры кода выглядят примерно так, но не стоит следовать им до буквы. Иногда стоит заглянуть в документацию немного глубже. Я не могу понять из приведенного кода, как должно работать ваше приложение в целом, хотя и догадываюсь, поэтому посоветовать как исправить вашу модель данных я не могу, это в любом случае будет "угадайка". Но, для примера, я набросал простенькую форму, которая демонстрирует как можно сделать проще все то, что есть в приведенном вами фрагменте кода. Модель данных разумеется упрощена до предела, но я думаю будет несложно расширить ее необходимыми вам данными после того как разберетесь как работает мой пример. Ну и вопросы новые появятся, только уже другие.
//модель отображаемого элемента
public class ItemList
{
public BindingList<Item> Items { get; } = new BindingList<Item>();
}
public class Item
{
public string Name { get; set; }
public BindingList<string> Details { get; } = new BindingList<string>();
}
//собственно форма
public partial class Form1 : Form
{
private ListBox listBox2 = new ListBox();
private ListBox listBox1 = new ListBox();
private ItemList model = new ItemList();
public Form1()
{
Controls.Add(listBox2);
Controls.Add(listBox1);
listBox1.Dock = DockStyle.Left;
listBox2.Dock = DockStyle.Fill;
//указываем какое свойство необходимо отображать в списке
listBox1.DisplayMember = "Name";
//указываем откуда брать элементы списка
listBox1.DataSource = model.Items;
listBox1.SelectedIndexChanged += ListBox1_SelectedIndexChanged;
listBox1.KeyUp += ListBox1_KeyUp;
for (int i = 1; i < 10; i++)
{
var item = new Item();
item.Name = $"item{i}";
item.Details.AllowEdit = true;
for (int j = 1; j < 5; j++)
{
item.Details.Add($"i{i}_d{j}");
}
model.Items.Add(item);
}
}
private void ListBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var lb = sender as ListBox;
if (lb.SelectedItem != null)
{
//При наличии выбранного элемента задаем источник данных для ListBox2
//т.к. ListBox2 будет отображать список простых строк, то имя
//отображаемого свойства для него можно не указывать
listBox2.DataSource = (lb.SelectedItem as Item).Details;
}
else
{
//При отсутствии выбранного элемента очищаем источник данных ListBox2
listBox2.DataSource = null;
}
}
private void ListBox1_KeyUp(object sender, KeyEventArgs e)
{
var lb = sender as ListBox;
if (e.KeyCode == Keys.Delete && lb.SelectedItem != null)
{
//удаление производится не из ListBox, а из его источника данных
(lb.DataSource as IList<Item>).Remove(lb.SelectedItem as Item);
}
}
}
Из дополнительного советую почитать про привязку данных (Data Bining) в WinForms и паттерн MVP. В текущем примере использована только привязка данных. Единственный контрол WinForms, требующий ручного заполнения элементами - TreeView, но и эта проблема решается с помощью паттерна MVP.