Creating Word Find Puzzles for the Web in C# and GDI+ Part II


 

Figure 1 - Word Find Puzzle in Internet Explorer with Answers Shown

In our last article we showed you how to create Word Find Puzzles inside of a Windows Form.  This article will show you the migration process of bringing it over to an ASP.NET application.

The first step in bringing the word find puzzle to the web is to create a new ASP.NET Web Form Project.  This will generate a WebForm in which we can begin bringing our word find puzzle code.  We can pretty much borrow all of the code out of the window form that generates and draws the puzzle in our windows app.  We simply leave the event handler methods behind because they have no equivalent in our ASP.NET world.  

We can get a graphical representation of our puzzle by creating a bitmap large enough to hold our puzzle.  We then produce a Graphics surface from the bitmap in order to draw our puzzle using GDI+.  Once we have the Graphics surface, we can use the same exact painting methods to produce our puzzle.  Once we've drawn to the Graphics surface, we can output to the web by saving the bitmap to the output stream

private void DrawWordFindBitmap()
{
// Create a bitmap that will hold the whole puzzle
Bitmap bmp = new Bitmap(310, 310);
// Create a Graphics surface for the bitmap
Graphics g = Graphics.FromImage(bmp);
// Draw the board to the surface the same exact way as in the Windows Form
DrawWordFindBoard(g);
// Set up the response stream for a jpeg image
Response.ContentType="image/jpeg";
// Save the bitmap (that now contains the puzzle) to the response stream
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
}

Listing 1 - Drawing to the Output Stream of the Web Page

Unfortunately its not completely quite that simple.  These changes  will only produce a web page only containing the puzzle with absolutely no controls.  Therefore we need to create a new main Web Form that will house our puzzle.  In this main form we will place the puzzle,  a button to signal a new puzzle, a drop down to choose a new category for the puzzle, a check box to allow us to display the answers, and an update button to redraw the puzzle with or without the answers showing. (See figure 1).  The UML design for the parent form and the puzzle page is shown in the WithClass diagram below (Figure 2).

 

Figure 2 - WithClass UML Diagram showing parent form and puzzle page form

In order to put the puzzle form  in the main form, we drag a simple HTML image control onto the main form and point the source of the image to the web puzzle page (PuzzlePage.aspx). The html for the  inclusion of the puzzle page that paints the puzzle is shown in the line below from our main form:

<IMG style="Z-INDEX: 106;
LEFT: 72px; POSITION: absolute; TOP: 56px"
height="310" alt="" src="PuzzlePage.aspx" width="310">&nbsp;<asp:button id="NewButton"
style="Z-INDEX: 104; LEFT: 80px; POSITION: absolute; TOP: 392px" runat="server"
Width="96px" Text="New Puzzle"
ForeColor
="Maroon"></asp:button></asp:button>&nbsp;

Listing 2 -  HTML code that contains the Puzzle as an Image

Using SessionStates for Page to Page Data Tranfer

Remember that we have also added controls to the main form in order manipulate the puzzle page.  So how do we communicate the fact that the control has changed in the main form to the puzzle page form?  As you know, ASP.NET is based on Application States, Session States, and SqlServer States.  We used session states to maintain all of our session persistent data between forms.  When a control changes in the main form and a post back occurs, we simply record the state of the controls inside the session state.  For example, when the new puzzle button is pressed, we record the selection state of the drop down list box so we know which category to generate.  Listing 3 shows how we record session states after the button is pressed:

private void NewButton_Click(object sender, System.EventArgs e)
{
// Track that we have just created a new puzzle so the PuzzlePage will regenerate a random puzzle
Session["NewPuzzle"] = true;
// Track Category information from the drop down list box so the puzzle page
// will generate a puzzle for the category listed here
Session["Category"] = this.DropDownList1.SelectedItem.Text;
Session["CategoryID"] =
this.DropDownList1.SelectedItem.Value;
// Post Back
Validate();
}

Listing 3 - New Puzzle Button Press Event Handler

When the Puzzle Page proceeds to create a new puzzle, it transfers  the category displayed in the dropdown by reading the session state variable shown in listing 4:

private void LoadDiagram()
{
string CategoryName = "Animal";
// Get Category Name from Sssion State Variable (remember to unbox it)
if (Session["Category"] != null)
CategoryName = (
string)Session["Category"];
..........

Listing 4 - Getting the CategoryName from the Session State Variable

One other important detail needs to be handled for the migration to the Web Page.  The Puzzle Page needs to have a way to maintain the memory of the letters each time the update button is pressed for highlighting or unhighlighting answers.  The reason it needs to maintain the memory of the letters is because when we post back to the puzzle page, it's in a new session state, so it has no memory of any class variables.  

The approach for maintaining a memory of the matrix is the same as remembering control states.  We simply shove the letters into the session state and assign each letter a different key that reflects the x,y position of the letter in the puzzle.  Below are the two methods that read and write all the letters into and out of the session state in the Puzzle Page:

string NextLetterIndex(int i, int j)
{
// Get the next matrix index string containing the row and column of the letter
return (i.ToString().Trim() + "_" + j.ToString().Trim());
}
// Read matrix and answer matrix from the session state variables
void ReadMatrices()
{
for (int i = 0; i < _maxRow; i++)
{
for (int j = 0; j < _maxCol; j++)
{
string nextLetterIndex = NextLetterIndex(i,j);
matrix[i,j] = (
char)Session["Letter" + nextLetterIndex];
matrixAnswers[i,j] = (
char)Session["Answer" + nextLetterIndex];
}
}
}
// Write the matrix and answer matrix letters to the session state variables
void WriteMatrices()
{
for (int i = 0; i < _maxRow; i++)
{
for (int j = 0; j < _maxCol; j++)
{
string nextLetterIndex = NextLetterIndex(i,j);
Session["Letter" + nextLetterIndex] = matrix[i,j];
Session["Answer" + nextLetterIndex] = matrixAnswers[i,j];
}
}
}

Listing 5 - Using Session State Variables to store the Letter Matrix of the Word Puzzle

One nice thing about working with web applications is you can use the web.config file as sort of a preference file.  In this file you can place all kinds of configurable parameters in order to manipulate your web application.  In the case of our Word Find application, we need to make the database connection configurable because there is no way that the connection on my machine to sql server matches yours. Below is the appsettings section of the web.config file where we have declared the connection.  You can change this string to match your own word puzzle database or a remote one on the web.

<appSettings>
<add key="SqlConnectionString" value="data
source=MICROGOL-HQHJXU\NETSDK;uid=sa;pwd=;Database=WordFind;persist security info=True;workstation id=MICROGOL-HQHJXU;packet size=4096"
> <BR></appSettings>

Listing 6 - Web Configuration File Settings for MSDE Sql Server Connection to Puzzle DB

We can obtain this string from the web.config file simply by using the built in System.Configuration namespace.  Below is the way to obtaining the value of the SqlConnectionString from the file:

this.sqlConnection1.ConnectionString = ConfigurationSettings.AppSettings["SqlConnectionString"];

Listing 7 - Retrieving the Connection String from the web.config File

Conclusion

This article has given you a glimpse as to how you would migrate a Windows Form GDI+ application over to a Web Form Application.  In future articles we will examine how to move animation games over to the Web (Such as the action-packed Space Invaders Game).  In the meantime, I hope this article has left you less  puzzled about Graphics on the Web in .NET.