Page 1 of 1

Building an application - POS/Cash Register - Part one Custom keyboard and inheritence

#1 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 07 June 2010 - 09:25 AM

*
POPULAR

I keep seeing a lot of questions that basically fall into a few categories:
  • How do I make custom events?
  • How do I get two forms to talk to each other?
  • How do properties work?
  • I've gotten 50% done, but I didn't plan ahead now I'm stuck. How can I fix this?

So I decided to build a cash-register type application step-by-step and take you along for the ride. In this article we will learn/make use of:
  • Enumerations
  • Inheritance
  • Custom events
  • User controls
  • Multiple projects in one solution


Planning
Before jumping right in to any project you must PLAN AHEAD. Expect that later in life you will want to make changes to your application. Your boss is going to ask you to add features and so on. So even the simplest of programs need to be modularized. This is one of the tenat of C# and object oriented programming: Lots of little black boxes that have one purpose in life, but can be wired together in various ways to do different things.

What does a cash register UI need, at least to start with:
  • A numeric keypad
  • A display
  • Running total
  • Tax calculation

What might get added later, or be a nice feature that we will plan for now, but add later:
  • A secondary display (the one that faces the customer)
  • Receipt printing
  • Barcode reader (for UPC codes on products)
  • Audit trail / reporting


Basic UI
Let's start a new Windows Forms project and name it DemoPOS.
This automatically wraps this first project in a solution of the same name. A solution can contain multiple projects if you didn't know already.
Attached Image

Attached Image

Attached Image
Next let's add another project to this solution. This is going to be a Windows Control Library. Name it Display.

Make one more Windows Control Library project and name it Keypad.
Attached Image
Your solution should contain 3 projects and look like this
Attached Image

Planning ahead: There are lots of different kinds of keypads. A cash register pad has different keys and layout than a telephone keypad or a computer keyboard 10-key pad. Wouldn't it be nice if we created a Keypad project, with several different types of Keypads inside it? That way we can re-use our Keypad project in other solutions. Then we just use Keypad.RegisterKeypad or Keypad.TelephoneKeypad as needed. Here is where our inheritance example is going to be used. All of these keypads share a lot of commonality, with just minor changes to GUI. So we will make a generic keypad, then inherit from it for our more specialized keypads. Lots of on-line tutorials do this with animals. First you make an animal, then you inherit a mammal from that, then you inherit a canine from mammal, then a poodle from canine. Whether it be animals, or products or keypads it is the same principal. You start out with the most generic form of the object, and with each inheritance get more specific.

In your Keypad project there is a UserControl named UserControl.cs. Right-click on it and rename it to GenericKeypad.cs
Attached Image
This generic keypad will be our base that we will inherit from for our more specific keypad later. It will contain all the properties and methods that all keypads have in common. Ok... What do all keypads have in common? What do they do? They press keys. That's it. Nothing else. Don't get caught up in the trap of trying to make one control do everything. A keypad is not a calculator. A Keypad does not add. It does not display. It does not record to a database. It presses keys, which in our case means it will raise events to tell any other control/class that a key has been pressed. So we need to make a custom event that other classes can subscribe to, that will tell what key has been pressed/clicked.

Right-click on the GenericKeypad.cs in the Solution Explorer and choose "View Code"
Add the new code shown in bold
namespace Keypad.cs
{
    public partial class GenericKeypad : UserControl
    {
        public GenericKeypad()
        {
            InitializeComponent();
        }

       #region Events
        public event KeyPressEventHandler ButtonPressed;
        #endregion Events

