Windows Forms' PictureBox with zooming and panning - c#

My application needs control displaying bitmaps (jpg) but also zooming and panning them (so if you press mouse button you can 'move' zoomed picture inside frame)
What I did was placing panel at the Form, then pictureBox inside panel (anchored Top,Left).
So if I need zoom it I'm just executing below code from Zoom buttons events:
private void ZommInOut(bool zoom) {
int zoomRatio = 10; // percent
int widthZoom = pBox.Width * zoomRatio /100;
int heightZoom = pBox.Height * zoomRatio /100;
if (zoom) {
widthZoom *= -1;
heightZoom *= -1;
}
pBox.Width += widthZoom;
pBox.Height += heightZoom;
}
Works petty well. Image is zoomed, panel displaying scrollbars - so I have working simple panning functionality.
What is missing to me is possibility to use mouse for panning - I'd like to drag picture in any direction to see other part of picture (as eg Acrobat Reader does).
I've looked for the way to use MouseMove event and change scrollbars programically but I couldn't manage that.
Any suggestion(s)?

You need to set the AutoScrollPosition property in the MouseMove event.
You'll need to track the location of the MouseDown event and update AutoScrollPosition using an offset.

I would suggest creating a control and drawing part of the image using Graphics.DrawImage - in this way you can control how the image is scaled (trilinear etc) and it will also use less memory. You can override OnMouseMove to get the mosue movements

Related

Scroll an image quickly and smoothly

When scrolling an image in a browser using the scrollbars - the image scrolls quickly and smoothly. On the other hand, making a tight loop with Graphics.DrawImage, incrementing the location's X-coordinate by 1 each iteration - returns a slow motion. (It's also somewhat jerky even after making the Control DoubleBuffered.)
How can I get fast rendering like a browser's?
EDIT
void DoNow()
{
Rectangle rec1 = new Rectangle(Point.Empty, panel1.BackgroundImage.Size);
Rectangle rec2 = new Rectangle(Point.Empty, panel1.BackgroundImage.Size);
using (Graphics g = Graphics.FromImage(panel1.BackgroundImage))
{
for (int i = 0; i < 100; i++)
{
rec2.Location = new Point(rec2.Location.X + 1, rec2.Location.Y);
g.DrawImage(image, rec1, rec2, GraphicsUnit.Pixel);
panel1.Refresh();
}
}
}
It seems from the comments like the answer to my question is that browsers use hardware acceleration which is unavailable to Winforms. (Please feel free to correct me if I'm wrong.)
The easiest solution is to use a Panel object with the scroll bars set to Auto. Place a picturebox as a child object with the size mode set to Auto. The picture box will expand to the size of the image that you assign to it. Since the picture box will expand to be larger than the panel, the panel's scroll bars will appear. When you scroll the image using the panel, it will be smooth.
If you still want to manually provide the drawing yourself, you are getting the "flicker" because the Invalidate method automatically performs background erasing which is not performed during the OnPaint event. You need to subclass the parent control that you are drawing on and override the OnPaintBackground event. Like below:
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// base.OnPaintBackground(pevent);
}
Also, remember to perform ALL drawing during the OnPaint event and use the e.Graphics object.

How to resize element by multiplier of X with Thumb in WPF?

I'm using Thumb to resize an element. On DragDelta I'm doing myElement.Width += e.HorizontalChange. What if I want to resize it by multiplier of 100?
I tried myElement.Width += 100 * e.HorizontalChange but it causes the element to "dance" when I drag it. I assume it happens because of the big change that causes wrong calculation of mouse position relative to the element.
How it can be done?
UPDATE
I recorded what I get. I resize the rectangle to the right and you can see how it flickering. The playback of the video is low but still you can see the flickering. It's much worse in reality.
It looks that Thumb computes the HorizontalChange relative to itself rather than to the screen. So, if the thumb itself does not move accurately with the mouse while dragging, then HorizontalChange takes unreliable values. (Another example of such unwanted behavior)
So, it seems that Thumb is of a little help when implementing custom move/resize behavior. A "manual" handling of mouse movement is needed. Though, we can still use Thumb's events as convenient points to handle the mouse.
FrameworkElement elementToResize;
double initialWidth;
Point initialMouse;
private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
{
initialWidth = elementToResize.ActualWidth;
initialMouse = Mouse.GetPosition(Window.GetWindow((DependencyObject)sender));
}
private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
{
var horzChange = Mouse.GetPosition(Window.GetWindow((DependencyObject)sender)).X - initialMouse.X;
elementToResize.Width = initialWidth + 100 * Math.Round(horzChange / 100);
}
We need to compute mouse position change in the screen coordinates. This is not straightforward in WPF. Assuming that window is not moving on the screen while dragging the thumb, getting mouse position relative to the window will suit, as in the above code. For better reliability, you can get actual mouse screen coordinates, but this requires conversion from pixels to WPF device independent units.

