PictureBox select switchnig with arrow keys - c#

I need help I have a set PictureBox (40), and I need to select these pictureboxes with arrows. I mean when I'm on the first picture and press right arrow key (border changing - selected), the border of the first should switch to none and next one switch to border FixedSingle.
One idea is:
if (keyData == Keys.Right) {
if (PictureBox1.BorderStyle == BorderStyle.FixedSingle) {
PictureBox1.BorderStyle = BorderStyle.None;
PictureBox2.BorderStyle = BorderStyle.FixedSingle;
} else if (PictureBox2.BorderStyle == BorderStyle.FixedSingle) {
pictu.....
}
}
but this method takes too much time so I'm looking for a simpler method.
Can somebody help me find a simpler way to do this?
EDIT new code:
namespace testPics
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_KeyDown_1(object sender, KeyEventArgs e)
{
changePictureBox(e.KeyData);
}
List<PictureBox> myPictureBoxes;
int index;
public void iniPictureBoxes()
{
myPictureBoxes = new List<PictureBox>();
myPictureBoxes.Add(pictureBox1);
myPictureBoxes.Add(pictureBox2);
myPictureBoxes.Add(pictureBox3);
index = 0;
}
public void changePictureBox(Keys keyData)
{
myPictureBoxes[index].BorderStyle = BorderStyle.None;
if (keyData == Keys.Right)
{
if (index < myPictureBoxes.Count - 1)
index++;
}
else if (keyData == Keys.Left)
{
if (index > 0)
index--;
}
myPictureBoxes[index].BorderStyle = BorderStyle.FixedSingle;
}}}

You could create a list of pictureboxes.
Then for example you can add an indexer (just to keep it simple).
The indexer is an int (or in your case can be a byte) that stores the index of the currently selected picturebox.
If the user presses "right arrow" key just change the border of the current indexed picturebox increment the indexer and update "the now indexd picturebox".
List<PictureBox> myPictureBoxes;
int index;
public void iniPictureBoxes()
{
myPictureBoxes = new List<PictureBox>();
myPictureBoxes.Add(pictureBox1);
myPictureBoxes.Add(pictureBox2);
index = 0;
}
public void changePictureBox(Keys keyData)
{
myPictureBoxes[index].BorderStyle = BorderStyle.None;
if(keyData == Keys.Right)
{
if(index < myPictureBoxes.Count - 1)
index++;
}
else if(keyData == Keys.Left)
{
if(index>0)
index--;
}
myPictureBoxes[index].BorderStyle = BorderStyle.FixedSingle;
}
this just sets the borderstyle. If you want to really select the picturebox you also need to implement it (picturebox.select();)
It may be better to create the pictureboxes generically. So you don't have to do add all of them manually to the List.
Here is the generic code for adding pictureboxes (in this case 5):
public void iniPictureBoxes()
{
myPictureBoxes = new List<PictureBox>();
for (int i = 0; i < 5; i++)
{
PictureBox tempPB = new PictureBox();
tempPB.Location = new Point(i * 15, 10);
tempPB.Size = new Size(10, 10);
tempPB.BackColor = Color.Black;
Controls.Add(tempPB);
myPictureBoxes.Add(tempPB);
}
index = 0;
}
and here the way to add an event: just double click the event u want to have.
Then a method is being auto generated for you. And you should change it to
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
changePictureBox(e.KeyData);
}

Related

How do I make the code inside the 2nd if statement, which is inside a foreach loop, run only once?

