Creating Your Own Drawing Application with Visual Basic .NET, Part 1

Because I'm a Visual Basic 6 veteran, I've always wanted to be able to write a proper Drawing Application. Without the use of the Windows API, Visual Basic 6's graphics capabilities was a bit too limited for my taste. GDI+ came to my rescue. This article series will introduce you to most of the features of GDI+ and how easy GDI+ actually is in a hands-on manner.

Designing the Interface

First, start with the Main Window. It probably comes as no surprise that this will be an MDI application. The reason for this is that you can have more than one drawing open at any given time. To add this MDI form to your application, Select Project, Add Windows Form.... Name this Form frmMDICanvas. The next thing you need to do with frmMDICanvas is to set the IsMdiContainer property to True.

Add a MainMenu component to this From; name the menu CMMFile. Include the following Menu items:

&File and leave the default name

&New and name this item mnuFNew

Voilà! Your first form is designed!

At a bare minimum, every graphics application has a ToolBox (to select tools and other various options, such as Colors and Drawing styles) and a Canvas on which to draw. With the included sample program, I have included the following options in the ToolBox:

Shapes

Circle

Square

Triangle

Drawing Options

Outlined

Filled

Color Selector

Eraser

Pencil Tool

In Design mode, the Toolbox looks like the following picture:

The Canvas in the included sample consists of an empty PictureBox, to be drawn on, and a Button to clear the drawing. In Design mode, the Canvas looks like the following picture:

As you can see, the design of this application is very basic at this stage.

Get Building!

Start Visual Studio .NET.

When prompted for a new Project name, enter Canvas for the Project's name.

When the first Form appears, open its Properties Window, and set the following properties:

Name: frmCanvas

Text: Computer Canvas

WindowState: Maximized

Add a PictureBox control to frmCanvas by double-clicking on it in the Toolbox. Set the following properties for the PictureBox:

Name: picCDraw

BackColor: White

Cursor: Cross

Dock: Fill

Add a Button to the PictureBox in the same way you did the PictureBox. Set the following properties for the Button control:

Name: butCClear

Anchor: Bottom, Left

BackColor: White

FlatStyle: Flat

Image: Set the Button's Image Property to any picture that would describe the button's use. If you choose not to include a picture for the button, set the Button's Text Property to Clear.

Size: 75, 32. Width: 75 Height: 32

Add a ToolTip control to the Designer, and set the following property for the ToolTip:

Name: tipCanvas

That's it! Your canvas has been designed. It doesn't look like much at this stage, but you will see further improvements to your canvas during the course of this article series. What you need now is a Toolbox for your drawing program.

Creating the Toolbox

To create your Toolbox, follow these steps:

From the Project menu, select Add Windows Form... to add a new form to your project. When the Add New Item dialog box appears, type the following name for the new form: frmTools.vb.

When the new form appears in the Designer Window, set the following properties for frmTools:

Name: frmTools

BackColor: White

ControlBox: False

FormBorderStyle: FixedToolWindow

ShowInTaskBar: False

Size: 112, 312 Width: 112 Height: 312

StartPosition: Manual

Text: Tools

TopMost: True

Add a Button to your frmTools, and set the following properties:

Name: butCCircle

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Location: 16, 8 X: 16, Y: 8

Size: 75, 23 Width: 75, Height: 23

Text: Circle

Add a Button to your frmTools, and set the following properties:

Name: butCSquare

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Location: 16, 8 X: 16, Y: 8

Size: 75, 23 Width: 75 Height: 23

Text: Square

Add a Button to your frmTools, and set the following properties:

Name: butCTriangle

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Location: 16, 8 X: 16, Y: 8

Size: 75, 23 Width: 75 Height: 23

Text: Triangle

Add a Button to your frmTools, and set the following properties:

Name: butCFillColor

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Image: Set the Button's Image Property to any picture, that would describe the button's use. If you choose not to include a picture for the button, set the Button's Text Property to Fill.

Note: You may need to adjust the Width property if you choose to include Text instead of an Image.

Location: 16, 208 X: 16 Y: 208

Size: 32, 32 Width: 32 Height: 32

