#.net #wpf #events #delegates
#.net #wpf #Мероприятия #делегаты
Вопрос:
Я не уверен, как спросить об этом, но вот что. У меня есть окно WPF, в котором есть делегат, который отвечает на событие TextChanged текстового поля. Когда я загружаю данные в окно, а затем подписываю свой класс контроллера на это событие, вызывается метод обработчика делегирования.
Последовательность такова. 1. Создайте окно 2. Загрузите данные для этого окна. 3. Подпишитесь на событие TextChanged для окна с помощью метода textDidChange.
В этом сценарии вызывается мой метод textDidChange, даже если «событие» произошло на шаге 2. Это ожидаемое поведение? Если нет, то что может происходить?
РЕДАКТИРОВАТЬ: Вот соответствующий код. Я не опубликовал обработку события из UserControl, поскольку это шаблонно (если делегат!= null, вызовите делегата).
Из конструктора контроллера:
public ServiceRequestVM(Boolean isDataSourceProd, codExistServiceRequestSearchType requestIdOrMapNo, String aMapNumber, Decimal aRequestId) {
//create the schema and load any necessary data
_sroc = new ServiceRequestOracleController();
_sroc.IsProd = isDataSourceProd;
_isProd = isDataSourceProd;
_isNewRequest = false;
_searchType = requestIdOrMapNo;
createSchema();
if (requestIdOrMapNo == codExistServiceRequestSearchType.MapNumber) {
loadMatchingRequest(aMapNumber);
} else {
loadMatchingRequest(aRequestId);
}
Decimal _reqId = (Decimal)_serviceRequestTable.Rows[0]["REQUESTID"];
loadNotesForRequest(_reqId);
loadTagsForRequest(_reqId);
Decimal _custId = (Decimal)_serviceRequestTable.Rows[0]["CUSTOMERID"];
getNameForCustomerAndSetCustomerIdForRequest(_custId);
//configure the UI
configureUI();
customerListBoxVisibility = Visibility.Hidden;
tagListBoxVisibility = Visibility.Hidden;
//create the view (a UserControl)
_serviceRequestView = new ServiceRequestView();
_serviceRequestView.DataContext = this;
//load customers and tags
loadCustomers();
loadTags();
_shouldListBoxesBeSeen = false;
//subscribe to delegates
subscribeToRequestDelegates();
}
Метод subscribeToRequestDelegates
private void subscribeToRequestDelegates() {
_serviceRequestView.addNoteButtonWasClicked = new ServiceRequestView.AddNoteButtonWasClickedHandler(addNote);
_serviceRequestView.addTagButtonWasClicked = new ServiceRequestView.AddTagButtonWasClickedHandler(addTag);
_serviceRequestView.locateButtonWasClicked = new ServiceRequestView.LocateButtonWasClickedHandler(locateMap);
_serviceRequestView.openButtonWasClicked = new ServiceRequestView.OpenButtonWasClickedHandler(openMap);
_serviceRequestView.saveButtonWasClicked = new ServiceRequestView.SaveButtonWasClickedHandler(saveRequest);
_serviceRequestView.noteWasDoubleClicked = new ServiceRequestView.NoteWasDoubleClickedHandler(openSelectedNote);
_serviceRequestView.dateCompletedLostFocus = new ServiceRequestView.DateCompletedLostFocusHandler(dateCompletedDidChange);
_serviceRequestView.titleLostFocus = new ServiceRequestView.TitleLostFocusHandler(titleDidChange);
_serviceRequestView.customerTextChanged = new ServiceRequestView.CustomerTextChangedHandler(customerTextDidChange);
_serviceRequestView.selectedCustomerChanged = new ServiceRequestView.SelectedCustomerChangedHandler(selectedCustomerDidChange);
_serviceRequestView.tagTextChanged = new ServiceRequestView.TagTextChangedHandler(tagTextDidChange);
_serviceRequestView.selectedTagChanged = new ServiceRequestView.SelectedTagChangedHandler(selectedTagDidChange);
_serviceRequestView.tagTextLostFocus = new ServiceRequestView.TagTextLostFocusHandler(tagTextLostFocus);
_serviceRequestView.customerTextLostFocus = new ServiceRequestView.CustomerTextLostFocusHandler(customerTextLostFocus);
_serviceRequestTable.ColumnChanged = new DataColumnChangeEventHandler(serviceRequestTableColumnValueDidChange);
}
Комментарии:
1. Это странное поведение. Не могли бы вы предоставить свой код?
Ответ №1:
Я не могу сказать вам наверняка, что делает WPF, но под капотом в Windows текст элемента управления обычно обновляется путем отправки окну сообщения. Это помещается в очередь сообщений приложения и обрабатывается только тогда, когда поток пользовательского интерфейса приступает к обработке сообщений — поэтому, если элемент управления обрабатывается вашим потоком пользовательского интерфейса, тогда вам нужно завершить инициализацию и вернуть управление в ваш основной цикл обработки сообщений (или вызвать DoEvents ()), прежде чем элемент управления получит и обработает сообщение. т. е. это происходит асинхронным образом. (Вы также можете «отправить» сообщение элементу управления, который ожидает, пока элемент управления обработает сообщение, прежде чем оно вернется в ваш код, но возможно / вероятностно, что WPF не вызывает его таким образом)
Самое простое решение — поместить в свой класс переменную «guard», чтобы избежать реакции на «внутренние» вызовы, и заставить ваш обработчик событий игнорировать события, когда он установлен:
bool suppressTextChanged;
void Initialize()
{
suppressTextChanged = true;
control.SetText("abcd");
suppressTextChanged = false;
...
}
void Control_TextChanged(object sender, EventArgs e)
{
if (suppressTextChanged) return;
...
}
Это уродливое, но эффективное решение. Жаль, что Windows никогда не предоставляла способа различать события, управляемые пользователем (например, текст, измененный пользователем, редактирующим его в элементе управления), и внутренние события (управляемые вашей программой, обновляющей элемент управления внутренне) — хотя для некоторых событий вы можете использовать «отправитель» для определения источника события, чтобы выяснить, как оно было вызвано.
Комментарии:
1. Спасибо. Это то, чего я боялся. Я пришел к тому же выводу относительно переменных guard, но это было настолько запутанно, что я решил, что мне чего-то не хватает. Какая боль. Еще раз спасибо.