Skip to content

Instantly share code, notes, and snippets.

@peace2048
Created May 19, 2015 08:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peace2048/3a8e0801cbbaff6e4f53 to your computer and use it in GitHub Desktop.
Save peace2048/3a8e0801cbbaff6e4f53 to your computer and use it in GitHub Desktop.
WPF DropDownBox
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace WpfApplication1
{
[TemplatePart(Name = PART_ContentPresenter, Type = typeof(ContentPresenter))]
[TemplatePart(Name = PART_DropDownButton, Type = typeof(ToggleButton))]
[TemplatePart(Name = PART_Popup, Type = typeof(Popup))]
internal class DropDownButton : ContentControl
{
public static readonly DependencyProperty DropDownContentProperty =
DependencyProperty.Register("DropDownContent", typeof(object), typeof(DropDownButton), new PropertyMetadata(null, OnDropDownContentPropertyChanged));
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(DropDownButton), new PropertyMetadata(false, OnIsOpenPropertyChanged));
private const string PART_ContentPresenter = "PART_ContentPresenter";
private const string PART_DropDownButton = "PART_DropDownButton";
private const string PART_Popup = "PART_Popup";
private ToggleButton _button;
private ContentPresenter _contentPresenter;
private Popup _popup;
static DropDownButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton)));
}
public DropDownButton()
{
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, new MouseButtonEventHandler(OnPreviewMouseDownOutsideCapturedElement));
}
public object DropDownContent
{
get { return (object)GetValue(DropDownContentProperty); }
set { SetValue(DropDownContentProperty, value); }
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
protected ToggleButton Button
{
get { return _button; }
set
{
if (_button != null)
{
_button.Click -= DropDownButton_Click;
}
_button = value;
if (_button != null)
{
_button.Click += DropDownButton_Click;
}
}
}
protected Popup Popup
{
get { return _popup; }
set
{
if (_popup != null)
{
_popup.Opened -= Popup_Opened;
}
_popup = value;
if (_popup != null)
{
_popup.Opened += Popup_Opened;
}
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_contentPresenter = GetTemplateChild(PART_ContentPresenter) as ContentPresenter;
this.Button = GetTemplateChild(PART_DropDownButton) as ToggleButton;
this.Popup = GetTemplateChild(PART_Popup) as Popup;
}
private static void OnDropDownContentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as DropDownButton;
if (obj != null)
{
var oldValue = e.OldValue;
var newValue = e.NewValue;
obj.OnDropDownContentChanged(oldValue, newValue);
}
}
private static void OnIsOpenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var obj = d as DropDownButton;
if (obj != null)
{
var oldValue = (bool)e.OldValue;
var newValue = (bool)e.NewValue;
obj.OnIsOpenChanged(oldValue, newValue);
}
}
private void CloseDropDown(bool isFocusOnButton)
{
if (IsOpen)
{
IsOpen = false;
}
ReleaseMouseCapture();
if (isFocusOnButton)
{
Button.Focus();
}
}
private void DropDownButton_Click(object sender, RoutedEventArgs e)
{
OnClick();
}
private void OnClick()
{
}
private void OnDropDownContentChanged(object oldValue, object newValue)
{
}
private void OnIsOpenChanged(bool oldValue, bool newValue)
{
}
private void OnPreviewMouseDownOutsideCapturedElement(object sender, MouseButtonEventArgs e)
{
CloseDropDown(false);
}
private void Popup_Opened(object sender, EventArgs e)
{
if (_contentPresenter != null)
{
_contentPresenter.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
}
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<local:InverseBoolConverter x:Key="InverseBoolConverter" />
<Style TargetType="{x:Type local:DropDownButton}">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DropDownButton}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="True">
<ToggleButton x:Name="PART_DropDownButton" IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" IsHitTestVisible="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolConverter}}">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<ContentPresenter />
</ControlTemplate>
</ToggleButton.Template>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
<Grid x:Name="arrowGlyph" Grid.Column="1">
<TextBlock Text="▼" />
</Grid>
</Grid>
</ToggleButton>
<Popup x:Name="PART_Popup" AllowsTransparency="True" StaysOpen="False" Placement="Bottom" Focusable="False" IsOpen="{Binding IsChecked, ElementName=PART_DropDownButton}">
<Border BorderThickness="1" Background="White">
<ContentPresenter x:Name="PART_ContentPresenter" Content="{TemplateBinding DropDownContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
using System;
using System.Windows.Data;
namespace WpfApplication1
{
internal class InverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment