Running on Empty

The few things I know, I like to share.

WPF Popup control. (Part 2, User controls as Popups)

Introduction

In Part 1 of this series I outlined a way of creating a Popup control using WPF.  In this article I will demonstrate creating a Popup using Dependency Properties, not quite so quick, but much more elegant.  The main benefit of creating user controls is reusability.

Creating a Popup User Control.

public class PopupTest : System.Windows.Controls.Control
{
    Popup _parentPopup;
    /// <summary>
    /// Constructor
    /// </summary>
    public PopupTest()
        : base()
    {
    }
 
    static PopupTest()
    {
        //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
        //This style is defined in themes\generic.xaml
        DefaultStyleKeyProperty.OverrideMetadata(typeof(PopupTest), new FrameworkPropertyMetadata(typeof(PopupTest)));
    }
}

This is pretty much the template class for a user control with one small addition, the property Popup.  Note the visual style for the control will appear in Generic.xaml.

Adding Dependency Properties to add Popup Functionality.

Basically you have a shell for a User Control at this point.  All you need to add are some dependency properties to add Popup functionality to the control.

#region Properties
public bool IsOpen
{
    get { return (bool)GetValue(IsOpenProperty); }
    set { SetValue(IsOpenProperty, value); }
}

public bool StaysOpen
{
    get { return (bool)GetValue(StaysOpenProperty); }
    set { SetValue(StaysOpenProperty, value); }
}
#endregion
#region DependencyProperties
//Placement
public static readonly DependencyProperty PlacementProperty =
            Popup.PlacementProperty.AddOwner(typeof(PopupTest));
public PlacementMode Placement
{
    get { return (PlacementMode)GetValue(PlacementProperty); }
    set { SetValue(PlacementProperty, value); }
}
//PlacementTarget
public static readonly DependencyProperty PlacementTargetProperty =
            Popup.PlacementTargetProperty.AddOwner(typeof(PopupTest));
public UIElement PlacementTarget
{
    get { return (UIElement)GetValue(PlacementTargetProperty); }
    set { SetValue(PlacementTargetProperty, value); }
}
//PlacementRectangle
public static readonly DependencyProperty PlacementRectangleProperty =
            Popup.PlacementRectangleProperty.AddOwner(typeof(PopupTest));
public Rect PlacementRectangle
{
    get { return (Rect)GetValue(PlacementRectangleProperty); }
    set { SetValue(PlacementRectangleProperty, value); }
}
//HorizontalOffset
public static readonly DependencyProperty HorizontalOffsetProperty =
            Popup.HorizontalOffsetProperty.AddOwner(typeof(PopupTest));
public double HorizontalOffset
{
    get { return (double)GetValue(HorizontalOffsetProperty); }
    set { SetValue(HorizontalOffsetProperty, value); }
}
//VerticalOffset
public static readonly DependencyProperty VerticalOffsetProperty =
            Popup.VerticalOffsetProperty.AddOwner(typeof(PopupTest));
public double VerticalOffset
{
    get { return (double)GetValue(VerticalOffsetProperty); }
    set { SetValue(VerticalOffsetProperty, value); }
}
public static readonly DependencyProperty StaysOpenProperty =
Popup.StaysOpenProperty.AddOwner(
        typeof(PopupTest),
        new FrameworkPropertyMetadata(
                false,
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                new PropertyChangedCallback(OnStaysOpenChanged)));
public static readonly DependencyProperty IsOpenProperty =
 Popup.IsOpenProperty.AddOwner(
         typeof(PopupTest),
         new FrameworkPropertyMetadata(
                 false,
                 FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                 new PropertyChangedCallback(OnIsOpenChanged)));
#endregion

Now add some event handlers for the PropertyChangedCallback events.

private static void OnStaysOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    PopupTest ctrl = (PopupTest)d;
 
    if ((bool)e.NewValue)
    {
        ctrl.StaysOpenParentPopop((bool)e.NewValue);
    }
}
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    TenKeyPopup ctrl = (TenKeyPopup)d;
    if ((bool)e.NewValue)
    {
        if (ctrl._parentPopup == null)
        {
            ctrl.HookupParentPopup();
        }
    }
}
private void StaysOpenParentPopop(bool newValue)
{
    _parentPopup.StaysOpen = newValue;
}
private void HookupParentPopup()
{
    _parentPopup = new Popup();
    _parentPopup.AllowsTransparency = true;
    Popup.CreateRootPopup(_parentPopup, this);
}

Conclusion

This method of creating Popup controls is a little bit more difficult than the quick and dirty method described in Part 1.  However, we can now benefit from reusability and encapsulation of the control.  We could easily add dependency properties at this point to add functionality to the control as well as visual styles.

Actually using the Popup control is the same as what is describe in Part 1.  Please feel free to leave comments and suggestions.

January 8, 2008 Posted by | C#, WPF, XAML | 13 Comments