        #region Methods
        public void RaiseButtonPressed(char WhatToSend)
        {
            KeyPressEventHandler handler = ButtonPressed;
            if (handler != null)
            {
                handler(this, new KeyPressEventArgs(WhatToSend));
            }
        }
        #endregion Methods
    }


Congratulations: Your generic keypad now has a new event named ButtonPressed. Other classes (controls/forms) can subscribe to that and they will 'hear' when a button is pressed and what that button is. You could make your own custom EventArgs but we dodn't need to do that here because the .NET framework already had something we can make use of: The KeyPressEvent and the KeyPressEventArgs. Afterall, isn't that what we are doing? Pressing keys. Except for the actual buttons on the face, you've just built a virtual keyboard, or at least the controller for one.

By now you probably want to see *something* do *anything*. Just to make sure you aren't wasting your time. Fair enough. We are going to make a Keypad that uses the generic pad as a base, then use that to test our event.

INHERITANCE
Now we are going to make an inherited keypad that will become our RegisterKeypad. Right-click on Keypad.cs and choose "Add New.." then choose "UserControl". Name it RegisterKeypad.
Attached Image
Now view the code of this control just as we did earlier. As you can see this control is already an inherited control. We just going to change the base class that it inherits from. See where it says "RegisterKeypad : UserControl"... double-click UserControl to select all of it, then start typing GenericKeypad. Notice that Intellisense starts showing you all the matching options. You can just select from the menu if you like
Attached Image

Open the blank user control in the designer and drag ONE button from the tool box. Resize it so it square. In the example I made mine 64 x 64 pixels. Then copy/paste buttons to make a keypad
Attached Image
Select Button1 with a single left click. Go to the Properties pallet. Select the name property and rename it to "btn1"
Attached Image
Now we can quickly name each of the buttons without too much mouse-ing around. Select Button2 and WITHOUT going to the properties pallet just type "btn2". Notice that since we were already in the name field, its the name that changes. Click on Button3 - type "btn3". Click on Button4 - type btn4 and so on until you have renamed all the buttons. I used button names of "btn0", "btn00", "btnEnter", "btnPlus", "btnMinus" for the extra buttons.

We now use the same technique to change the text on the face. Select btn1. Go to the properties pallet and select the text of "button1" and type '1'
Attached Image
Now you can fast change all the other text. Select btn2 and type '2'. Select btn3 and type '3'

I then selected all the buttons and changed the font size to something larger
Attached Image

The buttons now all have worthwhile names, and meaningful text on the front. We just need to give them methods to run when they are clicked on. Double-click btn1, and you will be taken to the code. Visual Studio will be kind enough to stub-in a handler for the btn1_Click event. Go back to the designer and do the same for all the rest of the buttons, preferably in numeric order so the code is organized with btn1, btn2, btn3 and so on.

Now will give each method some code to raise our KeyPressed event. In the designer just double-click on button 1. Visual Studio will fill in an empty handler for you. Go to the designer and do button 2, 3, ... This way a new handler for every button is put in for you, and they will all be done at the same time and grouped together for your convenience.
Attached Image

I trust you are smart enough to have btn1 send a '1', btn2' send a '2' and so on. For now, fill in code for buttons 0-9. Buttons 00, +, - and Enter will need something more complex and we can update them later

At this point we want to hit function key F5 to cause the project to build and run. The results are boring: A blank Form1. That's ok. The reason we build here is because we need to compile the code for these controls so our toolbox will update. Notice that our two Keypad user controls are now in the toolbox for us to drag onto a form.
Attached Image


Let's do exactly that. Open From 1. Make it bigger so we have some room to work. Then drag a RegisterKeypad onto it. Then just above the keypad drag a textbox to act as our temporary display readout
Attached Image

Select the RegisterKeypad control. In the properties pallet click on the events button (yellow lightning bold) Notice the custom event we created is here: "ButtonPressed"

Any time someone clicks a button (0-9 for now) a ButtonPressed event is going to be raised by the control. Let's give it a handler. double-click in the blank field next to the 'ButtonPressed' label and Visual Studio will once again stub-in a handler for this event, in your code. Add line 12 as shown
namespace DemoPOS
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void registerKeypad1_ButtonPressed(object sender, KeyPressEventArgs e)
        {
            textBox1.Text += e.KeyChar;
        }
    }
}

Hit F5 again to run your code. Click any of the 0-9 keys and you should see the the display textbox update
Attached Image