Add a Button to your frmTools, and set the following properties:

Name: butCPencil

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Image: Set the Button's Image Property to any picture that would describe the button's use. If you choose not to include a picture for the button, set the Button's Text Property to Pencil.

Note: You may need to adjust the Width property if you choose to include Text instead of an Image.

Location: 56, 208 X: 56, Y: 208

Size: 32, 32 Width: 32 Height: 32

Add a Button to your frmTools, and set the following properties:

Name: butCColor

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Image: Set the Button's Image Property to any picture that would describe the button's use. If you choose not to include a picture for the button, set the Button's Text Property to Color.

Note: You may need to adjust the Width property if you choose to include Text instead of an Image.

Location: 16, 248 X: 16 Y: 248

Size: 32, 32 Width: 32 Height: 32

Add a Button to your frmTools, and set the following properties:

Name: butCEraser

BackColor: White

FlatStyle: Popup

Font:Arial, Arial, 8.25pt

ForeColor: Black

Image: Set the Button's Image Property to any picture that would describe the button's use. If you choose not to include a picture for the button, set the Button's Text Property to Eraser.

Note: You may need to adjust the Width property if you choose to include Text instead of an Image.

Location: 56, 248 X: 56 Y: 248

Size: 32, 32 Width: 32 Height: 32

Add a ToolTip control to the Designer, and set the following property for the ToolTip:

Name: tipCanvas

Now that you have finished the design of your first drawing application in .NET, you can start coding. Before you do that, however, let me explain what the whole goal of the above forms and their controls are.

Functions of the Various Controls

frmTools

Object

Description

frmTools

Provides a holding place for all drawing tools

butCCircle

Draws a circle

butCSquare

Draws a square

butCTriangle

Draws a triangle

butCFillColor

Draws a filled shape

butCPencil

Draws freehand

butCColor

Opens the color selector

butCEraser

Erases drawings

tipCanvas

Provides tooltips for the various tools

frmCanvas

Object

Description

frmCanvas

Provides the drawing canvas

picCDraw

PictureBox to draw onto

butCClear

Clears the entire drawing

tipCanvas

Provides tooltips

Now, get coding!

Creating Your Own Drawing Application with Visual Basic .NET, Part 1

Now that you have the design in place, you can finally get started coding. I will demonstrate all the necessary code, step by step. All you have to do is follow along and enjoy, and, most importantly, learn how easy GDI+ is!

Because you are dealing with two forms (one for the tools, one for the canvas), you will need to add a Module to your project. You need to do this because you are going to declare Global variables that are variables that are used with multiple forms. To add a Module to your project, follow these easy steps:

Select the Project menu.

Select Add Module. A box similar to the following picture appears:

[AddModule.png]

Type in a descriptive name: modCanvas, for example.

Click OK.

In modCanvas, type in the following:

Public blnDrawClicked As Boolean 'Is The Pencil Tool Clicked?
Public blnSquareClicked As Boolean 'Is The Square Tool Clicked?
Public blnTriangleClicked As Boolean 'Is The Triangle Tool Clicked?
Public blnCircleClicked As Boolean 'Is The Circle Tool Clicked?
Public blnEraserClicked As Boolean 'Is The Eraser Tool Clicked?
Public blnFillClicked As Boolean 'Is The Fill Tool Clicked?
Public blnColorClicked As Boolean 'Is The Color Tool Clicked?
Public cColor As Color 'Selected Color To Draw With

As you have probably noticed, the variables above will be used to determine which tool has been selected. The variables will return a True value when the particular tool has been selected. Boolean variables can be either True or False. The last variable that is declared inside modCanvas is named cColor; it will be used to hold the currently selected color.

frmTools

The first event you are going to create is the butCCircle_Click event. To add coding to the click event of the Circle button, simply double-click on it in the designer, and then type the following code:

Follow the same procedure as above to add code to the butCSquare_Click, butCTriangle_Click, butCPencil_Click, and the butCEraser_Click events. Your code for these four functions should look similar to the following code segments:

What you have accomplished with the preceding code is mostly just setting the global variables (declared in modCanvas) to True or False, depending on which button is selected. You also have called the Refresh methods of the buttons to repaint their appearance once the button is clicked. Speaking of button appearance, experiment a little with the button controls. With the next section of code, you are going to make the Circle button circular, the Square button square shaped, and the Triangle button will end up looking like a triangular-shaped button. To change the shapes of the button controls, you create a GraphicsPath Object, add a shape to the GraphicsPath object, and set the Button's Region = to the GraphicsPath object. Add the following code in the buttons' respective Paint events:

Private Sub butCCircle_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles butCCircle.Paint
'Declare A GraphicsPath Object, Which Is Used To Draw The Shape Of
'The Button
Dim CirclePath As System.Drawing.Drawing2D.GraphicsPath =
New System.Drawing.Drawing2D.GraphicsPath
'Create A 60 x 60 Circle Path
CirclePath.AddEllipse(New Rectangle(0, 0, 60, 60))
'Size Of The Button
butCCircle.Size = New System.Drawing.Size(60, 60)
If blnCircleClicked Then
'If The Button Is Selected To Draw, Change The Color
butCCircle.BackColor = Color.DeepSkyBlue
Else
'If The Button Is Not Selected To Draw With, Change Back To
'Original Color
butCCircle.BackColor = Color.Aquamarine
End If
'Create The Circular Shaped Button, Based On The Graphics Path
butCCircle.Region = New Region(CirclePath)
'Release All Resources Owned By The Graphics Path Object
CirclePath.Dispose()
End Sub
Private Sub butCSquare_Paint(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles butCSquare.Paint
'Declare A GraphicsPath Object, Which Is Used To Draw The Shape Of
'The Button
Dim SquarePath As System.Drawing.Drawing2D.GraphicsPath =
New System.Drawing.Drawing2D.GraphicsPath
'Create A 60 x 60 Square Path
SquarePath.AddRectangle(New Rectangle(0, 0, 60, 60))
'Size Of The Button
butCSquare.Size = New System.Drawing.Size(60, 60)
If blnSquareClicked Then
'If The Button Is Selected To Draw, Change The Color
butCSquare.BackColor = Color.DeepSkyBlue
Else
'If The Button Is Not Selected To Draw With, Change Back To
'Original Color
butCSquare.BackColor = Color.Aquamarine
End If
'Create The Square Shaped Button, Based On The Graphics Path
butCSquare.Region = New Region(SquarePath)
'Release All Resources Owned By The Graphics Path Object
SquarePath.Dispose()
End Sub
Private Sub butCTriangle_Paint(ByVal sender As Object, _
ByVal e As System.Windows.Forms.PaintEventArgs) _
Handles butCTriangle.Paint
'Declare A GraphicsPath Object, Which Is Used To Draw The Shape Of
'The Button
Dim TrianglePath As System.Drawing.Drawing2D.GraphicsPath =
New System.Drawing.Drawing2D.GraphicsPath
'Create A Triangle Path, With A Total Width And Height Of 60, The
'Lines From The Bottom Meet At 30 At The Top
Dim pTPoints As Point() = { _
New Point(30, 0), _
New Point(60, 60), _
New Point(0, 60)}
'Add The Point Array Object To The GraphicsPath Object. The Point
'Array Is Needed To Create The Three Interconnected Points
TrianglePath.AddLines(pTPoints)
'Size Of The Button
butCTriangle.Size = New System.Drawing.Size(60, 60)
If blnTriangleClicked Then
'If The Button Is Selected To Draw, Change The Color
butCTriangle.BackColor = Color.DeepSkyBlue
Else
'If The Button Is Not Selected To Draw With, Change Back To
'Original Color
butCTriangle.BackColor = Color.Aquamarine
End If
'Create The Triangular Shaped Button, Based On The Graphics Path
butCTriangle.Region = New Region(TrianglePath)
'Release All Resources Owned By The Graphics Path Object
TrianglePath.Dispose()
End Sub

The code above will create a circle-shaped button, a square-shaped button, and a triangular-shaped button. All these buttons will also change colors once they are clicked, and then change back to the original color once a different shape is selected.

The FillColor Button has two options:

Outlined Color

Filled Color

What I did in the next code segment was to declare a Modular variable that keeps track of how many times the FillColor button is clicked. When the variable has a value of 1, you will have filled colors. If the variable's value is 2, you will return to Outlined color mode. I have also changed the Images on this button to give the user a visible clue to what option is currently selected. You can omit the Images if you want to. Have a closer look:

Declare the sFillClicked variable in the Declarations section of frmTools:

Private sFillClicked As Short 'Keeps Track Of How Many Times The
'Fill Tool Is Clicked

All this talk about shapes and Outlined or Filled colors, and you haven't got colors yet! You can quickly fix that problem. The following code handles the butCColor event. What I did here was to declare a new ColorDialog object, which will be used to select colors to draw with. If a color is selected, the selected color is stored in the cColor variable; if no color has been selected, cColor will default to black.

Creating Your Own Drawing Application with Visual Basic .NET, Part 1

frmCanvas, as you know by now, will be your physical drawing area for the tool the user selected from the Toolbox. All the drawings will take place in the PictureBox's MouseUp event, meaning, when the user releases the mouse button, the object will be drawn. I also made use of the MouseDown event of the PictureBox, to keep track of the starting points of the particular drawing. The drawing will be drawn from the Starting points to the Ending points as will be specified in the MouseUp event. The picCDraw_MouseMove event will be used when the user has selected the Pencil tool to draw freehand shapes, and with the Eraser tool, so that the drawing will be erased as the user move his mouse.

To actually start drawing, you need a couple of declarations. Here, I am declaring the starting and ending X and Y positions for your drawings. sStartX and sStartY will be set to a value once the mouse button is pressed down. sEndX and sEndY will be set to values at the psoition where the Mouse Button is released.

'Declare Starting Points For Drawn Objects
Private sStartX As Short
Private sStartY As Short
'Declare Ending points For Drawn Objects
Private sEndX As Short
Private sEndY As Short

The following two declarations will be used to establish whether the Drawing or Erasing tools are selected:

With the next two declarations, I create a Bitmap object and a Graphics object. The Bitmap object has a Width Of 1100 And Height Of 800. The Drawn Shapes will become part of this Image Object. The Graphics object will be used to draw onto.

'Create A New Image Object With Width Of 1500 And Height Of 1200.
'The Drawn Shapes Will Become Part Of This Image Object
Private bImage As New Bitmap(1500, 1200)
'Declare The Graphics Object To Draw Onto
Private gCanvas As Graphics = Graphics.FromImage(bImage)

As mentioned earlier, the MouseDown event of the PictureBox initializes the Starting X and Y values of your drawing. This event also checks to see whether the user is currently drawing Freehand with the Pencil tool, or if the user is currently erasing.

Private Sub picCDraw_MouseDown(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles picCDraw.MouseDown
'Determine Whether Or Not The Pencil (Free Hand) Tool Has Been Clicked
If blnDrawClicked Then
blnDrawing = True
End If
'Determine Whether Or Not The Eraser Tool Has Been Clicked
If blnEraserClicked Then
blnErasing = True
End If
'Initialise Starting Points Of Shape, Once Mouse Button Is Pressed Down
sStartX = e.X
sStartY = e.Y
End Sub

The MouseUp event is where all the drawing of the shapes actually takes place.

Private Sub picCDraw_MouseUp(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles picCDraw.MouseUp
'Create And Initialise Pens To Draw The Particular Outline Shapes
'with a Width of 3 and a black color
Dim pCirclePen As New Pen(Color.Black, 3)
Dim pSquarePen As New Pen(Color.Black, 3)
Dim pTrianglePen As New Pen(Color.Black, 3)
'Create And Initialise Brushes To Fill The Particular Shapes With Black
Dim sbCircleBrush As New SolidBrush(Color.Black)
Dim sbSquareBrush As New SolidBrush(Color.Black)
Dim sbTriangleBrush As New SolidBrush(Color.Black)
'Initialise Ending Points Of Shape, Once Mouse Button Is Released
sEndX = e.X
sEndY = e.Y
'If The Pencil (Free Hand) Tools Is NOT Selected, In Other Words,
'A Shape Tool Is Selected
If Not blnDrawing Then
'Set The Images Drawn Thus Far In The Picture Box = To The
'In-Memory Image Object
Me.picCDraw.Image = bImage
'A Color Has Not Been Selected, Revert To Default Black Color
If Not blnColorClicked Then
cColor = Color.Black
Else
'A Color Has Been Selected, Use Selected Color To Draw With
cColor = cColor
End If
'Determine If The Circle Tool Has Been Clicked
If blnCircleClicked Then
'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
pCirclePen.Color = cColor
'Draw The Circle With The Current Starting, And Ending Values.
'We must subtract the Starting values from the Ending values,
'to make sure the shape's Starting and ending values are
'precisely those where you started drawing, and where you
'ended drawing.
gCanvas.DrawEllipse(pCirclePen, sStartX, sStartY, _
sEndX - sStartX, sEndY - sStartY)
'Was The Fill Tool Clicked
If blnFillClicked Then
'Yes, Set The Brush Color To The Selected Color
sbCircleBrush.Color = cColor
'Fill The Shape Being Drawn
gCanvas.FillEllipse(sbCircleBrush, sStartX, sStartY, _
sEndX - sStartX, sEndY - sStartY)
End If
End If
'Determine If The Square Tool Has Been Clicked
If blnSquareClicked Then
'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
pSquarePen.Color = cColor
'Draw The Square With The Current Starting, And Ending Values
'Get Exact Starting X values
Dim SquareX As Integer = Math.Min(sStartX, sEndX)
'Get Exact Starting Y values
Dim SquareY As Integer = Math.Min(sStartY, sEndY)
'Subtract End from Start
Dim SquareWidth As Integer = Math.Abs(sStartX - sEndX)
'Subtract End from Start
Dim SquareHeight As Integer = Math.Abs(sStartY - sEndY)
'Draw precise Square with correct starting and ending coordinates
gCanvas.DrawRectangle(pSquarePen, SquareX, SquareY, SquareWidth, _
SquareHeight)
'Was The Fill Tool Clicked
If blnFillClicked Then
'Yes, Set The Brush Color To The Selected Color
sbSquareBrush.Color = cColor
'Fill The Shape Being Drawn
gCanvas.FillRectangle(sbSquareBrush, SquareX, SquareY, _
SquareWidth, SquareHeight)
End If
End If
'Determine If The Triangle Tool Has Been Clicked
If blnTriangleClicked Then
'Yes, It Has Been Clicked, Set The Pen's Color To Selected Color
pTrianglePen.Color = cColor
'Create The Triangle Based On User's Mouse Coordinates + 150,
'To Make The Triangle Display A Bit "Fatter"
gCanvas.DrawLine(pTrianglePen, sStartX, sStartY, sEndX, sEndY)
gCanvas.DrawLine(pTrianglePen, sEndX, sEndY, Me.MousePosition.X, _
Me.MousePosition.Y + 150)
gCanvas.DrawLine(pTrianglePen, sStartX, sStartY, _
Me.MousePosition.X, _
Me.MousePosition.Y + 150)
'Was The Fill Tool Clicked
If blnFillClicked Then
'Use A Point Array, In Order To Be Able To Use The
'FillPolygon Method - To Fill The Triangle
Dim pTPoints As Point() = { _
New Point(sStartX, sStartY), _
New Point(sEndX, sEndY), _
New Point(Me.MousePosition.X, Me.MousePosition.Y + 150)}
'Yes, Set The Brush Color To The Selected Color
sbTriangleBrush.Color = cColor
'Fill The Shape Being Drawn
gCanvas.FillPolygon(sbTriangleBrush, pTPoints)
End If
End If
End If
'If The User Is Not Erasing, Refresh The Picturebox's Display
If Not blnErasing Then
picCDraw.Refresh()
End If
'Dispose Of All Pens
pCirclePen.Dispose()
pSquarePen.Dispose()
pTrianglePen.Dispose()
'Dispose Of All Brushes
sbCircleBrush.Dispose()
sbSquareBrush.Dispose()
sbTriangleBrush.Dispose()
'Set The Drawing And Erasing Flags To False, Because The Mouse
'Button Is Realease - We're Not drawing/Erasing ANymore
blnDrawing = False
blnErasing = False
End Sub

The PictureBox's MouseMove event is used to keep track of the mouse position while Drawing Freehand or erasing.

Private Sub picCDraw_MouseMove(ByVal sender As System.Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) _
Handles picCDraw.MouseMove
'Declare A Eraser Pen, Used To Erase Parts Of Drawing.
Dim pEraserPen As New Pen(Color.White, 15)
'Declare A Drawing Pen, Used To Draw Free Hand, Once Pencil Tool
'Is Selected.
Dim pDrawingPen As New Pen(Color.Black, 3)
'Are We Drawing?
If blnDrawing Then
'Yes We Are, Set The Pen Color To Selected Color
pDrawingPen.Color = cColor
'Draw The Free Hand Lines
gCanvas.DrawLine(pDrawingPen, sStartX, sStartY, e.X, e.Y)
'Set New Starting Points For Next Line. New Starting Points Are
'The End Points Of Previous Line
sStartX = e.X
sStartY = e.Y
'Set The Images Drawn Thus Far In The Picture Box = To The
'In-Memory Image Object
Me.picCDraw.Image = bImage
End If
'Are We Erasing?
If blnErasing Then
'Yes We Are, 'Draw The Eraser Lines Lines
gCanvas.DrawLine(pEraserPen, sStartX, sStartY, e.X, e.Y)
'Set New Starting Points For Next Line. New Starting Points Are
'The End Points Of Previous Line
sStartX = e.X
sStartY = e.Y
'Set The Images Drawn Thus Far In The Picture Box = To The
'In-Memory Image Object
Me.picCDraw.Image = bImage
End If
'Dispose Of Both Pens
pEraserPen.Dispose()
pDrawingPen.Dispose()
End Sub

All you need to do now is add code to the Clear button to Clear the canvas, so that the user can start drawing again.

frmMDICanvas

All that you still need to do in your project is to load and display the forms. You will quickly do this inside the Load event of frmMDICanvas. You also need to Load new instances of your Drawing form when the New menu item is clicked.

Before you build and run your very first drawing application, you need to make sure that the VB .NET IDE knows which form to start with. You have to change it because, when you first started building the design of frmCanvas, you changed the name of the default form created. To change the Default startup form, follow these steps:

On the Main menu, select Project.

Select Canvas Properties.

Select General under Common Properties.

On the Startup Object drop down list control, select frmMDICanvas.

Click OK.

Now, Build and Run Your Program!

Feel free to hone your drawing skills. As you can see, I'm not much of an artist! After drawing some random shapes, my canvas ended up looking like the following picture.

[Finished.jpg]

Conclusion

I hope that you have enjoyed creating a basic framework for your Drawing Application. In the next article in this series, you will continue to build your Graphics Application. Improvements that you can expect in Part 2 are:

Showing a marquee while drawing

Filling the drawn objects

About the Author

Hannes du Preez

Hannes du Preez is a Microsoft MVP for Visual Basic. He is a trainer at a South African-based company. He is the co-founder of hmsmp.co.za, a community for South African developers.

Top White Papers and Webcasts

The 2014 State of DevOps Report — based on a survey of 9,200+ people in IT operations, software development and technology management roles in 110 countries — reveals:
Companies with high-performing IT organizations are twice as likely to exceed their profitability, market share and productivity goals.
IT performance improves with DevOps maturity, and strongly correlates with well-known DevOps practices.
Job satisfaction is the No. 1 predictor of performance against organizational …

This white paper examines the economics of deploying Red Hat's Storage Server. Based on GlusterFS, a distributed file system that Red Hat acquired as part of Gluster, Red Hat Storage Server is ushering in a new era of software-based storage (also known as software-defined storage by many suppliers) solutions. Such solutions leverage commodity x86-based hardware from server vendors and a distributed shared nothing architecture that allows businesses to build out a service-based storage infrastructure in an …