I have this foreach loop to iterate through the controls in my form, so I can find the picturebox that I am moving with the arrow keys and checking if it collides with other pictureboxes via tags. I now want to check if the picturebox collides with a picturebox with the tag "portal", and if so, it has to open a new form and close/hide the current one, which I did and works fine. The only problem I have is that it opens the second form 13 times, and I think it's because it runs the foreach loop for 13 times and runs those 3 lines of code 13 times, but I want them to run only once, open the new form and close/hide the current one just once.
private void moveTimerEvent(object sender, EventArgs e)
{
if (moveLeft == true && pictureBox1.Left > 10)
{
pictureBox1.Left -= speed;
}
if (moveRight == true && pictureBox1.Left < 1850) //right border of the screen.
{
pictureBox1.Left += speed;
}
if (moveUp == true && pictureBox1.Top > 10)
{
pictureBox1.Top -= speed;
}
if (moveDown == true && pictureBox1.Top < 962) //bottom border of the screen.
{
pictureBox1.Top += speed;
}
foreach (Control x in this.Controls) //foreach loop that I am referring to
{
if (x is PictureBox && (string)x.Tag == "wall")
{
if (pictureBox1.Bounds.IntersectsWith(x.Bounds))
{
pictureBox1.Location = new Point(123, 962);
}
}
else
{
if (x is PictureBox && (string)x.Tag == "portal") //if statement that I am also referring to
{
if (pictureBox1.Bounds.IntersectsWith(x.Bounds)) //if statement that runs 13 times, but only has to run once
{
Form2 f2 = new Form2(); //open form2
f2.Show(); //show it
this.Hide(); //close this one
}
}
}
}
}
I tried adding a break after the code I want to run only once, but it still opened the new form 13 times. I was expecting the break to run it once and stop running those lines.
Your post describes some problems you're having with the timer scheme and inner loop that you show in your code. But what is your actual objective? You state in the post that its raison d'etre is:
[...] so I can find the picturebox that I am moving with the arrow keys and checking if it collides with other pictureboxes via tags.
This "might" be considered an X-Y Problem
because there may be a more optimal way than a timer to achieve what you wanted to do in the first place (because a timer with a 20ms interval might present some race conditions that make it hard to start and stop predictably). Here's one alternative:
ArrowKeyPictureBox
Consider customizing PictureBox that can MoveProgrammatically(Keys direction) when Left, Right, Up or Down is pressed. It would also need to keep track of whether this particular picture box is the one that's supposed to move, based on its IsCurrentMoveTarget property. And when it does become the current move target, it notifies all the other instances that they no longer are.
class ArrowKeyPictureBox : PictureBox
{
public ArrowKeyPictureBox()
{
LostFocus += (sender, e) =>Refresh();
GotFocus += (sender, e) =>Refresh();
MouseDown += (sender, e) =>
{
Focus();
foreach (var control in Parent.Controls.OfType<ArrowKeyPictureBox>())
{
if (!ReferenceEquals(control, this)) control.IsCurrentMoveTarget = false;
}
IsCurrentMoveTarget = true;
};
Paint += (sender, e) =>
{
if (Focused && IsCurrentMoveTarget) using (var pen = new Pen(Color.Fuchsia))
{
var rect = new Rectangle(
e.ClipRectangle.Location,
new Size(
(int)(e.ClipRectangle.Width - pen.Width),
(int)(e.ClipRectangle.Height - pen.Width)));
e.Graphics.DrawRectangle(pen, rect);
}
};
}
Move method has collision detection and fires a cancellable event when imminent.
const int INCREMENT = 1;
public void MoveProgrammatically(Keys direction)
{
Rectangle preview = Bounds;
if (IsCurrentMoveTarget)
{
BringToFront();
switch (direction)
{
case Keys.Up:
if (Top <= 0)
{
return;
}
preview.Y -= INCREMENT;
if(detectCollision())
{
return;
}
Top = preview.Y;
break;
case Keys.Down:
if (Bottom >= Parent.Bottom)
{
return;
}
preview.Y += INCREMENT;
if (detectCollision())
{
return;
}
Top = preview.Y;
break;
case Keys.Left:
if (Left <= 0)
{
return;
}
preview.X -= INCREMENT;
if (detectCollision())
{
return;
}
Left = preview.X;
break;
case Keys.Right:
if (Right >= Parent.Right)
{
return;
}
preview.X += INCREMENT;
if (detectCollision())
{
return;
}
Left = preview.X;
break;
}
}
bool detectCollision()
{
foreach (Control control in Parent.Controls)
{
if(!ReferenceEquals(this, control))
{
if(preview.IntersectsWith(control.Bounds))
{
var e = new CollisionDetectedEventArgs(control: control);
CollisionDetected?.Invoke(this, e);
return !e.Cancel;
}
}
}
return false;
}
}
public static event CollisionDetectedEventHandler CollisionDetected;
Determines whether this is the control to move, and whether to draw the focus rectangle.
public bool IsCurrentMoveTarget
{
get => _isCurrentMoveTarget;
set
{
if (!Equals(_isCurrentMoveTarget, value))
{
_isCurrentMoveTarget = value;
Refresh();
}
}
}
bool _isCurrentMoveTarget = false;
}
Main Form
The main form employs a MessageFilter for Up Down Left Right key events and broadcasts any occurrence to all of the ArrowKeyPictureBox instances it might hold.
public partial class MainForm : Form , IMessageFilter
{
public MainForm()
{
InitializeComponent();
Application.AddMessageFilter(this);
Disposed += (sender, e) => Application.RemoveMessageFilter(this);
ArrowKeyPictureBox.CollisionDetected += onAnyCollisionDetected;
}
const int WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
if (Controls.OfType<ArrowKeyPictureBox>().Any(_ => _.Focused))
{
Keys key = (Keys)m.WParam;
switch (key)
{
case Keys.Up:
case Keys.Down:
case Keys.Left:
case Keys.Right:
BeginInvoke(new Action(() =>
{
foreach (var target in Controls.OfType<ArrowKeyPictureBox>())
{
target.MoveProgrammatically(key);
}
}));
// Suppress OS tabbing to prevent loss of focus.
return true;
}
}
break;
}
return false;
}
Display collision status:
private void onAnyCollisionDetected(object sender, CollisionDetectedEventArgs e)
{
e.Cancel = !e.Control.Name.Contains("Portal");
if (sender is Control control)
{
if (!_prevWarning.Equals(control.Bounds))
{
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Bold);
richTextBox.SelectionColor = e.Cancel ? Color.Green : Color.Red;
richTextBox.AppendText($"{Environment.NewLine}Collision{Environment.NewLine}");
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Regular);
richTextBox.SelectionColor = Color.Black;
List<string> builder = new List<string>();
builder.Add($"Moving: {control.Name}");
builder.Add($"# {control.Bounds}");
builder.Add($"Collided with: {e.Control.Name}");
builder.Add($"# {e.Control.Bounds}");
richTextBox.AppendText($"{string.Join(Environment.NewLine, builder)}{Environment.NewLine}");
richTextBox.ScrollToCaret();
_prevWarning = control.Bounds;
}
}
}
Rectangle _prevWarning = new Rectangle();
}