So what is really happening when we click button 'btn1'?
In our RegistryKeypad.cs the handler for btn1 is being executed.
RegisterKeypad.cs runs a method it inherited from its base class (GenericKeypad.cs): RaiseButtonPressed. Giving it the parameter of '1'.
The base-class method then runs, generating an event with that parameter.
Our instance of RegisterKeypad.cs (RegisterKeypad1) on Form1 then hears the event, and runs it's handler for that control (Form1.cs) - adding the passed in parameter to the .Text property of the textbox1.

SAFETY
After you have everything working as it should be, close the solution.
In Windows Explorer go into your Visual Studio projects folder.
Select the POScalc folder.
[Control] C
[Control] V
Rename "POSCalc - copy" to "POScalc - {today's date}" like "POScalc 07feb10 1100hrs"

Now when you re-open the project it doesn't matter how much you break it. You have a functioning backup to this point.
Get used to doing this. The ability to roll back to working versions after completely screwing up a project will have you hours of frustration!

Part 0 - Custom events
Part 1 - Virtual Keypad (inheritance)
Part 2 - The LCD display panel

This post has been edited by JackOfAllTrades: 21 June 2010 - 10:38 AM
Reason for edit:: Remove b code tag from code block


Is This A Good Question/Topic? 22
  • +

Replies To: Building an application - POS/Cash Register - Part one

#2 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 26 January 2011 - 02:12 PM

Sorry about the long delay guys. Somehow I managed to unsubscribe to my own tutorial so I didn't realize there were questions waiting.

View PostJunicus, on 19 September 2010 - 11:45 PM, said:

Nice stuff

So what did you use for the +, -, 00 and Enter. Other than that I'd like to see your project, I'm thinking that adding a QwertyKeypad would be nice


What do you mean by "What did I use?" +, - and Enter are all available to send just like 0-9. For double zero send zero twice.

If you need a qwerty keyboard there is an onscreen keyboard built into Windows. Otherwise you can expand on this tutorial to make your own. The goal here is to teach, not to provide a finished copy/paste solution for those unwilling to put in some effort of their own.

View PostMatt, on 04 September 2010 - 03:16 AM, said:

Does this tutorial require Visual Studio professional?
I am using Visual Studio Express 2010, there is no "User control library" available.
Any suggestions?

Upgrade.

Or you can put all your controls in one regular windows forms project. Then for each solution you can Include... Existing Project... and put your controls project into every project that needs them. it just won't ever a proper DLL you can use from anywhere, it will have to be included in every project.

But seriously, when you think you are going to be doing more than just kitchen-table hobby coding you'll want to upgrade to VSpro.

This post has been edited by tlhIn'toq: 26 January 2011 - 02:28 PM

Was This Post Helpful? 0
  • +
  • -

#3 AdamSpeight2008  Icon User is offline

  • MrCupOfT
  • member icon


Reputation: 2264
  • View blog
  • Posts: 9,470
  • Joined: 29-May 08

Posted 26 January 2011 - 02:32 PM

View PostMatt, on 04 September 2010 - 11:16 AM, said:

Does this tutorial require Visual Studio professional?

I am using Visual Studio Express 2010, there is no "User control library" available.

Any suggestions?


Start with a Windows Form Project then change the Project's Application Type to Class Library.
Add to the Project a User Control.
Delete the Include Form (Form1).
Save

Now you can import and reference the DLL, in your other projects.
Was This Post Helpful? 2
  • +
  • -

#4 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 26 January 2011 - 02:40 PM

View PostAdamSpeight2008, on 26 January 2011 - 02:32 PM, said:

View PostMatt, on 04 September 2010 - 11:16 AM, said:

Does this tutorial require Visual Studio professional?

I am using Visual Studio Express 2010, there is no "User control library" available.

Any suggestions?


Start with a Windows Form Project then change the Project's Application Type to Class Library.
Add to the Project a User Control.
Delete the Include Form (Form1).
Save

Now you can import and reference the DLL, in your other projects.


That's a very cool tip. I never had to deal with the Express version. I just assumed if the option for a Class Library wasn't available for a new project, then that carried through to the available options when changing the types
Was This Post Helpful? 0
  • +
  • -

#5 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 02 February 2012 - 12:15 PM

CandymanJP said:

hey, i'm working on the POS register application that you designed. i can't get the buttons to display the number that was pressed. might have something to do with the part where i have to make button one send a 1 and so on.. right where you said.. "I trust you are smart enough to have btn1 send a '1', btn2' send a '2' and so on. For now, fill in code for buttons...


Rather than make a public comment on someone's profile page for something like this, a private message works a lot better so there can be two way communication.

UPDATE: Being a new user you can't yet use the PM system. Sorry. So about the only thing I can do is post your question and answer here. Hopefully that will clear up issues for anyone else that goggle's across this thread as well.


Ok, so you're having trouble getting a button to send a character. Maybe you missed the code earlier in the article where we have a method to raise an event.
namespace Keypad.cs
{
    public partial class GenericKeypad : UserControl
    {
        public GenericKeypad()
        {
            InitializeComponent();
        }

       #region Events
        public event KeyPressEventHandler ButtonPressed;
        #endregion Events

        #region Methods
        public void RaiseButtonPressed(char WhatToSend)
        {
            KeyPressEventHandler handler = ButtonPressed;
            if (handler != null)
            {
                handler(this, new KeyPressEventArgs(WhatToSend));
            }
        }
        #endregion Methods
    }




So when someone clicks the '1' button, you call RaiseButtonPressed with the char '1'. When someone clicks the '2' button, you call RaiseButtonPressed with the char '2' ... and so on.
Was This Post Helpful? 0
  • +
  • -

#6 Celerian  Icon User is offline

  • D.I.C Regular


Reputation: 144
  • View blog
  • Posts: 384
  • Joined: 30-March 12

Posted 27 February 2013 - 02:35 PM

I just wanted to say this tutorial is great. I'm using is as template to build a small suite of keypads (telephone, qwerty, register) that I can easily reuse down the line. I've mentioned on this site before that my wife intends to open her own day spa and I would like to help out by providing a custom software suite that she can use to manage her business, and I can see a lot of reusable application for something like this beyond just a simple PoS. She could easily allow for a Kiosk for booking appointments or viewing products and a qwerty control would be useful for allowing the customer to enter more information.

For design purposes, I'm getting ideas by just emulating everyday objects my 10 key on a keyboard, a cashiers register, my cell phones dial pad and virtual keyboard.

I did have a few questions:

1) After you build the initial control and place it in a project, I'm not seeing any good way to scale the control. It seems that however large you build the control is how big the control will be in the actual product. Maybe I'm missing something, but do you know how to get the control to scale? Maybe that's an additional feature that I can program in. it would certainly be nice to not need to make several different sizes.

2) Is there any difference between building this in C# or VB.net? Personally, I'm much more comfortable in VB.net, since I've been using basic since I started learning how to code and even my current position is mostly satisfied by coding quick applications with VB.net. However, I have taken to building dual projects (on in VB.net and on in C#) so I can keep practiced in my secondary programming languages. I would assume it does not matter, as after I built the project and imported the DLL, the C# version worked in both a C# application and a VB.net application.

