Click here to Skip to main content
Click here to Skip to main content

MATPaint - Simple App with Simple Features

By , , 10 Dec 2012
Rate this:
Please Sign up or sign in to vote.

Introduction

Being electronics engineering students, we are taught programming but not as properly as CS guys learn it. This App implements many of the simple features which are taken as granted in Windows GUI Apps. The primary goal was to make something similar to this app:  LED Matrix Studio http://freshney.org/electronics/builder.htm - but with time constraints and finding it is not as easy as it seems we settled with an app which is somewhat useful. 

We call our app, MATPaint, which is short for MATrix Paint - the app currently allows for designing and saving of Dot-Matrix patterns for up to 32x32 size matrix.  

 

MATPaint - Application screenshot 

Background  

This article is written as it is a requirement of our C# project - our programming teacher Engr. Sajid Hussain (IIEE-PCSIR, Karachi) asked us to make an application as part of our C# Course and put on CodeProject.com and this application and article are the result of that. 

I had worked on LED Dot-Matrix displays as a side project a few months back using AVR and 8x8 LED Displays and had made a quick and dirty app for generation of the hex-code for the display pattern.
http://zaidpirwani.com/1054/8x8-led-matrix-dot-matrix-font-demo/
https://github.com/zaidpirwani/Hex-Code-Generator-for-LED-Dot-Matrix-Display  

So for this C# Project, I along with my group-mate (Maaz Ahmed) decided to make an app which can generate codes for various sizes of dot-matrix displays and also has other features available in regular apps that we use. We did find a similar app [mentioned in the Intro] which did all that during our search for similar work on the Internet but that was written in Delphi and not C#, so we basically have written this app from scratch. 

How this Article is Written 

From here on, this article has been divided into two parts, the first part describes the application working, features and how it was made plus explanation of some of the tasks (File save/Open etc) the other part details the working of the Matrix/Grid and the relevant functions (Invert/Flip/Roll etc). The first part mainly done and written by me [Zaid Pirwani] and the second part by Maaz Ahmed. 

Making of MATPaint 

We started with a Visual Studio 2010 C# project but half way through we moved over to VS2012 and the files uploaded along this article were last saved/built with VS2012 - (the solutions files can still be opened in VS2010 if it has SP1 update installed). The App does not use any external libraries other than those provided by Windows and should run on any Windows PC without any additional installation (well maybe .Net Framework).  

MATPaint in its current form has the following features:  

Application Features 

  • File New/Open/Save properly implemented as in regular apps 
  • Remembers its last state when closed and opens in same state 
  • Saving the pattern as a PNG Image 
  • Generation of HEX Code for Micro-controller 
  • Print preview and Printing of the Pattern 
  • Install Shield Setup Wizard 
  • FileType Association [.maz file extension ] 
Task Specific Features
  • Pattern sizes can be from 1x1 to 32x32 cell Matrix 
  • Patterns can be re-sized  
  • Mirror / Flip / Rotate 90deg (CW and CCW) / Invert / Clear for complete Matrix  
  • Pattern movement buttons (Up/Down/Left/Right) with Roll/Rotate across edges  
  • 4 different sizes for display of Matrix cells

For the Matrix paint area we used DataGridView; as Maaz Ahmed puts it:

We used the DataGridView control for the purpose of pattern generation because this control provides flexibility and customization to the user for creating any types of cells. We exploited its properties to design the grid with the desired functionality. With the extensive support to design and adjust the appearance of the cells we find it suitable for our needs. 

Now, feature by feature code explanation and snippets are provided, the code below is not verbatim as used in the app. For the sake of explanation we have omitted some repetitions etc in the code.

Feature/Code Explanation    

Application Features

File New/Open/Save  

The first thing we wanted in the app was to be able to save patterns and open them later on. For the file type and format we decided to simply save the files as text files having row and column data comma separated and divided by lines changes. The files are saved with an extension .maz to differentiate them from other text files. 

We used the OpenFileDialog and SaveFileDialog objects available in VS2012 - however simply putting in the Open/Save dialogs was not enough as we wanted proper notepad-like functionality for opening/saving files so that the user is prompted when opening a file when already a file was being edited and so on. This was implemented by putting in 2 flag variables; isChanged (boolean) and currentFile (enum with Opened and New as values), the values of these 2 flag variables is checked whenever a file is opened / closed / new file etc.

