I'm trying to create a custom dropdown control that acts like a ComboBox, such that the Popup opens when you click mouse down (not up), and closes when you click outside of the control.
The problem is that it only behaves if I set ClickMode to "Release". But what I really want is ClickMode="Press", such that the Popup opens on MouseDown instead of MouseUp.
But when I set it to ClickMode="Press", the popup won't close when you click outside the control.
Any ideas how I can achieve this?
Usage :
<StackPanel>
<local:CustomDropdown Width="200"
Height="50"
Content="Custom!" />
<ComboBox Width="200"
Margin="20">
<ComboBoxItem>A</ComboBoxItem>
<ComboBoxItem>B</ComboBoxItem>
<ComboBoxItem>C</ComboBoxItem>
</ComboBox>
</StackPanel>
Class :
internal class CustomDropdown : ContentControl
{
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(CustomDropdown), new PropertyMetadata(false));
}
Xaml :
<Style TargetType="{x:Type local:CustomDropdown}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<ToggleButton IsChecked="{Binding IsOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<Popup StaysOpen="False"
Placement="Bottom"
IsOpen="{Binding IsOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<Border Background="White"
BorderBrush="Black"
BorderThickness="1"
Padding="50">
<TextBlock Text="Popup!" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
CodePudding user response:
If you want it to work as expected with ClickMode.Press
, you should programmatically set the IsOpen
property to false
whenever you want to the close the Popup
. For example whenever you detect a click outside of the ToggleButton
.
You could for example handle the PreviewMouseLeftButtonDown
event for the parent window in your control. Something like this:
internal class CustomDropdown : ContentControl
{
private ToggleButton _toggleButton;
public CustomDropdown()
{
Loaded = onl oaded;
Unloaded = OnUnloaded;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_toggleButton = GetTemplateChild("toggleButton") as ToggleButton;
}
private void onl oaded(object sender, RoutedEventArgs e)
{
Window window = Window.GetWindow(this);
window.PreviewMouseLeftButtonDown = OnWindowPreviewMouseLeftButtonDown;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Window window = Window.GetWindow(this);
window.PreviewMouseLeftButtonDown -= OnWindowPreviewMouseLeftButtonDown;
}
private void OnWindowPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ToggleButton toggleButton = FindParent<ToggleButton>(e.OriginalSource as DependencyObject);
if (toggleButton != _toggleButton)
IsOpen = false;
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null)
return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(CustomDropdown), new PropertyMetadata(false));
}
}
XAML:
<ControlTemplate>
<Grid>
<ToggleButton x:Name="toggleButton" ...