Как получить выпадающий список для правильной установки фокуса сразу после закрытия всплывающего окна - PullRequest
5 голосов
/ 15 марта 2012

Когда пользователь выбирает значения из выпадающего списка, если они выбирают значение, запускается событие «SelectionChanged», и устанавливается новое значение, и все в порядке. Однако, если они решили не изменять значение и щелкнуть в другом месте пользовательского интерфейса (например, в текстовом поле, которое они хотят редактировать), они должны щелкнуть дважды - первый щелчок просто закрывает всплывающее окно со списком, а следующий щелчок будет фокусировать элемент, который они хотели активировать при первом клике.

Как я могу предотвратить всплывающее окно со всплывающим списком для захвата цели фокуса при первом щелчке?

Я пытался отслеживать событие ComboBox_LostFocus, но оно срабатывает не в то время. Когда пользователь щелкает раскрывающийся список и отображается всплывающий список, возникает событие ComboBox_LostFocus - оно теряет фокус на свой собственный раскрывающийся список. Я не хочу ничего делать, чтобы изменить это. Когда пользователь щелкает, а всплывающее окно закрывается, ComboBox никогда не восстанавливает фокус (фокус просто «потерян» для всего), и поэтому это событие бесполезно.

Ответы [ 2 ]

6 голосов
/ 15 марта 2012

Я думаю, что мог бы найти решение. У комбинированных списков есть событие DropDownClosed - проблема в том, что это не RoutedEvent, поэтому вы не можете создать стиль для ComboBox и заставить их всех наследовать событие через EventSetter. (Вы получаете ошибку 'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event")

Однако, событие Loaded является объектом RoutedEvent, поэтому мы можем подключиться к нему в стиле:

<Style x:Key="ComboBoxCellStyle" TargetType="ComboBox">
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded" />
</Style>

Теперь, когда у нас есть событие, которое всегда будет срабатывать, прежде чем что-либо еще будет сделано с ComboBox, мы можем подключиться к событию, которое нас действительно волнует:

private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
    ((ComboBox)sender).DropDownClosed -= ComboBox_OnDropDownClosed;
    ((ComboBox)sender).DropDownClosed += new System.EventHandler(ComboBox_OnDropDownClosed);
}

Теперь, когда у меня наконец есть доступ к событию, которое срабатывает, когда закрывается DropDown, я могу выполнять любые необходимые мне действия, чтобы убедиться, что фокус завершен на надоедливом ComboBox. В моем случае следующее:

void ComboBox_OnDropDownClosed(object sender, System.EventArgs e)
{
    FrameworkElement visualElement = (FrameworkElement)sender;

    while( visualElement != null && !(visualElement is DataCell) )
        visualElement = (FrameworkElement)visualElement.TemplatedParent;
    if( visualElement is DataCell )
    {
        DataCell dataCell = (DataCell)visualElement;
        dataCell.EndEdit();
        if( !(dataCell.ParentRow is InsertionRow) ) dataCell.ParentRow.EndEdit();
    }
}

У меня был ComboBox в качестве шаблона DataCell в GridView, и эта конкретная проблема не позволяла DataRow завершить редактирование, когда пользователь открыл ComboBox, а затем щелкнул за пределами сетки.

Это была моя самая большая проблема с этой ошибкой. Вторичная проблема установки фокуса в этом событии если пользователь нажал. Возможно, этот комбинированный список также был закрыт, потому что пользователь нажал на вкладку или ушел, поэтому мы не можем просто установить фокус на положение мыши. Нам нужно больше информации о том, что вызвало событие DropDownClosed. Вероятно, подразумевает подключение к более не маршрутизированным событиям в обработчике событий _Loaded.

2 голосов
/ 15 марта 2012

Есть событие DropDownClosed:

private void comboBox_DropDownClosed(object sender, EventArgs e)
{
    Point m = Control.MousePosition;
    Point p = this.PointToClient(m);
    Control c = this.GetChildAtPoint(p);
    c.Focus();
}

Это позволит установить фокус только на тот элемент управления, на который они нажали.Например, если они щелкают текстовое поле, каретка будет слева, а не там, где они щелкнули.Если они нажмут на другой ComboBox, он сфокусируется на нем, но не отобразит его всплывающее окно.Тем не менее, я уверен, что вы могли бы иметь дело с этими случаями в этом обработчике событий, если вам нужно.

РЕДАКТИРОВАТЬ: Ой, вы используете WPF!Тогда не беспокойся;это то, как вы делаете это в WinForms.Тем не менее, у вас все еще есть событие DropDownClosed в WPF.

РЕДАКТИРОВАТЬ 2: Это, кажется, делает это.Я не знаком с WPF, поэтому я не знаю, насколько он надежен, но он сосредоточится на TextBox, например.Это стандартное приложение WPF с окном MainWindow.Когда вы закрываете DropDown comboBox, он фокусирует самый верхний фокусируемый элемент управления на позиции мыши, отличной от MainWindow:

private void comboBox_DropDownClosed(object sender, EventArgs e)
{
    Point m = Mouse.GetPosition(this);
    VisualTreeHelper.HitTest(this, new HitTestFilterCallback(FilterCallback),
        new HitTestResultCallback(ResultCallback), new PointHitTestParameters(m));
}

private HitTestFilterBehavior FilterCallback(DependencyObject o)
{
    var c = o as Control;
    if ((c != null) && !(o is MainWindow))
    {
        if (c.Focusable)
        {
            c.Focus();
            return HitTestFilterBehavior.Stop;
        }
    }
    return HitTestFilterBehavior.Continue;
}

private HitTestResultBehavior ResultCallback(HitTestResult r)
{
    return HitTestResultBehavior.Continue;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...