Set an object as "Solid" in C# windows Form when developing a stickman platform game

For a school project I need to develop a platform style game purely in C# Windows forms and cannot use any other languages. I have a gravity and movement system sorted already but my character is still able to jump off the map or jump through picture boxes. How would I go about making these objects solid so that the character cannot run through them. Here is my code
What my game looks like:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool left;
bool right;
int gravity = 20;
int force;
bool jump;
private void Timer(object sender, EventArgs e)
{
if (left == true)
{
Character.Left -= 15;
if (Character.Image != Properties.Resources.LeftChar)
{
Character.Image = Properties.Resources.LeftChar;
}
}
if (right == true)
{
Character.Left += 15;
if (Character.Image != Properties.Resources.RightChar)
{
Character.Image = Properties.Resources.RightChar;
}
}
if (jump == true)
{
Character.Top -= force;
force -= 1;
}
if (Character.Top + Character.Height >= GameBoundary.Height)
{
Character.Top = GameBoundary.Height - Character.Height;
jump = false;
}
else
{
Character.Top += 10;
}
}
private void keydown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
left = true;
if (e.KeyCode == Keys.D)
right = true;
if (jump != true)
{
if (e.KeyCode == Keys.W)
{
jump = true;
force = gravity;
}
}
}
private void keyup(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
left = false;
if (e.KeyCode == Keys.D)
right = false;
}
}
I created an invisible panel that was the same size of the game called "Gameboundary", this made it possible for the player to walk on the bottom of the window, but I am not sure how I would apply this to the rest of the code. If anybody has any suggestions it will be greatly welcome. Not too good at C# just yet!
Here is a basic Collision Detection system:
public class CollisionDetection
{
public static bool ObjectTouchingOthers(Control Object, int SpaceBetweenObjects)
{
for (int i = 0; i < Object.Parent.Controls.Count; i++)
{
if (Object.Parent.Controls[i].Name != Object.Name)
{
if (Object.Left + Object.Width + SpaceBetweenObjects > Object.Parent.Controls[i].Left && Object.Top + Object.Height + SpaceBetweenObjects > Object.Parent.Controls[i].Top && Object.Left < Object.Parent.Controls[i].Left + Object.Parent.Controls[i].Width + SpaceBetweenObjects && Object.Top < Object.Parent.Controls[i].Top + Object.Parent.Controls[i].Height + SpaceBetweenObjects)
{
return true;
}
}
}
return false;
}
public static bool ObjectTouchingOthers(Control Object, int SpaceBetweenObjects, Control[] ControlsToExclude )
{
for (int i = 0; i < Object.Parent.Controls.Count; i++)
{
if (ControlsToExclude.Contains(Object.Parent.Controls[i]) == false && Object.Parent.Controls[i].Name != Object.Name)
{
if (Object.Left + Object.Width + SpaceBetweenObjects > Object.Parent.Controls[i].Left && Object.Top + Object.Height + SpaceBetweenObjects > Object.Parent.Controls[i].Top && Object.Left < Object.Parent.Controls[i].Left + Object.Parent.Controls[i].Width + SpaceBetweenObjects && Object.Top < Object.Parent.Controls[i].Top + Object.Parent.Controls[i].Height + SpaceBetweenObjects)
{
return true;
}
}
}
return false;
}
}
The first argument Object is the control you want collision detection for, and it will only detect other controls in the same container, so if you used it for a control in a panel, for example, it would only work with other controls in the panel, likewise with a Form or any other control. The second argument simply specifies how much distance you want between the controls when they are touching. If you're using the second overload, the third argument is an array of controls that you do not want collision detection for. This is how you would use the second overload:
private void btnMoveLeft_Click(object sender, EventArgs e)
{
btnPlayer.Left -= 1;
if (CollisionDetection.ObjectTouchingOthers(btnPlayer, 1, new Control[] {button1, button2}) == true)
{
btnPlayer.Left += 1;
}
}
With this overload, it will continue right through the controls you specified in the array.
And just to wrap it up, here's a basic example of how to set up collision detection:
private void btnMoveLeft_Click(object sender, EventArgs e)
{
btnPlayer.Left -= 1;
if (CollisionDetection.ObjectTouchingOthers(btnPlayer, 1) == true)
{
btnPlayer.Left += 1;
}
}
private void btnMoveRight_Click(object sender, EventArgs e)
{
btnPlayer.Left += 1;
if (CollisionDetection.ObjectTouchingOthers(btnPlayer, 1) == true)
{
btnPlayer.Left -= 1;
}
}
private void btnMoveUp_Click(object sender, EventArgs e)
{
btnPlayer.Top -= 1;
if (CollisionDetection.ObjectTouchingOthers(btnPlayer, 1) == true)
{
btnPlayer.Top += 1;
}
}
private void btnMoveDown_Click(object sender, EventArgs e)
{
btnPlayer.Top += 1;
if (CollisionDetection.ObjectTouchingOthers(btnPlayer, 1) == true)
{
btnPlayer.Top -= 1;
}
}
Just remember that you're going to have to change the names of the controls in the code I have here. And for reference, here is my test form:
You need to implement collision detection. Draw an imaginary box around your person and any objects you don't want him to pass through. Check if any of the boxes overlap. If they do, move one or both of the objects back until the boxes no longer overlap.
Picture boxes has a .Bounds with a .IntersectWith and this will take an object of type Rectangle.
In a game, you will typically have an ongoing timer, checking all sorts of stuff and progressing time.
In this timer i would make a:
if (pictureBox1.Bounds.IntersectsWith(pictureBox2.Bounds))
{
//They have collided
}
You might have a lot of objects, so you could make a List<PictureBox> and add those objects, then the list will have refferences to those PictureBoxes, that means that they will automatically be updated with the right bounds, when you go through the List.
Be sure to make the List a public property of your form class. Then before rolling through the List, instead set another List = to the public object List. This ensures that you can do a foreach() loop without getting exceptions.

