Interchange positions of two buttons - c#

I want to replace button location (Interchange location )by black button when i click it and it is next to black button (b9=black button and lable1 is a temp for saving location).
I made this method :
void checkLocation()
{
if (ActiveControl.Location == new Point(5, 7))//-----------for button 1
{
if (b9.Location == new Point(83, 7) || b9.Location == new Point(5, 71))
{
label1.Location = ActiveControl.Location;
ActiveControl.Location = b9.Location;
b9.Location = label1.Location;
}
}// it continue for every button
and I write this code for every button_click
private void button1_Click(object sender, EventArgs e)
{
checkLocation();
}
now,some button don't work currently . what is wrong ?

by thanks from p.s.w.g
I think it is shorter and fit :
void swapLocation()
{
var tmp = ActiveControl.Location;
if((ActiveControl.Location.X==b9.Location.X)&&(Math.Abs(b9.Location.Y-ActiveControl.Location.Y)<=60))
{
ActiveControl.Location = b9.Location;
b9.Location = tmp;
}
if ((ActiveControl.Location.Y == b9.Location.Y) && (Math.Abs(b9.Location.X-ActiveControl.Location.X) <= 70))
{
ActiveControl.Location = b9.Location;
b9.Location = tmp;
}
}

Just do this to swap the locations of two controls:
void swapLocation()
{
var tmp = ActiveControl.Location;
ActiveControl.Location = b9.Location;
b9.Location = tmp;
}
Or more generally
void swapLocation(Control x, Control y)
{
var tmp = x.Location;
x.Location = y.Location;
y.Location = tmp;
}
...
swapLocation(ActiveControl, b9);
Update
It looks like you're trying to implement a version of the 15-puzzle. There are numerous ways to solve this, but to avoid a radical rewrite of your program I'd recommend this:
private int buttonWidth = 82;
private int buttonHeight = 82; // adjust these values as needed
private void button_Click(object sender, EventArgs e)
{
if ((Math.Abs(ActiveControl.Location.X - b9.Location.X) == 0 &&
Math.Abs(ActiveControl.Location.Y - b9.Location.Y) == buttonHeight) ||
(Math.Abs(ActiveControl.Location.X - b9.Location.X) == buttonWidth &&
Math.Abs(ActiveControl.Location.Y - b9.Location.Y) == 0))
{
swapLocation(ActiveControl, b9);
}
}
This basically checks to see if the ActiveControl is either directly above, below, to the left, or the right of b9, and if it is, swaps them. You can use this click handler for all buttons 1 through 8. Note this method only works with the buttons are a fixed width and height.

Related

Doughts and crosses in windows form application

can someone tell me why I can't run my program? I'm trying to create doughts and crosses in windows form applications I tried changing the code and stuff but I've tried everything but I think something is wrong with my function at the bottom. By now, I wanted to program to run, generate 9 buttons and when I click on them an "X" or "O" would appear depending whose turn is it.
PS. I haven't added the win condition function yet I wanted to test if the program is working as it should.
Thanks in advance.
public partial class Form1 : Form
{
Button[] gameButtons = new Button[9]; //array of buttons for markers(X's and O's)
bool cross = true; //cross is set to true if the next marker is to be a cross
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "More Complex Version of Noughts and Crosses";
this.BackColor = Color.BlanchedAlmond;
this.Width = 400;
this.Height = 400;
for (int i = 0; i < gameButtons.Length; i++)
{
int index = i;
this.gameButtons[i] = new Button();
int x = 50 + (i % 3) * 50;
int y = 50 + (i / 3) * 50;
this.gameButtons[i].Location = new System.Drawing.Point(x, y);
this.gameButtons[i].Name = "btn" + (index + 1);
this.gameButtons[i].Size = new System.Drawing.Size(50, 50);
this.gameButtons[i].TabIndex = i;
//this.gameButtons[i].Text = Convert.ToString(index);
this.gameButtons[i].UseVisualStyleBackColor = true;
this.gameButtons[i].Visible = true;
gameButtons[i].Click += (sender1, ex) => this.buttonHasBeenPressed(sender,index);
this.Controls.Add(gameButtons[i]);
}
}
private void buttonHasBeenPressed(object sender,int i)
{
if (((Button)sender).Text == "")
{
if (cross == true)
{
((Button)sender).Text = "X";
gameButtons[i] = 'X';
}
else
{
((Button)sender).Text = "O";
gameButtons[i] = 'O';
}
cross = !cross;
}
}
}
Edit: The first problem was solved, thanks a lot to everyone :) But now I'm struggling to find the win condition. I've used this code but I'm getting a compiler error that I don't seem to understand how to fix it. This is the code I've made up:
private void threeInARow(int a, int b, int c, object sender)
{
if (gameButtons[a]==gameButtons[b] && gameButtons[a]==gameButtons[c])
{
if (gameButtons[a]='X')
{
MessageBox.Show("the winner is crosses");
}
else
{
MessageBox.Show("the winner is noughts");
}
}
The error is on my my first if it says "cannot implicitly convert type char to system.windows.forms.button"
Firstly, you have some compile errors:
// These two lines both throw the error:
// Cannot implicitly convert type 'char' to 'System.Windows.Forms.Button'
gameButtons[i] = 'X';
gameButtons[i] = 'O';
This is because you're trying to set a button to a character. This is not necessary, since you've already changed the text of the button, and you can remove these lines.
Next, you get a runtime exception when clicking on a button, on this line:
// The following line fails with the error:
// Unable to cast object of type 'WinFormTest.Form1' to type 'System.Windows.Forms.Button'.
if (((Button)sender).Text == "")
This is because the sender is the Form1 object, and not the button. The reason for this is that, in your assignment of the event to the button Click, you are passing sender instead of sender1 to the event, and, since the assignment of this event is happening in the Form.Load event, sender is the Form1. So you need to change the assignment to pass sender1 instead:
gameButtons[i].Click += (sender1, ex) => this.buttonHasBeenPressed(sender1, index);
The next problem you're having (since you've modified the code in the original question) is in the threeInARow method here:
if (gameButtons[a] = 'X') // Error: Cannot implicitly convert type 'char' to 'Button'
The reason for this is that gameButtons is an array of Button objects, so gameButtons[a] represents a Button, and you can't assign the character 'X' to a Button (they are two different types). Since you've already assigned a value to the Text property of each button (which is of string type), you can just use that instead.
Also, you are using a single = sign, which is an assignment. You want to do a comparison, which uses a double == sign. So, putting these together, you will get:
if (gameButtons[a].Text == "X")
You have added similar problematic code to your buttonHasBeenPressed method, which you should just remove since we can compare the Text properties and don't need this additional assignement:
gameButtons[i] = 'X'; // Remove these invalid assignments
That takes care of the compile error, but you still have another problem in your comparisons with this line:
if (gameButtons[a] == gameButtons[b] && . . .
This line is asking if the Button reference in the array at index a is pointing to the exact same object as the Button reference in the array at index b. This will never be the case because you (correctly) initialized your array with 9 unique buttons.
What you really want to do is compare the Text property of each button, like so:
if (gameButtons[a].Text == gameButtons[b].Text && . . .
Lastly, you have included an Object parameter named sender to your method that you aren't using, so you might as well remove that (or do something with it in your method).
So, putting this all together you have:
private void threeInARow(int a, int b, int c)
{
if (gameButtons[a].Text == gameButtons[b].Text && gameButtons[a].Text == gameButtons[c].Text)
{
if (gameButtons[a].Text == "X")
{
MessageBox.Show("the winner is crosses");
}
else
{
MessageBox.Show("the winner is noughts");
}
}
}
See code below :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Button[] gameButtons = new Button[9]; //array of buttons for markers(X's and O's)
bool cross = true; //cross is set to true if the next marker is to be a cross
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "More Complex Version of Noughts and Crosses";
this.BackColor = Color.BlanchedAlmond;
this.Width = 400;
this.Height = 400;
for (int i = 0; i < gameButtons.Length; i++)
{
int index = i;
this.gameButtons[i] = new Button();
int x = 50 + (i % 3) * 50;
int y = 50 + (i / 3) * 50;
this.gameButtons[i].Location = new System.Drawing.Point(x, y);
this.gameButtons[i].Name = "btn" + (index + 1);
this.gameButtons[i].Size = new System.Drawing.Size(50, 50);
this.gameButtons[i].TabIndex = i;
//this.gameButtons[i].Text = Convert.ToString(index);
this.gameButtons[i].UseVisualStyleBackColor = true;
this.gameButtons[i].Visible = true;
gameButtons[i].Click += new EventHandler(buttonHasBeenPressed);
this.Controls.Add(gameButtons[i]);
}
}
private void buttonHasBeenPressed(object sender, EventArgs e)
{
if (((Button)sender).Text == "")
{
if (cross == true)
{
((Button)sender).Text = "X";
//gameButtons[i].Text = "X";
}
else
{
((Button)sender).Text = "O";
//gameButtons[i].Text = 'O';
}
cross = !cross;
}
}
}
}

