#c# #winforms
#c# #winforms
Вопрос:
Моя форма выглядит примерно как трехпанельный почтовый клиент. Слева находится таблица со списком пользователей. Вверху справа находится подробная запись текущего пользователя. Внизу справа находится пользовательский элемент управления со множеством флажков, отображающих области знаний текущего пользователя:
[x] приготовление пищи [x] мытье окон [x] операция на мозге
[x] массажная терапия [x] пение [ ] случайные акты вандализма
При открытии формы фокус переходит к первому пользователю, указанному в таблице в левой части формы,, и срабатывает событие focused_row_changed сетки. В обработчике для этого события я получаю идентификатор текущего пользователя, затем извлекаю подробные данные для этого пользователя из базы данных и заполняю подробную запись, а также извлекаю строки области знаний пользователя и устанавливаю флажки. Все это работает нормально, за исключением момента первого открытия формы, потому что тогда пользовательский элемент управления с его множеством флажков еще не инициализирован. В этот момент значение MyCustomControl равно нулю.
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
Каков наилучший шаблон проектирования для решения этой ситуации? Что мне здесь делать, когда мой элемент управления еще не полностью инициализирован?
if (null != MyCustomControl)
{
MyCustomControl.SetCheckedValues( datasource);
}
else
{
// ?? Wait around for a bit and keep trying every 100ms?
}
Комментарии:
1. Не могли бы вы немного подробнее рассказать о том, когда MyCustomControl становится инициализированным и от чего зависит его инициализация?
Ответ №1:
Способ, которым я решил это в своих элементах управления, когда у них возникла эта проблема, заключается в реализации ISupportInitialize.
В свой элемент управления вы бы поместили что-то вроде:
public class MyCustomControl: ISupportInitialize
{
private bool _initializing = false;
private void BeginInit()
{
_initializing = true;
}
private void EndInit()
{
_initializing = false;
}
private void SomeMethodThatWouldRaiseAnEventDuringInit()
{
if (_initializing) return;
//...
}
}
Конструктор Windows forms проверяет, реализует ли ваш элемент управления интерфейс, и создает этот код в .Designer.cs
файле:
((System.ComponentModel.ISupportInitialize)(this.customControl1)).BeginInit();
///
/// customControl1
///
this.customControl1.SelectedIndex = 0; //this would normally raise the event
((System.ComponentModel.ISupportInitialize)(this.customControl1)).EndInit();
Комментарии:
1. относится ли это к быстрому управлению или к замедленному управлению? Я не уверен, как это будет работать. Мой элемент управления slowpoke просто отображает некоторые данные, которые ему отправляются, но элемент управления, который извлекает данные из базы данных, инициализируется и заполняется быстрее, чем элемент управления, который будет отображать данные. Есть ли способ отложить инициализацию более быстрого элемента управления до завершения инициализации элемента управления slowpoke?
Ответ №2:
Насколько я понимаю, вы настраиваете MyCustomControl.SetCheckedValues( datasource);
, когда срабатывает событие focused_row_changed.
Это также имеет тенденцию происходить, когда форма только загружается, что обычно нежелательно, потому что в конечном итоге вы получаете события, указывающие на загрузку, когда, например, выбранный индекс все еще равен -1.
Способ, которым я работал над этим, заключается в том, что у меня есть глобальное логическое значение в форме с именем doneLoading
. Оно начинается с false и становится true при вызове события Form_Shown().
Оттуда я просто ставлю if(doneLoading)
вокруг любого фрагмента кода, который должен дождаться фактической загрузки формы, прежде чем ей будет разрешено выполняться. В вашем случае я бы сделал:
if(doneLoading)
{
MyCustomControl.SetCheckedValues( datasource);
}
Комментарии:
1. Затем в событии Form_Shown() мне пришлось бы вручную установить фокус в таблице Persons на первую строку. Но фокус уже был бы на первой строке, и, следовательно, событие focused-row-changed не сработало бы. Итак, тогда мне пришлось бы вручную запускать подпрограммы, которые извлекают запись сведений о персонале и запись знаний о персонале, и, прежде чем вы это узнаете, это уже не программа, управляемая событиями, а некая комбинация событийно-управляемого кодирования и старого доброго структурированного кодирования. Это то, чего я пытаюсь избежать, хотя, возможно, избежать этого невозможно.
2. @Tim — Я не уверен, что понимаю проблему или, возможно, желаемое поведение. При первом открытии формы вы хотите, чтобы был заполнен MyCustomControl или вы хотите подождать, пока пользователь намеренно не выберет пользователя? Если это последнее, то именно этому будет способствовать мой ответ. Что делает код в моем ответе, так это подавляет вызов вашего метода SetCheckedValues() до завершения загрузки формы. Другими словами, вы просто помещаете код, который я поместил туда, в любое событие, которое у вас было раньше, и это должно изменить общее поведение только до полной загрузки формы.
3. при загрузке формы список контроллеров заполняется именами пользователей и идентификаторами, а фокус устанавливается (автоматически) на первую строку, что вызывает срабатывание события Focused_Row_Changed. В обработчике для этого события я получаю идентификатор целевого пользователя и извлекаю подробные данные из базы данных. Но другой элемент управления в форме еще не готов к приему этих данных. Мой список контроллеров инициализируется быстрее, чем элемент управления, который получает от него данные. В настоящее время мой обработчик продолжает стучаться в дверь элемента управления slowpoke, пока он, так сказать, не откроет дверь: «Доставка данных для мистера контроля!»
4. @Tim, не зная, что на самом деле происходит с этим другим элементом управления, почему вы не можете просто заставить этот элемент управления запускать событие по завершении инициализации, а затем обработчик событий устанавливает список контроллеров в первую строку (только в первый раз)? Таким образом, элемент управления slowpoke передает контроллеру список своих данных в первый раз, и с этого момента (уже инициализированный) элемент управления slowpoke получает свои приказы от события Focused_Row_Changed. Я вроде как правильно понимаю суть вашей программы или я все еще далек от истины?
5. вы уловили суть. В обработчике событий Focused_Row_Changed контроллера я могу проверить, готов ли элемент управления slowpoke к приему данных, и отправлять ему данные только в том случае, если это так. В этом обработчике я также могу продолжить тестирование, чтобы увидеть, активирован ли еще элемент управления slowpoke, с помощью таймера. Через каждые 100 мс я могу видеть, по-прежнему ли значение slowpoke равно нулю или оно инициализируется, или я могу периодически тестировать глобальную переменную, которая будет установлена элементом управления slowpoke, когда он проснется. Я также могу заставить элемент управления slowpoke установить фокус строки контроллеров, когда slowpoke просыпается. Это другой способ, хотя, возможно, и не более чистый.
Ответ №3:
Выполняйте функции инициализации пользовательского интерфейса в подпрограмме, которая не вызывается до тех пор, пока не будут инициализированы все остальные элементы пользовательского интерфейса, или основывайте свои вычисления на внутренних значениях, а не на пользовательском интерфейсе.
в ответ на комментарии и другие сообщения, если вы не можете заставить работать что-либо еще, вы можете добавить кнопку «Обновить» в пользовательский интерфейс
Комментарии:
1. Сетка, содержащая список пользователей, запускает событие focused-row-changed всякий раз, когда фокус перемещается от строки к строке. Это пользовательский интерфейс, с которым взаимодействует пользователь, и записи person-detail-record и person-experience-records должны извлекаться всякий раз, когда пользователь выбирает другого пользователя. Я не могу контролировать, когда эта сетка завершит инициализацию.
2. Я надеюсь избежать кнопки Обновить, потому что это требует, чтобы пользователь знал, что данные необходимо обновить, и предпринимал действия. В идеале я хотел бы найти способ заставить быстрый элемент управления просыпаться только после того, как проснулся медленный элемент управления. Я хочу, чтобы элемент управления earlybird оставался в постели, а элемент управления sleepyhead просыпался и говорил элементу управления earlybird «Проснись и пой!». Я полагаю, что элемент управления sleepyhead мог бы добавить элемент управления earlybird в список элементов управления формы в событии InitializationComplete от sleepyhead.
3. re: Что мне здесь делать, когда мой элемент управления еще не полностью инициализирован? вы должны дождаться, пока он будет полностью инициализирован, затем обновить. Может ли ваш медленный элемент управления уведомить ваш быстрый элемент управления, когда это будет сделано?