UserControl as a Popup (WPF, C#) – Part 1
The built in Popup in WPF is ok, but it needs to do a LOT more. It would be nice if it could also do the following:
- one popup that can be used anywehre easily in any user control
- modal-like (can disable the background and force the user to interact with the popup)
- can be defined in xaml or prgrammatically
- is movable
- and can (in xaml) be wrapped around UIElements or can have elements added in programmatically
In this post I will take care of the first 3 items.
I got a few of my ideas from Roecode’s nice post on his blog at:
http://roecode.wordpress.com/?s=WPF+Popup+control#
I didn’t see the need for most of the dependency properties. Most of the functionality could be defined in the properties.
Here is the PopupControl class:
public partial class PopupControl : UserControl
{
private Grid mainGrid = null;
public PopupControl()
{
InitializeComponent();
Window mainWindow = Application.Current.MainWindow as Window;
if (mainWindow != null && mainWindow.Content is Grid)
mainGrid = mainWindow.Content as Grid; // Main Window's first UIElement must be a Grid
else
mainGrid = new Grid(); // This will prevent and error if the main window's first element is not a Grid
}
///
/// Opens/closes the popup on true/false
///
public bool IsOpen
{
get
{
if (Visibility == Visibility.Visible)
return true;
return false;
}
set
{
if (value == true)
{
mainGrid.Children.Remove(this); // Remove the popup - in case they click the open button more than once
mainGrid.Children.Add(this); // Add to the end of the bottom of the list so it is above all other items
Visibility = Visibility.Visible;
}
else
{
mainGrid.Children.Remove(this); // Remove the popup
Visibility = Visibility.Collapsed;
}
}
}
// Clicking outside the popup closes the popup when 'false'
public bool StaysOpen
{
get; set;
}
// Set a title for the popup
public string Title
{
get { return (string)lblPopupTitle.Content; }
set { lblPopupTitle.Content = value; }
}
// This hides the Width for the UserControl - this is a good thing
public double Width
{
get { return borderOutside.Width; }
set { borderOutside.Width = value; }
}
// This hides the Height for the UserControl - this is a good thing
public double Height
{
get { return borderOutside.Height; }
set { borderOutside.Height = value; }
}
public UIElementCollection Children
{
get { return contentContainer.Children; }
}
///
/// Places a rectangle over the page to prevent access
///
public bool ForceFocus
{
get
{
if (MainRectangle.Visibility == Visibility.Collapsed)
return false;
return true;
}
set
{
if (value == true)
MainRectangle.Visibility = Visibility.Visible;
else
MainRectangle.Visibility = Visibility.Collapsed;
}
}
///
/// Close the popup
///
///
///
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
IsOpen = false;
}
}
In the main window, I define the popup in code. I add a simple button to open it.
Each time it is used, I will create a new instance of it.
public partial class Window1 : Window
{
private PopupControl popup = null;
public Window1()
{
InitializeComponent();
popup = new PopupControl();
popup.Width = 300;
popup.Height = 300;
popup.Title = "This is the popup";
}
private void ShopPopup_Click(object sender, RoutedEventArgs e)
{
popup.IsOpen = true;
}
}
When it is opened and closed, I am adding and removed the popup dynamically to/from the main window’s Grid.
This will not work if the main window’s first element is not a grid. I am check for this. If it is not, nothing will happen.
Because I am add it dynamically to the main window, the PopupControl and MainWindow must be initialized first.
For this reason, in Window1, I declare the PopupControl and assign ‘null’. I cannot initialize it at that point because the MainWidnow’s Grid has not yet been created.
The XAML for the PopupContainer is simple.
These are all defined in the XAML:
<UserControl x:Class="Sandbox.PopupControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
HorizontalAlignment="Stretch" Margin="0" VerticalAlignment="Stretch"
Width="Auto" Height="Auto" Visibility="Collapsed">
<UserControl.Resources>
<Style x:Key="UserControlOverlayRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="silver"/>
<Setter Property="Opacity" Value="0.61"/>
</Style>
</UserControl.Resources>
<Grid Opacity="1" Width="Auto" Height="Auto" Background="{x:Null}">
<Rectangle Name="MainRectangle" Margin="0" Style="{DynamicResource UserControlOverlayRectangleStyle}"/>
<Border x:Name="borderOutside" BorderThickness="1" BorderBrush="DarkGray" Background="White" Margin="0">
<Grid>
<Button Content="Close" Click="CancelButton_Click" Width="75" Height="30"/>
<StackPanel Name="contentContainer" MinWidth="150" MinHeight="50" />
</Grid>
</Border>
</Grid>
</UserControl>
In the XAML you will see the 'Rectangle' that covers the main content and prevents it from being accessed - forcing the focus on the popup.