How to centre picture box - c#

I am trying to use this plugin for C#, which works great. However I am having difficulty getting the image to center when zooming in. By default, whenever zooming, the picture box focuses on the top left corner. I would like it to focus on the centre.
Any idea? I have searched far and wide and cannot find an example which does this within this plugin.

Ok, this getting complicated so I will write complete answer:
The plugin is not open in the way You can hook on the zooming events. Actualy thing You can do (as You have open sources) is to "hack" inside - change the source code and adjust to Your needs.
Options:
1. Change inner implementation:
Get to file ScalablePictureBoxImp.cs , find method ScalePictureBoxToFit(), and add the codes after lines:
//some previous code
this.pictureBox.Left = left;
this.pictureBox.Top = top;
this.AutoScroll = true;
// add this line under:
this.AutoScrollPosition = new Point(this.Width / 2, this.Height / 2);
Now You can simply use the component as before, and it will do all the things just with resizing them on middle (it possibly need some more math to fit the screen properly).
2. Change outer implementation:
Author is ussing wrapper for the inner implementation, where he is hooking on the events and You are just using the wrapped object (it is like pressing the buttons on a mouse instead of connecting 1 wire with some other).
In file ScalablePictureBox.cs edit the Constructor and add following implementation:
public ScalablePictureBox()
{
//some code ...
this.pictureTracker.ScrollPictureEvent += new PictureTracker.ScrollPictureEventHandler(this.scalablePictureBoxImp.OnScrollPictureEvent);
this.pictureTracker.PictureTrackerClosed += new PictureTracker.PictureTrackerClosedHandler(this.pictureTracker_PictureTrackerClosed);
//Enter the line below to hook on event
this.scalablePictureBoxImp.ZoomRateChangedEvent += ScalablePictureBoxImp_ZoomRateChangedEvent;
}
Now You have hooked on the event of zooming, and You just need to adjust the scrolling:
private void ScalablePictureBoxImp_ZoomRateChangedEvent(int zoomRate, bool isFullPictureShown)
{
this.scalablePictureBoxImp.AutoScrollPosition = new Point(this.Width / 2, this.Height / 2);
}
Inner implementation of the ScalablePictureBox is invoking event ZoomRateChangedEvent -> Once this zoom change, You will get the size of a window and move the scroll buttons to the middle position. This can be adjusted in any way You requiring.
I believe, originaly autor wanted to write ScalablePictureBoxImp only and the wrapper was added just for test/simply case purpose. It is completely up to You if You write it all Yourself or adjust -> Correct way should be using the outer implementation then.

Related

Finding the Center of a Winforms Control hosted in a Wpf WindowsFormsHost

I am in the process of converting a Winforms project to WPF, and in doing so I have hit a very particular issue. Allow me to explain. Within my project I am using OpenTK, which means I need to host the GLControl within a WindowsFormHost. The GLControl makes use of a First Person Camera, and for it to work I need to calculate the exact center of the GLControl, which is where I place the mouse when the user is "looking around". Here is an example of my application in WPF:
The function that I had previously used in WinForms was:
public static Point FindCenterOfControl(Form parent, Control control)
{
Point location = parent.PointToScreen(control.Location);
return new Point(location.X + control.Width / 2, location.Y + control.Height / 2);
}
But naturally, this does not work in WPF. I have tried various parameters, but I keep getting null crashes, mainly from my GLControl, which apparently no longer uses the "Location" property when it's hosted within a WPF application. So, quite frankly, I am at a loss of how to accomplish this.
As another note, my code works as long as I guess where the center should be. I can move the camera around and it looks just fine. The issue arises when I resize the window, because then the center is nowhere near where it should be and my first person camera goes ballistic. I need a way to replicate my previous function of calculating the absolute center of a control within my WPF application.
Does anyone have any ideas how I can convert my old function to something that supports hosted controls within a WPF environment?
Nevermind, I found a solution, or at least...a dirty hack perhaps. The trick was to not try and find the center of my GLControl, but find the center of the WindowsFormsHost instead. Here is my code so that it may help someone else in the future.
public Point FindCenter()
{
System.Windows.Point relativePoint = _host.TransformToAncestor(_parent).Transform(new System.Windows.Point(0, 0));
return new Point((int) (relativePoint.X + _host.ActualWidth / 2), (int) (relativePoint.Y + _host.ActualHeight / 2));
}
Update:
As it turns out, the above method was actually wrong. It centered my cursor at a predefined location regardless of my window size or position. This location was wrong. I have crafted a new method, however, which gives the desired result.
public Point FindCenter()
{
System.Windows.Point relativePoint = _host.PointToScreen(new System.Windows.Point(0, 0));
return new Point((int)(relativePoint.X + (this.Width/2)), (int)(relativePoint.Y + (this.Height/2)));
}
That method DOES center the cursor on the control at the correct location. If you're going to use this within your own application I would suggest trying both to make certain which one suits your needs.