The methods used are NewMatrix();, SaveMatrix();, SaveOpenMatrix();, OpenMatrix(); along with click events for New, Open, Save and Save menu items.

Menu Item (New, Open, Save, ) Click Events

private void newToolStripMenuItem1_Click(object sender, EventArgs e)
{ 
    if (isChanged == true && currentFile == FileStatus.New)
    {
        DialogResult z = MessageBox.Show("Do you want to SAVE the current Pattern", 
           "Save Pattern", MessageBoxButtons.YesNoCancel);
        if (z == DialogResult.Yes)
        {
            SaveMatrix();
            NewMatrix();
        }
        else if (z == DialogResult.No)
            NewMatrix();
    }
    else if (isChanged == true && currentFile == FileStatus.Open)
    {
        DialogResult z = MessageBox.Show("Do you want to SAVE the current Pattern", 
           "Save Pattern", MessageBoxButtons.YesNoCancel);
        if (z == DialogResult.Yes)
        {
            SaveOpenMatrix();
            NewMatrix();
        }
        else if (z == DialogResult.No)
            NewMatrix();
    }
    else if (isChanged == false)
        NewMatrix();
}

private void saveToolStripMenuItem1_Click(object sender, EventArgs e)
{
    if (currentFile == FileStatus.New)
        SaveMatrix();
    else if (currentFile == FileStatus.Open)
        SaveOpenMatrix();
}

private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
    SaveMatrix();
}

private void openToolStripMenuItem1_Click(object sender, EventArgs e)
{
    // SIMILAR TO THE newToolStripMenuItem1_Click   
}

NewMatrix() method

private void NewMatrix()
{
    // Code to set Rows and Cols of DataGridView OMITTED 
    UpdateMatrixCellSize();
    currentFile = FileStatus.New;
    isChanged = false;
    this.Text = "MATPaint - ";
}

SaveMatrix() Method  

private void SaveMatrix()
{
    if (saveFileDialog1.ShowDialog() == DialogResult.OK)
    {
        string matrixCode = "";
        matrixCode = dataGridView1.ColumnCount.ToString() + "," + dataGridView1.RowCount.ToString() + "\r\n";
        int row = 0, col = 0, page = 0, z = 0;
        for (page = 0; page <= dataGridView1.RowCount / 8; page++)
        {
            for (col = 0; col < dataGridView1.ColumnCount; col++)
            {
                for (row = 0; row < 8; row++)
                {
                    if (dataGridView1.RowCount > (page * 8) + row)
                        if (dataGridView1.Rows[(page * 8) + row].Cells[col].Style.BackColor == Color.Black)
                            z = z + Convert.ToInt16(Math.Pow(2.0, row));
                    z += 0;
                }
                matrixCode = matrixCode + z.ToString() + ",";
                z = 0;
            }
            matrixCode = matrixCode + "\r\n";
        }
        TextWriter tw = new StreamWriter(saveFileDialog1.FileName);
        tw.Write(matrixCode);
        tw.Close();
        matrixFileName = saveFileDialog1.FileName;
        isChanged = false;
        currentFile = FileStatus.Open;
        this.Text = "MATPaint - " + matrixFileName;
        saveFileDialog1.FileName = "";
    }
}

private void SaveOpenMatrix()
{
    // Similar to SAVEMatrix - Code Omitted HERE - will omit from the
    // app as well and overload the SaveMatrix() method in later revision of app
}   

OpenMatrix() Method 