DataGridView OnRowPostPaint draw

Actually, my code is "selecting hover" all the line, when i have the mouse over the cell, but, i want to change it, i want to select all the column, not the line.
Data Grid
How can i manage it?
My code is:
protected override void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e)
{
base.OnRowPostPaint(e);
if (this.RectangleToScreen(e.RowBounds).Contains(MousePosition))
{
using (var b = new SolidBrush(Color.FromArgb(50, Color.Black)))
using (var p = new Pen(Color.MediumVioletRed))
{
var r = e.RowBounds;
r.Width -= 1;
r.Height -= 1;
e.Graphics.FillRectangle(b, e.RowBounds.X, e.RowBounds.Y,e.RowBounds.Width, e.RowBounds.Height);
e.Graphics.DrawRectangle(p, r);
}
}
}
I can find the same method, but to draw vertically...
Thank you
If You just want to select entire column while mouse is over any cell then You don't have to use OnRowPostPaint event , You can make the selection mode for the entire grid to select entire column instead of only one cell or entire row but to do that You need to make the SortMode for the Columns set to anything other than Automatic. here is how to do it:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < dataGridView1.ColumnCount; i++)
{
dataGridView1.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;
}
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullColumnSelect;
}
then use either OnCellMouseEnter or OnCellMouseMove events to select whole column like this:
private void OnCellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex == -1 || e.ColumnIndex == -1)
return;
dataGridView1.CurrentCell = dataGridView1[e.ColumnIndex, e.RowIndex];
}
refer to this Answer for more information on selecting entire column.
Maybe not exactly what you want but maybe this example can get you started:
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
Rectangle newRect = new Rectangle(e.CellBounds.X + 1,
e.CellBounds.Y + 1, e.CellBounds.Width - 4,
e.CellBounds.Height - 4);
using (
Brush gridBrush = new SolidBrush(this.dataGridView1.GridColor),
backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
using (Pen gridLinePen = new Pen(gridBrush))
{
// Erase the cell.
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
// Draw the grid lines (only the right and bottom lines;
// DataGridView takes care of the others).
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Left,
e.CellBounds.Bottom - 1, e.CellBounds.Right - 1,
e.CellBounds.Bottom - 1);
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Right - 1,
e.CellBounds.Top, e.CellBounds.Right - 1,
e.CellBounds.Bottom);
// Draw the inset highlight box.
e.Graphics.DrawRectangle(Pens.Blue, newRect);
// Draw the text content of the cell, ignoring alignment.
if (e.Value != null)
{
e.Graphics.DrawString((String)e.Value, e.CellStyle.Font,
Brushes.Crimson, e.CellBounds.X + 2,
e.CellBounds.Y + 2, StringFormat.GenericDefault);
}
e.Handled = true;
}
}
}
The example comes from this page learn.microsoft.com DataGridView.OnCellPainting
This will select all the cells within the hovered column... though it's not really clear what you are asking for.
using System.Windows.Forms;
namespace DatagridViewSelectAllCells_58842788
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
for (int i = 0; i < 10; i++)
{
dataGridView1.Rows.Add($"col1_{i}", $"col2_{i}");
}
dataGridView1.CellMouseEnter += DataGridView1_CellMouseEnter;
}
private void DataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == -1)//don't do anything if the rowheader is being hovered
{
return;
}
if (dataGridView1.SelectedCells.Count > 1 && e.ColumnIndex == dataGridView1.SelectedCells[0].ColumnIndex)//don't do anything if the column is already selected
{
return;
}
dataGridView1.ClearSelection();//clear the current selection
//select all the cells in that hovered column
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[e.ColumnIndex, i].Selected = true;
}
}
}
}
here's an updated answer for just changing the color of the cells
using System.Windows.Forms;
using System.Drawing;
namespace DatagridViewSelectAllCells_58842788
{
public partial class Form1 : Form
{
static Color originalColor;
static int highlightedCol = -1;
static DataGridView dataGridView1 = new DataGridView();
public Form1()
{
InitializeComponent();
Controls.Add(dataGridView1);
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
for (int i = 0; i < 10; i++)
{
dataGridView1.Rows.Add($"col1_{i}", $"col2_{i}");
}
dataGridView1.CellMouseEnter += DataGridView1_CellMouseEnter;
dataGridView1.CellMouseLeave += DataGridView1_CellMouseLeave;
dataGridView1[1, 4].Style.BackColor = Color.DarkOliveGreen;
}
private void DataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
//WithCellStyle(sender, e, "Leave");
WithDefaultStyle(sender, e, "Leave");
}
private void DataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
//WithCellStyle(sender, e, "Enter");
WithDefaultStyle(sender, e, "Enter");
}
/*
* to handle cells that have specific style attributes
* like cell dataGridView1[1, 4]
* there would be more to this to keep track of multiple different cell styles on the Leave side of thing
* but that's up to you to handle if you have a need for it.
*/
private static void WithCellStyle(object sender, DataGridViewCellEventArgs e, string eType)
{
switch (eType)
{
case "Enter":
if (originalColor == null)
{
originalColor = ((Control)sender).BackColor;//store the color for leave event
}
highlightedCol = e.ColumnIndex;//set which column is highlighted for leave event
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[highlightedCol, i].Style.BackColor = Color.Aquamarine;
}
break;
case "Leave":
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[highlightedCol, i].Style.BackColor = originalColor;
}
break;
default:
break;
}
}
/*
* if you only have default styles
*/
private static void WithDefaultStyle(object sender, DataGridViewCellEventArgs e, string eType)
{
switch (eType)
{
case "Enter":
if (originalColor == null)
{
originalColor = ((Control)sender).BackColor;//store the color for leave event
}
highlightedCol = e.ColumnIndex;//set which column is highlighted for leave event
//change color of all the cells
dataGridView1.Columns[highlightedCol].DefaultCellStyle.BackColor = Color.Aquamarine;//set the new color
break;
case "Leave":
//per defaultstyle
dataGridView1.Columns[highlightedCol].DefaultCellStyle.BackColor = originalColor;
break;
default:
break;
}
}
}
}

