Почему мне нужно вызвать Dispatcher.BeginInvoke для SetFocus? - PullRequest
2 голосов
/ 15 сентября 2010

У меня есть WPF UserControl, который содержит StackPanel, который становится видимым в результате изменения состояния. Когда StackPanel становится видимым, я хочу установить фокус клавиатуры на определенный дочерний TextBox. Я обнаружил (после многих проб и ошибок), что вызов TextBox.Focus () не сможет установить фокус (и вернет false), если я не оберну этот вызов в вызове BeginInvoke, как показано здесь:

    private void CtlClientLookupPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) {
        LogThreadMsg(string.Format("CtlClientLookupPanel_IsVisibleChanged to {0}", CtlClientLookupPanel.Visibility));
        if (CtlClientLookupPanel.Visibility == Visibility.Visible) {
            Dispatcher.BeginInvoke((ThreadStart)delegate {
                bool gotFocus = CtlClientSearchText.Focus();
                LogThreadMsg(string.Format("CtlClientSearchText.Focus() returned {0}", gotFocus));
            });
        }
    }

    private void LogThreadMsg(string msg) {
        string fullMsg = string.Format("Thread: {0} - {1}",   Thread.CurrentThread.ManagedThreadId, msg);
        System.Diagnostics.Trace.WriteLine(msg);
    }

Оба вызова LogThreadMsg указывают, что они находятся в одном потоке (UI), как показано здесь:

[5232] Thread: 1 - CtlClientLookupPanel_IsVisibleChanged to Visible 
[5232] Thread: 1 - CtlClientSearchText.Focus() returned True 

Так зачем нужен этот "взлом"? Кажется, это какая-то временная проблема, и я искал последующее событие, которое, возможно, было бы лучшим местом для вызова Focus (), не прибегая к этому, но не нашло его. Кто-нибудь может объяснить, что здесь происходит?

Ответы [ 2 ]

3 голосов
/ 15 сентября 2010

Это действительно проблема времени.Когда CtlClientLookupPanel становится видимым, я думаю, ваш TextBox еще не виден и не может быть сфокусированВы можете попытаться обработать событие IsVisibleChanged на TextBox вместо

1 голос
/ 15 сентября 2010

На самом деле можно более точно сказать, что текстовое поле еще не отображается, когда вы пытаетесь сфокусировать его.В основе WPF лежит своего рода насос сообщений, очень похожий на WinForms, но гораздо более продвинутый - Dispatcher.Диспетчер используется для постановки в очередь - некоторые инициированные вами действия выполняются позже в том же потоке, когда сообщения в очереди обрабатываются в соответствии с их приоритетами.BeginInvoke ставит в очередь еще один рабочий элемент в очереди Dispatcher, и он выполняется после некоторых других элементов, которые нужны в первую очередь.

Это очень хакерское объяснение, и я призываю вас прочитать больше об этом - просто GoogleДиспетчер WPF и прочитайте любую тонну статей, большинство из них очень хорошие.

Редактировать: также, хорошим событием для обработки в вашем случае будет событие Loaded текстового поля;как правило, событие Loaded запускается только после того, как все другие работы с макетом выполнены и элемент управления фактически виден.Тем не менее, организация очереди в Диспетчере также является хорошим способом решения проблем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...