c# Windows Forms adding pictureBoxes to an Array

I need to add PictureBox's (pictureBox11 to pictureBox30) to an array.
So instead of adding PictureBox's like this:
PictureBox[] Coins = new PictureBox[20];
Coins[0] = pictureBox11;
...
Coins[19] = pictureBox30;
I wrote a for cycle like this (DOESN'T WORK) :
for (int i = 11; i < 31; i++)
{
for (int j = 0; j < Coins.Length; j++)
{
Coins[j] = (PictureBox)Controls.Find(
"pictureBox" + i.ToString(), true)[0];
}
}
There might be a small stupid mistake somewhere because I use the same cycle for another thing and it works, idk, maybe I'm just blind and cant see the mistake.
Maybe it is relevant, so I will include the code I am assigning for the array elements:
for (int i = 0; i < Coins.Length; i++)
{
if (player.Bounds.IntersectsWith(Coins[i].Bounds))
{
Coins[i].Visible = false;
}
}
EVERYTHING works fine if I add them as shown in first code, but it is not very practical.
Why isn't the second code (the for cycle) I wrote working for me?
Is there a better way to add multiple pictureboxes to an array?
I am guessing you are making this more complicated than it has to be. To try and understand better, I assume that when you say I need to add pictureboxes (pictureBox11 to pictureBox30) to an array. that you mean there are 30+ PictureBoxs on your form and each PictureBox uses a naming convention such that each is named “pictureBoxX” where “X” is 1,2,3…30,31. Then you want to get a (consecutive?) group of “PictureBoxes” on the form to make invisible. I hope this is correct.
To simply make the picture boxes invisible, I do not think an array is needed. Simply loop through the picture boxes and make it invisible if the name matches a string of the form “pictureBoxX “. I used IndexsAreValid method to validate the start and end indexes. It is also used in the code for an array implementation below this code.
Make PictureBoxes invisible without an array
private void SetPictureBoxesInvisible(int start, int end) {
int size = -1;
string targetString = "";
if (IndexsAreValid(start, end, out size)) {
for (int i = start; i < end + 1; i++) {
try {
targetString = "pictureBox" + i;
PictureBox target = (PictureBox)Controls.Find(targetString, true)[0];
if (target != null) {
target.Visible = false;
}
}
catch (IndexOutOfRangeException e) {
return;
}
}
}
}
If you must have a PictureBox array returned, then the code below should work.
First, to get an array of PictureBoxs as you want you need an array to store them. But first you need to know how big to make it. From your posted code it appears that you want to get picture boxes 11-30 and put them in an array. So we can get the size from these numbers… i.e. 30-11=19 +1 = 20. That’s about all you need. Simply create the array and loop through all the picture boxes and grab pictureBox11-pictureBox30. When done we can use this array to make these `PictureBoxes” invisible.
I created a method IsValidPic similar to a tryParse to validate if the given index (1,2,3..30) is valid. If it is out of range, I simply ignore that value. This gives you the ability to grab an individual picture box in case the desired picture boxes are not contiguous. I used a few buttons to test the methods.
Hope this helps.
private PictureBox[] GetPictureBoxes(int start, int end) {
int size = - 1;
if (IndexsAreValid(start, end, out size)) {
PictureBox curPic = null;
PictureBox[] allPics = new PictureBox[size];
int index = 0;
for (int i = start; i <= end; i++) {
if (IsValidPic(i, out curPic)) {
allPics[index] = curPic;
index++;
}
}
return allPics;
}
else {
return new PictureBox[0];
}
}
private Boolean IndexsAreValid(int start, int end, out int size) {
if (start < 1 || end < 1) {
size = -1;
return false;
}
if (start > end) {
size = -1;
return false;
}
size = end - start + 1;
return true;
}
private Boolean IsValidPic(int index, out PictureBox picture) {
string targetName = "pictureBox" + index;
try {
PictureBox target = (PictureBox)Controls.Find(targetName, true)[0];
if (target != null) {
picture = target;
return true;
}
picture = null;
return false;
}
catch (IndexOutOfRangeException e) {
picture = null;
return false;
}
}
private void ResetAll() {
foreach (PictureBox pb in this.Controls.OfType<PictureBox>()) {
pb.Visible = true;
}
}
private void button1_Click(object sender, EventArgs e) {
TurnInvisible(2, 3);
}
private void button3_Click(object sender, EventArgs e) {
TurnInvisible(11, 30);
}
private void button4_Click(object sender, EventArgs e) {
TurnInvisible(1,7);
}
private void TurnInvisible(int start, int end) {
PictureBox[] pictureBoxesToChange = GetPictureBoxes(start, end);
foreach (PictureBox pb in pictureBoxesToChange) {
if (pb != null)
pb.Visible = false;
}
}
private void button2_Click(object sender, EventArgs e) {
ResetAll();
}

ListView Columns/GridlinesOffset/Alignment error [duplicate]

I am trying to write a Windows Forms MusicPlayer application in C#.
The application should show a list and have some play / stop buttons.
I just started half an hour ago, but my design is almost finished. Now I got 3 things to fix. A bug and 2 good looking things:
on the picture you can see the bug I've found. You might say that's nothing, but its a eye catcher. How can I fix this?
how can I align center the headline of a column, without centering the content?
how can I make the last column filling out the rest of the listView?
You can set the TextAlign of all but the 1st Column's Header; it is always left aligned. To change that you need to owner draw it.
There is no automatic filling option so you need to write a setColumnwidth function, that loops over all but the last columns and sums their Widths; then it subtract the sum from the ListView's Clientsize.Width and set the last column's Width.
The display bug in the gridlines is new to me; so far I don't know how to fix it; maybe owner-drawing will help there as well..?
Update:
Here is some code:
void setLastColumnTofill(ListView lv)
{
int sum = 0;
int count = lv.Columns.Count;
for (int i = 0; i < count - 1; i++) sum += lv.Columns[i].Width;
lv.Columns[count - 1].Width = lv.ClientSize.Width - sum;
}
After setting OwnerDraw = true you could code the three (all are needed!) Draw event :
private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.Graphics.FillRectangle(SystemBrushes.Menu, e.Bounds);
e.Graphics.DrawRectangle(SystemPens.GradientInactiveCaption,
new Rectangle(e.Bounds.X , 0, e.Bounds.Width , e.Bounds.Height) );
string text = listView1.Columns[e.ColumnIndex].Text;
TextFormatFlags cFlag = TextFormatFlags.HorizontalCenter
| TextFormatFlags.VerticalCenter;
TextRenderer.DrawText(e.Graphics, text, listView1.Font, e.Bounds, Color.Black, cFlag);
}
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawDefault = true;
}
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
e.DrawDefault = true;
}
You may want to play a little with the colors or the widths..
If you have an ImageList containing images for displaying the sort order (or other things) you can add this to draw them as well:
ColumnHeader colH = listView1.Columns[e.ColumnIndex];
int ii = colH.ImageIndex;
if (ii >= 0 && ii < imageList1.Images.Count)
e.Graphics.DrawImage(imageList1.Images[ii],
e.Bounds.Width + e.Bounds.X - imageList1.ImageSize.Width, 0);
After setting OwnerDraw to true you can do other stuff like this:
Draw ListView
int sortIndex = 0;
private void listView1_DrawColumnHeader(object sender,
DrawListViewColumnHeaderEventArgs e)
{
var state = e.State == ListViewItemStates.Selected ?
VisualStyleElement.Header.Item.Hot : VisualStyleElement.Header.Item.Normal;
var sortOrder = listView1.Sorting == SortOrder.Ascending ?
VisualStyleElement.Header.SortArrow.SortedUp :
VisualStyleElement.Header.SortArrow.SortedDown;
var itemRenderer = new VisualStyleRenderer(state);
var sortRenderer = new VisualStyleRenderer(sortOrder);
var r = e.Bounds;
r.X += 1;
itemRenderer.DrawBackground(e.Graphics, r);
r.Inflate(-2, 0);
var flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter |
TextFormatFlags.SingleLine;
itemRenderer.DrawText(e.Graphics, r, e.Header.Text, false, flags);
var d = SystemInformation.VerticalScrollBarWidth;
if (e.ColumnIndex == sortIndex) //Sorted Column
sortRenderer.DrawBackground(e.Graphics,
new Rectangle(r.Right - d, r.Top, d, r.Height));
}
private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.DrawDefault = true; /*Use default rendering*/
}
private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
e.DrawDefault = true; /*Use default rendering*/
}
Fill ListView using Last Column
private void ListViewSample_Load(object sender, EventArgs e)
{
var otherItemWisth= this.listView1.Columns.Cast<ColumnHeader>()
.Where(x => x.Index < this.listView1.Columns.Count - 1)
.Sum(x => x.Width);
this.listView1.Columns[this.listView1.Columns.Count - 1].Width =
this.listView1.ClientSize.Width - otherItemWisth;
}
Result

Prevent ToolStripMenuItems from jumping to second screen

I have an application that is mostly operated through NotifyIcon's ContextMenuStrip
There are multiple levels of ToolStripMenuItems and the user can go through them.
The problem is, that when the user has two screen, the MenuItems jump to second screen when no space is available. like so:
How can I force them to stay on the same screen? I've tried to search through the web but couldn't find an appropriate answer.
Here is a sample piece of code i'm using to test this senario:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var resources = new ComponentResourceManager(typeof(Form1));
var notifyIcon1 = new NotifyIcon(components);
var contextMenuStrip1 = new ContextMenuStrip(components);
var level1ToolStripMenuItem = new ToolStripMenuItem("level 1 drop down");
var level2ToolStripMenuItem = new ToolStripMenuItem("level 2 drop down");
var level3ToolStripMenuItem = new ToolStripMenuItem("level 3 drop down");
notifyIcon1.ContextMenuStrip = contextMenuStrip1;
notifyIcon1.Icon = ((Icon)(resources.GetObject("notifyIcon1.Icon")));
notifyIcon1.Visible = true;
level2ToolStripMenuItem.DropDownItems.Add(level3ToolStripMenuItem);
level1ToolStripMenuItem.DropDownItems.Add(level2ToolStripMenuItem);
contextMenuStrip1.Items.Add(level1ToolStripMenuItem);
}
}
It is not easy, but you can write code in the DropDownOpening event to look at where the menu is at (its bounds), the current screen, and then set the DropDownDirection of the ToolStripMenuItem:
private void submenu_DropDownOpening(object sender, EventArgs e)
{
ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
if (menuItem.HasDropDownItems == false)
{
return; // not a drop down item
}
// Current bounds of the current monitor
Rectangle Bounds = menuItem.GetCurrentParent().Bounds;
Screen CurrentScreen = Screen.FromPoint(Bounds.Location);
// Look how big our children are:
int MaxWidth = 0;
foreach (ToolStripMenuItem subitem in menuItem.DropDownItems)
{
MaxWidth = Math.Max(subitem.Width, MaxWidth);
}
MaxWidth += 10; // Add a little wiggle room
int FarRight = Bounds.Right + MaxWidth;
int CurrentMonitorRight = CurrentScreen.Bounds.Right;
if (FarRight > CurrentMonitorRight)
{
menuItem.DropDownDirection = ToolStripDropDownDirection.Left;
}
else
{
menuItem.DropDownDirection = ToolStripDropDownDirection.Right;
}
}
Also, make sure you have the DropDownOpening event hooked up (you would really need to add this to every menu item):
level1ToolStripMenuItem += submenu_DropDownOpening;
I have solved it this way:
For the ContextMenuStrip itself to open on a desired screen, I created a ContextMenuStripEx with the following methods:
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
Rectangle dropDownBounds = new Rectangle(x, y, width, height);
dropDownBounds = ConstrainToBounds(Screen.FromPoint(dropDownBounds.Location).Bounds, dropDownBounds);
base.SetBoundsCore(dropDownBounds.X, dropDownBounds.Y, dropDownBounds.Width, dropDownBounds.Height, specified);
}
internal static Rectangle ConstrainToBounds(Rectangle constrainingBounds, Rectangle bounds)
{
if (!constrainingBounds.Contains(bounds))
{
bounds.Size = new Size(Math.Min(constrainingBounds.Width - 2, bounds.Width), Math.Min(constrainingBounds.Height - 2, bounds.Height));
if (bounds.Right > constrainingBounds.Right)
{
bounds.X = constrainingBounds.Right - bounds.Width;
}
else if (bounds.Left < constrainingBounds.Left)
{
bounds.X = constrainingBounds.Left;
}
if (bounds.Bottom > constrainingBounds.Bottom)
{
bounds.Y = constrainingBounds.Bottom - 1 - bounds.Height;
}
else if (bounds.Top < constrainingBounds.Top)
{
bounds.Y = constrainingBounds.Top;
}
}
return bounds;
}
(ConstrainToBounds method is taken from the base class ToolStripDropDown via Reflector)
for the nested MenuItems to open on the same screen as ContextMenuStrip, I created a ToolStripMenuItemEx (which derives from ToolStripMenuItem). In my case it looks like this:
private ToolStripDropDownDirection? originalToolStripDropDownDirection;
protected override void OnDropDownShow(EventArgs e)
{
base.OnDropDownShow(e);
if (!Screen.FromControl(this.Owner).Equals(Screen.FromPoint(this.DropDownLocation)))
{
if (!originalToolStripDropDownDirection.HasValue)
originalToolStripDropDownDirection = this.DropDownDirection;
this.DropDownDirection = originalToolStripDropDownDirection.Value == ToolStripDropDownDirection.Left ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left;
}
}
The code of #David does not fix if the menu is opened in the left side of second screen. I have improved that code to work on all screen corner.
private void subMenu_DropDownOpening(object sender, EventArgs e)
{
ToolStripMenuItem mnuItem = sender as ToolStripMenuItem;
if (mnuItem.HasDropDownItems == false)
{
return; // not a drop down item
}
//get position of current menu item
var pos = new Point(mnuItem.GetCurrentParent().Left, mnuItem.GetCurrentParent().Top);
// Current bounds of the current monitor
Rectangle bounds = Screen.GetWorkingArea(pos);
Screen currentScreen = Screen.FromPoint(pos);
// Find the width of sub-menu
int maxWidth = 0;
foreach (var subItem in mnuItem.DropDownItems)
{
if (subItem.GetType() == typeof(ToolStripMenuItem))
{
var mnu = (ToolStripMenuItem) subItem;
maxWidth = Math.Max(mnu.Width, maxWidth);
}
}
maxWidth += 10; // Add a little wiggle room
int farRight = pos.X + mnuMain.Width + maxWidth;
int farLeft = pos.X - maxWidth;
//get left and right distance to compare
int leftGap = farLeft - currentScreen.Bounds.Left;
int rightGap = currentScreen.Bounds.Right - farRight;
if (leftGap >= rightGap)
{
mnuItem.DropDownDirection = ToolStripDropDownDirection.Left;
}
else
{
mnuItem.DropDownDirection = ToolStripDropDownDirection.Right;
}
}
I did not try the solution by tombam. But since the others didn't seem to work, I came up with this simple solution:
private void MenuDropDownOpening(object sender, EventArgs e)
{
var menuItem = sender as ToolStripDropDownButton;
if (menuItem == null || menuItem.HasDropDownItems == false)
return; // not a drop down item
// Current bounds of the current monitor
var upperRightCornerOfMenuInScreenCoordinates = menuItem.GetCurrentParent().PointToScreen(new Point(menuItem.Bounds.Right, menuItem.Bounds.Top));
var currentScreen = Screen.FromPoint(upperRightCornerOfMenuInScreenCoordinates);
// Get width of widest child item (skip separators!)
var maxWidth = menuItem.DropDownItems.OfType<ToolStripMenuItem>().Select(m => m.Width).Max();
var farRight = upperRightCornerOfMenuInScreenCoordinates.X + maxWidth;
var currentMonitorRight = currentScreen.Bounds.Right;
menuItem.DropDownDirection = farRight > currentMonitorRight ? ToolStripDropDownDirection.Left :
ToolStripDropDownDirection.Right;
}
Note that in my world, I was not concerned about multiple levels of cascading menus (as in the OP), so I did not test my solution in that scenario. But this works correctly for a single ToolStripDropDownButton on a ToolStrip.

checking a color of a button and changing it using a 2D array

I'm creating a small game just like the game Reversi/Othello I have managed to created a 2x3 board with buttons.
The buttons change colour ones you click on them but I'm having trouble to detect if there is a white colour in between 2 black colours and if so change that white colour into black.. I hope this make sense. the buttons are in a 2D array. Any suggestions that could help me do this would be much appreciated.
The image:
Here is my code:
![namespace reversitest
{
public partial class Form1 : Form
{
private Button\[,\] squares;
public Form1()
{
InitializeComponent();
squares = new Button\[3, 2\];
squares = new Button\[,\] {{button1, button2, button3},
{button4, button5, button6,}};
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (Button sqrr in squares)
{
sqrr.Click += new System.EventHandler(this.DrawCharacter);
}
}
int _turn = 0;
private void DrawCharacter(object sender, EventArgs e)
{
Button sqrr = (Button)sender;
int col = 0;
if (sqrr.BackColor.Equals(Color.Black) || sqrr.BackColor.Equals(Color.White))
{
MessageBox.Show("Move Not Allowed!");
}
else
{
for ( int i = 0; i < squares.GetLongLength(1); ++i)
{
// check othere squares and change color
if (i < 2)
{
for (int f = 0; f < 3; ++f)
{
var ss = squares\[i, f\];
if (ss.BackColor.Equals(Color.Black))
{
MessageBox.Show("we have a black");
//ss = squares\[i, f+1\];
ss.BackColor = Color.Black;
}
else
{
MessageBox.Show("no black");
}
}
}
if (_turn == 0)
{
_turn = 1;
sqrr.BackColor = Color.Black;
}
else
{
_turn = 0;
sqrr.BackColor = Color.White;
}
}
}
}
}
}
First name your buttons with the array index. It will help you to find the button.
For example according to you picture button1 name would be btn_1_1.
Then inside your button click event first get the button name and then identify the button positioned.
Button b = sender as Button;
string[] btnData = b.Name.Split('_');
int x = int.Parse(btnData[1]);
int y = int.Parse(btnData[2]);
//check for possible combinations
int top = y - 2;
int botton = y + 2;
int left = x - 2;
int right = x + 2;
if (top >= 0 && squares[top, y].Background == Color.Black)
{
squares[top+1, y].Background = Color.Black;
}
...
...
Continue like that. If you need more detail please free to ask.
Final Answer
//check for possible combinations
int top = x - 2;
int botton = x + 2;
int left = y - 2;
int right = y + 2;
if (top >= 0 && squares[top, y].BackColor == Color.Black)
{
squares[top + 1, y].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
Will be extended for a 8x8 board later on
Do you need it to be elegant? A kind of brute force method: You could check for pieces in the 8 different directions it is possible for them to be aligned. So for example, you start with a black piece. Check the next piece over in one direction. If it's white, keep going and take a note of the position that was white so you can change it to black later. When you finally hit a black piece, change all the stored positions to black and move on to the next direction and repeat the process until you've done all 8 directions.

Categories