WPF Popup скрывается проблема - PullRequest
4 голосов
/ 25 февраля 2010

Предположим, у вас есть ToggleButton для открытия Popup, такое же поведение, как и у всех известных элементов, как ComboBox и т. Д.

... что это за код:

<ToggleButton x:Name="PART_OpenToggleButton"
    Focusable="False"   
    IsChecked="False"
    Template="{StaticResource MyToggleButton}"> 
    <Grid>                                           
        <Popup x:Name="PART_PopupControl"
               Style="{StaticResource MyPopupStyle}"
               StaysOpen="False"
               VerticalAlignment="Bottom"
               IsOpen="False"
               PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}" />
    </Grid>
</ToggleButton>

Затем в коде вы работаете с. IsOpen для Popup и. IsChecked для ToggleButton. Все работает, но проблема возникает, когда вы открываете Popup и щелкаете за пределами. Popup будет закрыт, но ToggleButton останется отмеченным .

Вы не можете установить в PopupOnClosed Handler значение ToggleButton.IsChecked = false, потому что когда вы нажимаете ToggleButton, чтобы закрыть Popup, Popup закрывается, устанавливает ToggleButton.IsChecked = false, но в то же время нажал на ToggleButton и он пытается открыть Popup снова. Таким образом, вы не можете закрыть его.

1st ToggleButtonClick:

-> ToggleButton IsChecked = true

2nd ToggleButtonClick:

-> ToggleButton IsChecked = false
-> ToggleButton IsChecked = true

Поэтому, если вы нажмете кнопку Toggle, когда всплывающее окно открыто, оно мигает, но остается открытым.

Как бы вы решили эту проблему, пожалуйста?

РЕДАКТИРОВАНИЕ:

Попробуйте это в MyWindow.XAML и добавьте свойство зависимостей IsDropDownOpen в приведенном ниже коде:

<Grid>
        <ToggleButton x:Name="PART_OpenToggleButton"
                      Focusable="False"                          
                      Height="20"
                      Width="50"
                      IsChecked="{Binding ElementName=TestWindow, Mode=TwoWay, Path=IsDropDownOpen}">
            <Grid>

                <Popup x:Name="PART_PopupControl"
                       Width="100"
                       Height="100"
                       StaysOpen="False"
                       Focusable="False"
                       VerticalAlignment="Bottom"
                       IsOpen="{Binding ElementName=TestWindow, Path=IsDropDownOpen}"
                       PlacementTarget="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ToggleButton, AncestorLevel=1}}">                  
                </Popup>
            </Grid>
        </ToggleButton>
    </Grid>

public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }        
        public static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(Window), new UIPropertyMetadata(false));

Ответы [ 6 ]

3 голосов
/ 06 августа 2013

Я нашел решение на этом посту: https://stackoverflow.com/a/5821819/651161

Использование следующего класса позволит обрабатывать щелчок до нажатия кнопки переключения. Всплывающее окно закрывается из-за щелчка, но затем оно обрабатывается, поэтому оно не вызывает щелчок ToggleButton.

public class MyPopup : Popup {
    protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) {
        bool isOpen = this.IsOpen;
        base.OnPreviewMouseLeftButtonDown(e);

        if (isOpen && !this.IsOpen)
            e.Handled = true;
    }
}
2 голосов
/ 02 марта 2010

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

Вот содержимое ComboBox-подобного UserControl:

<ToggleButton x:Name="Button" Height="19"> 
   <Grid>
        <Label Name="DisplayList" Content="Whatever" />
        <Popup Name="SelectionPopup" MinHeight="100" MinWidth="200"
                    StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=Button}">
        </Popup>
     </Grid>
</ToggleButton>

И от пользовательского шаблона до фактического ComboBox:

<ToggleButton
      Name="ToggleButton"
      Template="{StaticResource ComboBoxToggleButton}"
      Grid.Column="2"
      Focusable="false"
      IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
      ClickMode="Press">
 </ToggleButton>
 <Popup
      Name="Popup"
      Placement="Bottom"
      IsOpen="{TemplateBinding IsDropDownOpen}"
      AllowsTransparency="True"
      Focusable="False"
      PopupAnimation="Slide">
1 голос
/ 05 сентября 2016

Мне кажется, есть две проблемы - Одним из них является решение проблемы, когда клики внутри всплывающего окна потенциально снова обрабатываются в соответствии с положением в визуальном дереве.

Вторая проблема - автоматическое закрытие с помощью щелчка - происходит на самом деле каждый раз, когда вы нажимаете вне всплывающего окна, и может вызывать дополнительные события. Даже вы нажимаете «кнопку открытия», чтобы закрыть. Проблема в том, что вы не знаете, какое значение было установлено ранее в popup.isOpen, потому что оно всегда ложно для обработчика событий нажатия кнопки открытия.

Я не использую toggleButton для сохранения памяти, но я думаю, что ключевая проблема та же. Кнопка toggleButton уже ложна в тот момент, когда вы нажимаете на нее.

Мой пример всплывающего окна определен так:

<Popup Placement="Bottom" PopupAnimation="Slide" Name="PART_Popup" VerticalOffset="3" AllowsTransparency="True" StaysOpen="False">

Если щелкнуть цель размещения - это была «кнопка открытия», всплывающее окно закрывается, и в то же время событие нажатия кнопки было обработано, но свойство popup.IsOpen уже было равно «false» - поэтому оно было открыто еще раз.

Чтобы решить эту проблему, я подписался на событие «Закрыто» всплывающих окон, сохранив время - блокировка снова открывается на секунду.

DateTime? LastClose = new DateTime?();

private void Popup_Closed(object sender, EventArgs e)
{    LastClose = DateTime.Now;    }

public bool AllowReopen
{
    get {
            if ((popup == null) || (popup.IsOpen)) return false; 
            //You cannot open, when the template isn't applied or it is already open

            return !LastClose.HasValue || (DateTime.Now - LastClose.Value) > new TimeSpan(0,0,1) /*1s*/;
        }
}


public void OpenPopup()
{
     if (!AllowReopen) return;

     popup.IsOpen = true;
}
1 голос
/ 05 марта 2015

Вы можете просто привязать свойство Popups StaysOpen к свойству Buttons IsMouseOver. Таким образом, всплывающее окно будет закрываться всякий раз, когда вы щелкаете что-то вне всплывающего окна (IsMouseOver = false = StaysOpen), и закрывает всплывающее окно при нажатии ToggleButton (IsMouseOver = true = StaysOpen). Таким образом, даже клики за пределами всплывающего окна будут обрабатываться.

<ToggleButton x:Name="Toggle" />
<Popup x:Name="Popup" IsOpen="{Binding ElementName=Toggle, Path=IsChecked, Mode=TwoWay}"
StaysOpen="{Binding ElementName=Toggle, Path=IsMouseOver}" />
0 голосов
/ 16 февраля 2018

Чтобы предотвратить закрытие всплывающего окна, щелкнув его фон, вставьте что-нибудь, что заполнит его.

В этом примере, щелкнув незаполненное пространство , вы закроете всплывающее окно:

<Popup x:Key="MyPop" Width="200" Height="200" StaysOpen="False">            
                <CheckBox Content="abc" />
</Popup>

В этом примере нажатие на незаполненное пространство НЕ закроет всплывающее окно:

<Popup x:Key="MyPop" Width="200" Height="200" StaysOpen="False">
        <StackPanel Background="Red" Width="200" Height="200"> <!--EXTRA PANEL -->
                <CheckBox Content="abc" />
        </StackPanel>
</Popup>
0 голосов
/ 25 февраля 2010

Я бы связал обоих парней с одним и тем же свойством во ViewModel. Вы можете найти хороший пример в шаблоне инструментов по умолчанию:

<ToggleButton x:Name="OverflowButton"
            FocusVisualStyle="{x:Null}"
            IsEnabled="{TemplateBinding HasOverflowItems}"
            Style="{StaticResource ToolBarHorizontalOverflowButtonStyle}"
            IsChecked="{Binding Path=IsOverflowOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
            ClickMode="Press"/>
    <Popup x:Name="OverflowPopup"
         AllowsTransparency="true"
         Placement="Bottom"
         IsOpen="{Binding Path=IsOverflowOpen,RelativeSource={RelativeSource TemplatedParent}}"
         StaysOpen="false"
         Focusable="false"
         PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}">
        <theme:SystemDropShadowChrome Name="Shdw" Color="Transparent">
            <Border Background="{StaticResource ToolBarSubMenuBackground}"
                    BorderBrush="{StaticResource ToolBarMenuBorder}"
                    BorderThickness="1">
                <ToolBarOverflowPanel x:Name="PART_ToolBarOverflowPanel"
                                    Margin="2"
                                    WrapWidth="200"
                                    Focusable="true"
                                    FocusVisualStyle="{x:Null}"
                                    KeyboardNavigation.TabNavigation="Cycle"
                                    KeyboardNavigation.DirectionalNavigation="Cycle"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            </Border>
        </theme:SystemDropShadowChrome>
    </Popup>

Надеюсь, это поможет,

Ура, Анвака.

...