We are going to be taking a look at how to use two WPF controls today,
the
Popup
control and the
Thumb.
Both are located in the
system.windows.controls.primitives
namespace,
because they are used in a number of regular WPF controls. What are we
going to do with these two controls? Well, as the title of the tutorial
says, we are going to make a resizeable popup.
You can probably guess what the
popup
control does - it makes a popup
window. In the standard WPF controls, the popup control is used mainly
for menus and tooltips, but it can hold pretty much any content you want
to throw at it. The thumb
control does not have an as obvious a name
as popup
, but it serves an important purpose. It is used to do mouse
movement tracking, and the two places where it shows up most often are
the
Scrollbar
and the
Slider.
The thumb is the component that the user grabs and drags around.
So today we are going to create a popup that uses thumbs to make
resizing that popup extremely easy. Below is a screenshot of the silly
little application we will be building:
Sadly, popups do not have any user resizing capability built right in.
But as you're about to see, using thumbs makes it pretty easy to do.
First, the XAML:
<Window x:Class="ResizePopup.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Resizeable Popup" Height="100" Width="100">
<Window.Resources>
<Style TargetType="{x:Type Thumb}"
x:Key="PopupThumb">
<Setter Property="HorizontalAlignment"
Value="Stretch"/>
<Setter Property="VerticalAlignment"
Value="Stretch"/>
<EventSetter Event="DragStarted"
Handler="ThumbDragStarted" />
<EventSetter Event="DragDelta"
Handler="ThumbDragDelta" />
<EventSetter Event="DragCompleted"
Handler="ThumbDragCompleted" />
</Style>
<Popup StaysOpen="False" Width="100" Height="100"
x:Key="myPopup" Placement="Mouse">
<Border BorderBrush="Black" BorderThickness="1">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0"
x:Name="PopupTxt"
TextAlignment="Center"
VerticalAlignment="Center">
I'm A Popup!!
</TextBlock>
<Thumb Grid.Row="0" Grid.Column="1"
Cursor="SizeWE"
Style="{StaticResource PopupThumb}"/>
<Thumb Grid.Row="1" Grid.Column="0"
Cursor="SizeNS"
Style="{StaticResource PopupThumb}"/>
<Thumb Grid.Row="1" Grid.Column="1"
Cursor="SizeNWSE"
Style="{StaticResource PopupThumb}"/>
</Grid>
</Border>
</Popup>
</Window.Resources>
<Button Click="ShowPopup" Content="Popup!!" />
</Window>
OK, let's work our way down from the top. First, we have a resources
section, and the first thing in there is a style. We have a style
because we are actually going to be using multiple thumbs for resizing
(one for the bottom edge, one for the corner, and one for the right
edge), and this way we don't have to repeat these property and event
setters. Setting the alignment properties to stretch just guarantees
that the thumbs will fill their respective containers - not very
interesting. The event setters, however are interesting. We will be
getting to the code behind in a minute, but at this point you are
probably realizing what using a
Thumb
gives us. We get these three
events
(DragStarted,
DragDelta
and
DragCompleted)
that give us all sorts of information when a user tries to drag a
Thumb
.
Next comes the popup. I've put it here in resources because I like to
keep my popups out of the way, but you can actually put it anywhere in
your XAML that a regular control would go. The only difference when
doing that is that the Popup does not get added as a visible element
(they only appear when told to, and even then, as a separate window),
even though it is part of the logical tree.
The popup here doesn't have much to it: a border, a grid, and a
textblock taking up most of that grid. But we do have our three thumbs -
one on the right, one on the bottom, and one in the corner, each with an
appropriate cursor. And as you might expect, the style on each of the
thumbs is set to the style we defined above.
And that brings us to the end of the resources, which is almost the end
of the XAML file. The only thing left is the button which triggers the
popup to show, by calling the method
ShowPopup
when clicked. And since
the code behind is next, we will be getting to the content of that
method real soon.using System;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace ResizePopup
{
public partial class Window1 : Window
{
private const int MaxSize = 500;
private const int MinSize = 50;
private Popup _MyPopup;
public Window1()
{
InitializeComponent();
_MyPopup = Resources["myPopup"] as Popup;
}
private void ShowPopup(object sender, RoutedEventArgs e)
{
_MyPopup.IsOpen = true;
}
private void ThumbDragDelta(object sender,
DragDeltaEventArgs e)
{
Thumb t = sender as Thumb;
if (t.Cursor == Cursors.SizeWE
|| t.Cursor == Cursors.SizeNWSE)
{
_MyPopup.Width = Math.Min(MaxSize,
Math.Max(_MyPopup.Width + e.HorizontalChange,
MinSize));
}
if (t.Cursor == Cursors.SizeNS
|| t.Cursor == Cursors.SizeNWSE)
{
_MyPopup.Height = Math.Min(MaxSize,
Math.Max(_MyPopup.Height + e.VerticalChange,
MinSize));
}
}
private void ThumbDragStarted(object sender,
DragStartedEventArgs e)
{
//This is called when the user
//starts dragging the thumb
}
private void ThumbDragCompleted(object sender,
DragCompletedEventArgs e)
{
//This is called when the user
//finishes dragging the thumb
}
}
}
First off, we have ourselves two constants - a minimum size and a
maximum size, just because it is always good to put limits on something
like this. Next, in the constructor, we pull the popup out of the
resources for the window and store it in the private variable
_MyPopup
. This is mostly because we are going to be poking at it a
lot, and so I didn't want to have to keep pulling it out of the resource
dictionary.
Next, we have the
ShowPopup
method that the button on the window
calls. It doesn't take much to show a popup, does it? You can control
exactly where the popup appears through a couple different properties on
the popup - namely
PlacementTarget,
PlacementRectangle,
Placement.
And if those don't give you enough control, there is always the
CustomPopupPlacementCallback
property, which allows you to give the popup a method of yours to call
when it wants to figure out its position.
By default, a popup will disappear when it loses focus, but you can
override that behavior using the
StaysOpen
property. If that is set to true, the popup will stay open until
IsOpen
gets set to false.
OK, enough about opening the popup. Now we get to the meat - resizing
the popup. In this particular case, we don't actually care about the
DragStarted and DragCompeted parts - we don't need them - but I left
them in there for you to see. All the actual logic is in DragDelta -
although there isn't that much.
In DragDelta, we first deal with width. If the thumb being dragged is
the right edge or the corner (that is the cursor check), we add the
HorizontalChange (which could be positive or negative) on the
DragDeltaEventArgs
to the width of the popup. We then constrain the new width within the
min and max constants using the math min and max functions, and set the
new value as the new width of the popup. We do the exact same thing for
height, except in that case we only do it if the thumb being dragged is
the bottom edge or the corner.
And that is it! So there you go, you have now learned how to use Popups
and Thumbs in one fell swoop. If you would like to download the Visual
Studio project for this example, you can grab it
here, and as
always, questions or comments are welcome.
Source Files:
Comments
Post a Comment