Restricting usercontrol from moving beyond the canvas xaml

I am developing Windows 8.1 Store Apps using XAML.
The scenario is I am having a Canvas in which I am placing more than one user controls. The user controls can be moved on click of a button. I should restrict the user controls from moving beyond the Canvas. What is the way to do it.? I am having Manipulation events within the usercontrol for movement.
I expect that your controls that you move are children from the canvas?
If so i think you can do a check to see if the bounds of you control moving on your canvas hits the border of you canvas. If this is happening then make it stop.
Basic gameprogramming stuff. I will look for a example
Here it is.
This part of code will get you the place of your control inside your container
public static class BoundsHelper
{
public static Rect GetBoundsInParentContainer(Control control)
{
Vector offset = VisualTreeHelper.GetOffset(control);
Rect rect = new Rect(offset.X, offset.Y, control.ActualHeight, control.ActualWidth);
return rect;
}
}
So if you have a button in the top left corner of the canvas and you ask that button the control where it is it will give you a rect where the X and Y are 0 and the Width and Height of your control. So if you move your control inside your canvas. Call this method and check if your control is still inside the Height and width of your canvas.
Here the code to move to the right but first check if you are allowed to move
private void MoveRightAndCheck(Rect boundsControl, Control container)
{
int stepSize = 10;
if (container.ActualWidth > (boundsControl.X + boundsControl.Width + stepSize))
{
//Your code to move your control inside the canvas
}
}

Windows 8 Store App how do you determine if a point is inside of your container?

I am trying to move shapes around on a canvas using the ManipulationDelta. It works but I am having an issue keeping them on my Canvas and by extension on the screen. I was trying to somehow determine the bounds of my canvas and whether its X, Y is still on the canvas. For instance, I was able to keep the Ellipse from being dragged off the top by setting Y to 0 when it is less than 0.
void Shape1_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
Ellipse shape = sender as Ellipse;
TranslateTransform tt = shape.RenderTransform as TranslateTransform;
tt.X += e.Delta.Translation.X;
tt.Y += e.Delta.Translation.Y;
if (tt.Y < 0)
tt.Y = 0;
}
The problem is that resolutions for screens differ and I can't figure out to determine the boundaries. Is this right approach to take or is there a better way to allow users to drag items but keep them inside a defined area?
Thanks for any help you can give me!
If the Canvas is the same size as the screen (aka Window), then you can inspect Window.Current.Bounds for the size of the Window.

WM6 .Net Form Effects C#

I would like to know if it's possible to create nice form effects on the compact framework.
My plan is that when a user selects an image on the main form this is opened in a new form, this currently works. What I now want to do is make the form that contains the fullsize picture to load off the edge (left or right) of the screen at around 4 pixels high then slide into view. Once the form is fully on the screen then expand the height until it hits the max for the screen.
On close i would like to reduce the height back down to the 4 pixels high and the slide off the edge again before disposing the form.
I've tried the code below when instantiating the form and the dp.Top property was always 0 regardless of dp.Width == 240
DisplayPicture dp = new DisplayPicture(ImageUrl);
dp.WindowState = FormWindowState.Normal;
dp.Left = dp.Width * -1;
dp.Top = (dp.Height / 2) - 2;
dp.Height = 4;
dp.ShowDialog();
Within the DisplayPicture form i also have the following to try and move the form but as it isn't setting the Top property this code doesn't matter yet.
void t_Tick(object sender, EventArgs e)
{
if (this.Left < 0)
this.Left += 5;
if (this.Left > -1)
{
this.Left = 0;
if (this.Height < pictureBox1.ClientRectangle.Height)
{
this.Height += 4;
this.Top -= 2;
}
if ((this.Left == 0) && (this.Top == 0))
t.Enabled = false;
}
}
Any help would be greatly appreciated!
TIA
OneSHOT
To do this, start with a PictureBox control that has your image loaded. Set the Height to 4, the Width to the width of your form, and (very important) set the SizeMode of the PictureBox to StretchImage.
Next, position the PictureBox off the screen by setting Top to 0 and Left to -Width. Put a Timer control on your form with an interval of 100 (or whatever), and have its event gradually move the PictureBox to the right until its Left property is 0. Once you reach that point, have the timer event gradually increase the Height until it reaches the height of the form.
You'll probably have to deal with flicker, but this should get you started.
Update: I just read your question a little closer, and realized that you actually want to move the form itself from offscreen to full screen. This is not possible if you want the entire form (including the title bar at the top) to animate in this way, but you can sort of do this by setting the form's FormBorderStyle (or I think it's just called BorderStyle in the Compact Framework) to None. With the BorderStyle set to None, changing the Height, Width, Top and Left properties will actually have a visible effect on the form (although the form will be borderless). These properties are otherwise ignored completely in Windows Mobile, which is probably why your code didn't appear to be doing anything.
Update 2: here is my answer to a similar WM question, which may help you make your animated window look like a real window.

Categories

Resources