Design Considerations For a Paint-Like Windows Application
<p>I'm in process of developing a new front end for an old open source fluid modeling engine written in C.  I am using C# and WPF. The apps network generation requires the user to draw a network of pipes, reservoirs, nodes, tanks, etc.  The app is more or less a fancy version of paint ;)</p>

<p>Right now I have the graphics set up as follows.  I have an embedded win-forms Panel that has mouseclick, mousemove, and paint events.  Mouse click saves the clicked coordinates to an array in the singleton class and then triggers the paint event via <code>invalidate();</code>.  The paint event then loops through the array and draws all the node coordinates in the array via: <code>g.FillEllipse(x,y,20,20);</code>  The user can click a node and bring up a menu via a function I wrote called <code>DoesPointExist(xCord, yCord);</code>.  It loops through the coordinates array and returns true if both xcord and ycord are within 5px of the clicked coordinates.  A somewhat archaic solution but it seems to work well enough.</p>

<p>This has worked pretty well for me thus far.  But in the future I am going to have to give each node (or circle on the Panel) more and more properties.  Nothing fancy, just numerical values like elevation that must be associated with each node.  Also, I'm going to need to add in an option to delete nodes at some point.</p>

<p>I could probably do this by setting all values of the deleted row to 0 and placing in if statement in the paintevents loop to not draw deleted points, or even figure out how to just get rid of the row period and shift all the others down.</p>

<p>My question is there a more intelligent and OOP type way to go about this?  Looping through and array seems a little old fashioned, there must be a better way to make use of C# features.  Is there some sort of object or class I could setup to make this process simpler?  The array is fine but by the end its going to be 40-50 columns.  My background is more based in functional type programming with lower level languages like C.  My program seems very bare of objects and classes other than a singleton glass for global data.</p>

<p>I know there are different ways to skin the cat; but it's important that the code I write will be accessible and easy to modify for future engineers so I'd like to add as much within the OOP paradigm as possible.  My current code is very functional... but not very tidy.</p>

<p>Paint event code:</p>

<pre><code>private void wfSurface_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
        Graphics g;
        Graphics h;
        g = wfSurface.CreateGraphics();
        h = wfSurface.CreateGraphics();
        epanet epa = epanet.GetInstance();
        SolidBrush black = new SolidBrush(System.Drawing.Color.Black);
        SolidBrush blue = new SolidBrush(System.Drawing.Color.Pink);
        SolidBrush green = new SolidBrush(System.Drawing.Color.Green);
        System.Drawing.Pen line = new System.Drawing.Pen(System.Drawing.Color.FromArgb(255, 0, 0, 0));

        //Loop to draw vertical grid lines
        for (int f = 50; f &lt; 1100; f += 50)
        {
            e.Graphics.DrawLine(line, f, 0, f, 750);
        }

        //Loop to draw vertical grid lines
        for (int d = 50; d &lt; 750; d += 50)
        {
            e.Graphics.DrawLine(line, 0, d, 1100, d);
        }

        //Loop nodes, tanks, and resevoirs
        for (int L = 1; L &lt; index; L += 1)
        {
            g.FillEllipse(black, Convert.ToInt32(epa.newNodeArray[L, 0] - 8), Convert.ToInt32(epa.newNodeArray[L, 1] - 8), 19, 19);
            h.FillEllipse(blue, Convert.ToInt32(epa.newNodeArray[L, 0] - 6), Convert.ToInt32(epa.newNodeArray[L, 1] - 6), 15, 15);
        }

        for (int b = 1; b &lt; resIndex; b += 1)
        {
            g.FillRectangle(green, Convert.ToInt32(epa.ResArray[b, 0] - 8), Convert.ToInt32(epa.ResArray[b, 1] - 8), 16, 16);
        }
        for (int c = 1; c &lt; tankIndex; c += 1)
        {
            g.FillRectangle(black, Convert.ToInt32(epa.tankArray[c, 0] - 8), Convert.ToInt32(epa.tankArray[c, 1] - 8), 20, 20);
            g.FillRectangle(green, Convert.ToInt32(epa.tankArray[c, 0] - 6), Convert.ToInt32(epa.tankArray[c, 1] - 6), 16, 16);
        }
}
</code></pre>

<p>Code for click event:</p>

