I am creating a Sudoku game with ASP.NET and C#. I am required to use classes and inheritance to build the structure entirely in the code-behind page (i.e. no asp:TextBox controls on the aspx page).
I am having an awful time trying to get my inputs to clear when I start a new game. I generate a new puzzle solution, but my previous boxes don't clear and I've tried everything I can think of.
Below are several chunks of code that relate to the problem.
The code that builds the puzzle and stores it in a Puzzle object.
private Puzzle newPuzzle(int[,] solution, int numbersVisible, int maxNumbersPerBox, int maxOccurancesPerNumber)
{
Puzzle newPuzzle = new Puzzle();
SudokuTextBox newTextbox;
Number newNumber;
Random randomRC = new Random();
//variable to hold the correct answer at the given location
int answer;
//variables to hold the location within the answers array
int rowLoc;
int colLoc;
//counter to count the number of times we while loop
int counter = 0;
//variables to hold the randomly-chosen rows & col values
int row;
int col;
//array to hold the positions of the numbers we are going to show
string[] show;
show = new string[numbersVisible];
while(counter < numbersVisible)
{
//generate random numbers that gives us the location of the numbers in the solution array that we are going to show
row = randomRC.Next(0, 9);
col = randomRC.Next(0, 9);
//if the random numbers are not already in the array
if (!show.Contains(row.ToString() + ":" + col.ToString()))
{
//add them to the array
show[counter] = row.ToString() + ":" + col.ToString();
//increase the counter
counter++;
} //end if...contains
} //end while
// BUILDING THE PUZZLE
//start looping through the puzzle rows
for (int pr = 0; pr < 3; pr++)
{
//another loop for puzzle columns
for (int pc = 0; pc < 3; pc++)
{
box = new Box(); //create a new Box object
//another loop for box rows
for (int br = 0; br < 3; br++)
{
//another loop for box columns
for (int bc = 0; bc < 3; bc++)
{
newTextbox = new SudokuTextBox();
newNumber = new Number();
//grab the answer to this particular SudokuTextBox from the solutions array
rowLoc = (pr + br + (2 * pr));
colLoc = (pc + bc + (2 * pc));
answer = solution[rowLoc, colLoc];
newNumber.setNumber(answer); //set the Number to the found answer
newTextbox.setTextBoxValue(newNumber); //fill in the textbox with Number
//if this SudokuTextBox is chosen to be given at the start of the puzzle
if (show.Contains((rowLoc + ":" + colLoc).ToString()))
{
//make this SudokuTextBox visible
newTextbox.setVisibility(true);
}
else {
newTextbox.setVisibility(false);
} //end if
box.setItem(newTextbox, br, bc); //add the SudokuTextBox to the correct position inside Box
} //end box column loop
} //end box row loop
newPuzzle.setItem(box, pr, pc); //add the Box to the correct position inside Puzzle
} //end puzzle column loop
} //end puzzle row loop
return newPuzzle;
} //end easy()
Storing the new puzzle in Session:
//when the Easy button is pressed
protected void btnEasy_Click(object sender, EventArgs e)
{
//generate a new random number
Random newRandomSoln = new Random();
//keep picking new solutions until we get one that's different than the last one
do
{
solution = chooseSolution(newRandomSoln.Next(1, 11));
}
while (solution == Session["solution"]);
//store the new solution
Session["solution"] = solution;
//generate a new puzzle
Session["puzzle"] = newPuzzle( (int[,])Session["solution"], 32, 4, 4 );
}
The code that builds the table structure, fills it with the answers stored in Puzzle, and adds it to the aspx page:
////////////////////////////////
// CREATING THE PUZZLE
///////////////////////////////
Table structure = new Table(); //table to be the outer structure of the puzzle
TableRow row; //row variable to make new rows
TableCell cell; //cell variable to make new cells
Table boxTable; //table that will hold individual Boxes
TableRow boxRow; //row that will hold 3 SudokuTextBoxes
TableCell boxCell; //cell that will hold a single SudokuTextBoxes
TextBox input; //textbox that will hold the textbox in SudokuTextBox
int answer; //int to hold the answer to a particular textbox
//start looping through the puzzle rows
for (int pr = 0; pr < 3; pr++)
{
row = new TableRow(); //create a new outer row
//another loop for puzzle columns
for (int pc = 0; pc < 3; pc++)
{
cell = new TableCell(); //create a new outer cell
boxTable = new Table(); //create a new inner table
box = new Box(); //create a new Box object
box = ((Puzzle)Session["puzzle"]).getItem(pr, pc); //find the box at the current location in the puzzle
//another loop for box rows
for (int br = 0; br < 3; br++)
{
boxRow = new TableRow(); //create a new inner row
//another loop for box columns
for(int bc = 0; bc < 3; bc++)
{
boxCell = new TableCell(); //create a new inner cell
textbox = new SudokuTextBox(); //create a new SudokuTextBox object
textbox = box.getItem(br, bc); //find the SudokuTextBox at the current location in the box
//grab the answer to this particular SudokuTextBox from the solutions array
answer = ((int[,])Session["solution"])[ (pr + br + (2 * pr)), (pc + bc + (2 * pc)) ];
input = textbox.getTextBox(); //grab the textbox inside SudokuTextBox and store it
input.MaxLength = 1; //only allow 1 character to be typed into the textbox
//give the textbox an ID so we can find it later
input.ID = ("tb" + (pr + br + (2 * pr)) + "_" + (pc + bc + (2 * pc))).ToString();
boxCell.Controls.Add(input); //add the textbox to the inner cell
boxRow.Controls.Add(boxCell); //add the inner cell to the inner row
} //end box column loop
boxTable.Controls.Add(boxRow); //add the inner row to the inner table
} //end box row loop
cell.Controls.Add(boxTable); //add the inner table to the outer cell
row.Controls.Add(cell); //add the outer cell to the outer row
} //end puzzle column loop
structure.Controls.Add(row); //add the outer row to the outer table
} //end puzzle row loop
pnlPuzzle.Controls.Add(structure);
////////////////////////////////
// end puzzle
///////////////////////////////
And the SudokuTextBox class code:
public class SudokuTextBox
{
private System.Web.UI.WebControls.TextBox textbox;
private bool visible;
private Number number;
public SudokuTextBox()
{
textbox = new System.Web.UI.WebControls.TextBox();
visible = false;
number = new Number();
} //end constructor
//function to make a new textbox
public System.Web.UI.WebControls.TextBox getTextBox()
{
return textbox;
}
//function to get the value of a textbox
public Number getTextBoxValue()
{
return number;
}
//????????????
public void setTextBoxValue(Number newNumber)
{
this.number.setNumber(newNumber.getNumber());
}
//function to get the visibility of a textbox
public bool getVisibility()
{
return visible;
}
//function to change the visibility of a textbox
public void setVisibility(bool newVisible)
{
if (newVisible)
{
//if the textbox is visible
//get the number
//and make it disabled
textbox.Text = number.getNumber().ToString();
textbox.ReadOnly = true;
textbox.BackColor = System.Drawing.Color.FromArgb(150, 148, 115);
} else
{
//if it is not visible
//hide the number
//and make it enabled
textbox.ReadOnly = false;
textbox.Text = "";
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to change the color of the textbox if it is wrong
public void setWrongNumber()
{
textbox.BackColor = System.Drawing.Color.FromArgb(5, 156, 202, 252);
}
//function to change the color of the textbox if it is correct
public void setCorrectNumber()
{
//but don't change disable text boxes
if(textbox.ReadOnly != true)
{
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to change the color of the textbox if it is blank
public void setBlankNumber()
{
//but don't change disable text boxes
if (textbox.ReadOnly != true)
{
textbox.BackColor = System.Drawing.Color.White;
}
}
//function to show the value of a textbox when clicking the "Hint" button
//also changes the color of the textbox so we know it was shown with a hint
public void setHint()
{
setVisibility(true);
}
} //end class
Thank you to all who gave your two cents. I just wanted to let everyone know that I have solved the issue:
I ended up separating out the code that builds the table structure, taking it out of Page_Load and putting it into its own function. I then called this function from Page_Load. I also called this function when I click the "new puzzle" button. I also added similar logic to that which I commented above to clear the previous table structure before building a new one:
foreach(Control c in pnlPuzzle.Controls){
pnlPuzzle.Controls.Remove(c);
}
I'm not exactly sure why this solve my problem, but it worked!
Related
Good Day Everyone.
I'm creating function in which i dynamically generate textbox depending on the selected value in the dropdown list.
Here's the code.
comboboxNameHolder = ((ComboBox)sender).Name;
string comboboxNoHolder =comboboxNameHolder.Replace("cbFunctionList", "");
comboboxNo = Int32.Parse(comboboxNoHolder);
funcSelected = ((ComboBox)sender).SelectedItem.ToString();
for (int i = 0; i < optionList1.GetLength(0); i++)
{
if (funcSelected == optionList1[i, 0])
{
funcNoOfFields = optionList1[i, 1];
}
}
if (lineFieldController[comboboxNo, 1] == 0)
{
fieldCounter = Int32.Parse(funcNoOfFields);
lineFieldController[comboboxNo, 1] = fieldCounter;
inputField1 = new TextBox[fieldCounter];
for (int i = 0; i < fieldCounter; i++)
{
btnAddField0.Visible = false;
inputField = new TextBox();
inputField.Font = new Font("Microsoft Sans Serif", 11.25f);
inputField.Size = new Size(75, 24);
inputField.Location = new Point(positionController[comboboxNo, 0], positionController[comboboxNo, 1]);
inputField.Name = "txtLine" + comboboxNo.ToString() + "Variable" + i.ToString();
this.Controls.Add(inputField1[i]);
positionController[comboboxNo, 0] += 81;
}
}
Now I want in the same function when the lineFieldController is not equal to zero means that there are already created textbox in that line. When the user chooses another value in the dropdown list the number of fields will change by deleting the existing fields then creating new ones depending on the selected item.
How do I delete the textboxes I created?? I tried calling it by name but it doesn't work.
else
{
for(int i = 0; i < lineFieldController[comboboxNo, 1]; i++)
{
string name = "txtLine" + comboboxNo.ToString() + "Variable" + i.ToString();
TextBox tb = this.Controls.Find(name, true);
}
}
Hoping for your kind response
you can put all controls that you have created put them to the list and hold reference on controls were created at runtime.
like
public class Form1
{
List<Control> createdList = new List<Control>(); // class field
void combobox_SelectedIndexChanged()
{
// removing controls were created before
foreach (var created in createdList)
{
this.Controls.Remove(created);
created.Dispose();
}
createdList.Clear(); // all created controls from previous index changed should be removed here
// add each control you are creating to the createList additionally
inputField1 = new TextBox[fieldCounter];
for (int i = 0; i < fieldCounter; i++)
{
btnAddField0.Visible = false;
inputField = new TextBox();
createdList.Add(inputField); //store reference
/// skipping init code
this.Controls.Add(inputField1[i]);
positionController[comboboxNo, 0] += 81;
}
}
}
another option is to add panel on the form as a placeholder for all controls are being created. You have to change this.Controls.Add(inputField1[i]); to the panelCreated.Controls.Add(inputField1[i]);
Then you can grab all controls from the panel and remove them without name search like below
foreach (Control created in panelCreated.Controls)
created.Dispose();
panelCreated.Controls.Clear();
Problem in short: Load saved map that was saved as an textfile where 0 = empty button/tile, 1 = wall, 2 = character and it back up on another form.
Longer more detailed explanation:
I have been working on a winform game for a bit, I know it isn't the best way to make a game but I am just trying to get accustomed. Currently I have two forms, one to create a map, another to load it up. On my map creation form I am able to say the length and width of my map, when I generate it, it displays tiles(that are buttons) with the length and width dimensions specified( 3 buttons wide, 4 buttons long.)
Now I am able to save it as numbers that were given values for that certain image ( 0 is for no image just the button, 1 is for a button with an wall, 2 is for a button with a character.). What I am trying to do now is read the text file and load up the tiles with the correct dimensions and image that was originally given.
My images are placed onto radio buttons which determine which image to play, I did tags for this
public SaveForm()
{
InitializeComponent();
rNoImage.Tag = 0;
rNoImage.Click += Check;
rCharacter.Tag = 1;
rCharacter.Click += Check;
rWall.Tag = 2;
rWall.Click += Check;
}
private void square_Click(object sender, EventArgs e)
{
Map square = (Map)sender;
Map.Type = (MapType)selected;
switch (selected)
{
case 0:
square.Image = null;
break;
case 1:
square.Image = Properties.Resources.Character;
break;
case 2:
square.Image = Properties.Resources.Wall;
break;
}
}
private void Check(object sender, EventArgs e)
{
RadioButton toolBtn = (RadioButton)sender;
selectedTool = (int)toolBtn.Tag;
}
When saving I have done the following:
private void saveButton(object sender, EventArgs e)
{
SaveFileDialog sfg = new SaveFileDialog();
sfg.Filter = "Game File(*.game)|*.game";
if (sfg.ShowDialog() == DialogResult.OK)
{
using (StreamWriter sw = new StreamWriter(sfg.FileName))
{
sw.WriteLine($"{rows},{cols}");
foreach (Map square in (pnlGameBoard.Controls))
{
sw.WriteLine(square.GetString());
}
var output = MessageBox.Show("saved", "saved success", MessageBoxButtons.OK);
}
}
}
I have a component class set up for this called Map in this I have an enum to show the types
public enum MapType
{
None,
Character,
Wall,
}
Now I have my map being made as an Button, when clicked based on selected radio button image changes on the button.
public partial class Map : Button
{
int row;
int col;
public MapType Type { get; set; }
const int OFFSET = 20;
const int MapSize = 50;
public Map()
{
}
public Map(int row, int col)
{
this.row = row;
this.col = col;
this.Size = new Size(MapSize, MapSize);
this.Location = new Point(OFFSET + col * MapSize, OFFSET + row * MapSize);
Type = MapType.None;
}
public string GetString()
{
return $"{(int)Type}";
}
}
So I am able to save it, but my issue now is loading it up on the second form which I am struggling to do.
What I have managed so far is:
private void openButton_Click(object sender, EventArgs e)
{
OpenFileDialog ofg = new OpenFileDialog();
ofg.Filter = "Game File(*.game)|*.game;"
if(ofg.ShowDialog() == DialogResult.OK)
{
File.ReadAllLines(ofg.FileName);
//Not Sure what to do next here
}
}
Apologize for the lengthy post but I wanted to make sure that I am clear enough, thanks.
The layout for the textfile that I saved would be:
3,3
1
1
1
0
2
0
1
1
1
3,3 represents the length and width that was chosen, I will most likely remove this as I am only trying to read the numbers below, top row is wall,wall,wall(1,1,1), middle row is empty button, character, empty button, bottom row is wall,wall,wall.
I have looked at some different examples here that involve reading 2D matrixes from files to 2D int arrays but I am not sure how to actually implement this in my above code or how it works.
You've created a way to represent a Map as a string, but not the other way around. One way to create a Map from a string is to write a public static Map Parse(string input) method that takes in a string and returns a Map. One tricky part is that we will also need to pass in the row and col, since those are private fields and not settable outside of the constructor.
For example:
public class Map : Button
{
// Existing code exluded from this example
public static Map Parse(string input, int row, int col)
{
if (input == null) return null;
int typeVal;
if (!int.TryParse(input, out typeVal))
{
throw new ArgumentException("input must be a valid integer");
}
// Return a new map with row, col, and Type
return new Map(row, col) {Type = (MapType) typeVal};
}
}
Now that we can create a Map with the proper Type from a string (along with the row and col), we can create a couple of loops: one to loop through each row, and on each row we loop through each column, creating a Map and adding it to our panel:
// You might need to set some standard sizes for the Map controls
private int mapHeight = 50;
private int mapWidth = 300;
// Not sure where these are actually defined
private int rows;
private int cols;
private void openButton_Click(object sender, EventArgs e)
{
var ofg = new OpenFileDialog {Filter = "Game File(*.game)|*.game;"};
if (ofg.ShowDialog() == DialogResult.OK)
{
// Read all our lines into an array
var lines = File.ReadAllLines(ofg.FileName);
// And now do the opposite of how we created the text file:
// First, set the rows and cols based on the first line
// (some validation should be done here, but this works with "3,3")
var rowsCols = lines[0].Split(',');
int.TryParse(rowsCols[0], out rows);
int.TryParse(rowsCols[1], out cols);
// Next, create Maps from the rest of the lines and add them to our controls
// Note we should validate that lines.Length = rows * cols + 1
var line = 1; // This gets incremented below, so we read each line
for (int row = 0; row < rows; row ++)
{
for (int col = 0; col < cols; col++)
{
// Create a map from the line, row, and column using our new method
var map = Map.Parse(lines[line++], row, col);
// Set the layout by rows and columns (?)
map.Width = mapWidth;
map.Height = mapHeight;
map.Left = (col + 1) * mapWidth;
map.Top = (row + 1) * mapHeight;
// Hook up the Click event so our images load on click
map.Click += square_Click;
// Add the control to our panel
pnlGameBoard.Controls.Add(map);
}
}
}
}
My table in MySQL is like this:
I have a panel in my Form, and I want to create a TextBox for each data on this table and write sikicerik data to that TextBox's text.
Actually I did it but it creates only one TextBox and selects only the first data on the table.
My code is like this:
int count = oku.FieldCount;
reader.Read();
{
for (int i = 0; i < count; i++)
{
TextBox txt1 = new TextBox();
Point txtyer = new Point(x, y);
txt1.Text = reader["sikicerik"].ToString();
txt1.Name = i.ToString();
x = x + 25;
y = y + 25;
panel1.Controls.Add(txt1);
}
}
It creates just one TextBox and writes "5" in it.
How can I do that repeatedly?
What you need to understand is that you need to call reader.Read() after going through each record. Let's say your result data set has 5 records, so in order to read the 1st record, you need to call reader.Read() which would populate the reader object with the appropriate data. In order to read the 2nd record, you need to again call reader.Read(). Something like this:
int count = reader.FieldCount;
for (int i = 0; i < count; i++)
{
reader.Read();
TextBox txt1 = new TextBox();
Point txtyer = new Point(x, y);
txt1.Location = txtyer;
txt1.Text = reader["sikicerik"].ToString();
txt1.Name = i.ToString();
y = y + 25;
panel1.Controls.Add(txt1);
}
The reason why you may not be able to view all TextBoxes is that you are updating both x and y coordinates of the TextBox. What you may probably want to do is just increment the y coordinate as above and all the TextBoxes would appear vertically. You may also want to resize your form so that the TextBoxes don't get hidden.
Also, as #Steve mentioned in the comments above (which I missed), you need to assign the location of the new TextBoxes created as is done in the code above.
I suppose that you get the FieldCount from the reader variable.
Now in this context you should loop over the read and inside the loop create the textbox for each field retrieved by the original query.
Finally, if you don't set the Location property of the TextBoxes they will be created each on top of the others and you see only the topmost one with the latest value obtained in the loop
// Get the number of fields present in the reader....
int count = reader.FieldCount;
// Read one record at time
while(reader.Read())
{
// Create a textbox for each field in the record
for (int i = 0; i < count; i++)
{
TextBox txt1 = new TextBox();
// Set its location on screen
// Probably if you have many fields you need to
// use a better algorithm to calculate x,y position
txt1.Location = new Point(x, y);
txt1.Text = reader[i].ToString();
txt1.Name = i.ToString();
x = x + 25;
y = y + 25;
panel1.Controls.Add(txt1);
}
}
If you want to create textboxes only for the field sikicerik you have two options. The first one is the recommended because it causes less overhead on your database consist in changing the SELECT query used to build the read to
SELECT sikicerik FROM yourTableName
The other option is a simple change to your code. You don't need to loop over FieldCount because you already know that you are interested in only one field
// Read one record at time
while(oku.Read())
{
textBox1.Text = oku["soru"].ToString();
label1.Text = Form2.sinavno;
// Create the single textbox required for the only field required
TextBox txt1 = new TextBox();
// Set its location on screen
txt1.Location = new Point(x, y);
txt1.Text = oku["sikicerik"].ToString();
txt1.Name = i.ToString();
x = x + 25;
y = y + 25;
panel1.Controls.Add(txt1);
// Repeat the loop for each record.
}
I am doing a crossword puzzle and i have 100 text boxes in a panel like this :
Every text box have an id of 00 - 99 since there is 100 of it .
First Row will have an id 00-09 , 2nd row will have an id of 10-19 and so on.
When user types something in some text box will be null and some text box will have values in it. How do I save values from a text box of a certain id to a database? For example the above image, HELP, text box id of 22 will have the value H , id of 23 will have the value of E , id of 24 will have value of L , id of 25 will have value of P.
I don't want to save the null values of the text box , I want to save values of the textboxes which are not null. I also need to take into account their textbox ids so that when I populate them back, I just have to insert them through ID .
I am new to C# , appreciate any help/advise/solutions on this.
Here is my code:
protected void Page_Load(object sender, EventArgs e)
{
//hw.Write("<table>");
for (int i = 0; i <= 9; i++)
{
//hw.Write("<tr>");
for (int j = 0; j <= 9; j++)
{
TextBox tb = new TextBox();
tb.MaxLength = (1);
tb.Width = Unit.Pixel(40);
tb.Height = Unit.Pixel(40);
tb.ID = i.ToString() + j.ToString(); // giving each textbox a different id 00-99
Panel1.Controls.Add(tb);
}
Literal lc = new Literal();
lc.Text = "<br />";
Panel1.Controls.Add(lc);
}
}
protected void btnShow_Click(object sender, EventArgs e)
{
foreach (Control control in Panel1.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
{
if (string.IsNullOrEmpty(textBox.Text))
{
textBox.Style["visibility"] = "hidden";
}
// textBox.Enabled = false;
textBox.Text = "";
}
}
}
The proper way to do this is to wrap these textboxes inside a Repeater or Datalist controls. You can ready about these controls from here. This way when number of rows increase you will not have to change to your loop or hard-coded values.
As for your question to store values for a given Id, you can define row# and col# in your database and sort on row# and col#, this should work.
The easiest way is to make a 2D array (or List) of your TextBoxes. In where you create your TextBoxes:
List<List<TextBox>> textBoxList = new List<List<TextBox>>();
for (int i = 0; i <= 9; i++)
{
List<TextBox> textBoxRow = new List<TextBox>(); //this could be columns, not sure
for (int j = 0; j <= 9; j++)
{
TextBox tb = new TextBox();
....
textBoxRow.Add(tb);
}
...
textBoxList.Add(textBoxRow);
}
Now you can read/write to those array entries, such as:
string readValue = textBoxList[2][5].Text;
textBoxList[1][7].Text = "asdf";
HI I have requirement that
1) display given no.of textboxes dynamically and save to DB
2) if I change the number then new textboxes should append to UI
EX: TextBox AddButton
If I give 2 in textbox and click on add
Then 2 textboxes should appear. I filled some data in those textboxes. Now When I change the value 2 to 5 then 3 more textboxes should append(condition:old textboxes data should retain)
If the second value is less than or equal to first value then do nothing.
My code is
void Append()
{
string Data = string.Empty;
TextBox tb;
if (Convert.ToInt32(hdnCnt.Value) < Convert.ToInt32(txtNoofGames.Text))
{
for (int i = 0; i < Convert.ToInt16(txtNoofGames.Text); i++)
{
if (i <= Convert.ToInt32(hdnCnt.Value))
{
tb = (TextBox)Form.FindControl("txtGame1");
Data = tb.Text;
}
TextBox Newtb = new TextBox();
Newtb.ID = "txtGame" + i;
Form.Controls.Add(Newtb);
if (i <= Convert.ToInt32(hdnCnt.Value))
{
Newtb.Text = Data;
}
}
}
}
protected void btnAdd_Click(object sender, EventArgs e)
{
if (hdnCnt.Value != "")
Append();
hdnCnt.Value = txtNoofGames.Text;
for (int i = 0; i < Convert.ToInt16(txtNoofGames.Text); i++)
{
TextBox tb = new TextBox();
tb.ID = "txtGame" + i;
Form.Controls.Add(tb);
}
}
I am getting exception "object reference not set to an instance of object" at Data = tb.Text; in append method.
You didn't initialize it from the looks of it
TextBox tb = new TextBox();
Hope that helps,
Instead of Form.Controls.Add(tb);
Please try with Page.Form.Controls.Add(tb);