I have a GroupBox that has multiple controls, specifically 4 PictureBoxes.
These are pieces of an image puzzle. Please note that the number of images can change. I want to allow a drag and drop effect to each of them. To mark the source and the destination I did the following:
foreach (var box in boxes)
{
box.DragEnter += new DragEventHandler(box_DragEnter);
box.MouseDown += new MouseEventHandler(box_MouseDown);
box.DragDrop += new DragEventHandler(box_DragDrop);
box.AllowDrop = true;
}
When a box is clicked it will be marked to a global variable (that will be the source) and when a box raises the DragDrop event that will be the destination.
As I previously mentioned, since the number of the boxes can be changed I do not want to add separate methods as handlers for each event.
My issue here is that I do not know how to "detect" which box has raised what event.
Example:
*If I press on the left-top image I want to know that I pressed on it to mark the source (let's say global picSource = 1) and if I drop on right-bottom image I want to know that as a destination (picDestination = 4). All that must occur in the handlers. *
Tests:
Adding a parent panel (same size as the GroupBox) with a Click event & doing a comparison between a Point issued with the X&Y provided by the event handler and a rectangle designated with the X,Y, width, height of the panel.
Adding handlers to the GroupBox (does not work either)
Notes:
e.X and e.Y refer to the position of the mouse
void box_MouseDown(object sender, MouseEventArgs e)
The purpose of this is to switch boxes (source <--> destination)
You can just detect puzzle element by mouse's coordinates in the controls container's MouseDown and MouseUp event as follows:
public partial class PuzzleForm : Form
{
private readonly Image[,] Images;
private readonly int Nx;
private readonly int Ny;
private int sourceIndexX;
private int sourceIndexY;
private int destinationIndexX;
private int destinationIndexY;
private PuzzleForm()
{
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
public PuzzleForm(Image[,] images)
: this()
{
Images = images;
Nx = Images.GetLength(0);
Ny = Images.GetLength(1);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Graphics g = e.Graphics)
{
for (int j = 0; j < Ny; j++)
for (int i = 0; i < Nx; i++)
{
Rectangle rect = new Rectangle(ClientSize.Width * i / Nx, ClientSize.Height * j / Ny, ClientSize.Width / Nx - 1, ClientSize.Height / Ny - 1);
g.DrawImage(Images[i, j], rect);
}
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (e.Button != MouseButtons.Left)
return;
sourceIndexX = e.X * Nx / ClientSize.Width;
sourceIndexY = e.Y * Ny / ClientSize.Height;
Cursor = Cursors.Hand;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
if (e.Button != MouseButtons.Left)
return;
destinationIndexX = e.X * Nx / ClientSize.Width;
destinationIndexY = e.Y * Ny / ClientSize.Height;
Cursor = Cursors.Default;
if (sourceIndexX != destinationIndexX || sourceIndexY != destinationIndexY)
{
swapImages();
MessageBox.Show(String.Format("From [{0}, {1}] to [{2}, {3}]", sourceIndexX, sourceIndexY, destinationIndexX, destinationIndexY));
}
}
private void swapImages()
{
Image tmp = Images[sourceIndexX, sourceIndexY];
Images[sourceIndexX, sourceIndexY] = Images[destinationIndexX, destinationIndexY];
Images[destinationIndexX, destinationIndexY] = tmp;
Invalidate();
}
}
Usage:
Image[,] images = new Image[2, 2];
// Fill array with images:
images[0, 0] = Bitmap.FromFile(#"...");
images[0, 1] = Bitmap.FromFile(#"...");
images[1, 0] = Bitmap.FromFile(#"...");
images[1, 1] = Bitmap.FromFile(#"...");
PuzzleForm puzzleForm = new PuzzleForm(images);
// Show form or whatever you want.
...
Related
I just wanted to know if in windows forms I can create a red line around the border of a combobox when its changed? Like just a flash of red and then gone again just to show that it was changed. Catch the user's eye or something. I will provide screens to represent what i would like.
If it is possible, please tell me where I can look it up to gain some information on it.
No border
Border flash on change
Border gone again after a second or two
Anytime the combobox changes, I want to flash a border to indicate it has changed.
The main idea is using a timer and drawing a border for some times. You can draw the border using different solutions. For example you can (1) draw the border on ComboBox or (2) you can draw border on Parent of ComboBox.
In the answer which I posed, I created a MyComboBox and added a FlashHotBorder method which can be called to flash border. Also I added a HotBorderColor property which can be used to set border color.
Flashing Border of ComboBox
To draw a border for ComboBox you can handle WM_Paint message of ComboBox and draw a border for control. Then to flash the border, you need to use a timer and turn on and turn off border for some times:
MyComboBox Code
I've created a FlashHotBorder method which you can call in SelectedIndexChanged event. Also if always you want to flash border when selected index changes, you can call it in OnSelectedIndexChanged. I prefer to call it in event handler. Here is the implementation:
using System.Drawing;
using System.Windows.Forms;
public class MyComboBox : ComboBox
{
int flash = 0;
private const int WM_PAINT = 0xF;
private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;
public Color HotBorderColor { get; set; }
private bool DrawBorder { get; set; }
Timer timer;
public MyComboBox()
{
this.HotBorderColor = Color.Red;
timer = new Timer() { Interval = 100 };
timer.Tick += new System.EventHandler(timer_Tick);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT && this.DrawBorder)
using (var g = Graphics.FromHwnd(this.Handle))
using (var p = new Pen(this.HotBorderColor))
g.DrawRectangle(p, 0, 0, this.Width - 1, this.Height - 1);
}
public void FlashHotBorder()
{
flash = 0;
timer.Start();
}
void timer_Tick(object sender, System.EventArgs e)
{
if (flash < 10)
{
flash++;
this.DrawBorder = !this.DrawBorder;
this.Invalidate();
}
else
{
timer.Stop();
flash = 0;
DrawBorder = false;
}
}
protected override void Dispose(bool disposing)
{
if (disposing) { timer.Dispose(); }
base.Dispose(disposing);
}
}
Then it's enough to use this event handler for SelectedIndexChanged event of eeach combo which you want to flash:
private void myComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var combo = sender as FlatCombo;
if (combo != null)
combo.FlashHotBorder();
}
You can create an outline/draw a border outside a comboBox or any other control using the DrawRectangle method.
The border will be drawn outside the comboBox if the SelectedIndex range condition satisfies else it'll revert to it's original state with no outline.
bool changed = false;
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (changed)
{
Pen p = new Pen(Color.Red);
Graphics g = e.Graphics;
int diff = 1;
g.DrawRectangle(p, new Rectangle(comboBox1.Location.X - diff, comboBox1.Location.Y - diff, comboBox1.Width + diff, comboBox1.Height + diff));
}
}
And, I am calling the Form1_Paint event on SelectedIndexChanged event of the comboBox.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex >= 1 && comboBox1.SelectedIndex <= 9)
{
changed = true;
this.Refresh();
}
else
{
changed = false;
this.Refresh();
}
}
Outline Without Outline
So I came up with this. It's the shortest and easiest way to do it I think. If you have any recommendation, feel free to post them or comment it. thanx for all the help :).
public partial class Form1 : Form
{
private int tick = 0;
public Form1()
{
InitializeComponent();
}
bool changed = false;
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (changed == true)
{
changed = false;
this.Refresh();
}
else
{
if(tick<3)
{
timer1.Enabled = true;
timer1.Start();
}
changed = true;
this.Refresh();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (changed)
{
Graphics g1 = e.Graphics;
int diff = 1;
Rectangle rect2 = new Rectangle(comboBox1.Location.X - diff, comboBox1.Location.Y - diff, comboBox1.Width + diff, comboBox1.Height + diff);
using (LinearGradientBrush br = new LinearGradientBrush(rect2,Color.Red,Color.Blue,LinearGradientMode.Horizontal))
{
ColorBlend color_blend = new ColorBlend();
color_blend.Colors = new Color[] { Color.Red, Color.Orange, Color.Yellow, Color.Lime, Color.Blue, Color.Indigo, Color.DarkViolet};
color_blend.Positions = new float[] { 0 / 6f, 1 / 6f, 2 / 6f, 3 / 6f, 4 / 6f, 5 / 6f, 6 / 6f };
br.InterpolationColors = color_blend;
Pen p = new Pen(br, 10);
e.Graphics.DrawRectangle(p, rect2);
}
}
else
{
Pen p = new Pen(Color.Transparent);
Graphics g = e.Graphics;
int diff = 1;
g.DrawRectangle(p, new Rectangle(comboBox1.Location.X - diff, comboBox1.Location.Y - diff, comboBox1.Width + diff, comboBox1.Height + diff));
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if(tick<3)
{
comboBox1_SelectedIndexChanged(null, null);
tick++;
}
else
{
timer1.Stop();
tick = 0;
}
}
}
My program can draw lines using canvas.Drawline(). How to click line and change this color (select line)?
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas=panel1.CreateGraphics();
}
Coordinate line stored in coordFirs & coodLast.
Here is a suitable Line class:
class Line
{
public Color LineColor { get; set; }
public float Linewidth { get; set; }
public bool Selected { get; set; }
public Point Start { get; set; }
public Point End { get; set; }
public Line(Color c, float w, Point s, Point e)
{ LineColor = c; Linewidth = w; Start = s; End = e; }
public void Draw(Graphics G)
{ using (Pen pen = new Pen(LineColor, Linewidth)) G.DrawLine(pen, Start, End); }
public bool HitTest(Point Pt)
{
// test if we fall outside of the bounding box:
if ((Pt.X < Start.X && Pt.X < End.X) || (Pt.X > Start.X && Pt.X > End.X) ||
(Pt.Y < Start.Y && Pt.Y < End.Y) || (Pt.Y > Start.Y && Pt.Y > End.Y))
return false;
// now we calculate the distance:
float dy = End.Y - Start.Y;
float dx = End.X - Start.X;
float Z = dy * Pt.X - dx * Pt.Y + Start.Y * End.X - Start.X * End.Y;
float N = dy * dy + dx * dx;
float dist = (float)( Math.Abs(Z) / Math.Sqrt(N));
// done:
return dist < Linewidth / 2f;
}
}
Define a List for the lines, probably at class level:
List<Line> lines = new List<Line>();
Here is how you can initialize it with a few lines:
for (int i = 0; i < 20; i++) lines.Add(new Line(Color.Black, 4f,
new Point(R.Next(panel1.Width), R.Next(panel1.Height)),
new Point(R.Next(panel1.Width), R.Next(panel1.Height))));
Here is the result of clicking on a crossing:
Whenever you add, change or remove a line you need to make the Panel reflect the news by triggering the Paint event:
panel1.Invalidate();
Here is the Paint event of the Panel:
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
foreach (Line L in lines) L.Draw(e.Graphics);
}
In the MouseClick event you do the test:
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
foreach(Line L in lines)
L.LineColor = L.HitTest(e.Location) ? Color.Red : Color.Black;
panel1.Invalidate();
}
To avoid flicker don't use the basic Panel class as it isn't doublebuffered. Instead use either a PictureBox or a Label (with AutoSize=false) or a doublebuffered Panel subclass:
class DrawPanel : Panel
{ public DrawPanel () { DoubleBuffered = true; } }
Notes:
There is no such thing as a 'Line' in WinForms, only pixels of various colors. So to select a line you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking.
The above example shows how to do it in math.
Instead one could test each line by drawing it onto a bitmap and test the pixel the mouse has clicked. But drawing those bitmaps would have to do math behind the scenes as well and also allocate space for the bitmaps, so the math will be more efficient..
Yes the Line class looks a little long for such a simple thing a s a line but look how short all the event codes now are! That's because the responsiblities are where they belong!
Also note the the first rule of doing any drawing in WinForms is: Never cache or store a Grahics object. In fact you shouldn't ever use CreateGraphics in the first place, as the Graphics object will never stay in scope and the graphics it produces will not persist (i.e. survive a Minimize-maximize sequence)..
Also note how I pass out the e.Graphics object of the Paint event's parameters to the Line instances so they can draw themselves with a current Graphics object!
To select thinner lines it may help to modify the distance check a little..
The Math was taken directly form Wikipedia.
You can change the color of everything on click. By using click event of particular object.
I give you an example for button. If you click on button then panal’s color will be change. You can modify the code as per your requirement.
private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;
private void Form1_Load(object sender, EventArgs e)
{
canvas = panel1.CreateGraphics();
}
private void panel1_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.Blue;
}
private void nonSelectableButton3_Click(object sender, EventArgs e)
{
panel1.BackColor = Color.BurlyWood;
}
I'm currently working on a battleships game,but i've ran into an issue.So far i've managed to draw the grid.the purpose of the draw method is to draw an image(don't know how to/if i can color a certain surface) inside a square in the grid,when i left-click.
the problem here is that,even if the image's size is 25x25(the size of a square) it occupies like half the screen,and that's when it works.50% of the times when i run nothing happens,and the other 50% it draws a huge image in the middle of the screen,regardless of where the cursor is located or if i left-click.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Paint += new PaintEventHandler(form1_paint);
}
private void form1_paint(object sender, PaintEventArgs e)
{
draw(e);
}
int x,y;
private void draw(PaintEventArgs e)
{
if (MouseButtons.Left != 0)
{
x = Cursor.Position.X;
y = Cursor.Position.Y;
Image poza = Image.FromFile("D://C//12E//c#//yellow4.jpg");
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(poza, coltz);
}
}
}
Does anyone know how i can solve this?or if someone has a better idea for a battleships grid, I am open to suggestions.Thanks!
Fisrt of all, this line of code: Cursor.Position.X gives you the global position of the cursor on the screen, not in the game window. I suggest you to handle MouseMove event to get the position relative to the content of your app.
The second thing is that you are loading the image from file on your computer. I think it's better to add the image to your app's resources, so you can load it easier just calling it by name, e.g.: AppName.Properties.Resources.ImageName - it returns Image object you can immediately use.
One more thing. This if (MouseButtons.Left != 0) won't check whether left mouse button is pressed or not. You have to check if MouseButtons property equals System.Windows.Forms.MouseButtons.Left.
Here's the full code that works for me:
public partial class Form1 : Form
{
private int x, y;
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
MouseDown += Form1_MouseMove;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Draw(e);
}
private void Draw(PaintEventArgs e)
{
if (MouseButtons == System.Windows.Forms.MouseButtons.Left)
{
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(AppName.Properties.Resources.ImageName, coltz);
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
Invalidate();
}
}
And here's the result:
I also subscribed MouseDown event to show yellow rectangle when user clicks the button without moving the cursor.
the thing is,before adding your code,the draw worked fine.now,every time i move the mouse,the grid gets redrawn,and it looks like it's constantly refreshing,bleeping somehow.not sure how to phrase this
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
MouseDown += Form1_MouseMove;
}
private int x,y;
void Form1_Paint(object sender, PaintEventArgs e)
{
Draw(e);
}
private void Draw(PaintEventArgs e)
{
if (MouseButtons == System.Windows.Forms.MouseButtons.Left)
{
if (x < 301 && x > 24 && y < 301 && y > 24)
{
PointF coltz = new PointF(x / 25 * 25, y / 25 * 25);
e.Graphics.DrawImage(battleships.Properties.Resources.yellow4, coltz);
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
x = e.X;
y = e.Y;
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g;
g = e.Graphics;
Pen pen = new Pen(Color.Black);
pen.Width = 1;
for (int i = 25; i <= 300; i = i + 25)
{
g.DrawLine(pen, i, 25, i, 300);
g.DrawLine(pen, 25, i, 300, i);
}
}
}
i have taken one Flow layout panel and placed multiple picture box inside in it. now i want when i will place my mouse at the right or left most edge of the Flow layout panel then rest of picture will scroll out. just think about windows 8 start screen where many tiles appear in screen and when we place mouse at right most edge on the screen then rest of the tiles scroll out. i want to simulate same thing in windows form with Flow layout panel.
i want my Flow layout panel will not show scroll bar but images will scroll out when i will place mouse right or left most part on the panel. here is my screen shot
some one told me to do it this way...here is bit code
Set AutoScrollPosition property in MouseMove event of Panel.
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
panel1.AutoScrollPosition = new Point(e.X, e.Y);
}
but this trick was not good. AutoScrollPosition works when scroll bar is visible but in my case i do not want to show scroll bar with Flow layout panel. i want smooth scrolling images from left to right or right to left. anyone can help me to achieve what i am trying to do....if possible guide me with respect of coding. thanks
EDIT
Here i am giving my full code after modification following #Taw suggestion but it is not working fine....rather flickering found when picture move. anyway here is the full code.
namespace ScrollTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
flowLayoutPanel1.MouseMove += MouseScroll;
foreach (Control x in this.Controls)
{
if (x is PictureBox)
{
((PictureBox)x).MouseMove += MouseScroll;
}
}
}
int near = 33;
private void MouseScroll(object sender, MouseEventArgs e)
{
Point mouse = flowLayoutPanel1.PointToClient(MousePosition);
Rectangle C = flowLayoutPanel1.ClientRectangle;
int dLeft = mouse.X - C.Left;
int dTop = mouse.Y - C.Top;
int dRight = C.Right - mouse.X;
int dBottom = C.Bottom - mouse.Y;
int dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
int dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;
if (dX != 0 | dY != 0) scrollFLP(dX, dY);
}
void scrollFLP(int deltaX, int deltaY)
{
flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
System.Threading.Thread.Sleep(11);
}
int getSpeedFromDistance(int delta)
{
int sig = Math.Sign(delta);
int d = Math.Abs(delta);
if (d > near / 2) return sig;
else if (d > near / 3) return near / 10 * sig;
else if (d > near / 4) return near / 8 * sig;
else if (d > near / 5) return near / 5 * sig;
else return near * sig;
}
}
}
basically i am trying achieve something like suppose i have flow layout panel and which has many picture box inside it with many images as the screen shot but scroll bar should not show rather scroll will happen automatically when i will place my mouse at the top or bottom of the flow layout panel like carousel.
see this picture of your application
when place my mouse at the right end then it scroll and form background shown which i do not want. i want picture box will scroll & scroll upto last one not more than that.
any idea how to do it. thanks
2nd Edit
this code i added as per your suggestion
public Form1()
{
InitializeComponent();
for (int i = 0; i < 666; i++)
{
PictureBox pan = new PictureBox();
//pan.MouseMove += MouseScroll;
//pan.MouseLeave += outSideCheck;
pan.Size = new Size(75, 75);
pan.BackColor = Color.FromArgb(255, (i * 2) & 255, (i * 7) & 255, (i * 4) & 255);
flowLayoutPanel1.Controls.Add(pan);
}
//flowLayoutPanel1.MouseMove += MouseScroll;
//this.flowLayoutPanel1.MouseLeave += outSideCheck;
mouseScroller MSC = new mouseScroller();
MSC.registerControl(flowLayoutPanel1); // FLP = your FlowLayouPanel
MSC.timerSpeed = 5; // optional
MSC.nearness = 100; // optional
flowLayoutPanel1.AutoScroll = false;
}
now the apps doing wired behavior after adding new code. if i am making any mistake then guide me please. thanks
This is a two-part problem:
How to grab the event
How to scroll a FlowLayoutPanel with its scrollbars invisible.
Second first. It is not an easy task from what I found, unless you use a simple and rather common trick: Don't actually scroll it! Instead place it into a Panel and then control its position inside that Panel.
To do this you add a Panel panel1 to your Form, Dock or Anchor it as you need to and set its Autoscroll = false (!) (Which is not the way it is usually done, when you want to make, say a PictureBox scrollable. But we don't want the Panel to show it Scrollbars either.)
Set the FLP to its desired size and place it into the Panel, it obviously also has Autoscroll = false, and we're ready to tackle the other problem of setting up the event..:
First you add the MouseScroll event below to your code and then you hook every control up to it you want to work with the mouse move, namely the FLP:
flowLayoutPanel1.MouseMove += MouseScroll;
..and also each of your PictureBoxes, maybe like this
// your creation loop..
PictureBox pbox = new PictureBox();
pbox.MouseMove += MouseScroll; // <<--- hook into to the mousemove
pan.MouseLeave += outSideCheck; // <<--- hook into to the mouseleave
// .. do your stuff.. here I put some paint on to test..
pbox.BackColor = Color.FromArgb(255, 111, (i * 3) & 255, (i * 4) & 255);
flowLayoutPanel1.Controls.Add(pbox);
or however you create them..
Edit 2 I have changed my original code once more. It now includes an outside check, a check for moving towards the closest edge and a workaround for tha mousemove bug. It uses a Timer set to maybe 30ms. The speed mapping is in a function of its own.
flowLayoutPanel1.MouseMove += MouseScroll;
this.flowLayoutPanel1.MouseLeave += outSideCheck;
flowLayoutPanel1.AutoScroll = false;
int near = 33;
Point lastLocation = Point.Empty;
int dX = 0;
int dY = 0;
private void MouseScroll(object sender, MouseEventArgs e)
{
Point mouse = panel1.PointToClient(MousePosition);
Rectangle C = panel1.ClientRectangle;
// mouseMove has a bug, we need to workaround
if (mouse == lastLocation) return;
if (lastLocation == Point.Empty) { lastLocation = mouse; return; }
// distance from each edge
int dLeft = mouse.X - C.Left;
int dTop = mouse.Y - C.Top;
int dRight = C.Right - mouse.X;
int dBottom = C.Bottom - mouse.Y;
// relevant distances with sign
dX = dLeft < near ? dLeft : dRight < near ? -dRight : 0;
dY = dTop < near ? dTop : dBottom < near ? -dBottom : 0;
// we need the closest edge to check if we are moving in or out
List<int> edges = new List<int>() { dLeft, dTop, dRight, dBottom };
var closest = edges.IndexOf(edges.Min());
// if we are moving
if (dX != 0 | dY != 0)
// if moving out: go else stop going
if (!movingIn(mouse, closest)) timer1.Start(); else timer1.Stop();
// remember position
lastLocation = mouse;
}
bool movingIn(Point current, int Edge)
{
switch (Edge)
{
case 0: return current.X > lastLocation.X;
case 1: return current.Y > lastLocation.Y;
case 2: return current.X < lastLocation.X;
case 3: return current.Y < lastLocation.Y;
}
return false;
}
void scrollFLP(int deltaX, int deltaY)
{
flowLayoutPanel1.Left += getSpeedFromDistance(deltaX);
flowLayoutPanel1.Top += getSpeedFromDistance(deltaY);
Size C = panel1.ClientSize;
if (flowLayoutPanel1.Left > 1) { flowLayoutPanel1.Left = 0; timer1.Stop(); }
if (flowLayoutPanel1.Right < C.Width)
{ flowLayoutPanel1.Left = C.Width - flowLayoutPanel1.Width; timer1.Stop(); }
if (flowLayoutPanel1.Top > 1) { flowLayoutPanel1.Top = 0; timer1.Stop(); }
if (flowLayoutPanel1.Bottom < C.Height)
{ flowLayoutPanel1.Top = C.Height - flowLayoutPanel1.Height; timer1.Stop(); }
}
int getSpeedFromDistance(int delta)
{
int sig = Math.Sign(delta);
int d = Math.Abs(delta);
if (d > near / 2) return sig;
else if (d > near / 3) return 2 * sig;
else if (d > near / 4) return 4 * sig;
else if (d > near / 5) return 6 * sig;
else return 10 * sig;
}
private void timer1_Tick(object sender, EventArgs e)
{
if (insidePanel()) scrollFLP(dX, dY); else timer1.Stop();
}
bool insidePanel()
{
return panel1.ClientRectangle.Contains(panel1.PointToClient(MousePosition));
}
private void outSideCheck(object sender, EventArgs e)
{
if (!insidePanel()) {timer1.Stop(); lastLocation = Point.Empty;}
}
Of course you'll want to play with the various 'magic' numbers :-)
Stop code and direction check are now included.
As usual, key is to know precisely what you want.. I hope this gets you started on ways to achieve it!
It's been a while since this question was asked. I just encountered the problem. My scenario was a little different, but I still think it's a solution to the same problem (at worst a timer control can be used because autoscroll is not turned on).
Here is my scenario: I have one panel control (normal panel). I have an PictureBox in it that I made with zoom. I'm making rectangular selections on top of this image, and when the selections spilled out of the panel, my panel was supposed to slide in the direction I was selecting. (in my scenario, mouse is pressed)(also in my scenario, autoscroll is on). This is how I solved it without writing so much code:
I added two private variable for scroll position (Valid for the whole class scope).
private int xPos;
private int yPos;
private int speed = 5;
and I assigned them the current scroll positions when the form is loaded.
private void Form1_Load(object sender, EventArgs e)
{
//when I change the scrollbar manually or change with zoom I still
//need to add these lines to the related event
xPos = panel1.HorizontalScroll.Value;
yPos = panel1.VerticalScroll.Value;
}
and inside my picturebox's mousemove event
private void picturebox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
Point mouse = panel1.PointToClient(MousePosition);
if (!panel1.ClientRectangle.Contains( mouse ))
{
Rectangle CRect = panel1.ClientRectangle;
int dLeft = mouse.X - CRect.Left;
int dRight = CRect.Right - mouse.X;
int dTop = mouse.Y - CRect.Top;
int dBottom = CRect.Bottom - mouse.Y;
if(dLeft < 0 && panel1.HorizontalScroll.Value > 0)
{
xPos = -panel1.AutoScrollPosition.X - speed;
}
if (dRight < 0 && panel1.HorizontalScroll.Value < panel1.HorizontalScroll.Maximum)
{
xPos = -panel1.AutoScrollPosition.X + speed;
}
if (dTop < 0 && panel1.VerticalScroll.Value > 0)
{
yPos = -panel1.AutoScrollPosition.Y - speed;
}
if (dBottom < 0 && panel1.VerticalScroll.Value < panel1.VerticalScroll.Maximum)
{
yPos = -panel1.AutoScrollPosition.Y + speed;
}
panel1.AutoScrollPosition = new Point(xPos, yPos);
}
}
}
How can I remove the "-----Series1" text (top right, near button1)
How can I make the blue stepline thinker, and remove the background lines?
How can I draw color to the background, such as the area from y=0 to y=1 colored as grey.
How can I add mousewheel event, so that i can use Ctrl+mousewheel to zoom in and out the chart?
Thank you!
Below I have provided some example code to answer your questions. My answers to 3 and 4 are basic examples, and you will have to calculate the correct values for painting and zooming in your application.
private void button1_Click(object sender, EventArgs e)
{
setupChart();
}
private void setupChart()
{
// 1.
foreach (System.Windows.Forms.DataVisualization.Charting.Legend legend in chart1.Legends)
legend.Enabled = false;
// 2.
chart1.Series[0].BorderWidth = 5;
chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
// 3.
chart1.Paint += new PaintEventHandler(chart1_Paint);
// 4.
chart1.MouseWheel += new MouseEventHandler(chart1_MouseWheel);
//chart must have focus for MouseWheel event to fire
chart1.Focus();
}
private void chart1_Paint(object sender, PaintEventArgs e)
{
//basic example of painting
//determine size of area to paint
Size areaSize = new Size(50, 50);
//determine location to paint
Point point = new Point(100, 450);
e.Graphics.FillRectangle(new SolidBrush(Color.Gray),
point.X, point.Y, areaSize.Width, areaSize.Height);
}
private void chart1_MouseWheel(object sender, MouseEventArgs e)
{
//basic example of zooming in and out
if (e.Delta > 0)
{
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(
chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum / 2,
chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum / 2);
chart1.ChartAreas[0].AxisY.ScaleView.Zoom(
chart1.ChartAreas[0].AxisY.ScaleView.ViewMinimum / 2,
chart1.ChartAreas[0].AxisY.ScaleView.ViewMaximum / 2);
}
else
{
if (chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum <
chart1.ChartAreas[0].AxisX.Maximum ||
chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum >
chart1.ChartAreas[0].AxisX.Minimum)
{
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(
chart1.ChartAreas[0].AxisX.ScaleView.ViewMinimum * 2,
chart1.ChartAreas[0].AxisX.ScaleView.ViewMaximum * 2);
chart1.ChartAreas[0].AxisY.ScaleView.Zoom(
chart1.ChartAreas[0].AxisY.ScaleView.ViewMinimum * 2,
chart1.ChartAreas[0].AxisY.ScaleView.ViewMaximum * 2);
}
else
{
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
}
}
}