<pre><code>private void wfSurface_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
    {
        //Initialize epanet and save clicked coordinates to singleton class
        epanet epa = epanet.GetInstance();
        epa.xCord = e.X;
        epa.yCord = e.Y;

        //Check if point exists, if does open property window, doesn't do whatever drawing control is selected
        if (epa.DoesPointExist(e.X, e.Y, index) == false)
        {
            switch (epa.controlSelected)
            {
                case "Node":
                    epa.newSetCords(index, e.X, e.Y);
                    wfSurface.Invalidate();
                    index += 1;

                    break;

                case "Res":
                    epa.setResCords(resIndex, e.X, e.Y);
                    wfSurface.Invalidate();
                    resIndex += 1;
                    wfPanel.Cursor = Cursors.Arrow;
                    break;

                case "Tank":
                    epa.setTankCords(tankIndex, e.X, e.Y);
                    wfSurface.Invalidate();
                    tankIndex += 1;
                    break;

                case "Pointer":
                    break;

                default:
                    //epa.newSetCords(index, e.X, e.Y);
                    wfSurface.Invalidate();
                    break;
            }


        }
        else if (epa.DoesPointExist(e.X, e.Y, index) == true)
        {

            MessageBox.Show("Point Already Exists");
            if (epa.propOpen == false)
            {
                // Open control properties in right pannel
            }

        }
</code></pre>

<p>Code for singleton class:</p>

<pre><code>public class epanet 
</code></pre>

<p>{
    private static epanet instance = new epanet();</p>

<pre><code>private epanet() { }

public static epanet GetInstance()
{
    return instance;
}

//Microsoft.Win32.SaveFileDialog save = new Microsoft.Win32.SaveFileDialog();

//Network Node Data
public int nodeIndex { get; set; }
public int newNodeIndex { get; set; }
public double xCord { get; set; }
public double yCord { get; set; }
public double x1Cord { get; set; }
public double y1Cord { get; set; }
public int selectedPoint { get; set; }
//public List&lt;double&gt; nodeList = new List&lt;double&gt;();

//Saving Data
public int fileCopyNum { get; set; }
public string filename { get; set; }
public string path { get; set; }
public string fullFileName { get; set; }

//Window Condition Data
public bool drawSurfStatus { get; set; }
public bool windowOpen { get; set; }
public bool OpenClicked { get; set; }
public bool saveASed { get; set; }
public bool newClicked { get; set; }
public bool propOpen { get; set; }

//Drawing Controls
public string controlSelected { get; set; }

//Declare Array to store coordinates
public double[,] nodeArray = new double[100000, 3];
public double[,] newNodeArray = new double[100000, 7];
public double[,] ResArray = new double[100000, 7];
public double[,] tankArray = new double[100000, 7];

public void newSetCords(int newNodeIndex, double xCord, double yCord)
{
    newNodeArray[newNodeIndex, 0] = xCord;
    newNodeArray[newNodeIndex, 1] = yCord;
    newNodeArray[nodeIndex, 2] = nodeIndex;

}

public void setResCords(int newNodeIndex, double xCord, double yCord)
{
    ResArray[newNodeIndex, 0] = xCord;
    ResArray[newNodeIndex, 1] = yCord;
    ResArray[nodeIndex, 2] = nodeIndex;

}

public void setTankCords(int newNodeIndex, double xCord, double yCord)
{
    tankArray[newNodeIndex, 0] = xCord;
    tankArray[newNodeIndex, 1] = yCord;
    tankArray[nodeIndex, 2] = nodeIndex;

}

public void setCords(int nodeIndex, double xCord, double yCord)
{
    nodeArray[nodeIndex, 0] = xCord;
    nodeArray[nodeIndex, 1] = yCord;
    //nodeArray[nodeIndex, 2] = nodeIndex;

}

public bool DoesPointExist(double xcord, double ycord, int index)
{
    int count = 1;
    bool outcome = false;        
    while (count &lt; index)
    {            
        if (Math.Abs(xcord - newNodeArray[count, 0]) &lt; 20)
        {
            if (Math.Abs(ycord - newNodeArray[count, 1]) &lt; 20 )
            {                    
                outcome = true;
                selectedPoint = count;
                index = 0;                    
            }
        }
        count += 1;
    }

    return outcome;        
}
</code></pre>

<p>As I said everything works completely fine.  I'm just looking for some feedback on if there's a more professional way to go about doing this.</p>