private void OpenMatrix(string matrixFileNameToOpen = null)
{
    bool readFile = false;
    openFileDialog1.FileName = matrixFileNameToOpen;
    
    if(matrixFileNameToOpen != null)
        readFile = true;
    else if (openFileDialog1.ShowDialog() == DialogResult.OK)
        readFile = true;
    if (readFile == true)
    {
        // Open FILE SPECIFCIATIONS
        // File should be in simple TXT format, ',' delimited
        // First Line: ColumnCount, RowCount
        // Second Line and so on: Each line has data of 8 rows in INT format and commas separate the different Columns
        try
        {
            TextReader tr = new StreamReader(openFileDialog1.FileName);
            string matrixCode = tr.ReadToEnd();
            tr.Close();
            string[] cellDataS = matrixCode.Split(new char[] { ',', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            int[] cellData = new int[cellDataS.Length];
            int count = 0;
            foreach (string s in cellDataS)
                cellData[count++] = Convert.ToInt16(s);
            int row = cellData[1], col = cellData[0], page = 0, z = 0;
            count = 1;
            dataGridView1.ColumnCount = col;
            dataGridView1.RowCount = row;
            bt_Clear_Click(this, EventArgs.Empty);
            try
            {
                for (page = 0; page <= row / 8; page++)
                    for (col = 0; col < dataGridView1.ColumnCount; col++)
                    {
                        z = cellData[++count];
                        for (row = 0; row < 8; row++)
                            if (dataGridView1.RowCount > (page * 8) + row)
                                if ((z & Convert.ToInt16(Math.Pow(2.0, row))) > 0)
                                    dataGridView1.Rows[(page * 8) + row].Cells[col].Style.BackColor = Color.Black;
                    }
            }
            catch { }
            dataGridView1.ClearSelection();
            matrixFileName = openFileDialog1.FileName;
            isChanged = false;
            currentFile = FileStatus.Open;
            num_Col.Value = dataGridView1.ColumnCount;
            num_Row.Value = dataGridView1.RowCount;
            this.Text = "MATPaint - " + matrixFileName;
        }
        catch { MessageBox.Show("Cannot Read File", "File Read Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
    }
} 

Remembering Last State of Application: Another good feature which is seen in many commercial apps is the saving of status on close and the app opens as it was closed. We used the built-in App Settings option in VS2012 (also in VS2010) to store and retrieve app settings like App Size, Location, State, Matrix Size etc. The settings are retried during the Form_Load event and are saved in the Form_Closing event.

private void Form1_Load(object sender, EventArgs e)
{
    this.Location = Properties.Settings.Default.appLocation;
    this.WindowState = Properties.Settings.Default.appState;
    this.Size = Properties.Settings.Default.appSize;
    num_Col.Value = Properties.Settings.Default.MatCols;
    num_Row.Value = Properties.Settings.Default.MatRows;
    recent1toolStripMenuItem.Text = Properties.Settings.Default.Recent1;
    recent2toolStripMenuItem.Text = Properties.Settings.Default.Recent2;
    recent3toolStripMenuItem.Text = Properties.Settings.Default.Recent3;
    // Code Omitted
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    // Code Omitted
    Properties.Settings.Default.appLocation = this.Location;
    Properties.Settings.Default.appState = this.WindowState;
    Properties.Settings.Default.appSize = this.Size;
    Properties.Settings.Default.MatCols = Convert.ToInt16(num_Col.Value);
    Properties.Settings.Default.MatRows = Convert.ToInt16(num_Row.Value);
    Properties.Settings.Default.Recent1 = recent1toolStripMenuItem.Text;
    Properties.Settings.Default.Recent2 = recent2toolStripMenuItem.Text;
    Properties.Settings.Default.Recent3 = recent3toolStripMenuItem.Text;
    Properties.Settings.Default.Save();
}

Saving the pattern as a PNG Image: Almost all controls in VS provide a method DrawToBitmap(), we used this method to generate a screenshot of the Matrix Grid and then saved it using the SaveFileDialog. 

private void imagePNGToolStripMenuItem_Click(object sender, EventArgs e)
{
    Bitmap matrixPNG = new Bitmap(dataGridView1.Width, dataGridView1.Height);
    dataGridView1.DrawToBitmap(matrixPNG, dataGridView1.ClientRectangle); 
    saveFileDialog1.Filter = "PNG Image File|*.png"; 
    saveFileDialog1.Title = "Save PNG Screenshot";
    if (saveFileDialog1.ShowDialog() == DialogResult.OK)
        matrixPNG.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Png);
    // Resetting SaveFileDialog for Patterns
    saveFileDialog1.Filter = "Matrix Pattern Files|*.maz";
    saveFileDialog1.Title = "Save Pattern File";
    saveFileDialog1.FileName = "";
}  

Exporting the pattern as a HEX File (Binary Code): What good is a Dot-Matrix Pattern generator when it cannot produce hex code for micro-controllers so we added a very simple hex generator which saves the Row/Column codes as Binary data rather than text. 

private void binaryCodeHEXToolStripMenuItem_Click(object sender, EventArgs e)
{
    saveFileDialog1.Filter = "Binary Code File |*.hex";
    saveFileDialog1.Title = "Save Pattern Hex Code";
    if (saveFileDialog1.ShowDialog() == DialogResult.OK)
        using (FileStream hex = new FileStream(saveFileDialog1.FileName, FileMode.Create))
        {
            using (BinaryWriter writer = new BinaryWriter(hex))
            {
                int row = 0, col = 0, page = 0;
                byte z=0; 
                for (page = 0; page <= dataGridView1.RowCount / 8; page++)
                    for (col = 0; col < dataGridView1.ColumnCount; col++)
                    {
                        for (row = 0; row < 8; row++)
                        {
                            if (dataGridView1.RowCount > (page * 8) + row)
                                if (dataGridView1.Rows[(page * 8) + row].Cells[col].Style.BackColor == Color.Black)
                                    z = Convert.ToByte(z + Math.Pow(2.0, row));
                            z += 0;
                        }
                        writer.Write(z);
                        z = 0;
                    }
                writer.Close();
            }
        }
    saveFileDialog1.Filter = "Matrix Pattern Files|*.maz";
    saveFileDialog1.Title = "Save Pattern File";
    saveFileDialog1.FileName = "";
} 

Print preview and Printing of the Pattern: In the end, we just added the PrintDocument, PrintDialog and PrintPreview for one more feature, the print option is very crudely implemented and prints the screenshot capture of the Matrix control but for now, it seems to be working. 

private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e)
{
    printPreviewDialog1.Document = printDocument1;
    printPreviewDialog1.ShowDialog();
}
private void printToolStripMenuItem_Click(object sender, EventArgs e)
{
    if (printDialog1.ShowDialog() == DialogResult.OK)
        printDocument1.Print();
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    Bitmap matrixPNG = new Bitmap(dataGridView1.Width, dataGridView1.Height);
    dataGridView1.DrawToBitmap(matrixPNG, dataGridView1.ClientRectangle);
    e.Graphics.DrawImage(matrixPNG, new Point(0,0));
}

Install Shield Setup Wizard and FileType Association:  Last but not the least, to make an installer for the app, we opted to use a InstallShield Setup Project to our app and with some simple steps in a wizard the whole thing was ready, as a bonus we added FileType Association as well for .maz extension and added support for command line arguments so that our app opens the appropriate pattern when a .maz file is double clicked. 

Task Specific Features  

Matrix (Grid) Size: A matrix of any specified order between 1x1 and 32x32 can be generated by NewMatrix() method, we kept it maximum at 32 cause after that the form repaint on screen was taking quite a lot of time making the app slow. 

private void NewMatrix()
{ 
    dataGridView1.ColumnCount = Convert.ToInt16(num_Col.Value);   
    dataGridView1.ColumnCount = Convert.ToInt16(num_Col.Value);
    dataGridView1.RowCount = Convert.ToInt16(num_Row.Value);
    for (int z = 0; z < dataGridView1.RowCount; z++)
        for (int y = 0; y < dataGridView1.ColumnCount; y++)
            dataGridView1.Rows[z].Cells[y].Style.BackColor = Color.White;
}    

Matrix Cell Size: UpdateMatrixCellSize() method is used to set the size of grid cells. There are four different sizes available for designing in addition to the Fill screen mode, where the cells are expanded to the corners of the window. This method is called whenever the user selects a different size type from the list or if Fill Screen is selected whenever the application form size is changed (checked by the Form_Resize Event).   

private void UpdateMatrixCellSize()
{ 
    int RowHeight = 10, ColumnWidth = 10;
    switch (cmbx_Grid.SelectedIndex)
    {
        case 0:
            // Fill Screen
            if (dataGridView1.RowCount > 0)
                RowHeight = dataGridView1.Height / dataGridView1.RowCount;
            if (dataGridView1.ColumnCount > 0)
                ColumnWidth = dataGridView1.Width / dataGridView1.ColumnCount;
            break;
        case 1:
            // Tiny
            RowHeight = 12;
            ColumnWidth = 12;
            break;
            // CODE OMITTED HERE for Medium and Huge cell sizes
        default:
            // nothing to do in default case
            break;
    } 
    // Setting size of all rows and columns individually
    for (int z = 0; z < dataGridView1.RowCount; z++)
        dataGridView1.Rows[z].Height = RowHeight;
    for (int z = 0; z < dataGridView1.ColumnCount; z++)
        dataGridView1.Columns[z].Width = ColumnWidth;
} 

Pattern Resize (change dimensions): Changing of the dimension of the matrix/grid is done by the following code. This also alerts the user when there is a chance of loosing any data while reducing the size of the matrix. 

int tmpCol = Convert.ToInt16(num_Col.Value);
int tmpRow = Convert.ToInt16(num_Row.Value);
if ((tmpCol < dataGridView1.ColumnCount) || (tmpRow < dataGridView1.RowCount))
{ 
    if (MessageBox.Show("The New Size is Smaller than current Size, some Clipping may occur, \nContinue ?", 
          "Resize Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes)
    {
        dataGridView1.ColumnCount = tmpCol;
        dataGridView1.RowCount = tmpRow;
        UpdateMatrixCellSize();
        isChanged = true;
    }
}

Mirror / Flip / Rotate 90deg (CW and CCW) / Invert / Clear for complete Matrix:

Using simple algorithms features such as mirroring, flipping, clearing, inverting and rotating the pattern on the grid were implemented. The rotate is only done when the matrix is square, i.e. Rows == Cols  

Invert

for (int z = 0; z < dataGridView1.RowCount; z++)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        if (dataGridView1.Rows[z].Cells[y].InheritedStyle.BackColor != Color.Black)
            dataGridView1.Rows[z].Cells[y].Style.BackColor = Color.Black;
        else
            dataGridView1.Rows[z].Cells[y].Style.BackColor = Color.White;

Clear

for (int z = 0; z < dataGridView1.RowCount; z++)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[z].Cells[y].Style.BackColor = Color.White; 

Mirror

int x = dataGridView1.ColumnCount - 1;
for (int z = 0; z < dataGridView1.RowCount; z++)
    for (int y = 0; y < dataGridView1.ColumnCount / 2; y++)
    {
        Color temp = dataGridView1.Rows[z].Cells[y].Style.BackColor;
        dataGridView1.Rows[z].Cells[y].Style.BackColor = dataGridView1.Rows[z].Cells[x - y].Style.BackColor;
        dataGridView1.Rows[z].Cells[x - y].Style.BackColor = temp;                    
    }

Flip

int x = dataGridView1.RowCount - 1;
    for (int z = 0; z < dataGridView1.RowCount / 2; z++)
        for (int y = 0; y < dataGridView1.ColumnCount; y++)
        {
            Color temp = dataGridView1.Rows[z].Cells[y].Style.BackColor;
            dataGridView1.Rows[z].Cells[y].Style.BackColor = dataGridView1.Rows[x - z].Cells[y].Style.BackColor;
            dataGridView1.Rows[x - z].Cells[y].Style.BackColor = temp;
        }

Rotate 90 deg ( CW and CCW )

int x = dataGridView1.ColumnCount - 1;
Color[,] GridArray = new Color[dataGridView1.RowCount, dataGridView1.ColumnCount]; 
// Saving grid data in a 2D array 
for (int z = 0; z < dataGridView1.RowCount; z++)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        GridArray[z,y] = dataGridView1.Rows[z].Cells[y].Style.BackColor; 
// Rotating anticlockwise by 90 deg 
for (int z = 0; z < dataGridView1.RowCount; z++) 
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[z].Cells[y].Style.BackColor = GridArray[y, x - z];
// Rotating clockwise by 90 deg
for (int z = 0; z < dataGridView1.RowCount; z++)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[z].Cells[y].Style.BackColor = GridArray[x - y, z];

Pattern movement (Up/Down/Left/Right) with Roll across Edges

Pattern movement allow us either to shift the data on the grid or to rotate it across its edges in the Up/Down/Left/Right directions if the Roll Grid checkbox is checked. 

This is done by this code, only code for UP and Right shifting is provided here as the Down and Left are quite similar to these. 

UP SHIFT/ROLL

// Storing the data of the critical Row 0 - the first row
Color[] temp = new Color[dataGridView1.ColumnCount];
for (int y = 0; y < dataGridView1.ColumnCount; y++)
    temp[y] = dataGridView1.Rows[0].Cells[y].Style.BackColor; 
// Moving the rest of the grid UP 
for (int z = 0; z < dataGridView1.RowCount - 1; z++)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[z].Cells[y].Style.BackColor = dataGridView1.Rows[z + 1].Cells[y].Style.BackColor; 
// Checking whether to roll or shift the grid UP
if (cbx_GridRoll.Checked == true)
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[dataGridView1.RowCount - 1].Cells[y].Style.BackColor = temp[y];
else
    for (int y = 0; y < dataGridView1.ColumnCount; y++)
        dataGridView1.Rows[dataGridView1.RowCount - 1].Cells[y].Style.BackColor = Color.White;  

RIGHT SHIFT/ROLL

// Storing the data of the critical Column 0 - the last column
Color[] temp = new Color[dataGridView1.RowCount];
for (int x = 0; x < dataGridView1.RowCount; x++)
    temp[x] = dataGridView1.Rows[x].Cells[dataGridView1.ColumnCount - 1].Style.BackColor; 
// Moving the rest of the grid to the LEFT 
for (int y = dataGridView1.ColumnCount - 1; y > 0; y--) 
    for (int x = 0; x < dataGridView1.RowCount; x++)
        dataGridView1.Rows[x].Cells[y].Style.BackColor = dataGridView1.Rows[x].Cells[y - 1].Style.BackColor;
// Checking whether to roll or shift the grid to the RIGHT
if (cbx_GridRoll.Checked == true)
    for (int x = 0; x < dataGridView1.RowCount; x++)
        dataGridView1.Rows[x].Cells[0].Style.BackColor = temp[x];
else
    for (int x = 0; x < dataGridView1.RowCount; x++)
        dataGridView1.Rows[x].Cells[0].Style.BackColor = Color.White;

Points of Interest   

  • Using Controls for things they are NOT designed for: We used the DataGridView control as the base grid for our Matrix paint area and it worked out quite great, though usually DataGridViews are not used for painting purposes and are for data output to user in grid form. 
  • Simple Features are sometimes NOT very simple: Though we are saying this cause we are beginners but the most time on the app was spent on the simple features we usually take for granted like Open/Save, File Association, etc. 
  • Goal of this Project was Learning C#: I think working on this app gave us a good practice of making a simple GUI Application and though the app may not seem to do much, much effort was put into it and the main goal was to add regular-app-features not make a complete pattern generator.
  • Probably NOT the Best Practices: The code in this project may not be the BEST-PRACTICE code, but this is what gets the work done and doesn't go into the complexities which a beginner usually runs from as this is made by us who are very new to C# and GUI and VS. 

GitHub Repository

The complete source code is available at GitHub as well and while it is unlikely that I will be updating the app in the coming months but I wish to add a few more things to this app and if time allows will keep on updating it there.  

https://github.com/zaidpirwani/MatPaint 

More Features to Add

 A few more features/improvements that come to mind are 

  • Undo / Redo [Google searches show it is NOT going to be an easy task] 
  • Selection and Cut/Copy/Paste of Pattern 
  • Selection Movement 
  • Rectangle, Circle, Line etc Controls for drawing on pattern
  • Font for writing Text 
  • Better Handling of Larger Matrix
  • Sending Live Matrix Data via Serial to Hardware Matrix
  • Animation Frames
  • More Windows integration 
  • Making a completely-custom-control for Matrix-Grid 
  • .....  [the list can go on and on and on....] 

Article Update 

  • Updated the project to .NET Framework 2.0.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Authors

Zaid Pirwani
Ejaad
United States United States
Founded Ejaad, a Technology and Engineering consultancy Startup, based in Karachi, Pakistan.
 
Student at Industrial Electronics Engineering Student at IIEE-PCSIR, Karachi, Pakistan.
 
Freelance Web Developer and General Tech Support provider at Guru.com and Local Businesses
Follow on   Twitter   Google+

Maaz Ahmed
Student
Pakistan Pakistan
No Biography provided

Comments and Discussions

 
GeneralThank you for this piece of software PinmemberPranjal Saxena3-Aug-13 8:33 
GeneralRe: Thank you for this piece of software PinmemberZaid Pirwani3-Aug-13 18:15 
QuestionCool! Pinmemberpoi11928-Nov-12 4:46 
AnswerRe: Cool! PinmemberZaid Pirwani28-Nov-12 6:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 10 Dec 2012
Article Copyright 2012 by Zaid Pirwani, Maaz Ahmed
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid