I faced the same problem. None of the answers offered here worked correctly.
After a little research, I can say that the suspicions of the author of the question are correct. During a mouse click, the first click (down) closes the popup and set togglebutton as unchecked, the second click (up) causes the observed action when the popup appears again.
The first way to avoid this problem is to discard the second click by delay:
<ToggleButton x:Name="UserPhotoToggleButton"/>
<Popup x:Name="UserInfoPopup"
IsOpen="{Binding IsChecked, ElementName=UserPhotoToggleButton, Delay=200, Mode=TwoWay}"
StaysOpen="False">
It looks simple enough to fix problem. Although it is not an ideal solution. The best way would be to extend the functionality of the popup by Behavior:
Add these namespaces
xmlns:behaviors="clr-namespace:WpfClient.Resources.Behaviors;assembly=WpfClient.Resources"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
then extend your popup by i:Interaction.Behaviors
<Popup x:Name="UserInfoPopup"
StaysOpen="False">
<i:Interaction.Behaviors>
<behaviors:BindToggleButtonToPopupBehavior
DesiredToggleButton="{Binding ElementName=UserPhotoToggleButton}"/>
</i:Interaction.Behaviors>
<Border>
<!--Your template-->
</Border>
</Popup>
Finally add the behavior. In a minimal form, it may look like this:
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace WpfClient.Resources.Behaviors
{
public class BindToggleButtonToPopupBehavior : Behavior<Popup>
{
public ToggleButton DesiredToggleButton
{
get { return (ToggleButton)GetValue(DesiredToggleButtonProperty); }
set { SetValue(DesiredToggleButtonProperty, value); }
}
public static readonly DependencyProperty DesiredToggleButtonProperty =
DependencyProperty.Register(nameof(DesiredToggleButton), typeof(ToggleButton), typeof(BindIconToggleButtonToPopupBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
DesiredToggleButton.Checked += DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked += DesiredToggleButton_Unchecked;
AssociatedObject.Closed += AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp += AssociatedObject_PreviewMouseUp;
}
private void DesiredToggleButton_Unchecked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = false;
private void DesiredToggleButton_Checked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = true;
private void AssociatedObject_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.Source is Button)
AssociatedObject.IsOpen = false;
}
private void AssociatedObject_Closed(object sender, EventArgs e)
{
if (DesiredToggleButton != Mouse.DirectlyOver)
DesiredToggleButton.IsChecked = false;
}
protected override void OnDetaching()
{
base.OnDetaching();
DesiredToggleButton.Checked -= DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked -= DesiredToggleButton_Unchecked;
if (AssociatedObject != null)
{
AssociatedObject.Closed -= AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp -= AssociatedObject_PreviewMouseUp;
}
}
}
}