This post has been edited by tlhIn`toq: 27 February 2013 - 03:18 PM

Was This Post Helpful? 0
  • +
  • -

#7 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 27 February 2013 - 03:11 PM

The tutorial is a couple years old now.
If I were doing this today, or building a new program as you are, I wouldn't do it in WinForms as it is outdated.

Scaling in Winforms is a bit of a pain in ass because things are anchored to the upper-left corner unless otherwise specified. And you can't anchor to another control. So you have to create a method for the UserControl.SizeChanged event, then programmatically calculate new size and location for each control. If you do it in a 'cascading' way it's not too horrible.
  • Resize and position the first button.
  • Reset the .Location of the second button relative to the first's .Bounds.Right property.
  • Resize the second.
  • Repeat with 3rd relative to 2nd
  • Repeat with 4th relative to 1st (same x, but drop down from the new bottom)
  • repeat
  • repeat
  • repeat
  • Open Beer


Any program built today should be done in WPF. That immediately solves all the scaling issues and creates a program meant for today's OSes (Win8) and hardware (gestural input touchscreens).

Presentation is literally WPF's middle name. Its what it was build for. I have numerous controls made that reposition automatically by space available and geometry: They stack vertical or horizontally depending on the landscape or portrait shape of the container.

Everything else from the tutorial remains usable: its still a UserControl, events are still events and so on. (When I get some free time I need to create a 'getting started in WPF' tutorial.)

As you learned by trial (best way to learn!) once it is in a dll you can mix VB and C#. You don't even have to export it to a DLL. I don't make DLL's until I consider the library *done*. Just include the control's project in your solution, and make a reference to it. (You do know a solution can have multiple projects, right?)

Posted Image
Was This Post Helpful? 1
  • +
  • -

#8 Celerian  Icon User is offline

  • D.I.C Regular


Reputation: 144
  • View blog
  • Posts: 384
  • Joined: 30-March 12

Posted 28 February 2013 - 06:36 AM

Yeah. I tend to work a little differently, since I have a few work projects, and then a few pet projects. Back in my high school days when I was first learning on VB6, I used to keep code snippits (when they told you to save all your code for reuse, encapsulation and modulation stuff), but since I've lost most of that stuff, I am now rebuilding my "codebase." Basically creating my own controls and modules that I should be able to easily drop into any project and have a jumping off point. For instance, I have one that handles my SQL connections, and while its currently tuned for my job, it wouldn't be too difficult to change the connection details and have it connect to any other SQL database I need. For that, I almost want to get into a position where I'm working with other types of databases so I can optimize that code to work with different RDBs, or build different ones that are structured for different RDBs. I also have a fairly robust Assert module, which allows for debugging and error logging and a Core module, which I usually use as a backbone for small applications.

I do import the projects for the controls. It makes it easy, since I can technically modify the control from any project its being used in and changes will propagate throughout all my projects. I just have to be careful not to break the control, or make it unusable for one application while trying to enhance it for another.

I'm not too far into the project, so I might take a shot at building it in a WPF instead, and see if that doesn't work better for me. Thanks for the suggestions.
Was This Post Helpful? 1
  • +
  • -

#9 Sir_Gnoy  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 11-May 14

Posted 11 May 2014 - 04:58 AM

Love this stuff! It corrects many things I'm doing wrong. Thanks for teaching!

This post has been edited by tlhIn`toq: 11 May 2014 - 07:12 AM

Was This Post Helpful? 0
  • +
  • -

#10 dusanr  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 13-June 14

Posted 13 June 2014 - 05:04 AM

Hi, I have problems and errors... and i thin that is here somewhere :

View PosttlhIn`toq, on 07 June 2010 - 09:25 AM, said:

Now view the code of this control just as we did earlier. As you can see this control is already an inherited control. We just going to change the base class that it inherits from. See where it says "RegisterKeypad : UserControl"... double-click UserControl to select all of it, then start typing GenericKeypad. Notice that Intellisense starts showing you all the matching options. You can just select from the menu if you like
Attachment 009_uc_inherit.jpg


I receive following error :
Posted Image

Please help ...
Was This Post Helpful? 0
  • +
  • -

#11 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5537
  • View blog
  • Posts: 11,866
  • Joined: 02-June 10

Posted 13 June 2014 - 05:20 AM

"The base class 'keypad.generickeypad' could not be loaded."

So did you make that class? Is it in the same namespace? Check carefully for typos in the class or namespace name.

"Ensure that the assembly has been references and all projects have been built"

If you hit F6 to build your solution, do you get different errors? If you have the classes in different namespace you will need to add a reference.

If you have these in different projects within the same solution make sure all the projects are set to the same .NET framework version. If one is set to 4.5 and one to 2.0 for example, the 2.0 project won't be able to include the 4.5 code. So just make sure that all the projects are set to framework 4.5.2 (or the highest you have installed which could be 4.5 or 4.5.1)
Was This Post Helpful? 0
  • +
  • -

#12 dusanr  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 13-June 14

Posted 13 June 2014 - 05:33 AM

AAAArghhhh ... I just havent Buit it :(
Is working :) And I can continue (for my Homework)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1