Почему вызывается подписчик-делегат после прохождения события?

#.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, но это было настолько запутанно, что я решил, что мне чего-то не хватает. Какая боль. Еще раз спасибо.