【问题标题】:Why does delegate subscriber get called after event has passed?为什么在事件过去后会调用委托订阅者?
【发布时间】:2011-04-26 21:03:59
【问题描述】:

我不知道怎么问这个问题,但是就这样吧。我有一个 WPF 窗口,它有一个响应 TextBox 的 TextChanged 事件的委托。当我将数据加载到窗口中,然后为我的控制器类订阅该事件时,委托处理程序方法被调用。

顺序是这样的。 1.创建窗口 2. 加载该窗口的数据。 3. 使用 TextDidChange 方法订阅窗口的 TextChanged 事件。

在这种情况下,我的 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);
    }

【问题讨论】:

  • 这是一种奇怪的行为。你能提供你的代码吗?

标签: .net wpf events delegates


【解决方案1】:

我不能确定 WPF 在做什么,但是在 Windows 的引擎盖下,控件的文本通常通过向窗口发送消息来更新。这被放置在应用程序的消息队列中,并且仅在 UI 线程开始处理消息时处理 - 因此,如果控件正在由您的 UI 线程处理,那么您必须完成初始化并将控制权返回给您的主消息处理循环(或调用 DoEvents())在控件接收和处理消息之前。即它以异步方式发生。 (您也可以向控件“发布”一条消息,该控件会等待控件处理该消息,然后再返回您的代码,但 WPF 可能/很可能不会以这种方式调用它)

最简单的解决方法是在您的类中添加一个“保护”变量以避免对“内部”调用做出反应,并让您的事件处理程序在设置时忽略这些事件:

bool suppressTextChanged;

void Initialize()
{
    suppressTextChanged = true;
    control.SetText("abcd");
    suppressTextChanged = false;
    ...
}

void Control_TextChanged(object sender, EventArgs e)
{
    if (suppressTextChanged) return;
    ...
}

这是一个丑陋但有效的解决方案。遗憾的是,windows 从来没有提供区分用户驱动事件(例如,用户在控件中编辑它更改的文本)和内部事件(由您的程序在内部更新控件驱动)的方法 - 尽管对于某些事件,您可以使用“发送者”来确定事件的来源,以便确定它是如何被触发的。

【讨论】:

  • 谢谢。这就是我害怕的。关于保护变量,我得出了相同的结论,但这太笨拙了,我想我必须遗漏一些东西。多么痛苦。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多