Resizing User Control - anchor controls to center of form

I have a user control which consists of two controls and four buttons, arranged on a Form: the two controls are on the sides and the buttons in the middle in a vertical row.
When using the control in an app, I place it on a form.
Now, when re-sizing the form horizontally, the two controls just move left or right w/o changing their size.
What I need is that the controls stay anchored to the middle of the form and grow to the sides (sorry about the lack of clarity, I prepared screenshots but the site wouldn't let me attach them).
Is there a way to accomplish this without overriding the Resize event?
Use a TableLayoutPanel as base for your user control.
You need 3 columns and one row. The middle column needs to have a fixed size, and the other 2 you set to 50%. Not to worry, .Net is smart enough to calculate the percent they actually take.
Inside the right and left columns you put your controls and set the Dock property of both to fill. In the middle column you put a panel and set it's Dock property to fill as wall, and In that panel you put the buttons in the middle.
Set your table layout panel Dock to fill as well, and when adding the user control to the form use Dock top, bottom or fill as well.
Erratum:
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.
It seems indeed that it cannot be done in the Designer, but here is the solution using overrides.
It works ok, except for some control flickering which I haven't been able to overcome.
public partial class SB : UserControl
{
//variables to remember sizes and locations
Size parentSize = new Size(0,0);
Point parentLocation = new Point (0,0);
......
// we care only for horizontal changes by dragging the left border;
// all others take care of themselves by Designer code
public void SB_Resize(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
// get former values
int fcsw = this.parentSize.Width;//former width
int fclx = this.parentLocation.X;//former location
Control control = (Control)sender;//this is our custom control
// get present values
int csw = control.Parent.Size.Width;//present width
int clx = control.Parent.Location.X;//present location
// both parent width and parent location have changed: it means we
// dragged the left border or one of the left corners
if (csw != fcsw && clx != fclx)
{
int delta = clx - fclx;
int lw = (int)this.tableLayoutPanel1.ColumnStyles[0].Width;
int nlw = lw - delta;
if (nlw > 0)
{
this.tableLayoutPanel1.ColumnStyles[0].Width -= delta;
}
}
this.parentSize = control.Parent.Size;//always update it
this.parentLocation = control.Parent.Location;
}
//contrary to documentation, the Resize event is not raised by moving
//the form, so we have to override the Move event too, to update the
//saved location
private void SB_Move(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
this.parentSize = this.Parent.Size;//always update it
this.parentLocation = this.Parent.Location;
}
}
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.

How to paint additional things on a drawn panel?

I'm reading a lot about C# drawing and reading MSDN tutorial on GDI+ using Graphics handlers.
I want to be able to paint a graph, which nodes I have in a list but I can't use auto-placing, I need the nodes to be in a specified place and have specific appearance and so on, changing over time, that's why I stopped looking for graph libraries.
It all works great when painted for the first time but when I want something painted after something else happens in the code (not after clicking the control), I can't do it. For example:
if (somethingHappens) {
// repaint the panel adding some things
}
All I got is either nothing new happens or my earlier painting disappears.
I found some examples on OnPaint overriding and drawings disappearing when minimalized. When I need to paint something additional when something happens in an application, do I have to override it or is it completely different?
I looked for another Q&A that would include the information you need. Frankly, it's a fundamental question, how to properly deal with drawing a Forms control. MSDN topics like Overriding the OnPaint Method and Custom Control Painting and Rendering provide some detail on the right way to do this. But I'm surprised I wasn't able to find any StackOverflow Q&A that at least addresses this basic question directly (there are many which address it tangentially of course).
Anyway…
This is the basic sequence for drawing in Forms code:
Generate some data to be drawn
Invalidate the control where the data will be drawn
Handle the Paint event by actually drawing that data
Repeat the above as necessary, i.e. any time the data changes (such as "something happens", as in your question) you move back to step #1, adding whatever new data you want to your existing collection of data, then invalidating the control, and finally responding to the Paint event in your event handler.
In the case of drawing a graph, this might look something like this:
private List<Point> _points = new List<Point>();
void AddPoint(Point point)
{
_points.Add(point);
panel1.Invalidate();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
if (_points.Count < 2)
{
return;
}
Point previousPoint = _points[0];
for (int i = 1; i < _points.Count; i++)
{
currentPoint = _points[i];
e.Graphics.DrawLine(Pens.Black, previousPoint, currentPoint);
previousPoint = currentPoint;
}
}
Note that the panel1_Paint() event is an event handler. Normally you would create this via the Designer by selecting the Panel object, switching to the "Events" list in the "Properties" window for the control, and double-clicking in the edit field for the Paint event.
That is of course the simplest example. You can add things like updating the data in a batched manner (i.e. don't invalidate the control until you've added a group of points), use different colors or line styles to draw, draw other elements of the graph like axes, ticks, legend, etc. The above is simply meant to illustrate the basic technique.
One final note: depending on how many points in your graph you need to draw, the above may or may not be fast enough. It should be fine up to thousands of points or so, but if you start getting to tens or hundreds of thousands or more, you'll probably find that it's useful to cache the drawing into a bitmap and draw just the bitmap. But that's a whole separate question. First, you need to make sure you understand the Forms drawing model and are using it correctly.

Windows 8 App: "Snapping" Drag and Drop for Card Game

After trying to use the following snippet to move cards (Images right now) around I was not satisfied with the result.
Card.ManipulationDelta += (o, args) => {
var dragableItem = o as Image;
if (dragableItem == null) return;
var translateTransform = dragableItem.RenderTransform as TranslateTransform;
if (translateTransform == null) return;
translateTransform.X += args.Delta.Translation.X;
translateTransform.Y += args.Delta.Translation.Y;
};
Card.RenderTransform = new TranslateTransform();
The control had a funny behavior to be accelerated and would move / slide a bit after "dropping" it. Although cool I do not want this behavior and therefore changed my mind: what I am looking for is a solution to define specific areas for one active card, a bench for a few more cards and stacks for the deck, such that one can freely drag one card but it can only be dropped if it is above these certain areas otherwise it will get back to the area designated for the hand cards.
What could I try to implement this desired behavior?
I think you and I are in the same boat, I'm working on a Metro card game application and I'm not relly happy with what I've found as far as Drag and Drop goes. My inital approach was going to be to have a grid \ stackpanel or other underlying framework that the user could drag a card image ( acually a custom control ) over and then the image would snap to that framework when the user let go. I have not yet found a suitable way to obtain this behavior however because it seems like drag-drop of controls from one parent to another is not supported in Metro.
As far as your question goes, the sliding effect that you are refering to is most likely intertia, You can disable this by not setting the TranslateInertia mode, for example
Ellipse el = new Ellipse();
el.Fill = new SolidColorBrush(Windows.UI.Colors.Magenta);
el.ManipulationMode = (ManipulationModes.All ^ MainipulationModes.TranslateInteria);
//more code to add your ManipulationDelta handler etc.
You can also gain some control over the Interia by adding a handler for MainipulationInertiaStarting, though just setting e.Handled = true in the handler does not disable interia entirely for me as others have suggested.
I'd love to hear back from you and see what approach you've come up with for snapping cards, at this point I'm considering just using a large Canvas object and writing my own customer handlers for mouse moved to get card objects to be draggable and then snap into a row or other location on the playing board.
Cheers,
James

How do I move a control to the center of a screen at runtime in C#?

So, I have a control (it's a label) of varying size. I want to recenter it in the form each time it changes (horizontally centered, not vertically). How would I do that programmatically?
YourLabel.Left = (YourForm.Width / 2) - (YourLabel.Width / 2);
If you want this to be adjusted every time the form dimensions change, just utilize the Form.Resize event.
No code required: AutoSize = False, TextAlign = TopCenter. Make it as big as you'll allow it to get. Anchor to the right is optional.
Easiest Way to center any component. might be helpful for someone. Right click Project -> Add -> Class
Update that class with below code.
public static class MyClass
{
public static void center(this Control component)
{
float compWidth = component.Width;
float parentWidth = component.Parent.Width;
float middled = (parentWidth / 2) - (compWidth / 2);
component.Left = Convert.ToInt32(middled);
}
}
and then you can middle any component. you can use on any component like this
MyLabel.center();
MyPanel.center();
Ok, so I stand corrected - thanks guys.
Here is a workaround to do it without code using RAD (design time). Note I would go with #Shark's answer as I dont think this will achieve the result your after but here it is:
Drop a button in the form
Set its Text as the text in the label
Size button to fit the text
In the buttons properties "FlatStlye =Flat"
In the buttons properties, expand Flat Appearance and set Border Size= 0
Now set the Anchor to Left and Right

Categories