I am trying to make a Tic Tac Toe game in Silverlight that can be played in 1 player mode.
So for that after clicking on any button and change it's content to "X" or "O" i need to change the content of a random button.
I've tried making a list of all buttons and getting a random value:
public List<string> avail = new List<string>() { "button1", "button2", "button3", "button4", "button5", "button6", "button7", "button8", "button9" };
public string Ran()
{
Random b1 = new Random();
int index = b1.Next(avail.Count);
if (index > 0)
return avail[index];
else
return null;
}
but i don't know how to make my random string a Button so i can call the following method:
public void buttonchange(Button b)
{
if (b.Content.ToString() == "")
if (x == true)
{
x = false;
b.Content = "X";
}
else
{
x = true;
b.Content = "O";
}
if(b.Name!=null)
avail.Remove(b.Name);
}
Any ideas?
thank you!
Make a list of button references instead:
public List<Button> avail = new List<Button>() { button1, button2, button3, button4, button5, button6, button7, button8, button9 };
public Button Ran()
{
Random b1 = new Random();
int index = b1.Next(avail.Count);
if (index > 0) {
return avail[index];
} else {
return null;
}
}
public void buttonchange(Button b)
{
if(b != null) {
if (b.Content.ToString() == "") {
b.Content = x ? "X" : "O";
x = !x;
}
avail.Remove(b);
}
}
Not sure why the button with index zero isn't to be used, though...
Related
I've made a small tool bar that sits in a transparent form, it loads a variable sized menu from a text file and can be changed on the fly. Each button is a type of Label, the bar is just a list of buttons and adds/removes them in the correct spots. Width of the form is only a little bigger than the menu bar so that sub menu isn't cut off
Everything is working sweet except, when I reload everything part of the toolbar is lost. I've attempted to change the width so many ways, I've cleared and removed the controls from the form, refreshing the form/menu, updating it etc however nothing seems to make it work as intended EXCEPT if I call the reload function twice in a row, it works. I can't see why calling it once doesn't work but calling it twice works.
I'm fine with calling reload twice in a row as it would only be called a couple times a week.
Question: what on earth is causing this?
photo of issues first photo shows what it should look like, second is after removing a menu button and reloading, third is after adding a button and reloading
//calling this.reload() doesn't work
//calling this.reload();this.reload() works
void reload(Object o = null, EventArgs e = null)
{
this._menuBar.clear();
this.loadFromFile();
}
void loadFromFile(Object o = null, EventArgs e = null)
{
try
{
if (File.Exists("kpi.txt"))
{
string cline = "", cmenu = "", lhs = "";
menuList mb = null;
StreamReader sr = new StreamReader("kpi.txt");
while (!sr.EndOfStream)
{
cline = sr.ReadLine(); //get current line
if (cline.Length > 0 && cline[0] != ';')
{
//check if main menu/command
if (cline[0] == '[')
{
cmenu = Regex.Match(cline, #"(?<=^\[)[a-zA-Z -\#_{-~\^\r\n]+(?=\])").Value;
if (cmenu != "")
{
mb = this._menuBar.addMenuButton(cmenu);
mb.data["options"] = Regex.Match(cline, #"\/\w+$").Value;
var match = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
mb.data["count"] = (match.Success ? match.Value : "0");
mb.data["copy"] = "";
applyMenuOptions(mb, false);
}
}
//just a standard line
else
{
cline = cline.Trim();
lhs = Regex.Match(cline, #"^[^\;\<\[\]\r\n]+(?=$|\<|\;)").Value;
if (mb.getSubMenuItem(lhs) == null)
{
var newButton = mb.addSubMenu(lhs);
if (newButton != null)
{
newButton.parent = mb;
newButton.data["options"] = mb.data["options"];
newButton.data["copy"] = Regex.Match(cline, #"((?<=\;)[^\[\]\<\r\n]+(?=<|$))").Value;
var matches = Regex.Match(cline, #"(?<=<)([^>\[\]\r\n]+)(?=>)");
int intout = 0;
if (int.TryParse(matches.Value, out intout))
{//no description
newButton.data["description"] = "";
newButton.data["count"] = intout.ToString();
}
else
{
newButton.data["description"] = matches.Value;
newButton.data["count"] = (matches.NextMatch().Success ? matches.NextMatch().Value : "0");
}
applyMenuOptions(newButton);
newButton.addMiddleClick(this.addcopy);
if (newButton.data["options"].Contains("i"))
{
newButton.addRightClick(this.appendInfo);
newButton.addRightClick(this.increment);
}
}
}
}
}
}
sr.Close();
this._menuBar.squish();
this.Width = this._menuBar.Width+50;
}
else
{
menuList mb = this._menuBar.addMenuButton("menu");
mb.data["options"] = "\\m";
mb.data["count"] = "0";
mb.data["copy"] = "";
mb.data["description"] = "";
applyMenuOptions(mb, false);
saveDictonary();
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to load data " + ex);
//ILog log = LogManager.GetLogger(typeof(Program));
//log.Info(ex);
}
}
public menuList addMenuButton(string s, int w = 0, int h = 0, int x = -1, int y = -1)
{
menuList mb = new menuList(this._form, s);
if (this.menuItems.Exists(z => z.Text == s)) return null;
mb.Width = (w==0?settings.intOf("ButtonWidth"):w);
mb.Height = (h==0?settings.IntOf("ButtonHeight"):h);
if (x == -1 || y == -1)
mb.Location = new Point(this.menuItems.Count > 0 ? this.menuItems.Last().Location.X + this.menuItems.Last().Width : padding);
else mb.Location = new Point(x, y);
mb.BringToFront();
mb.Show();
this.menuItems.Add(mb);
// this.Refresh();
return mb;
}
internal void clear()
{
foreach(var i in this.menuItems)
{
this._form.Controls.Remove(i);
i.clear();
i.Dispose();
}
this.menuItems.Clear();
this._form.Controls.Remove(this);
this.menuItems = new List<menuList>();
this._form.Controls.Add(this);
}
internal void squish()
{
try
{
this.Width = (this.menuItems.Count * this.menuItems.First().Width) + (2 * padding);
}
catch(Exception ex) { MessageBox.Show(""+ex); }
}
Found the culprit, bother the button class and the tool bar class were both adding themselves to the form control instead of button class adding to the tool bar (picture box) controls!
Removing transparency showed the buttons not moving when the tool bar was moved!
I store in dictionary all the values of listView in dictionary-value and store its index in dictionary-keys at form load.
I am using dictionary as a medium of index storage for corresponding transferred listitems.
Now i transfer from a to b on a button click (a listview is full and b is empty) then i do again transfer the same element from b to a on another button click. It is now appended at last when i do back to b->a transfer. But i want it to append on same index on which it was before transferring to a->b.
I mean when i do b->a transfer then it must go to same index where it used to stay before
My code is this (please correct me where i am wrong and please give me a solution)
private void Form1_Load(object sender, EventArgs e)
{
//storing data in dictionary
}
private void button1_Click(object sender, EventArgs e)
{
MoveSelectedItems(listView1, listView2,0);
}
private void button2_Click(object sender, EventArgs e)
{
MoveSelectedItems(listView2, listView1,1);
}
private void MoveSelectedItems(ListView source, ListView target, int flag)
{
ListViewItem temp = source.SelectedItems[0];
source.Items.Remove(temp);
if(flag==0)//called when i do a->b
{
target.Items.Add(temp);
}
else
{
int index=getIndex(temp);
target.Items.Insert(index, temp);
}
}
private int getIndex(ListViewItem temp)
{
int index = 0;
if (dictList.ContainsValue(temp.Text))
{
foreach (KeyValuePair<int, string> pair in dictList)
if (temp.Text.Equals(pair.Value))
{
index=Convert.ToInt32(pair.Key);
return index;
}
}
return index;
}
What you actually need is, after taking the desired index from the dictionary, to search the items currently in the list view and find the actual insert position.
There are different way you can do that, here is one using binary search:
else
{
foreach (ListViewItem item in source.SelectedItems)
{
ListViewItem lvItem = item.Clone() as ListViewItem;
int index = dictList[item.Text];
// Insert at appropriate position based on index value
if (index == 0) // Always first
target.Items.Insert(0, lvItem);
else if (index == dictList.Count - 1) // Always last
target.Items.Add(lvItem);
else
{
// Binary search the current target items
int lo = 0, hi = target.Items.Count - 1;
while (lo <= hi)
{
int mid = lo + (hi - lo) / 2;
if (index < dictList[target.Items[mid].Text])
hi = mid - 1;
else
lo = mid + 1;
}
// Here lo variable contains the insert position
target.Items.Insert(lo, lvItem);
}
source.Items.Remove(item);
}
}
EDIT: Here is a [mcve] proving that it works:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Samples
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var splitView = new SplitContainer { Dock = DockStyle.Fill, Parent = form, Orientation = Orientation.Vertical };
var listView1 = new ListView { Dock = DockStyle.Fill, Parent = splitView.Panel1, View = View.List };
var listView2 = new ListView { Dock = DockStyle.Fill, Parent = splitView.Panel2, View = View.List };
var buttonPanel = new Panel { Dock = DockStyle.Bottom, Parent = form };
var button1 = new Button { Parent = buttonPanel, Left = 16, Top = 8, Text = ">" };
var button2 = new Button { Parent = buttonPanel, Left = button1.Right + 16, Top = 8, Text = "<" };
buttonPanel.Height = button1.Height + 16;
var dictList = new Dictionary<string, int>
{
{ "first", 0 },
{ "second", 1 },
{ "third", 2 },
{ "fourth", 3 },
{ "fifth", 4 },
{ "sixth", 5 },
{ "seventh", 6 },
};
foreach (var item in dictList)
listView1.Items.Insert(item.Value, item.Key);
Action<ListView, ListView, int> MoveSelectedItems = (ListView source, ListView target, int flag) =>
{
while (source.SelectedItems.Count > 0)
{
var item = source.SelectedItems[0];
source.Items.Remove(item);
if (flag == 0)
{
target.Items.Add(item);
}
else
{
int index = dictList[item.Text];
// Insert at appropriate position based on index value
if (index == 0) // Always first
target.Items.Insert(0, item);
else if (index == dictList.Count - 1) // Always last
target.Items.Add(item);
else
{
// Binary search the current target items
int lo = 0, hi = target.Items.Count - 1;
while (lo <= hi)
{
int mid = lo + (hi - lo) / 2;
if (index < dictList[target.Items[mid].Text])
hi = mid - 1;
else
lo = mid + 1;
}
// Here lo variable contains the insert position
target.Items.Insert(lo, item);
}
}
}
};
button1.Click += (sender, e) => MoveSelectedItems(listView1, listView2, 0);
button2.Click += (sender, e) => MoveSelectedItems(listView2, listView1, 1);
Application.Run(form);
}
}
}
You can add a ListViewItemComparer to the list view. Like this.
public class ListViewItemComparer : IComparer
{
public int Compare(object x, object y)
{
return (((ListViewItem)x).Index > ((ListViewItem)y).Index ? 1 : -1);
}
}
And then in MoveSelectedItems:
this.listView1.ListViewItemSorter = new ListViewItemComparer();
Good luck.
As the title says, I'm trying to make a piece of code that will randomly select a group (A-H) with no repeats in a windows form using C#. It seems to work, but after 4 times it will return that all groups have been selected. When I remove the line of code to output that they've been selected then I get an infinite loop instead. Anyone see what my issue may be?
public Form1()
{
InitializeComponent();
}
//these booleans are set to true if the group has already been selected
bool A = false;
bool B = false;
bool C = false;
bool D = false;
bool E = false;
bool F = false;
bool G = false;
bool H = false;
private void selectButton_Click(object sender, EventArgs e)
{
string selection = "error";
while (selection == "error")
{
int group = RandomNumber();
if (group == 1 && A == false)
{
selection = "A";
A = true;
}
else if (group == 2 && B == false)
{
selection = "B";
B = true;
}
else if (group == 3 && C == false)
{
selection = "C";
C = true;
}
else if (group == 4 && D == false)
{
selection = "D";
D = true;
}
else if (group == 5 && E == false)
{
selection = "E";
E = true;
}
else if (group == 6 && F == false)
{
selection = "F";
F = true;
}
else if (group == 7 && G == false)
{
selection = "G";
G = true;
}
else if (group == 8 && H == false)
{
selection = "H";
H = true;
}
else if (A == true && B == true && C == true && D == true && E == true && F == true && G == true && H == true)
{
selection = "all have been selected";
}
//else
// {
// selection = "oops";
// };
};
outputRichTextBox.Text = selection;
}
private int RandomNumber()
{
Random rnd = new Random();
int num = rnd.Next(1, 9);
return num;
}
}
}
You shouldn't recreate a Random for every use. Make it an instance or static field instead.
private Random rnd = new Random();
private int RandomNumber()
{
int num = rnd.Next(1, 9);
return num;
}
On top of that, you'd probably be better-off using an array to store your values. That would just make your code a bit more readable.
Just for the sake of making this a little easier to understand, this is what I would do.
public Form1()
{
var poss = (new [] { "A", "B", "C", "D", "E", "F", "G", "H" })
.OrderBy(c => RandomNumber())
.GetEnumerator();
}
private IEnumerator<string> selections;
private Random rnd = new Random();
private void selectButton_Click(object sender, EventArgs e)
{
if (selections.MoveNext())
outputRichTextBox.Text = selections.Current;
else
outputRichTextBox.Text = "all have been selected";
}
private int RandomNumber()
{
int num = rnd.Next();
return num;
}
The idea here is to create and randomize the list at the beginning (through ordering by a random number), then loop through on each click until that list is empty.
First Random in .NET does not generate a real random number and based on the seed you pass to it's constructor, it can generate the exact series again and again. It's not a bug. It is designed so for testing your app against pseudo-random series of data, you must use RNGCryptoServiceProvider for generating a real random number:
public class SampleRandomNumber
{
static readonly object Lock = new object();
static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider();
public static int NextInt32()
{
lock (Lock)
{
var bytes = new byte[4];
Random.GetBytes(bytes);
return BitConverter.ToInt32(bytes, 0);
}
}
}
And NextInt32 gives us an int (including even negative numbers; of-course you can just use the abs of it).
C# TextBox
AutoCompleteCustomSource has a List<string>,
AutoCompleteMode = Suggest.
I can see the List when I type a Letter.
How to show entire list without Typing a Letter Programmatically? This must be done while the User presses the Down Arrow Key in the TextBox.
Is there any Win32 API Available?
My Solution
I refined a Better Solution.
Add a ListBox Control to the form and make it as Visible = false
int curSelIndex = -1;
The below given Code will be executed Form_Load Event.
txtEmpId.AutoCompleteCustomSource.AddRange(EmpIds.ToArray());
lstAutoComplete.Items.Clear();
lstAutoComplete.Items.AddRange(EmpIds.ToArray());
txtEmpId.KeyDown += (ks, ke) =>
{
if (!(ke.KeyCode == Keys.Down ||
ke.KeyCode == Keys.Up ||
ke.KeyCode == Keys.Enter))
{
lstAutoComplete.Visible = false;
return;
}
ke.Handled = true;
if (ke.KeyCode == Keys.Enter)
{
if (lstAutoComplete.Visible)
{
var str = lstAutoComplete.SelectedItem + "";
// Process the Selected Item and set to TextBox.
}
}
if (!lstAutoComplete.Visible && txtEmpId.Focused)
{
var loc = txtEmpId.Location;
loc.Y += txtEmpId.Height;
lstAutoComplete.Location = loc;
lstAutoComplete.Size = txtEmpId.Size;
lstAutoComplete.Height = 100;
lstAutoComplete.SelectedIndex = 0;
curSelIndex = 0;
lstAutoComplete.Visible = true;
}
else if(lstAutoComplete.Visible && txtEmpId.Focused)
{
if (ke.KeyCode == Keys.Down)
{
curSelIndex++;
if (curSelIndex >= lstAutoComplete.Items.Count)
curSelIndex = lstAutoComplete.Items.Count - 1;
if (lstAutoComplete.Items.Count > 0)
lstAutoComplete.SelectedIndex = curSelIndex;
}
else if (ke.KeyCode == Keys.Up)
{
curSelIndex--;
if (curSelIndex < 0)
curSelIndex = 0;
if (lstAutoComplete.Items.Count > 0)
lstAutoComplete.SelectedIndex = curSelIndex;
}
}
};
txtEmpId.Leave += (ls, le) => lstAutoComplete.Visible = false;
I didn't find any API for your problem, so I just make a my own suggestion box by using ListBox to show when the Down Arrow Key is pressed, when you do other operation, it disappeares. I hope it is useful to you. code sample is bellow:
//string datasource
List<string> strList = null;
//suggestion listbox
ListBox sugBox = null;
public FrmTextSuggest()
{
InitializeComponent();
//setting the textbox control
strList = new List<string>()
{
"USA",
"England",
"China",
"Japan",
"Korea",
"India",
"France",
"Canada"
};
var autoCollection = new AutoCompleteStringCollection();
autoCollection.AddRange(strList.ToArray());
this.txtCountry.AutoCompleteCustomSource = autoCollection;
this.txtCountry.AutoCompleteMode = AutoCompleteMode.Suggest;
this.txtCountry.AutoCompleteSource = AutoCompleteSource.CustomSource;
//register the Down Arrow Key event
this.txtCountry.KeyDown += new KeyEventHandler(txtCountry_KeyDown);
}
void txtCountry_KeyDown(object sender, KeyEventArgs e)
{
//show the your own suggestion box when pressing down arrow and the text box is empty
if (e.KeyCode == Keys.Down && txtCountry.Text.Trim().Equals(""))
{
sugBox = new ListBox();
//define the box
sugBox.Width = txtCountry.Width;
Point p = txtCountry.Location;
p.Y += txtCountry.Height;
sugBox.Location = p;
sugBox.Items.AddRange(strList.ToArray());
//copy the value to the textbox when selected index changed.
sugBox.SelectedIndexChanged += new EventHandler(sugBox_SelectedIndexChanged);
//show box
if (sugBox.Items.Count > 0)
{
sugBox.SelectedIndex = 0;
this.Controls.Add(sugBox);
sugBox.Focus();
}
}
//remove and hide your own suggestion box when other operation
else
{
if (sugBox != null && this.Controls.Contains(sugBox))
{
this.Controls.Remove(sugBox);
sugBox.Dispose();
sugBox = null;
}
}
}
void sugBox_SelectedIndexChanged(object sender, EventArgs e)
{
string selText = this.sugBox.SelectedItem.ToString();
if (!string.IsNullOrEmpty(selText))
{
this.txtCountry.Text = selText;
}
}
here is my result of test:
Is there a way to reference buttons using a numerical value in C#? I am trying to manipulate the text on buttons using one reusable method. Here is my current coding:
One button click method (there are a total of 16):
private void Card1_Click(object sender, EventArgs e)
{
buff = CardClick(1);
if (buff != null)
{
Card1.Text = buff;
}
}
And the reusable method (the code does have holes, it's in development):
private string CardClick(int card)
{
guesses[g++] = card; //alternate g
if ((guesses[0] != null) && (guesses[1] != null))
{
//Reset Card guesses[0]
//Reset Card guesses[1]
return null;
}
else
{
if (card > 8)
{
return map[2, card];
}
else
{
return map[1, card];
}
}
You can also use Controls.Find() to get a reference to your desired button based on its name:
int i = 1;
Control[] matches = this.Controls.Find("Card" + i.ToString(), true);
if (matches.Length > 0 && matches[0] is Button)
{
Button btn = (Button)matches[0];
// ... do something with "btn" ...
btn.PerformClick();
}
You can use an array of buttons
Button[] buttonArray = new Button[10];
You can get all the buttons from your form by Type and then extract an array:
public Button[] AllButtons()
{
var buttons = new List<Button>();
foreach (var control in this.Controls)
{
if (control.GetType() == typeof(Button))
buttons.Add((Button)control);
}
return buttons.ToArray();
}