C# KeyDown Issue

I am trying to make a menu with three buttons (in order: Play, Options, Exit) in which only selected button has a border and that is controlled with arrows UP and DOWN. Unfortunately nothing seems to be happening when buttons are pressed atm. Here's the code:
public partial class
{
int i = 0;
List<Button> menuButtons = new List<Button>();
Button selectedButton = new Button();
public Menu()
{
InitializeComponent();
menuButtons.Add(btnPlay);
menuButtons.Add(btnOptions);
menuButtons.Add(btnExit);
selectedButton = menuButtons[i];
if (menuButtons[i] == selectedButton)
{
menuButtons[i].FlatAppearance.BorderSize = 1;
}
}
private void Menu_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
if (i < menuButtons.Count)
{
i++;
}
else if (i >= menuButtons.Count)
{
i = 0;
}
}
if (e.KeyCode == Keys.Up)
{
if (i > 0)
{
i--;
}
else if (i <= 0)
{
i = menuButtons.Count;
}
}
if (e.KeyCode == Keys.Enter)
{
switch (i)
{
case 0:
btnPlay.PerformClick();
break;
case 1:
btnOptions.PerformClick();
break;
case 2:
btnExit.PerformClick();
break;
}
}
}
Have a nice day :)
There are two problems in your code. The first one is in the first two if statements. You are correctly changing the index, but you are not setting the border to the new selected button. You must remove the border of the previously selected button and set the new selected button's border.
The second is that you forgot to set click events for your buttons, so they'll do nothing when clicked. Here's how your code should be:
public partial class
{
int i = 0;
List<Button> menuButtons = new List<Button>();
Button selectedButton = new Button();
public Menu()
{
InitializeComponent();
//Assigning click events for the buttons.
btnPlay.Click += BtnPlay_Click;
btnOptions.Click += BtnOptions_Click;
btnExit.Click += BtnExit_Click;
menuButtons.Add(btnPlay);
menuButtons.Add(btnOptions);
menuButtons.Add(btnExit);
selectedButton = menuButtons[i];
if (menuButtons[i] == selectedButton)
{
menuButtons[i].FlatAppearance.BorderSize = 1;
}
}
private void Menu_KeyDown(object sender, KeyEventArgs e)
{
//Removing border from previously selected button.
menuButtons[i].FlatAppearance.BorderSize = 0;
if (e.KeyCode == Keys.Down)
{
if (i < menuButtons.Count)
{
i++;
}
else if (i >= menuButtons.Count)
{
i = 0;
}
}
if (e.KeyCode == Keys.Up)
{
if (i > 0)
{
i--;
}
else if (i <= 0)
{
i = menuButtons.Count;
}
}
//Setting border for the newly selected button.
menuButtons[i].FlatAppearance.BorderSize = 1;
if (e.KeyCode == Keys.Enter)
{
switch (i)
{
case 0:
btnPlay.PerformClick();
break;
case 1:
btnOptions.PerformClick();
break;
case 2:
btnExit.PerformClick();
break;
}
}
}
private void BtnExit_Click(object sender, EventArgs e)
{
//Code for the Exit button.
}
private void BtnOptions_Click(object sender, EventArgs e)
{
//Code for the Options button.
}
private void BtnPlay_Click(object sender, EventArgs e)
{
//Code for the Play button.
}
}
PS: pay attention to unnecessary code like the the selectedButton variable and the if statement in the constructor. They do not affect the functionality, but may hinder later code maintenance.

