Recently I needed to allow users to resize a WPF Popup. After implementing a non-generic solution, I decided to attempt to turn the concept into a generic WPF control. The Resizer control is a specialised ContentControl that can be used as follows:
<kb:Resizer xmlns:kb="http://kent.boogaart/controls"> Content </kb:Resizer>
The Resizer control adds resizing behaviour to the content you place within it. The default template uses a ResizeGrip and allows the user to drag the grip in order to resize the content. For example, here is how you might add resizing behaviour to a TextBox (XML namespace mapping assumed from hereon in):
<kb:Resizer> <TextBox>My TextBox</TextBox> </kb:Resizer>
The result is this:
And the user can drag the grip to resize the TextBox:
And here is how you might implement a resizable Popup:
<Popup> <kb:Resizer Width="100"> <Border Background="Black"> <Image Source="/Tempany.jpg"/> </Border> </kb:Resizer> </Popup>
Here is the result:
And after resizing:
The Resizer control contains some other functionality worth mentioning:
IsGripEnabled : enables / disables the resize grip (defaults to true).
IsGripVisible : displays / hides the resize grip (defaults to true).
IsAutoSizeEnabled : determines whether the user can double-click the resize grip to return the Resizer's content to its natural size (defaults to true).
ResizeDirection : determines the direction the user needs to drag in order to increase the size of the Resizer's content (default to ResizeDirection.SouthEast).
You can download source for the Resizer control below. Included in the source is a sample application showing the various ways to use the control, including the use of a custom template to redefine the way the Resizer looks.
Enjoy!
53 comments:
IsXYZ? I would expect such a property to be read-only. Properties shouldn't be prefixed with Is. Visible and Enabled are the correct property names, a la MS guidelines.
Then MS should follow their own guidelines. I refer you to any number of read/write IsXxx properties in WPF: IsEnabled, IsEditable, IsDropDownOpen etc etc. I don't have the guidelines handy (book is at work) but IIRC, "Is" is a *recommended* prefix for boolean properties. Will confirm tomorrow.
I think you'll find IsEnabled is correct.
From section 3.6.2 of Framework Design Guidelines (sic):
DO name Boolean proprieties with an affirmative phrase (CanSeek instead of CantSeek). Optionally, you can also prefix Boolean properties with "Is," "Can," or "Has" but only where it adds value.
I guess the WPF team thought that "Is" adds value, and so do I.
nice
Hi Kent,
I'm quite new to WPF and I find your control really helpful to learn the fundamentals. Anyways I've tried to use the Resizer control within a ControlTemplate of another custom control and one problem appeared. It seems that the eventhandlers hooked up on ResizeGrip doesn't work when control is used in this faction. Therefore none of commands defined in your control are executed. Am I doing sth wrong? Do u have any ideas how to fix this behavior? thanks Igor
Hi Kent,
I'm quite new to WPF and I find your control really helpful to learn the fundamentals. Anyways I've tried to use the Resizer control within a ControlTemplate of another custom control and one problem appeared. It seems that the eventhandlers hooked up on ResizeGrip doesn't work when control is used in this faction. Therefore none of commands defined in your control are executed. Am I doing sth wrong? Do u have any ideas how to fix this behavior? thanks Igor
Hi Kent,
I'm quite new to WPF and I find your control really helpful to learn the fundamentals. Anyways I've tried to use the Resizer control within a ControlTemplate of another custom control and one problem appeared. It seems that the eventhandlers hooked up on ResizeGrip doesn't work when control is used in this faction. Therefore none of commands defined in your control are executed. Am I doing sth wrong? Do u have any ideas how to fix this behavior? thanks Igor
Hi Kent,
I'm quite new to WPF and I find your control really helpful to learn the fundamentals. Anyways I've tried to use the Resizer control within a ControlTemplate of another custom control and one problem appeared. It seems that the eventhandlers hooked up on ResizeGrip doesn't work when control is used in this faction. Therefore none of commands defined in your control are executed. Am I doing sth wrong? Do u have any ideas how to fix this behavior? thanks Igor
sorry for repeating comment...my IE seems to be a little buggy or what :)
Well I've found the problem. the Window in which I use the Resizer had the attribute ResizeMode="CanResizeWithGrip". In this case, Resizer doesn't work. Thanks anyways :)
No problems Igor - glad you find this useful. And thanks for increasing my comments/post ratio! :)
Kent,
Thanks so much for this example. I'm glad I Googled before adding a resizer to my custom Popup control. WPF is so awesome and I've learned a bunch, but I didn't know about FrameworkElementAutomationPeer. Your code is both totally functional and a great learning tool.
Many Thanks, Richie
You're welcome Richie. Thanks for the kind words. Glad you find this useful.
Hi Kent,
I'm seeing a problem with the Resizer TestHarness Popup examples built with VS2008 (Orcas) Beta 2. The content doesn't expand to fill a Popup resized to a larger size. As you know, the Popups work fine on VS2005. All of the other test cases seem to be working correctly on VS2008.
I tried building with both Net 3.0 and 3.5 on VS2008 Beta 2.
I haven't figured out what's causing the problem, but you may have quick insights if you have access to a VS2008 Beta 2 machine.
Best,
Rich
Hey Rich,
Sorry but I don't have access to VS Orcas yet (but am planning to over the next week or two). Please let me know if you pin down the problem.
Kent
Hi Kent,
The problem appears to be a difference in behavior between Popups built with VS2005 and VS2008 Beta 2.
I've submitted the following example to the VS2008 WPF forum on MSDN.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1955983&SiteID=1
Hi Kent,
Thanks for the nice article and code to resize the WPF control. I am new to WPF and been researching on a sample or approach on how to resize a WPF window with all the controls at the same time. In your example, each control is individually resized. Is it possible that resizing a WPF window resizes all the child controls appropriately?
Thanks
Sivaram
Hi Sivaram,
Thanks for the kind words. For an answer to your question, you'll want to look into WPF's layout system. Specifically, how panels work and can be used to achieve exactly what you describe.
Regards,
Kent
Hi Kent,
I came around your Resizer control by chance and find it very useful.
I could learn quite a few things about WPF from studying your code, as I'm quite new to WPF and especially control authoring in WPF (as opposed to using them).
I have a question about the custom control template and the way you're attaching the event handlers in generic.xaml.cs
Is that really the way to do it? I have seen that in your example custom template, the event handlers had to be wired up again (in generic.xaml.cs in more or less the same way).
I have seen other examples where the events were attached in the override of OnApplyTemplate in the custom control class.
Thanks in advance!
Hi Kent,
Thank you for your excellent code and for sharing it with us all. To this purpose I'd like to ask what kind of licencing applies to your code. Can I reduce, reuse, and recycle it to fit my needs?
I will make sure you will be ranking hing in the credits of my app.
Hi Anon,
Thanks for the praise. Yes, you can do whatever you like with the source - learn, use as is, use modified, whatever you like. All I ask is that people don't claim that they authored it, and optionally give credit, even if it's just a comment in the source code :)
Hello Kent.
Thanks for your control, is very useful.
One question, how can I make the resizer move only diagonally?
Thanks once more.
Hi Kent, This is excellent example... but i want to use size grip to move control... is it possible!! if possible can you tell me how??? i am having very basic knowledge of WPF!!! Thanks in advance...
hi,
i use your Resizer in my project. I need the CanResizeWithGrip property for my main window. if i enable it, dragging your resizer grip resizes my main window. is there a way to fix it?
Hey Kent,
Your control looks very interesting and I'm considering using it to replace the custom Adorner I've created for my app. However, one feature that would be neat to have is the ability to preserve the aspect ratio of whatever element in you're resizing (perhaps enabled via a flag such as "IsPreserveAspectRationEnabled" or something like that). Any chance you will be adding this anytime soon?
Thanks for sharing this useful control with us!
Victor
hi
does ur resizer component work for a canvas also ...
i am unable to do it to a canvas
so help me out in it..
This looks like exactly what I need, only unfortunately the project files in the zip file don't seem to work in VS2005 or VS2008. Am I missing something?
@drew: 2008 does a conversion on the solution for me without error and everything works fine. I don't have 2005 installed to test.
This was just what I was looking for, but I found a bug in it. There appeared to be an off-by-one problem: the cursor would progressively get further and further away from the sizing control as the size grew.
I found it to be because the control's size was being changed relative to itself, which was itself changing, if that makes any sense. To fix the problem, I changed the two occurrences of "resizer._frameworkElement.PointToScreen(Mouse.GetPosition(resizer._frameworkElement))" to "Mouse.GetPosition(Application.Current.MainWindow)" so things are relative to a fixed coordinate system.
Cheers,
Harley Pebley
http://skylark-software.com
Hi Kent,
thanks for this great article, this is a really nice solution.
I'm currently playing with your control and it works great, but there is one problem:
I'm trying to resize a popup, but when hitting the screen border, it's enlarging in the opposite direction. This is even worse on multi monitor systems, as the WPF popup apparently is not able to cross screen boundaries, and it's only visible on one screen although it should overlap onto the second screen.
You can easily reproduce this with the resizable ComboBox DropDown in your demo app.
You don't happen to know a quick workaround for this?
Regards,
Chris
Hi Chris,
Thanks for that. I haven't come up against this scenario, so I'm afraid I don't have a quick fix for you. I don't have a multi-screen setup to test on, but you may be able to play around with Popup.Placement to get this working.
HTH,
Kent
Thanks for share.
Hi Kent, Thanks a ton for the control...
But I'm facing a problem(Your control works fine..)
I have custom buttons for my window for close, maximize & minimize. Before applying the Resizer control, my maximize worked fine, but after applying the control,
maximize doesn't change the state of the window (same windows size befor click)..
my code
-
private void max_Click(System.Object sender, System.Windows.RoutedEventArgs e)
{
if (this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else if (this.WindowState == WindowState.Maximized)
{
this.WindowState = WindowState.Normal;
}
}
I'm a designer, not much of a coder. Any help would be apprecited... Thanks
Revision- Maximize works as long as I don't use the Resizer
(runtime)
Update: now i know what's happening.. The size of my container is gettin altered at runtime, when I use the resizer, which is why the maximize is no longer applicable to the original size...
& the rezire also scales my contents like a viewbox does...
Can the resizer be used only to resize the outermostgrid such that the contents retain their original size with scrolling auto...
Thanks
Hi C-Dan. I'm not sure I follow exactly. I suspect you may have the resizer at the wrong level in your visual tree. If you post your XAML I might be able to point you in the right direction.
Thanks... That visual-tree tip helped...works fine now...
Hi Kent.
Thank you for sharing your control. It was very useful.
I'd like to share a little extension which enables ratio preservation during resizing.
I've added a couple of private fields for keeping initial width and height
private double? _initialWidth = null;
private double? _initialHeight = null;
I initialize them in
OnStartResizeCommand handler
if (resizer._initialHeight == null)
{
resizer._initialHeight = resizer.ActualHeight;
resizer._initialWidth = resizer.ActualWidth;
}
I've also added a couple of dependency properties
KeepInitialRatioProperty
KeepRatioProperty
and in OnUpdateSizeCommand:
after the resizer initializing
bool keepRatio = Keyboard.IsKeyDown(Key.LeftShift) || resizer.KeepRatio;
bool keepInitialRatio = (Keyboard.IsKeyDown(Key.LeftShift) && Keyboard.IsKeyDown(Key.LeftCtrl)) || resizer.KeepInitialRatio;
double ratio = 0;
if (keepInitialRatio)
{
ratio = resizer._initialWidth != null && resizer._initialHeight != null
? resizer._initialWidth.Value / resizer._initialHeight.Value
: resizer._originalWidth / resizer._originalHeight;
}
else if (keepRatio)
{
ratio = resizer._originalWidth / resizer._originalHeight;
}
and after deltas calculation:
//update the width and height, making sure we don't set to below zero
if (keepRatio || keepInitialRatio)
{
resizer.Height = Math.Max(0, resizer._originalHeight + heightDelta);
resizer.Width = ratio * resizer.Height;
}
else
{
resizer.Height = Math.Max(0, resizer._originalHeight + heightDelta);
resizer.Width = Math.Max(0, resizer._originalWidth + widthDelta);
}
That's all. Now you can preserve initial and original ratio while holding shift and ctrl+shift buttons.
@midnight coder: nice mod - thanks!
Hello Kent,
I like your control :)
Then I put your resizer around a DataGridTextColumn and wpf said its not possible to wrap a Collection...
Is there a workaround for that situation? I would like to offer my user the chance to resize the cell if they need more space for writing data...
Would be pleased to hear from you :)
cheers,
Lisa
Hi Lisa,
To be honest, I'm not sure that scenario makes sense. Given that your content is in a grid, wouldn't you be best off allowing the users to resize the columns and rows in the grid? Resizing an individual cell isn't really possible without affecting other cells in its column and row.
Best,
Kent
Hi Kent
How can I add a grid inside the Resizer dynamically? I tried setting the Content but nothing is shown like that.
@gabore; the same way you would with any other WPF control. If you're only adding a Grid, you won't see anything since a Grid is just a Panel. Try adding something like a Label or TextBox instead.
@Kent, I forgot to add your dll as a reference... Sorry for that ;)
I add a grid with a textblock inside the resizer and it works nice. You have some great work done.
Not sure if the backlink will get added automatically, I've made some mods to this control and documented them on my blog
(Sorry for the typo on your name, I fixed it in the text but Blogger didn't change the url)
Any idea if it should work under a userContorl? Cool app!
@anon: You mean can you use it in a user control? Sure. you mean can you use it to resize a user control? Sure, but it assumes the user control has been built with adaptive layout in mind.
Hi - great control. a quick question - have you gotten it to work with an Expander control?
Hi Kent
Fantastic control - thank you very much this has saved me a lot of time.
I just have one comment/suggestion/question: WPF elements are not necessarily axis-aligned, (particularly not in my case - I am building a multi-touch application), but your resize control always behaves as if they are - dragging down and to the right always increases size, up and to the left always decreases size. For example, when your control is used upside-down (after a RenderTransform is applied to it), it behaves in the exact opposite way that it should. Do you have any ideas for how to make it behave correctly, or intention for fixing this?
Best Regards
Ivan
@Ivan: Thanks for the kind words. To be honest, I have no intention to enhance this. The control is not something I actively support, but you can certainly modify the code to suit your needs.
Just thought I'd mention that this is an awesome little control! Thanks for the great job.
Wow! I'm surprised that Mircosoft hadn't implemented this control yet. It has been 5 years already.
Anyway, thanks a lot! This is really useful!
Post a Comment