I'm having a problem with panels.
I'm adding points to a panel with this code:
void panelDraw_Paint(object sender, PaintEventArgs e)
{
if (this.MeasuredValue == null)
return;
var g = e.Graphics;
int x = 0;
foreach (value m in this.MeasuredValue )
{
double percentage = m.MeasuredValue [(int)this.MeetType].GetValueOrDefault() / this.MaxValue * (double)100;
double y = this.Height / (double)100;
double pixels = y * percentage;
g.DrawRectangle(Pens.Green, x++, this.Height - (int)pixels, 1, 1);
g.DrawLine(Pens.GhostWhite, 0, this.Height / 4, panelDraw.Width, this.Height / 4);
g.DrawLine(Pens.GhostWhite, 0, this.Height / 2, panelDraw.Width, this.Height / 2);
g.DrawLine(Pens.GhostWhite, 0, (this.Height / 4) * 3, panelDraw.Width, (this.Height / 4) * 3);
if (x > panelDraw.Width)
{
panelDraw.AutoScroll = true;
}
}
}
The dimension of my panel is 230;218
I'ld like to see my points when x goes out of the boundaries (bigger then 230) but somehow autoscroll doesn't work ..
I did also set AutoScroll on true in the panel properties from the beginning, but that also doesn't work.
This is what I get, when x is bigger than my panel width:
How can I see my points when they are out of the boundaries of the panel?
The reason why the panel does not show any scroll bars is that it does not know anything of its content. Even if it would scroll, the view would not change because you don't take the scroll position into account when painting the content.
Instead of painting directly to the panel, you should add another control with the appropriate size to it and paint on this control. This will give the panel the necessary scroll hints and you don't have to handle scrolling manually.
Related
I am currently developping a personal theme that I entend to use in my application. I have a question regarding the custom controls I'm creating and their CPU/Memory Usage!
Code
I'll show you an example of my custom label control.
Constructor
This is where I set the DoubleBuffer mechanism and styles/properties.
public DarkLabel()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
DoubleBuffered = true;
ForeColor = Helpers.TextColor;
BackColor = Helpers.LightDarkColor;
_textAlign = TextAlignements.Left;
}
OnPaint override
This is where I do the drawing of the custom control.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Helpers.SetHighQuality(g);
// Background
g.Clear(BackColor);
// Text and Image
using (SolidBrush b = new SolidBrush(ForeColor))
{
// Image but no Text
if (string.IsNullOrEmpty(Text) && _image != null)
{
g.DrawImage(_image, Width / 2 - _image.Width / 2, Height / 2 - _image.Height / 2);
}
// Image and Text
else if (!string.IsNullOrEmpty(Text) && _image != null)
{
SizeF textSize = g.MeasureString(Text, Font);
if (_textAlign == TextAlignements.Left)
{
g.DrawImage(_image, 2, Height / 2 - _image.Height / 2);
g.DrawString(Text, Font, b, _image.Width + 5, Height / 2 - textSize.Height / 2);
}
else
{
g.DrawImage(_image, Width - Image.Width - 2, Height / 2 - _image.Height / 2);
g.DrawString(Text, Font, b, Width - _image.Width - textSize.Width - 7, Height / 2 - textSize.Height / 2);
}
}
// Text
else
{
SizeF textSize = g.MeasureString(Text, Font);
if (_textAlign == TextAlignements.Left)
{
g.DrawString(Text, Font, b, 2, Height / 2 - textSize.Height / 2);
}
else
{
g.DrawString(Text, Font, b, Width - textSize.Width - 2, Height / 2 - textSize.Height / 2);
}
}
}
base.OnPaint(e);
}
Result
This custom label gives me exactly what I need, a text and an image (if it exists) next to it, whether its aligned Left or Right.
Questions
What is the most professional way to create custom controls like these? Do professionals (like DevExpress, Telerik) use the same technique or is there something better to improve (or at least avoid) CPU/Memory usage?
I have a panel, inside which I am trying to center the controls. But apparently, panels don't like padding when they are docked to the edge of a control. Here is the current code for my panel:
buttonPanel = new Panel();
buttonPanel.Width = 300;
buttonPanel.Height = 50;
buttonPanel.Dock = DockStyle.Bottom;
buttonPanel.Padding = new Padding((this.ClientSize.Width - buttonPanel.Width) / 2, 0,
(this.ClientSize.Width - buttonPanel.Width) / 2, 0);
buttonPanel.BackColor = Color.Transparent;
this.Controls.Add(buttonPanel);
I have a single button placed inside the panel. What I would expect with the above code, is that the control is placed nicely to the left of a 300 wide "rectangle" centered in the panel. But the button is placed on the very left, as if the padding is ignored:
How can I center a collection of buttons to the center of my form?
Design Time Approach
At design time, put your button in your container and select your button. Then, use Center Horizontally and Center Vertically from Layout toolbar to put your button in center of panel. After, go to your buttons properties and remove every anchor from Anchor property.
Coding Approach
Panel panel = new Panel();
panel.Size = new Size(300, 100);
panel.Dock = DockStyle.Bottom;
Button button = new Button();
button.Size = new Size(70, 25);
button.Location = new Point((panel.Width - button.Width) / 2, (panel.Height - button.Height) / 2);
button.Anchor = AnchorStyles.None;
panel.Controls.Add(button);
this.Controls.Add(panel);
You have to align the button inside the panel by setting the button's position not the panel's padding:
button1.Left = (int)(panel1.Width * 0.5f - button1.Width * 0.5f);
button1.Top = (int)(panel1.Height * 0.5f - button1.Height * 0.5f);
or
button1.Location = new Point()
{
X = panel1.Width / 2 - button1.Width / 2,
Y = panel1.Height / 2 - button1.Height / 2
};
the result is the same.
If you override the OnResize method the button will stay centered also when you change the size of the form:
protected override void OnResize(EventArgs e)
{
button1.Location = new Point()
{
X = panel1.Width / 2 - button1.Width / 2,
Y = panel1.Height / 2 - button1.Height / 2
};
base.OnResize(e);
}
I'm working on a custom WPF Panel that works similar to a Canvas except that the placements of its children are expressed as percentages instead of absolute offsets. The Panel, which I'm calling PercentageCanvas would be used like so:
<Window>
<local:PercentageCanvas>
<ChildElement local:PercentageCanvas.Left=".30" local:PercentageCanvas.Top=".50" />
<ChildElement local:PercentageCanvas.Left="0" local:PercentageCanvas.Top=".9" />
... etc ...
</local:PercentageCanvas>
</Window>
I have the PercentageCanvas working, although not quite as I expected. The children of the PercentageCanvas are positioned relative to the center of the child, rather than the top-left edge, as I had expected. Here's the ArrangeOverride method I use that gives me this behavior:
protected override Size ArrangeOverride(Size finalSize)
{
int currentIndex = 0;
for (int index = InternalChildren.Count - 1; index >= 0; index--)
{
Rect rect = new Rect(finalSize);
rect.Location = new Point()
{
X = finalSize.Width * PercentagePanel.GetLeft(InternalChildren[index]) - finalSize.Width / 2,
Y = finalSize.Height * PercentagePanel.GetTop(InternalChildren[index]) - finalSize.Height / 2
};
InternalChildren[index].Arrange(rect);
currentIndex++;
}
return finalSize;
}
It seems that to counteract this center-positioning behavior, I'll need to know each child's dimensions and compensate for the child's size while computing the Location of the Rect. However, I don't seem to have access to the child's size in order to compute the appropriate offset.
How can I take into account the size of a Panel's child while computing the child's layout in the ArrangeOverride method?
Found the answer while in the middle of writing up the question. All UIElements have a DesiredSize property which gives me exactly what I'm looking for. My ArrangeOverride method now looks like this (you'll have to scroll to the right to see the difference):
protected override Size ArrangeOverride(Size finalSize)
{
int currentIndex = 0;
for (int index = InternalChildren.Count - 1; index >= 0; index--)
{
Rect rect = new Rect(finalSize);
rect.Location = new Point()
{
X = finalSize.Width * PercentagePanel.GetLeft(InternalChildren[index]) - finalSize.Width / 2 + (InternalChildren[index].DesiredSize.Width / 2),
Y = finalSize.Height * PercentagePanel.GetTop(InternalChildren[index]) - finalSize.Height / 2 + (InternalChildren[index].DesiredSize.Height / 2)
};
InternalChildren[index].Arrange(rect);
currentIndex++;
}
return finalSize;
}
The elements now position themselves relative to their top-left corner instead of their center.
I have a userControl library, which consists of the main Panel and a PictureBox, I want to make a zoomable PictureBox tool, I zoom in and out using mouseWheel event of the main Panel, the problem that I can't figure out how do I zoom in by the mouse position on the image, so whenever I zoom in, the zoom goes the Top-Left corner of the panel, so how do I fix that?
private double ZOOMFACTOR = 1.15; // = 15% smaller or larger
private int MINMAX = 5;
void picPanel_MouseWheel(object sender, MouseEventArgs e)
{
if (e.Delta > 0)
{
ZoomIn();
}
else
{
ZoomOut();
}
}
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
}
}
private void picBox_MouseEnter(object sender, EventArgs e)
{
if (picBox.Focused) return;
picBox.Focus();
}
Update :
I have tried this, it looks like working, but not exactly as it should be!! Any ideas?
private void ZoomIn()
{
if ((picBox.Width < (MINMAX * this.Width)) &&
(picBox.Height < (MINMAX * this.Height)))
{
picBox.Width = Convert.ToInt32(picBox.Width * ZOOMFACTOR);
picBox.Height = Convert.ToInt32(picBox.Height * ZOOMFACTOR);
Point p = this.AutoScrollPosition;
int deltaX = e.X - p.X;
int deltaY = e.Y - p.Y;
this.AutoScrollPosition = new Point(deltaX, deltaY);
}
}
This is the example of Zoom image on mouse position....
tested verified.
protected override void OnMouseWheel(MouseEventArgs ea)
{
// flag = 1;
// Override OnMouseWheel event, for zooming in/out with the scroll wheel
if (picmap1.Image != null)
{
// If the mouse wheel is moved forward (Zoom in)
if (ea.Delta > 0)
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width < (15 * this.Width)) && (picmap1.Height < (15 * this.Height)))
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width * 1.25);
picmap1.Height = (int)(picmap1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 1.25 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 1.25 * (ea.X - picmap1.Left));
}
}
else
{
// Check if the pictureBox dimensions are in range (15 is the minimum and maximum zoom level)
if ((picmap1.Width > (imagemappan.Width)) && (picmap1.Height > (imagemappan.Height)))
{
// Change the size of the picturebox, divide it by the ZOOMFACTOR
picmap1.Width = (int)(picmap1.Width / 1.25);
picmap1.Height = (int)(picmap1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
picmap1.Top = (int)(ea.Y - 0.80 * (ea.Y - picmap1.Top));
picmap1.Left = (int)(ea.X - 0.80 * (ea.X - picmap1.Left));
}
}
}
}
The problem is that your control is acting like a viewport - the origin is top left, so every time you stretch the image you're doing it from that corner - the upshot is you wind up zooming into the top left corner, you need to offset the stretched image and centre the point the user zoomed in on.
image size: 200,200
user clicks 100,50 and zooms in x2
stretch the image
image size 400,400, and the place the user clicked is now effectively at 200,100
you need to slide the image 100 px left and 50 px up to correct for re-sizing the image
You'll need to override the paint event handler to draw the image offset:
RectangleF BmpRect = new RectangleF((float)(Offset.X), (float)(Offset.Y), (float)(ZoomedWidth), (float)(ZoomedHeight));
e.Graphics.DrawImage(Bmp, ViewPort , BmpRect, GraphicsUnit.Pixel);
Bmp is your image; ViewPort is a Rectangle defined by your pictureBox control
Here is a thread that might help.
I am designing a simple picture viewer with ability to do some basic image processing. At the moment I have the problem of keeping the PictureBox centered inside a TabPage all the time as well as keeping the picturebox width and size same as the picture its showing. So far I had no success.
I have the following code that I call in form constructor to position it in center. it works the first time to center the picturebox:
private void SetPictureBoxOriginalSizeAndLocation(bool makeImageNull = false, DockStyle dockStyle = DockStyle.None)
{
if (makeImageNull) picBoxView.Image = null;
picBoxView.Dock = dockStyle;
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
if (picBoxView.Image == null) return;
//Resize image according to width
picBoxView.Image = ImageMethods.ResizeImage(picBoxView.Image.Tag.ToString(), width, height, false);
picBoxView.Location = new Point(xPoint, yPoint);
picBoxView.Width = width;
picBoxView.Height = height;
}
But it does not resize the picturebox to its image (you can see the black part that is back color for the picturebox control):
The problem is getting worse as soon as I resize the form, the picturebox position will goes to top:
I call the code above in form's resize event as well, no idea why it works when application starts. Would be nice if someone can tell me what properties I should take care of to achieve a nicely centered picturebox which always is as big as its image.
It's pretty easy if you just set the Anchor style to none:
picBoxView = new PictureBox();
picBoxView.SizeMode = PictureBoxSizeMode.AutoSize;
picBoxView.Anchor = AnchorStyles.None;
tabImageView.Controls.Add(picBoxView);
CenterPictureBox(picBoxView, myImage);
Then just center the PictureBox initially whenever you change the image of the PictureBox:
private void CenterPictureBox(PictureBox picBox, Bitmap picImage) {
picBox.Image = picImage;
picBox.Location = new Point((picBox.Parent.ClientSize.Width / 2) - (picImage.Width / 2),
(picBox.Parent.ClientSize.Height / 2) - (picImage.Height / 2));
picBox.Refresh();
}
Having the Anchor = None will center the PictureBox control for you whenever the parent container gets resized because it "isn't" anchored to the default Left and Top locations.
Givien a Form with TabControl, which has Dock set to Fill, below will keep your PictureBox in the centre. It also sets PictureBox size to Bitmap size:
public partial class Form1 : Form
{
Bitmap b = new Bitmap(320, 200);
public Form1()
{
InitializeComponent();
CenterTheBox();
}
private void Form1_Resize(object sender, EventArgs e)
{
CenterTheBox();
}
void CenterTheBox()
{
pictureBox1.Size = b.Size;
var left = (tabPage1.ClientRectangle.Width - pictureBox1.ClientRectangle.Width) / 2;
var top = (tabPage1.ClientRectangle.Height - pictureBox1.ClientRectangle.Height) / 2;
pictureBox1.Location = new Point(tabPage1.ClientRectangle.Location.X + left, tabPage1.ClientRectangle.Location.Y + top);
}
}
I believe your problem lies here
var xPoint = tabImageView.Location.X + ((splitContainer.Panel2.Width / 2) / 2);
var yPoint = tabImageView.Location.Y;
var width = tabImageView.Width / 2;
var height = (tabImageView.Height / 2) - toolStripImageView.Height;
ypoint is alwways set to tabImageView Y, althought it should be set to
tabImageView.Location.Y + (tabImageView.Size.Height - picBoxView.Size.Height)/2
should be almost the same with xPoint
tabImageView.Location.X + (tabImageView.Size.Width - picBoxView.Size.Width)/2
and
width = picBoxView.Image.Width;
height = picBoxView.Image.Height;