I am trying to create a series of UserControls that all inherit from a custom UserControl object. One of the key behaviors I want to implement is the ability to dynamically resize all of the controls to fit the control size.
In order to do this, I need to get the initial width and height of the control to compare it to the resized dimensions. In my inherited controls, I can put code in the constructor after the InitializeComponent() call to grab the dimensions. Is there any way I can do this from the base object code?
Also, if there is a better approach to doing this, I am open to suggestions.
I ended up using my base control's dimensions as the fixed size for all inherited controls. This was fairly easy to do by overriding the SetBoundsCore() method:
public partial class BaseControl : UserControl
{
private int _defaultWidth;
private int _defaultHeight;
public BaseControl()
{
InitializeComponent();
_defaultWidth = this.Width;
_defaultHeight = this.Height;
}
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
if (this.DesignMode)
{
width = _defaultWidth;
height = _defaultHeight;
}
base.SetBoundsCore(x, y, width, height, specified);
}
}
Any controls inherited BaseControl automatically default to its fixed dimensions.
At runtime, my resize code calculates a resize ratio based on the new Width and Height vs. the _defaultWidth and _defaultHeight members.
In the user control, take advantage of the docking and anchoring properties of every control in the container. When the user control is sized or resized, the contents should adjust themselves automatically. Then in code, all you need to do is set the size of the user control.
Related
I draw my contents on a form inside OnPaint event with e.graphics.DrawLine(), etc... . So far I was drawing according to form size (resizing my elements) but now I'd like draw as big as I want, and if I draw outside the visible area (the place where object will be drawn is decided at runtime dynamically), I want user to use scroll bars in order to see parts of whole content which I draw.
I have enabled AutoScrolling but I don't know how it may help me when I don't have any controls on that form.
How can I do it?
Simply set the AutoScrollMinSize property to the size you want. The scrollbar(s) automatically appear when the form's ClientSize is smaller than this value. You'll also need to offset what you draw according to the scroll position, like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.AutoScroll = true;
this.AutoScrollMinSize = new Size(3000, 1000);
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
e.Graphics.DrawLine(Pens.Black, 0, 0, 3000, 1000);
base.OnPaint(e);
}
}
First you should set AutoScroll = true; of that Form where you're drawing ,than the best way is to draw things into a Panel and Re-size the Panel to fit the Content drawled inside ,than the Form will Automatically Show it's Scroll Bar's.
I instantiate new Ui elements onto a canvas like so:
public class MainForm :Canvas
{
List<BannerImage> bannerList;
AddImages()
{
bannerImage = new BannerImage("title", "content");
//accompanied with animation
Children.Add(bannerImage);
bannerList.Add(bannerImage);
}
I need to call the bannerImages to get their current position, the following works:
foreach(bannerItem in bannerList)
{
double rightPosition = Canvas.GetRight(bannerItem);
}
But I can't do the following:
bannerItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)
Size s = bannerItem.DesiredSize;
Which always ends up to be
{0,0}
Why is it that I can get the position of the item on the canvas but not the size?
I am just going to take a guess that you didn't override MeasureOverride. I will provide a basic implementation assuming that each element is stacked, but you would need to modify it to take into consideration your child controls and what ever custom layout you may have created (I don't know if they are in a grid, horizontally stacked, in a some kind of scrolled container, etc).
protected override Size MeasureOverride(Size availableSize)
{
var height = 0.0;
var width = 0.0;
foreach (UIElement child in InternalChildren)
{
child.Measure(availableSize);
if (child.DesiredSize.Width > width) width = child.DesiredSize.Width;
height += child.DesiredSize.Height;
}
width = double.IsPositiveInfinity(availableSize.Width) ? width : Math.Min(width, availableSize.Width);
height = double.IsPositiveInfinity(availableSize.Height) ? height : Math.Min(height, availableSize.Height);
return new Size(width, height);
}
Edit
I realized that I explained the issue in a comment, but didn't add it into my answer. The reason you can't get the size is because you have to provide an override in your derived class to compute it. By default, Canvas returns a DesiredSize of 0 since it will adapt to whatever size is assigned to it. In the case of your derived control, you have a Canvas as the base class but you have added additional controls to it. If you don't provide an override of the MeasureOverride method, then the base one (the one implemented by Canvas) is the only one that is called. The base Canvas knows nothing of your controls size requirements. You probably also will need to override ArrangeOverride. This article provides a pretty good explanation about the two methods, what they do and why you need to override them. It also provides and example of both methods.
I am designing a control where the user can specify the X, Y, Width and Height of the DisplayRectangle property in relation to the ClientRectangle.
From what I have read in the MSDN documentation, DisplayRectangle only has a get accessor, and therefore its dimensions cannot be set, however this is imperative in my control!
Can any one suggest how I might safely implement a DisplayRectangle that has both get and set accessors? - or explain why this is bad practice?
Thanks.
TESTS:
Set TabControl style to UserPaint, and adjust the Alignment property, The DisplayRectangle moves to compensate for the location of the tabs. Assuming TabControl has a built in mechanism to set the Rectangle bounds.
Created DemoControl : Control, painted ClientRectangle in Red, and DisplayRectangle in Blue and tried calling SetDisplayRectLocation(x, y)...not quite what I wanted...and yielded no results!
In order to allow the user to set the bounds of the DisplayRectangle property for a control, I have come up with the following solution:
public class ExtendedControl : Control
{
private Rectangle displayRectangle;
protected override Rectangle DisplayRectangle
{
get { return this.displayRectangle; }
}
public void SetDisplayRectangle(Rectangle rect)
{
this.displayRectangle = rect;
}
public ExtendedControl()
{
this.displayRectangle = base.DisplayRectangle;
}
}
I want to implement an AutoSize property in a Custom Control (not User Control), in such a way that it behaves like other standard .NET WinForms controls which implement AutoSize (ala CheckBox) in design mode.
I have the property set up, but it's the way the control behaves in design mode that bugs me. it can still be resized, which doesn't make sense because the visual resize isn't reflected in the AutoSize and Size properties i've implemented.
Standard .NET controls do not allow resizing (or even show resize handles) in design mode when AutoSize is true. I want my control to behave in the same fashion.
Edit: I have it working using the SetBoundsCore() override, but it doesn't visually restrict a resize when AutoSize is set to true, it just has the same effect; the functionality is equivalent, but it feels unnatural.
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
if (!this.auto_size)
this.size = new Size(width, height);
base.SetBoundsCore(x, y, this.size.Width, this.size.Height, specified);
}
Any ideas on doing this the standard way?
Here's my recipe to have your control AutoSize.
Create a method GetAutoSize() to calculate the required size of the control according to your specific implementation. Maybe it's the size of the text it contains or the total height of the controls for the current width, whatever.
Create a method ResizeForAutoSize() to force the control to resize itself following a change in its state. For example if the control is sized for the text it contains, changing the text should have the control resized. Just call this method when the text changes.
Override GetPreferredSize() to notify whoever wants to know (like a FlowLayoutPanel for instance) what is our preferred size.
Override SetBoundsCore() to enforce our sizing rule the same way an AutoSize label cannot be resized.
See sample here.
/// <summary>
/// Method that forces the control to resize itself when in AutoSize following
/// a change in its state that affect the size.
/// </summary>
private void ResizeForAutoSize()
{
if( this.AutoSize )
this.SetBoundsCore( this.Left, this.Top, this.Width, this.Height,
BoundsSpecified.Size );
}
/// <summary>
/// Calculate the required size of the control if in AutoSize.
/// </summary>
/// <returns>Size.</returns>
private Size GetAutoSize()
{
// Do your specific calculation here...
Size size = new Size( 100, 20 );
return size;
}
/// <summary>
/// Retrieves the size of a rectangular area into which
/// a control can be fitted.
/// </summary>
public override Size GetPreferredSize( Size proposedSize )
{
return GetAutoSize();
}
/// <summary>
/// Performs the work of setting the specified bounds of this control.
/// </summary>
protected override void SetBoundsCore( int x, int y, int width, int height,
BoundsSpecified specified )
{
// Only when the size is affected...
if( this.AutoSize && ( specified & BoundsSpecified.Size ) != 0 )
{
Size size = GetAutoSize();
width = size.Width;
height = size.Height;
}
base.SetBoundsCore( x, y, width, height, specified );
}
In the constructor of your control, call SetAutoSizeMode(AutoSizeMode.GrowAndShrink).
Override SizeFromClientSize() method. in this method you must calculate required size for your control and return it.
You only need to add this two lines on top of your custom control declaration
[Designer("System.Windows.Forms.Design.LabelDesigner")]
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem")]
And naturally implement your Autosize logic
I'm working on a custom user control. How can I prevent the HEIGHT ONLY of the control from being modified during the design-time interface.
You can override the SetBoundsCore method and disallow changes to height by changing the height value before calling the base class implementation.
private const int FixedHeightIWantToKeep = 100;
protected override void SetBoundsCore(
int x,
int y,
int width,
int height,
BoundsSpecified specified)
{
// Fixes height at 100 (or whatever fixed height is set to).
height = this.FixedHeightIWantToKeep;
base.SetBoundsCore(x, y, width, height, specified);
}
You can override the Height attribute from the Control class and then set the BrowsableAttribute to prevent it from being displayed in the properties windows
You can also take a look at Attributes and Design-Time Support