Как я могу предотвратить преждевременное закрытие всплывающих панелей wpf из-за диалогов и выпадающих списков?

У меня есть всплывающая панель (как показано ниже), которая, если она не закреплена, должна быть невидимой, когда мышь покидает область панели в целом.

выпадающая панель

Однако я не хочу, чтобы панель закрывалась при возникновении любого из этих условий:

1) Пользователь открывает ContextMenu

2) Пользователь выбирает ComboBox элемент, который находится ниже панели (как показано выше).

3) Диалоговое окно подтверждения, которое появляется из-за действия пользователя (например, удаления элемента в DataGrid)

Легко отслеживать операции контекстного меню (события ContextMenuOpening и ContextMenuClosing) для обработки первого случая, но я пока не нашел хороших способов обработки двух других случаев, в частности открывающихся диалоговых окон отслеживания.

Любые идеи?

Моя всплывающая панель — это просто сетка, видимость и содержимое которой определяются в коде:

<Grid Name="UndockedGrid" ContextMenuOpening="Grid_ContextMenuOpening" ContextMenuClosing="Grid_ContextMenuClosing" MouseLeave="Grid_MouseLeave">
    <!-- Toolbox (undocked) -->
    <ScrollViewer Name="ToolBoxUndockedViewer">
        <StackPanel Name="ToolBoxUndockedPanel" />
    </ScrollViewer>
</Grid>

person Dave Clemmer    schedule 22.07.2011    source источник


Ответы (1)


Я выбрал RoutedEvent подход к этой проблеме, так как мои модели представления не обрабатывают диалоговые рабочие процессы напрямую в этой версии.

Чтобы получить поведение, которое я хотел для полей со списком сетки данных, я расширил DataGrid, чтобы добавить маршрутизируемые события, которые я хотел отслеживать:

public class RoutableDataGrid : DataGrid
{
    public static readonly RoutedEvent ElementOpenedEvent = EventManager.RegisterRoutedEvent("ElementOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutableDataGrid));

    public event RoutedEventHandler ElementOpened
    {
        add { AddHandler(ElementOpenedEvent, value); }
        remove { RemoveHandler(ElementOpenedEvent, value); }
    }

    public static readonly RoutedEvent ElementClosedEvent = EventManager.RegisterRoutedEvent("ElementClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutableDataGrid));

    public event RoutedEventHandler ElementClosed
    {
        add { AddHandler(ElementClosedEvent, value); }
        remove { RemoveHandler(ElementClosedEvent, value); }
    }

    public void RaiseElementOpened()
    {
        RaiseEvent(new RoutedEventArgs(RoutableDataGrid.ElementOpenedEvent, this));
    }

    public void RaiseElementClosed()
    {
        RaiseEvent(new RoutedEventArgs(RoutableDataGrid.ElementClosedEvent, this));
    }
}

И DataGridComboBoxColumn был расширен для запуска новых перенаправленных событий:

public class BindableDataGridComboBoxColumn : DataGridComboBoxColumn
{
    protected RoutableDataGrid ParentGrid { get; set; }

    protected FrameworkElement Element { get; set; }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
    {
        Element = base.GenerateEditingElement(cell, dataItem);
        Element.MouseEnter += new System.Windows.Input.MouseEventHandler(element_MouseEnter);
        Element.MouseLeave += new System.Windows.Input.MouseEventHandler(element_MouseLeave);
        CopyItemsSource(Element);
        return Element;
    }

    void element_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (ParentGrid != null)
        {
            ParentGrid.RaiseElementClosed();
        }
    }

    void element_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if (ParentGrid != null)
        {
            ParentGrid.RaiseElementOpened();
        }
    }
}

Больше хака, поскольку MessageBox является запечатанным классом, мне пришлось создать класс-оболочку для запуска маршрутизируемых событий с использованием MessageBox (и поместить экземпляр этого элемента в xaml, чтобы он был в визуальном дереве):

public class RoutedEventPlaceHolder : UIElement
{
    public static readonly RoutedEvent ElementOpenedEvent = EventManager.RegisterRoutedEvent("ElementOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventPlaceHolder));

    public event RoutedEventHandler ElementOpened
    {
        add { AddHandler(ElementOpenedEvent, value); }
        remove { RemoveHandler(ElementOpenedEvent, value); }
    }

    public static readonly RoutedEvent ElementClosedEvent = EventManager.RegisterRoutedEvent("ElementClosed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventPlaceHolder));

    public event RoutedEventHandler ElementClosed
    {
        add { AddHandler(ElementClosedEvent, value); }
        remove { RemoveHandler(ElementClosedEvent, value); }
    }

    public void RaiseElementOpened()
    {
        RaiseEvent(new RoutedEventArgs(RoutedEventPlaceHolder.ElementOpenedEvent, this));
    }

    public void RaiseElementClosed()
    {
        RaiseEvent(new RoutedEventArgs(RoutedEventPlaceHolder.ElementClosedEvent, this));
    }

    public MessageBoxResult ShowMessageBox(string text, string caption, MessageBoxButton button)
    {
        RaiseElementOpened();
        MessageBoxResult result = MessageBox.Show(text, caption, button);
        RaiseElementClosed();
        return result;
    }
}

Затем, наконец, я могу подписаться на новые маршрутизируемые события во всплывающей панели:

        <Grid Name="UndockedGrid" lib:RoutedEventPlaceHolder.ElementOpened="Grid_ElementOpened" lib:RoutableDataGrid.ElementOpened="Grid_ElementOpened" ContextMenuOpening="Grid_ContextMenuOpening" ContextMenuClosing="Grid_ContextMenuClosing" MouseLeave="Grid_MouseLeave">
            <!-- Toolbox (undocked) -->
            <ScrollViewer Grid.Row="0" Grid.Column="0" Name="ToolBoxUndockedViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Visibility="Collapsed" MouseLeave="ToolBoxUndockedViewer_MouseLeave">
                <StackPanel Name="ToolBoxUndockedPanel" MinWidth="200" Background="{StaticResource ControlBackgroundBrush}" />
            </ScrollViewer>
        </Grid>

Это не идеальное решение. Я приму любые другие ответы, которые более элегантны.

person Dave Clemmer    schedule 23.07.2011