How to drag images from a listview (together with an imageview) to pictureboxes?

I am pretty new to C# and struggle a lot with a small project I want to do.
I am busy with something like a collage maker where I have a list of pictures on the left hand side and I want to drag and drop multiple images to the right hand side where I want to be able to move them around to create my collage.
I wanted to show an image but I am not allowed to post images with less than 10 reputation points. But look here for the image:
I can't manage to get it to work. I've looked online for help but I can't really find what I'm looking for. The stuff I do find are too unclear and I struggle to understand.
This is what I have so far for the code to drag and drop from the left to the right but it doesn't work;
private void pictureBox1_DragEnter(object sender, DragEventArgs e)
{
int len = e.Data.GetFormats().Length - 1;
int i;
for (i = 0; i <= len; i++)
{
if (e.Data.GetFormats()[i].Equals("System.Windows.Forms.ListView+SelectedListViewItemCollection"))
{
//The data from the drag source is moved to the target.
e.Effect = DragDropEffects.Move;
}
}
}
private void pictureBox1_DragDrop(object sender, DragEventArgs e)
{
//Return if the items are not selected in the ListView control.
if (listView1.SelectedItems.Count == 0)
{
return;
}
ListViewItem dragitem = listView1.SelectedItems[0];
pictureBox2.Image = imageList1.Images[dragitem.ImageIndex];
listView1.Items.Remove(dragitem);
}
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
listView1.DoDragDrop(listView1.SelectedItems, DragDropEffects.Move);
}
And after I can add image to the left, how can I drag and move them around using the mouse coordinates?
Any help will be appreciated please.
Everything is done using C# in Windows Forms.
Here is a full example you may want to play with:
It uses a form with a ListView, an ImageList and a Panel to hold the PictureBoxes.
You could use a FlowLayoutPanel but this won't let you move the Pictureboxes later.
You also could use a grid of Panels and their BackgroundImage.
First we set up the form:
private void Form1_Load(object sender, EventArgs e)
{
KeyPreview = true;
FillLV(10);
AddPBs(36);
listView1.MouseMove += listView1_MouseMove;
}
void FillLV(int count)
{
for (int i = 0; i < count; i++) listView1.Items.Add("Item" + i, i);
}
void AddPBs(int count)
{
int iWidth = imageList1.ImageSize.Width;
int iHeight = imageList1.ImageSize.Height;
int cols = panel1.ClientSize.Width / iWidth;
for (int i = 0; i < count; i++)
{
PictureBox PB = new PictureBox();
PB.BackColor = Color.LightGray;
PB.Margin = new System.Windows.Forms.Padding(0);
PB.ClientSize = new System.Drawing.Size(iWidth , iHeight );
PB.Location = new Point(iWidth * (i % cols), iHeight * (i / cols));
PB.Parent = panel1;
PB.DragEnter += PB_DragEnter;
PB.DragDrop += PB_DragDrop;
PB.AllowDrop = true;
PB.MouseClick += (ss, ee) => { currentPB = PB; PB.Focus(); };
}
}
Note how we add events to the dynamically created PictureBoxes and how we set their hidden AllowDrop property.
Also note the little lambda to make the MouseClick prepare for moving them around with the keyboard.
For this we will also need a reference to the current box:
PictureBox currentPB = null;
Now we start the dragging. This should not be done in the MouseDown or else it will interfere with normal selection and also happen before the new selection is made..
Either you do it in the MouseMove:
void listView1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
if (listView1.SelectedItems.Count > 0)
{
listView1.DoDragDrop(listView1.SelectedItems[0], DragDropEffects.Move);
}
}
}
Or (Update) for ListViews better and much simpler (thanks Hans!) in their ItemDrag event:
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
listView1.DoDragDrop(e.Item, DragDropEffects.Move);
}
Update: Both ways now use the dragged Item, not just its imageIndex, to make it simpler to remove the Item from the LV..
void PB_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(ListViewItem)))
e.Effect = DragDropEffects.Move;
else
e.Effect = DragDropEffects.None;
}
void PB_DragDrop(object sender, DragEventArgs e)
{
PictureBox pb = sender as PictureBox;
var item = (ListViewItem)e.Data.GetData(typeof(ListViewItem));
int index = item.ImageIndex;
pb.Image = imageList1.Images[index]; // make sure you have images for indices!!
}
Finally we use the keyboard to move the current box around. I have added code to bring it up and down in the z-order as well.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (currentPB == null) return;
if (e.KeyData == Keys.Left) currentPB.Left -= 1;
else if (e.KeyData == Keys.Right) currentPB.Left += 1;
else if (e.KeyData == Keys.Up) currentPB.Top -= 1;
else if (e.KeyData == Keys.Down) currentPB.Top += 1;
else
{
int z = panel1.Controls.GetChildIndex(currentPB);
if (e.KeyData == Keys.Home) panel1.Controls.SetChildIndex(currentPB, 0);
else if (e.KeyData == Keys.End)
panel1.Controls.SetChildIndex(currentPB, panel1.Controls.Count);
else if (e.KeyData == Keys.PageUp)
panel1.Controls.SetChildIndex(currentPB, z + 1);
else if (e.KeyData == Keys.PageDown)
{ if (z > 0) panel1.Controls.SetChildIndex(currentPB, z - 1); }
}
panel1.Invalidate();
}
For this to work the form must have : KeyPreview = true;
Note that the sizes in an ImageList are restricted to 256x256. So you may want to use only the index and use it to load larger images from disk..

Categories