SQL with Vulcan.NET

 Top Next

SQL is a very established database 'format', and as such we need to be able to access tables and data items, from within our Vulcan.NET code.

This set of 'ClickStart' sections will take a 'beginner to SQL' through the necessary stages of development, and at each stage show clear working examples of Vulcan code in action.

The SQL provider used for all of the examples and projects, will be Microsoft - their SQL Server 2005/2008 will be the focus of the coding.

However, I have taken advice from senior colleagues and tried to make the code as close to 'provider agnostic' as possible. Details on this will be provided when appropriate.

As a matter of interest you may wish to know that I am using the Beta 2 version of Server 2008 - with its related software tools - to create the body of this work. This is running on a Vista 64 platform.

It is somewhat a bonus to those coming from Visual Objects, and the older world of DBFs, to realize that since Vulcan supports DBFs we can then write some simple routines to copy over data from the earlier database format to the now established SQL table format.

This will make it easy for people like myself to get into SQL, and migrate some on my older apps which my clients would certainly wish me to.

It is not intended that these notes will explain what a 'back-end' server is, or justify why it should be used.

These notes are a summary of my experiences over the last five weeks, in going from zero SQL skills (and virtually no knowledge as such) to being able to write a working app in Vulcan.NET- which uses a handful of tables which are related in the manner one would expect of a relation database.

Nothing startling, but it all works in Vulcan, and I for one, am on my way with SQL.

To feel as if we mean business, lets look at some Vulcan code which actually makes a stored procedure for the BEST company application (Project three).

======================================================================================================================================================================================================

Obviously, all we can appreciate at the moment is that the stored procedure is called "GetOrdersInPeriod" and that it does not look too complex.

You may also notice that it takes two input variables @start and @finish, which are in fact dates of the DateTime data type.

I hope this wets your apatite for getting to grips with Vulcan and SQL.

PHH - July 2008.


Overview

 Top Previous Next

These 'ClickStart' notes are a record of my experiences, and progress, into starting to learn and access SQL server from the Vulcan.NET language.

I had no previous experience, but I was aware of what SQL was, from a brief 'flirt' with MySQL years ago. Still have the book. Suspect a few of you may be like that too.

The documentation provided here is simply to allow others following on the trail, to learn SQL with Vulcan more easily. It is not an 'expert treatise' on the topic, and although I have tried to take good advice, and do things 'correctly', I am sure I will be found lacking in quite a few places ;-)

The 'whole point' of these notes is to get you going, and give you some early success, so that you keep moving forward.

Now that I am 'off', I intend to move from DBFs to SQL tables as soon, and as much as possible. Meetings with clients are to follow shortly.

If you get any SQL to work at all, you will be probably on your way for ever. It is a rewarding experience.

Also, by providing documentation of my experiences, I am passing on the good suggestions and ideas that experienced senior colleagues provided for me - so even if you have no such folk to guide you, you do not have any excuse for giving up this time.

Okay then, where are you likely to go wrong, or simply fail !?

First is that you must get a 'good install' of either SQL Server 2005 or 2008 from Microsoft. This includes Management Studio, the support tool. If you have XP 32 go for server 2005 - Advanced or Developer edition.

Once you have a good install, you need to find your 'connection string' so that your Vulcan code can 'talk with' the server.

If you can connect, then make sure you can access the databases required for both the video tuition, and also the Vulcan code shown here.

You will be pleased to know that one BIG area that gave me 'grief', is no longer there for you !

Getting the correct dotNet classes, and making them work together, as well as getting the correct Vulcan syntax - this has all been done for you, and is documented in a progressive fashion throughout the sections of these help notes. All code is working code, and full listings in text format are provided for all the projects. This can be read as well as cut and pasted.

I have to thank Alison Balter for her book, which got me into Vulcan coding from the VB examples she provides.

Thanks must be said to the following colleagues for their helpful input to this project :-

Mike Pitcher, Colin Newell, Mike Jones, Paul Piko, Ginny Caughey and Willie More.

To you all, I tried to take on board all you said, and make a cohesive entity for those following.

One final word to all, keep looking for the benefits of new developments from Microsoft. Recent progress in dotNet, with provision of new and extra classes, means that we can explore new and sometimes easier ways of doing what experienced colleagues can happily do without.

What I mean by this is that if you are experienced, and have your own wrapper classes which work, and work fine, you probably don't wish to look at alternative ways of doing things - and I quite understand. However, us new guys have no such excuse for NOT looking into the very latest stuff. I have found that some of it makes life a lot easier for those coming along fresh to SQL - who have no wrapper classes or anything else.

So keep a look out. I wonder just what may be new in the 2008 version. I am sure I have only found what was new in version 2005 :-)

Thanks to all.


Introduction

About five weeks ago I started out on the quest of learning SQL - and anything else that would be necessary to allow me to create, and run, Vulcan code using the dotNet platform to access SQL databases.

Although there is a wealth of material about SQL Server 2005 from Microsoft, there is a limited amount which covers the skills required by the application programmer, and system developer. There is even less written in Vulcan code itself. Most of what I have produced for inclusion in 'ClickStart SQL' has been based on VB.net book material.

To supplement these help notes I do thoroughly recommend you get the Microsoft free download videos presented by Bob Tabor of MS. These are essential to learning SQL.

The series is called 'Absolute Beginners' and I watched, and revisited them a couple of times - so that all the necessary detail eventually 'stuck'. They did get me going, so they do work!

In addition to these, I also purchased the DVD video material published by 'Total Training', and called 'Microsoft SQL Server Development' (Professional Series).

These are hosted by Paul Nielsen, a very experienced SQL professional, and it shows. I gained a great deal from these too. They go a lot deeper than the AB series, but the later sessions of the AB series do actually cover some useful topics (like Reporting Services) which the Paul Nielsen series does not. So together they cover some very necessary topics. I am glad that I have them both.

Also, I would say that the AB series probably covers learning the Management Studio software tool in a lot more detail.

It is now about time we saw some Vulcan / SQL related code, and coding success, so here is something we will get to in time :O)

This is what can be done (and lots more) if you follow what I suggest - it did for me.

===========================================================================================================================

Good Luck with your SQL and Vulcan.NET quest !

PHH - July 2008.


What is required

Download the Absolute Beginners video series :-

http://www.microsoft.com/downloads/details.aspx?FamilyID=ed43a82f-768b-4ecd-acc0-563db84f7d6d&DisplayLang=en

==================================================================================================================================================================

Make sure you get the full 13 sessions which were on offer when I downloaded them.

SQL Server Express Edition Learning Resources

Video Series: SQL Server 2005 Express Edition for Beginners

This video series is designed specifically for SQL Server beginners, individuals who are interested in learning the basics of how to create, manage, and connect to SQL Server Express databases. Whether you’re just a beginner or somewhat familiar with databases, these video lessons will help you get better acquainted with SQL Server 2005 Express.

The series includes almost 9 hours of video-based instruction that walks SQL Server beginners through the steps of learning about SQL Server databases to actually connecting a SQL Server database to a Web application. Select your starting point below based on your skill set.

Video 1 : What is a database?

Video 2 : Understanding Tables and Records

Video 3 : More about Column Data Types & other Properties

Video 4 : Designing Relational DB Tables

Video 5 : Manipulating DB Data

Video 6 : More Structured Query Language (I,U & D)

Video 7 : Understanding Security and Network Connectivity

Video 8 : Connecting your Web App to SQL Server 2005

Video 9 : Using SQL Server Management Studio

Video 10 : Getting Started with Reporting Services

Video 11 : Customized Reports - Embedding, Packing, Deploying

Video 12 : Creating and Using Stored Procedures

Video 13 : Enabling Full-Text Search in your Text Data

Lesson 1: What is a database?

This introduction covers the questions and terminology SQL Server beginners need to create and manage databases, such as: What is a SQL Server database? How is a SQL Server database different from an Excel spreadsheet? What do SQL Server databases do in my Web site or software applications? What's SQL Server Express' relation to Visual Studio Express? What do terms like "ADO.NET" mean?

Duration: 28 minutes 15 seconds

Lesson 2: Understanding Database Tables and Records

SQL Server beginners will learn about tables and definitions of data types, properties, keys, etc. in this second video. Find out how table rows, columns, and fields interrelate and whether columns can be empty.

Duration: 24 minutes 56 seconds

Lesson 3: More about Column Data Types and Other Properties

SQL Server beginners will explore data integrity, learn which data types to use for what, and avoid common data type pitfalls.

Duration: 21 minutes 37 seconds

Lesson 4: Designing Relational Database Tables

Why are relational databases the most popular? SQL Server beginners will learn to differentiate between primary and foreign keys, design relational database tables, and learn about normalization rules and de-normalization.

Duration: 34 minutes 10 seconds

Lesson 5: Manipulating Database Data

SQL Server beginners will learn about ADO.NET objects and execution of SQL statement from ADO.NET. Explore SQL and learn about insert, update, and delete statements.

Duration: 40 minutes 20 seconds

Lesson 6: More Structured Query Language

SQL Server beginners will discover more SQL statements like sub-select and learn stored procedures and their counterparts in C# and VB methods.

Duration: 23 minutes 13 seconds

Lesson 8: Connecting your Web Application to SQL Server 2005 Express Edition

SQL Server beginners will walk through the entire workflow of creating a database driven Web application and use visual tools to quickly create an administration application.

Duration: 65 minutes 58 seconds

Lesson 9: Using SQL Server Management Studio

SQL Server Management Studio Express will typically be your main interface into SQL Server Express. This lesson demonstrates the essential techniques and skills you’ll need to successfully connect to database instances, navigate the query window and scripting database objects, attaching, detaching and backing up databases, and much more.

Duration: 40 minutes 26 seconds

Lesson 10: Getting Started with Reporting Services

SQL Server Reporting Services allows you to design and deploy nicely formatted and interactive reports representative of your data. Suitable for print or online distribution, these reports … and it allows you to get professional results very quickly. This video demonstrates how to set up Reporting Services and the Business Intelligence Development Studio to build reports using the wizards and designers, and deploy them to your Report Server.

Duration: 32 minutes 51 seconds

Lesson 11: Building and Customizing Reports in Business Intelligence Development Studio

Building on the previous lesson, we dive into building reports from scratch and modifying the reports that were created using the wizards and designers. We also demonstrate how to embed your reports into your ASP.NET Web Form applications.

Duration: 44 minutes 50 seconds

Lesson 12: Creating and Using Stored Procedures

This video expands on the topic of Stored Procedures which was introduced several lessons ago. In this video we explain the basic syntax to create and update stored procedures, using input and output parameters, return values, declaring variables and adding other T-SQL programming elements into you stored procedures, how to execute your stored procedures from the query window, and finally the Visual Basic and C# syntax required to utilize stored procedures in your own applications.

Duration: 42 minutes 34 seconds

Lesson 13: Enabling Full-Text Search in your Text Data

Full-Text search allows you to save large portions of text even Microsoft Word documents, or other file formats into your database and perform complex queries based on this textual data while expecting great performance. This lesson demonstrates how to set up Full-Text search paying particular attention to the nuances of setting it up within SQL Server Express. Then we move to creating full-text indexes, configuring the index population settings, and performing queries that demonstrate the power of the CONTAINS predicate and its many, many variations.

Duration: 38 minutes 25 seconds

PHH - June 4th 2008.


How to get going

1. Download the Absolute Beginners video series - you can start watching those ASAP.

2. While you have breaks between video lessons, download the free SQL Server 2005 or 2008 files, depending on your PC and operating system.

If you have an XP 32 system I would suggest you use SQL Server 2005, as it should install relatively easily, and is totally consistent with all the support material.

Make sure you get the Advanced version, or the Developer version. You will need to view closely what is on offer from Microsoft.

The reason you are after the advanced version is that you will need to look at 'Reporting Services' once you get going with SQL generally.

3. It maybe that the Business Intelligence tools and the Management Studio tool come separately, you need to read their download pages carefully, as this information changes very quickly with time.

4. Bob Tabor in the AB series shows how to install the 2005 version and this is worth watching, if you are to about to install 2005.

However, the 2008 version is quite a bit different, so you will have to use your own skills here :-)

I found the 64 bit version of 2008 installed quite nicely on Vista 64, once you were prepared to focus on just this task, and not be in a rush to get into SQL query creation and Vulcan coding too quickly.

5. I can't stress too strongly just how important it is to prepare both yourself and your mind. So don't skimp the video watching.

6. When you are ready to do something for yourself, have a few sessions with MS before going anywhere near Vulcan. Try to repeat for yourself the exercises Bob does in the video.

To help with this I have included some text notes I made from the lesson 5 & 6 and there are also some results shown to help you get going. These are to be found in detail in sub-section called 'some material to try'.

7. Remember as you are easing your way into MS, that this will act as your 'right hand man' when you come to Vulcan coding itself. You work with MS on one screen and VS or VIDE on another.

MS lets you view and check the table structure, column data types, and try out SELECT statements before copying and pasting into Vulcan code.


Installing the software

Although this is not rocket science, it has to be done right, in order to work !

Having seen what I had to go through to get two 'good' installations (one a 32 bit 2005, the other a 64 bit 2008), it is not possible for me to give correct and 100% prescriptive instructions.

I will try and give you guide lines and tips however. So here goes.

Have plenty of time, do not be in a hurry.

Select your target PC and if possible make sure it is NOT the one you use for everything you do :-)

Find out whether you have a 32 or 64 bit installation of XP or Vista.

Go onto the Microsoft site and check you have the very latest downloads for your system, and take time to upgrade and install, both correctly and thoroughly.

Find the download site for the version of SQL Server which fits your operating system, and on top of that get the Advanced or Developer version.

When you download I suggest you 'save' it to a folder first - before running the install, as you may well have to do this many times - I know ;-)

Make sure you get the SQL Server, as well as Business Intelligence software, and very importantly, the Management Studio (MS).

The 2008 SQL Server install will definitely inform you of software inadequacies during the install process. You may even need to break off and follow their (MS) guidelines on what to update, before trying the install afresh.

( It will take quite a while to do all this. )

I seem to recall restarting the process at least twice, before I got the all green tick results below :-

When you get past the point (and it may take quite a while, and a few install restarts) where you software platform is being checked out, take your time.

You need to respond carefully to the install 'user input' screens. For most of the time you are wishing to have a local install (not network) but I think the choice may be 'system'.

Also make sure the 'Windows Authentication' install option is used - don't elect for password and user name.

Remember, we are trying to get a simple and initial install which will get first timers going with code from Vulcan.

If you need to, check that you wish for Advanced Services - Reporting Services in particular - to be installed.

When the install process 'supplies and suggests' a default name for the Server, leave it 'as-is' and keep going.

You also want to check the 'browser' facility is switched on initially, as this will help you 'find and clarify' what server names you have on offer when you try to go into MS for the first time.

Look at the images below - if at first you do not have any entries in the drop down list, only <Browse for more> then use this to see what MS finds.

This is why you need to check the install option for 'browse'.

Management Studio :

This is where you need to pause for thought, and to take a few deep breaths :O)

If you can launch MS then you have probably done enough (or everything) required to get you going, and learning SQL.

Oh! nearly forgot, in Vista, make sure you 'right click' the desktop icon for MS and set the necessary settings under properties - Compatibility tab - check 'Run as administrator'.

I may have caused myself unnecessary grief by not doing this !

With MS open at the 'Connect to Server' screen like those above - you need to find your server - I suggest you do the 'browse' to see what is found (if is is not grayed out!).

You ought to recognize a name similar to the one offered by the install screen. Vista / 2008 seems to use the name of your PC, whereas XP / 2005 seems to have a general name like 'SQLEXPRESS' or 'MSSQLSERVER' similar.

Be aware that it is possible to have names of servers in your list (see above) which do not 'work' ! Or more correctly perhaps, that do not have an entity behind the name in the drop down.

Below is an example of me trying to connect to one of those in my list which is not there as far as I know. My names are a result, it would appear, of multiple installs of various 'flavours' and then un-installs etc. etc. etc.

Testing to see if all is well :

Okay, so we have MS, and it launches. We have found a server name which seems correct and we have tried to connect.

If there are no nasty messages, then you are probably OK.

As far as 2005 folk are concerned they are probably nearly 'home and dry' by this stage. However, Vista folk have another few possible headaches to encounter.

Oh! it is probably best to mention here that it is quite possible to have multiple instances of MS open, no problem. Just be aware ;-)

The image above shows two instances of MS open, one with connection 55 and the other 57 - one with AdventureWorks in focus, the other MyCompany.

However, after you have seen the AB videos from Bob Tabor you will know all about MS and how you can have multiple instances in the one object explorer too.

Okay, back to business.

The last step is you need to install and then locate a suitable database. I suggest you start with the Company, or was it MyCompany database that downloads with the AB video series.

I suggest you place the DB files in a user area, particularly if you are a VISTA user. I had difficulty with some folders, and not others. However, making MS launch as administrator may have removed some problems.

After all, we just want to get going.

Now that I have more confidence with the Beta 2 version of MS, I realize that I can make it connect and work, I just need to be patient, try out some sensible options - AND - realize that there are a few 'glitches' in the software.

Try placing the files in the root folder of your Windows user area. For example I am using "C:\Users\Best\" as my user name is 'BEST'.

I have also had success following Willie's advice and making a folder "C:\SQLdatabases\", so try this too.

However, since we are trying to eventually run code from Vulcan, I would try and get the user area folder to work and then stick with that, as this will more likely to be OK with Vulcan code execution.

To get MS to 'see' the database once you have copied it to a folder, your right click on the 'Databases' name in the Object Explorer. This produces a set of options, choose 'Attach ...'

Follow the explorer type screens to point to and select the .MDF of your choosing.

It is at this point you may have to fight the MS shell and the location - but don't give up, you will eventually get it to stick, and then you are off :O)

I found that closing MS and re-launching it sometimes did miraculous things as far as clearing problems. But do remember I am using the Beta 2 version, and generally after this sticky part it runs brilliantly.

It is just that at the VERY time you are feeling vulnerable the 'bugs' get to you !

Good Luck,

PHH - July 2008.

Now go and watch the video with a nice cup of tea :o)


Learning and support materials

As well as free and downloadable material from Microsoft, there are a few other things you do really need if you are to be serious about learning SQL and getting into writing your own Vulcan applications.

I suggest a few good books, and a more in-depth set of videos. Search the web for the local supplier of 'Total Training' materials for you.

UK based folk can got to CareerVision at www.cvision.co.uk

They are to be found at 61, Caroline Street, Birmingham. B3 1UF tele: 0121 248 2400

When I purchased my set it was on special offer for about £68 before VAT. It is very good value and will give you many many hours of suitable material for both Server 2005 and 2008.

It may last you for years !

However, be aware that it does not cover 'Reporting Services', but Bob Tabor in the AB series does spend quite some time on this topic.

The following book was very helpful indeed while attempting to get Vulcan code to work by accessing the tables in the Company database.

Although it is written for Visual Basic I managed to extract enough information to make running examples of a whole set of scenarios for the Company Database.

This text by Alison Balter will certainly help you. [ ISBN 0-672-32741-4 ]

Rick Dobson - [ISBN 1-59059-523-8]

And now the 'large book from the master himself, Paul Nielsen. [ ISBN 0-7645-4256-7]

Finally, a very important general reference text for many provider dialects.

[ ISBN-10: 0-596-00481-8 ]

[ ISBN-13: 978-0-596-00481-1 ]

PHH - July 2008


Use of Management Studio

SQL Management Studio (MS) :

Management Studio (MS from now on) is a powerful yet flexible Interactive Development Environment (IDE) supplied by Microsoft to go with their SQL Servers.

The MS for 2005 and 2008 I got as free downloads from the Microsoft web site. The actual links will change with time so you are best to go and search in the Microsoft site itself.

MS for the Beta 2 version of 2008 looks like this :-

==================================================================================================================================================================================================

This is a Visual Studio type shell, with the appropriate tools for SQL development. However, it seems quite easy to get used to using in a productive manner.

I have to say that I feared it would prove as daunting a task to get into using it as it did VS initially, however, that did not prove to be the case.

After watching Bob Tabor on the AB series videos, I took to it quite quickly, and now enjoy using it a lot. Bob Tabor does have a later video lesson on using MS itself, as a focus.

It would seem to me now that the ideal way to work is to have MS open on one screen while having you Vulcan development shell on another. You can then use MS to 'see' inside the tables and check that what you are doing with code is actually working. This also goes for creating stored procedures from code, user defined functions (UDFs), and other database maintenance tasks you may code into you app.

It is also extremely handy to check out any SQL query statement in MS, get it working and correct, and then copy and paste into your code. Seems to work fine, I like it a lot.

MS Help :

Don't forget that there is a wealth of information within the help section of MS.

Try finding the information on the 'CONVERT' function as discussed in Appendix C.


Some material to try

Below is some test material I made after hand writing some notes while watching the Bob Tabor videos (numbers 5 & 6).

It is obviously tailor made for the accompanying database called Company, or 'My Company' - look at the download material to see which you have.

This should save you some tedious note taking - however, I do suggest you actually go through it in detail, and enter your own text into MS and generate your own responses.

When you get things going make some small changes and enter your own related queries as you work through the material. It is very much in-line with what the videos do.

If you are really lazy you can copy and paste into MS, but I suggest you don't cut corners, and get your sleeves rolled up :-)

Lesson 5 : trial statements for Absolute Beginners Video :

select * from customers

select firstname, lastname from customers

select firstname

as 'First Name', lastname as 'Surname'

from customers

-- this is a comment --

select firstname+' '+lastname

as 'Customer Name'

from customers

Customer Name

-------------------------------

Jim Taylor

Amy Johnson

Shawn Black

Bill Brown

Janna Smith

Evette LeBlanc

Drew Brisco

Angela Smith

(8 row(s) affected)

-- now try orders --

select * from orders

where orderdatetime > '01/10/2006'

OrderID     OrderDateTime           CustomerID  Tax                   Shipping

----------- ----------------------- ----------- --------------------- ----------

1           2006-01-13 07:00:00     3           3.95                  12.95

2           2006-01-12 07:45:00     5           4.95                  7.95

14          2006-01-10 08:33:00     3           6.95                  8.95

15          2006-01-10 11:49:00     4           4.95                  12.95

16          2006-01-11 14:05:00     7           3.95                  8.95

17          2006-01-12 17:26:00     6           4.95                  9.95

(6 row(s) affected)

-- now try orders - with compound statement --

select * from orders

where orderdatetime > '01/10/2006' and orderdatetime < '01/12/2006'

OrderID     OrderDateTime           CustomerID  Tax                   Shipping

----------- ----------------------- ----------- --------------------- ----------

14          2006-01-10 08:33:00     3           6.95                  8.95

15          2006-01-10 11:49:00     4           4.95                  12.95

16          2006-01-11 14:05:00     7           3.95                  8.95

(3 row(s) affected)

-- now with compound statement OR --

select * from customers

where customerid = 6 or customerid = 7

-- now with compound statement OR --

select firstname+' '+lastname

as 'Customer Name'

from customers

where customerid = 6 or customerid = 7

Customer Name

--------------------------------------------------

Evette LeBlanc

Drew Brisco

(2 row(s) affected)

-- trying functions --

select sum(tax) as 'Tax to Pay' from orders

Tax to Pay

---------------------

103.53

(1 row(s) affected)

-- trying functions --

select sum(tax) as 'Tax to Pay', sum(shipping) as 'Postage'

from orders

Tax to Pay            Postage

--------------------- ---------------------

103.53                170.15

(1 row(s) affected)

-- trying more functions --

select sum(shipping-tax) as 'silly calulated value'

from orders

silly calulated value

---------------------

66.62

(1 row(s) affected)

-- trying more functions --

select count(*) as 'number to send'

from orders

number to send

--------------

17

(1 row(s) affected)

-- now using Product table ---

select ProductPrice-3 as 'disc price'

from products

disc price

---------------------

26.95

46.95

6.95

11.95

12.95

96.95

66.95

(7 row(s) affected)

-- now using Product table ---

select ProductPrice*0.9 as 'sales price'

from products

sales price

---------------------------------------

26.95500

44.95500

8.95500

13.45500

14.35500

89.95500

62.95500

(7 row(s) affected)

-- some more functions --

select min(shipping) as 'lowest postage'

from orders

lowest postage

---------------------

5.95

(1 row(s) affected)

-- some more functions --

select max(tax) as 'biggest tax'

from orders

biggest tax

---------------------

12.44

(1 row(s) affected)

-- some more functions --

select avg(shipping) as 'average postage cost'

from orders

average postage cost

---------------------

10.0088

(1 row(s) affected)

-- working with datetime (date) values --

select orderid as 'Order Ref. :', month(orderdatetime)as 'Month :', day(orderdatetime) as 'Day :' from orders

where orderid > 7

Order Ref. : Month :     Day :

------------ ----------- -----------

8            1           6

9            1           7

10           1           8

11           1           8

12           1           9

13           1           9

14           1           10

15           1           10

16           1           11

17           1           12

(10 row(s) affected)

-- find today and now ! --

select getdate() as 'Today and Now!'

Today and Now!

-----------------------

2008-06-05 16:25:00.390

(1 row(s) affected)

-- more with dates and datetime --

select orderdatetime as 'Order Placed : ', datename(mm, OrderDateTime) as ' Month : '

from orders

where orderid > 9

Order Placed :           Month :

----------------------- ------------------------------

2005-01-08 04:00:00     January

2006-01-08 00:00:00     January

2006-01-09 16:35:00     January

2006-01-09 21:03:00     January

2006-01-10 08:33:00     January

2006-01-10 11:49:00     January

2006-01-11 14:05:00     January

2006-01-12 17:26:00     January

(8 row(s) affected)

-- more with dates and datetime --

select orderdatetime as 'Order Placed : ', datepart(mm, OrderDateTime) as ' Month No. : '

from orders

where orderid > 9

Order Placed :           Month No. :

----------------------- -------------

2005-01-08 04:00:00     1

2006-01-08 00:00:00     1

2006-01-09 16:35:00     1

2006-01-09 21:03:00     1

2006-01-10 08:33:00     1

2006-01-10 11:49:00     1

2006-01-11 14:05:00     1

2006-01-12 17:26:00     1

(8 row(s) affected)

-- more with dates and datetime --

select orderdatetime as 'Order Placed : ', datediff(dd, getdate(),OrderDateTime) as 'Days Difference : '

from orders

where orderid > 9

Order Placed :          Days Difference :

----------------------- ------------------

2005-01-08 04:00:00     -1244

2006-01-08 00:00:00     -879

2006-01-09 16:35:00     -878

2006-01-09 21:03:00     -878

2006-01-10 08:33:00     -877

2006-01-10 11:49:00     -877

2006-01-11 14:05:00     -876

2006-01-12 17:26:00     -875

(8 row(s) affected)

-- more with dates and datetime --

select orderdatetime as 'Order Placed : ', datediff(dd, OrderDateTime,getdate()) as 'Days Difference : '

from orders

where orderid > 9

Order Placed :          Days Difference :

----------------------- ------------------

2005-01-08 04:00:00     1244

2006-01-08 00:00:00     879

2006-01-09 16:35:00     878

2006-01-09 21:03:00     878

2006-01-10 08:33:00     877

2006-01-10 11:49:00     877

2006-01-11 14:05:00     876

2006-01-12 17:26:00     875

(8 row(s) affected)

-- more with dates and datetime --

select orderdatetime as 'Order Placed : ', dateadd(dd, 13, OrderDateTime) as 'Later Date : '

from orders

where customerid <= 3

Order Placed :          Later Date :

----------------------- -----------------------

2006-01-13 07:00:00     2006-01-26 07:00:00

2006-01-09 06:00:00     2006-01-22 06:00:00

2006-01-04 08:34:00     2006-01-17 08:34:00

2006-01-06 20:00:00     2006-01-19 20:00:00

2006-01-09 16:35:00     2006-01-22 16:35:00

2006-01-09 21:03:00     2006-01-22 21:03:00

2006-01-10 08:33:00     2006-01-23 08:33:00

(7 row(s) affected)

-- working with strings --

select substring(ProductDescription, 3,5) from products

---------------------------------------------------------------

intr

ese v

assic

mbine

dern

compe

om th

(7 row(s) affected)

-- working with strings --

select left(ProductDescription, 5) as 'Left Part : ' from products

Left Part :

---------------------------------------------------------------------------

An in

These

Class

Combi

Moder

A com

From

(7 row(s) affected)

-- working with strings --

select right(ProductName, 13) as 'Right Bit! : ' from products

Right Bit! :

-------------

g Widgets 1.0

tual Training

nster Widgets

ed By Widgets

o the Widgets

ing Multipack

ideo Training

(7 row(s) affected)

-- working with strings --

select upper(ProductName) as 'upper case :' from products

upper case :

-----------------------------------------------------------------

INTRODUCING WIDGETS 1.0

WIDGETS VIRTUAL TRAINING

ATTACK OF THE MONSTER WIDGETS

INSPIRED BY WIDGETS

ROCKING TO THE WIDGETS

COMPLETE WIDGETS TRAINING MULTIPACK

LEARNVISUALWIDGETS VIDEO TRAINING

(7 row(s) affected)

-- working with strings --

select lower(ProductName) as 'LOWER CASE :' from products

LOWER CASE :

-----------------------------------------------------------------

introducing widgets 1.0

widgets virtual training

attack of the monster widgets

inspired by widgets

rocking to the widgets

complete widgets training multipack

learnvisualwidgets video training

(7 row(s) affected)

-- working with strings --

select reverse(ProductName) as 'Mirror Name? :' from products

Mirror Name? :

----------------------------------------------------------------------------------------------------

0.1 stegdiW gnicudortnI

gniniarT lautriV stegdiW

stegdiW retsnoM eht fo kcattA

stegdiW yB deripsnI

stegdiW eht ot gnikcoR

kcapitluM gniniarT stegdiW etelpmoC

gniniarT oediV stegdiWlausiVnraeL

(7 row(s) affected)

-- working with strings --

select lastname, firstname

from customers

where lastname like 'J%'

lastname                                           firstname

-------------------------------------------------- --------------------------

Johnson                                            Amy

(1 row(s) affected)

-- working with strings --

select left(lastname,13) as 'Surname :', left(firstname,11) as 'Know as :'

from customers

where lastname like 'J%'

Surname :     Know as :

------------- -----------

Johnson       Amy

(1 row(s) affected)

-- working with strings --

select left(stateprovince,13) as 'State Prov.:',

left(lastname,13) as 'Surname :',

left(firstname,11) as 'Know as :'

from customers

where stateprovince in ('IL', 'KS', 'PA')

State Prov.:  Surname :     Know as :

------------- ------------- -----------

KS            Taylor        Jim

IL            Johnson       Amy

PA            Brown         Bill

(3 row(s) affected)

-- setting an ORDER to the returned data --

select left(lastname,16)as 'Surname : ', left(firstname,12) as 'Known By :'

from customers

order by lastname desc

Surname :        Known By :

---------------- ------------

Taylor           Jim

Smith            Janna

Smith            Angela

LeBlanc          Evette

Johnson          Amy

Brown            Bill

Brisco           Drew

Black            Shawn

(8 row(s) affected)

-- setting an ORDER to the returned data --

select left(lastname,16)as 'Surname : ', left(firstname,12) as 'Known By :'

from customers

order by lastname asc, firstname desc

Surname :        Known By :

---------------- ------------

Black            Shawn

Brisco           Drew

Brown            Bill

Johnson          Amy

LeBlanc          Evette

Smith            Janna

Smith            Angela

Taylor           Jim

(8 row(s) affected)

-- almost, finally this ! --

select customerid, count(*), sum(shipping)

from orders

group by customerid

having count(*) >=3

customerid

----------- ----------- ---------------------

3           3           29.85

6           3           28.85

7           3           28.85

(3 row(s) affected)

-- almost, finally this ! --

select customerid, count(*) as 'Number:', sum(shipping) as 'Postage:'

from orders

group by customerid

having count(*) < 3

customerid  Number:     Postage:

----------- ----------- ---------------------

1           2           17.90

2           2           25.90

4           2           20.90

5           2           17.90

(4 row(s) affected)

-- and FINALLY ! --

select ProductID, ProductPrice, left(ProductName,20) from Products p inner join ProductTypes pt

on p.ProductTypeID = pt.ProductTypeID

where p.ProductPrice > 29.95

ProductID   ProductPrice

----------- --------------------- --------------------

2           49.95                 Widgets Virtual Trai

6           99.95                 Complete Widgets Tra

7           69.95                 LearnVisualWidgets V

(3 row(s) affected)

PHH - June 2008

-- trying a Sub-Query --

select * from Orders

where OrderID in

(select distinct OrderID from OrderItems where ProductID =3)

1        2006-01-13 07:00:00        3        3.95        12.95

2        2006-01-12 07:45:00        5        4.95        7.95

9        2006-01-07 17:00:00        5        12.44        9.95

16        2006-01-11 14:05:00        7        3.95        8.95

-- trying a Sub-Query --

-- with colum names too --

select OrderID as 'Order Ref:', CustomerID as 'Customer Ref:', Tax, Shipping

from Orders

where OrderID in

(select distinct OrderID from OrderItems where ProductID =3)

1        3        3.95        12.95

2        5        4.95        7.95

9        5        12.44        9.95

16        7        3.95        8.95

-- Section on INSERTs --

insert Customers (FirstName, LastName, PostalCode)

values('Tommy','Tinderhouse','TH3 7FZ')

Msg 515, Level 16, State 2, Line 2

Cannot insert the value NULL into column 'Address', table 'MyCompany.dbo.Customers'; column does not allow nulls. INSERT fails.

The statement has been terminated.

-- Section on INSERTs --

insert Customers (FirstName, LastName, Address, PostalCode)

values('Tommy','Tinderhouse','13, Back Street','TH3 7FZ')

Msg 515, Level 16, State 2, Line 2

Cannot insert the value NULL into column 'City', table 'MyCompany.dbo.Customers'; column does not allow nulls. INSERT fails.

The statement has been terminated.

-- Section on INSERTs --

insert Customers (City, StateProvince, Country, FirstName, LastName, Address, PostalCode)

values('Aberdeen','Scotland','UK','Tommy','Tinderhouse','13, Back Street','TH3 7FZ')

(1 row(s) affected)

-- see if strange 'mapping' affected the results --

select * from Customers

where CustomerID > 6

7        Drew        Brisco        1690 Hollis Street        Ottawa        NS        B3J 2Y3        CA

8        Angela        Smith        4110 Nottingham        Burbank        CA        90230        USA

14        Tommy        Tinderhouse        13, Back Street        Aberdeen        Scotland        TH3 7FZ        UK

-- now for some UPDATEs --

update customers

set city = 'Glasgow', PostalCode = 'G13 7TY'

where FirstName like 'Tom%'

(1 row(s) affected)

-- see if 'something like' worked --

select * from Customers

where CustomerID = 14

14        Tommy        Tinderhouse        13, Back Street        Glasgow        Scotland        G13 7TY        UK

-- Add another ready for DETETE section --

insert Customers (City, StateProvince, Country, FirstName, LastName, Address, PostalCode)

values('Cardiff','Wales','UK','John','Jones','39, Side Alley','CF3 5TQ')

(1 row(s) affected)

-- see if 'something like' worked --

select * from Customers

where CustomerID > 13

14        Tommy        Tinderhouse        13, Back Street        Glasgow        Scotland        G13 7TY        UK

15        John        Jones        39, Side Alley        Cardiff        Wales        CF3 5TQ        UK

-- lets try a DELETE ! --

delete Customers

where LastName like 'Tind'

(0 row(s) affected)

-- lets try a DELETE ! --

delete Customers

where LastName like 'Tind%'

(1 row(s) affected)

-- see if 'something like' worked --

select * from Customers

where CustomerID > 13

15        John        Jones        39, Side Alley        Cardiff        Wales        CF3 5TQ        UK

create procedure CustomerInsert

@FirstName varchar(50)

@LastName varchar(50)

@Address varchar(50)

@City varchar(50)

@StateRegion varchar(50)

@PostCode varchar(50)

@Country varchar(50)

as

begin

insert Customers (FirstName, LastName, Address, City, StateProvince, PostalCode, Country)

values(@FirstName, @LastName, @Address, @City, @StateRegion, @PostCode, @Country)

end

go

Msg 102, Level 15, State 1, Procedure CustomerInsert, Line 4

Incorrect syntax near '@LastName'.

Msg 137, Level 15, State 2, Procedure CustomerInsert, Line 14

Must declare the scalar variable "@FirstName".

create procedure CustomerInsert

@FirstName varchar(50),

@LastName varchar(50),

@Address varchar(50),

@City varchar(50),

@StateProvince varchar(50),

@PostalCode varchar(50),

@Country varchar(50)

as

begin

insert Customers (FirstName, LastName, Address, City, StateProvince, PostalCode, Country)

values(@FirstName, @LastName, @Address, @City, @StateProvince, @PostalCode, @Country)

end

go

Command(s) completed successfully.

===========================================

execute CustomerInsert

'Dick','Whittington','Mansion House','London','The South','L1 1DW','UK'

(1 row(s) affected)

-- see if 'something like' worked --

select * from Customers

where CustomerID > 13

15        John        Jones        39, Side Alley        Cardiff        Wales        CF3 5TQ        UK

16        Dick        Whittington        Mansion House        London        The South        L1 1DW        UK

====================================================================

USE [MyCompany]

GO

/****** Object:  StoredProcedure [dbo].[CustomerInsert]    Script Date: 06/06/2008 15:13:12 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

ALTER procedure [dbo].[CustomerInsert]

@LastName varchar(50),

@FirstName varchar(50),

@Address varchar(50),

@City varchar(50),

@StateProvince varchar(50),

@PostalCode varchar(50),

@Country varchar(50)

as

begin

insert Customers (LastName, FirstName, Address, City, StateProvince, PostalCode, Country)

values(@LastName, @FirstName, @Address, @City, @StateProvince, @PostalCode, @Country)

end

=================================

Command(s) completed successfully.

execute CustomerInsert

'Gates','William','Microsoft Place','Paris','Central','PA1239','France'

(1 row(s) affected)

-- see if 'something like' worked --

select * from Customers

where CustomerID > 15

16        Dick        Whittington        Mansion House        London        The South        L1 1DW        UK

17        William        Gates        Microsoft Place        Paris        Central        PA1239        France

Good Luck !

PHH - June 2008.

==========================================================================================================

USE [C:\SQLDATABASES\MYCOMPANY.MDF]

GO

/****** Object:  Table [dbo].[Customers]    Script Date: 06/17/2008 15:07:19 ******/

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Customers]') AND type in (N'U'))

DROP TABLE [dbo].[Customers]

GO

USE [C:\SQLDATABASES\MYCOMPANY.MDF]

GO

/****** Object:  Table [dbo].[Customers]    Script Date: 06/17/2008 15:07:19 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

SET ANSI_PADDING ON

GO

CREATE TABLE [dbo].[Customers](

[CustomerID] [int] IDENTITY(1,1) NOT NULL,

[FirstName] [varchar](50) NOT NULL,

[LastName] [varchar](50) NOT NULL,

[Address] [varchar](50) NOT NULL,

[City] [varchar](50) NOT NULL,

[StateProvince] [varchar](50) NOT NULL,

[PostalCode] [varchar](50) NOT NULL,

[Country] [varchar](50) NOT NULL,

CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED

(

[CustomerID] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

SET ANSI_PADDING OFF

GO

=====================================================

USE [C:\SQLDATABASES\MYCOMPANY.MDF]

GO

/****** Object:  Table [dbo].[Orders]    Script Date: 06/17/2008 15:09:21 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE TABLE [dbo].[Orders](

[OrderID] [int] IDENTITY(1,1) NOT NULL,

[OrderDateTime] [smalldatetime] NOT NULL,

[CustomerID] [int] NOT NULL,

[Tax] [smallmoney] NOT NULL,

[Shipping] [smallmoney] NOT NULL,

CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED

(

[OrderID] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Customers] FOREIGN KEY([CustomerID])

REFERENCES [dbo].[Customers] ([CustomerID])

GO

ALTER TABLE [dbo].[Orders] CHECK CONSTRAINT [FK_Orders_Customers]

GO

==================================================================

USE [Ultra File 2008]

GO

/****** Object:  Table [dbo].[MainSet2]    Script Date: 06/17/2008 15:10:51 ******/

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MainSet2]') AND type in (N'U'))

DROP TABLE [dbo].[MainSet2]

GO

USE [Ultra File 2008]

GO

/****** Object:  Table [dbo].[MainSet2]    Script Date: 06/17/2008 15:10:52 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

SET ANSI_PADDING ON

GO

CREATE TABLE [dbo].[MainSet2](

[CardID] [int] NOT NULL,

[MainHead] [varchar](130) NOT NULL,

[SubHead] [varchar](130) NULL,

[Body] [varchar](690) NULL,

[phone] [varchar](24) NULL,

[mobile] [varchar](24) NULL,

[email] [varchar](70) NULL,

[website] [varchar](80) NULL,

[Company] [varchar](50) NULL,

[Person] [varchar](50) NULL,

[details] [varchar](390) NULL,

[date] [smalldatetime] NULL,

[datetimestamp] [timestamp] NULL,

[work] [bit] NULL,

[personal] [bit] NULL,

[finance] [bit] NULL,

[technical] [bit] NULL,

[software] [bit] NULL,

[VulcanVO] [bit] NULL,

[IT] [bit] NULL,

[hardware] [bit] NULL,

[government] [bit] NULL,

[reminder] [bit] NULL,

CONSTRAINT [PK_MainSet2_1] PRIMARY KEY CLUSTERED

(

[CardID] ASC

)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

) ON [PRIMARY]

GO

SET ANSI_PADDING OFF

GO

=====================================================================

PHH June 2008.


Specific or General ?

An important issue I was alerted to, quite early on in my SQL learning experience, was that of making the code as 'general' as possible to help 'future proof' application code.

My experienced colleague advisors pointed out that it was possible to use certain classes in dotNet which would keep the code much closer to being able to run with SQL servers from a range of providers.

The term used was 'provider agnostic'.

Although we are basing this whole set of help notes on Microsoft's SQL Server family, there are other important ones out there, and ones that Vulcan developers will need to write code to access.

The first difference is the actual connection string itself. This varies according to the server provider so a useful source of information to help out is :-

=========================================================================================================================================================================

To show just a couple of important other provider connection details is probably important to you so here goes :-

(Do remember that there are many at this site, and these are but a couple.)

===================================================================================================================================

Okay, now that I can leave you to sort out your own connection string, we can get to talk about other important business :-)

To overcome this problem of creating code specific to one provider, which will then need changing to run with a different 'flavour' of server, certain classes have been provided in the dotNet framework.

Although the project developed for section 3, called 'BEST company', uses this 'general' approach throughout, we will make some mention of the ideas here.

Firstly, lets look at the way we can select a provider. The image below is of the 'connection' form tool in section 3, and shows a drop down box in action :-

Here we notice that a short list of providers is available for selection in a combo box control. The 'SqlClient' high-lighted is for the Microsoft 'flavour' of server, and is working here with server 2008.

This list is provided for us by the dotNet framework when we use the 'DbProviderFactories' static method as shown below :-

=========================================================================

Once you have selected the correct 'provider' to use then you may pick up the related 'factory' as follows :-

EXPORT oDFactory AS DbProviderFactory

.............................................................

//----- get the factory for the provider we require -----

SELF:oDFactory := DbProviderFactories.GetFactory("System.Data."+SELF:oConnDetails:cProvider)

//----- create a connection STRING ------

oDConn := oDFactory:CreateConnection()

//====================================================

This is just part of the DoSetConnectionString() method from the 'BEST company' application.

Once you have the correct 'factory' for your instance of you app, you use this at the appropriate places to make sure you have other objects which match up.

For example :-

LOCAL oDComm AS DbCommand

oDComm := SELF:oParent:oDFactory:CreateCommand()

oDComm:Connection := SELF:oParent:oDConn

This you will see many times throughout later code, as it is setting properties for the command object.

Then you will see other lines like :-

LOCAL oDr AS DbDataReader

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.Text

oDr := oDComm:ExecuteReader()

These are all using 'general' code which would work simply by providing an alternative provider.

If you have the 'SQL Server 2005 Express' text by Alison Balter you will see she uses specific lines of code involving classes from those shown below :-

SqlConnection

SqlDataAdapter

SqlDataReader

SqlDataRecord

SqlDataSource

There are also some SQL data types specific to Microsoft it would appear, however, we are now getting outside the scope of the purpose of these help notes.

Once you can get SQL to work for you then you can worry further about the compatibility issue. At least I have made you aware of a few important and provider related issues. At the same time as using some useful 'generalised' code by section 3 as well. You may also like to know that using DataTables and DataSets, can possibly overcome many of the Microsoft specific SQL data types when the data is used by the dotNet class to construct a strongly type table for you. Look this out later in the notes.

There is a useful article called "Factory Class Hierarchy" which is to be found at the present using the link below :-

http://www.developer.com/net/net/article.php/3530396

The above link was given to me by a knowledgeable colleague, and this helped me develop a generalized code style used in section 3. It is recommended for those wishing to know more.

However, the author Thiru Thangarathinam makes it clear at the end of the article that although you can do a lot to make your app 'portable' it is unlikely to run seamlessly, as there are provider specifics within the database itself, and some of the ways you pass in parameters to statements and stored procedures varies depending on provider. So be aware in case you get that good that you need to worry about such things in a short space of time.

Right, back to our coding. You will notice that the DbProviderFactory class has methods such as :-

CreateCommand()

CreateConnection()

CreateConnectionString()

CreateDataAdapter()

CreateParameter()

These you will see used in section 3 in place of the the 'Sql' variety !

However, I must stress that the first issue is to get some code working at all. This is a great buzz the first time you get all the various parts 'talking' to each other.

I wouldn't say it verges on disbelief, but it comes close :-)

PHH - July 2008.


Finding a Connection String

Once you find the four crucial pieces of information which are needed to build up your Microsoft SQL Server 2005/2008 connection string, you will need to keep using this time after time.

Just to get going with test, trial and demo apps, it is sufficient to hard wire into the code the necessary details. However, for a more long term, and flexible solution, we need to build and use an interactive tool which will allow user input of connection details, as well as providing a facility to store the detail to disk file.

For such a connection 'tool' see the form in section 3 (a 'full' application), where runtime serialization is used to create an XML file after testing the connection string with the server.

For full details on setting and testing connection strings through an interactive form, see appendix A.

Below is shown the relevant page information for the SQL Server 2005 :-

See the previous section for details on finding alternative connection strings for other providers of SQL server.

However, before we leave this topic we ought to see and note that there is a difference for the compact edition from Microsoft and these details are shown below :-


Section 1 - A 'trial' project

Developing our skills :

In this initial section we will look at the techniques required to extract data from an SQL database. We will see what syntax to use in Vulcan to allow the developer / coder to receive data from the server and put it into variables which are directly useful to the coded solution being developed.

Suggestions from experienced senior colleagues would seem to be, put the data immediately into you own (customized) objects for use later. Irrespective of whether you display it or not.

Much of the support material in book and video form, deals with the structuring of the query to be sent in the SELECT statement, and shows results in the Management Studio results panes etc. However, there is far less of a body of useful material on the program code to 'receive and handle' the data coming back from the server. And till now the only Vulcan code I have seen is what I have generated myself. I have used the little Visual Basic material to good effect, I hope. Thanks to Alison Balter's book.

However, before we can handle any returned data, we also need to know the Vulcan code syntax for sending the query statement to the server in the first place.

In the next few sub-sections we will examine a few useful ways of doing both of these, sending queries and receiving data.

The app form (shown below) is nothing more than a 'platform' on which to place various controls we need to try out the different aspects of Vulcan coding and syntax, to allow us to 'talk' with the SQL server.

I use the word 'talk' loosely to mean two way communication, sending queries and getting back data.

Using this screen we will try various simple queries and focus mainly on dealing with the data which is returned by the server.

We will then process this appropriately and display it in various Form 2.0 controls.


Choosing a Database

To keep as much commonality as possible with the training and support materials you may have used to this point, we will use the Company database downloaded which came along with the free AB video series from Microsoft.

The Orders and Customers table will prove very useful in getting some basic code to work for us. No doubt you will recognize the following :-

=================================================================================================================================

In my code example you will see that the connection string will need to refer to the database file with file path "C:\SQLdatabases\MyCompany.mdf" which is shown above.


Connecting to the Server

Let us start the meat of this section by examining the connection string which works for me on this Vista 64 platform :-

================================================================================================

The image above, of the live code which works on my machine, has four important parts.

Before we go into the details of these I would like to point out that the connection string I am building, and using, is specifically for SQL Server 2008 from Microsoft. I am aware that server 2005 will be very similar, if not identical.

There is information about connection to other proprietary SQL servers in the appropriate sub-section in 'ClickStart SQL'.

As you will see from the image in 'Installing the Software' in the last section, my server is called "BEST-V64QUAD" and this was found with the 'browse for more' feature of the start screen of Management Studio.

Ignore the rest of my drop down list of servers, these are there because I tried to install so many different variations of SQL server, before I found the right one. The system seems to retain each of those you have had working previously, no matter how short the period of operation. So some of my drop down options don't actually work any more.

Enough of that, lets stick with what does work. View below to see my connection string in action - and working !

You will notice that the file path is :- "C:\SQLdatabases\MyCompany.mdf" , as shown in the previous sub-section in MS>

On this system I have found that the value for the 'Integrated Security' may be set to either SSPI or True and both seem to work fine. For some reason, at first, I though that SSPI was the only value which would work.

For those using Server 2005 I think 'True' will get you up and running. It does on my XP laptop with 2005 - after a standard type installation.

Finally, the TimeOut value can be anything from zero to .... I am not sure, but 30 is all I have seen used, especially if you realise that zero (0) actually means do not drop the connection. You may to check on this once you get passed beginner level and trial apps etc. etc. For now lets just get things working.

Once you change the code to suit your installation then the following is required :-

============================================================================

When you examine the full working code in another sub-section you will see that the class definition contains :-

PROTECT oConn AS SqlConnection

At this point let me say that the use of SQLConnection is specific to Microsoft as a provider and that you need to look at making you code more 'provider agnostic' later in the help notes.

It ought to be apparent to you that it is this code snippet which causes 'State = Open' to appear in the rich text box shown above. The value of SELF:cStrConn has been set in the

DoSetConnectionString() method also above.

Since one of the disciplines of SQL server use, is to disconnect when you are not actively using the server, then we need a method to disconnect. This can be done as follows :-

=================================================================================

The style of the code above is sufficient for us to get going but we may well polish our technique a little in further sections to allow a caption bar message to always indicate on the main form, whether a connection is established or not.

For full details on setting and testing connection strings, and many more related issues, check out appendix A.


Namespaces and References

To try and ease your task, when it comes to coding yourself, it may be useful to refer to the list of namespaces I have used in developing project 3.

There may be the odd 'redundant' one, meaning it is no longer used - but at least you have a guide :-)

First, here is an image of the References :-

#USING System.Windows.Forms

#USING System.Drawing

#using System.Data.Common

#using System.IO

#using System.Data.SqlClient

#using System.Data

#using System.Data.SqlTypes

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml

#using System.Diagnostics

#USING System.Drawing.Printing

#using system.globalization

#using System.Threading

#using System.Diagnostics

Remember, that one of the tricks about getting the correct namespaces at the head of your PRG files, is to use the SDK documentation (always use that for Framework 3.5 now) - and in either the class tab or the 'about' pane you get the namespace and the assembly shown :-

================================================================================================


Getting back Data

As a result of a SELECT query, the server will usually send back data. It is possible to get nothing in some cases.

Getting back nothing when you expect something is a problem for another day, or another section - we need to know what to do to handle data that does come back.

Of the data that is returned, it may be a single value - called a scalar - or a table.

By a table I mean something which has rows and columns. Obviously, a single column, a single row, and also a single 'cell', are all special cases of the table. But a table in some form we get.

It would appear that a single table is the most we can expect in return to a query, no matter how complex. So a query accessing data in multiple tables using a few JOINs will still end up making a results set in table form. This is what is sent back from the server to the generating Vulcan code.

Obviously it is possible to have a huge amount of information in the table returned, and this depends on both the query, and the data held by the server in the data tables within the database.

It may seam obvious to you by now, after studying the support material suggested, that the developer needs to know the data types of the data being queried and returned.

Well, this is true, but more recent dotNet support allows data to be received and displayed without such detailed knowledge. And what is more, the table returned is strongly typed, as far as the column data is concerned. As another aid to the coder, the column name is the field name, or alias, generated at the SQL server end. So the developer is given much support if he / she wishes to use these newer techniques.

I must admit having spent many, many, hours coding in dotNet, I tend to favour using the more modern support classes and techniques. We will study each in turn to allow you to see what is on offer, then make your own choice.

Let us make a start with the simpler approach first (probably the earliest too).

=======================================================================================================================================================

The method above is fired when the 'Count Records' push button is clicked. My normal style would be to call this 'pbCountRecords' but this was development code which worked and I moved on rapidly to greater things - so it will have to suffice to explain what works :-)

A couple of things to notice in the above code - changing to the commented out lines will find the total number of records in the customer table. The result is shown in the text box called 'otbSQL' in place of the orders record count.

You will notice that in the code we have used the provider specific 'SqlCommand' class and that the method 'ExecuteScalar()' provided the single piece of data to be displayed in the text box.

Now lets us see what is behind the 'Silly Calculation' button. This button also runs query code which returns a scalar value, and this time is is the rather meaningless calculation at the server end, of the sum of all the difference between the tax charged and the shipping cost - but hey! this is a learning exercise :O)

=========================================================================================================================

At this point we ought to focus on the TRY / CATCH / END TRY structure.

For this early code it is sufficient to use the System.Exception class. However, later we will see how to use a more specific exception class, as well as how to record exception data into a special table we make specifically for this, in the database.

Moving on the longer things !

Okay, now we can get back single values, let us try and get back a list, and a list with which we can populate a combo box.

It's always nice to see what we are trying to do so view the list above :-

The code to bring this about is shown below, but do realize we can improve the way we do this, by using a more dotNet approach later in the help notes.

==================================================================================================================

We see that the big difference to what we did previously is that we are using the 'ExecuteReader()' method to return a DataReader object. (And again this is provider specific for now - to the Microsoft Server 2005.)

The select statement gets the server to concatenate the for customers first and last name columns, and supply a single column as a returned data set, and this is ordered by last name only. Since the table returned is a single column table, the ordinal value is zero (0). For now we will use ordinal value, later we can see how to adapt this approach to use a name in place of a number.

Each time the code goes round the While loop, another line, or row, of data is read into the buffer to be processed. It is also possible to refer to the data items out of ordinal sequence. So 2 does not necessarily need to be before 3. Note that the ordinal index is zero based.

While we may be smiling at our success, do bear in mind it is very easy to read in data , and make complex objects which can just as easily be placed in the combo box too. Also we can control which related value is displayed in the list box. Keep reading to find out more, and this will be covered many times in the 'full' application section 3.

Three jobs in One ?

Okay then, now we can fill a comb, drop down box, let us fill three list boxes at the same time. Yes, possibly another meaningless task - but it is learning, so let's do it !

===========================================================================================================

From one SELECT query we will get back a table data set which is three columns wide and a number of rows deep. The return order of the data set is by StateProvince A-Z, or ascending.

===========================================================================================================

Note that for a working application it would have been a good idea to clear the list boxes, so that the data goes into empty lists. In this demo test framework, multiple clicks of the 'Populate List Boxes' button actually keeps adding the same data over and over - not a good idea for a working app !

Once again do appreciate that although this works fine, there are better ways to do this with objects. For example, it may have been a good idea to make one object from the three data fields and then display the same list of items in each list box, but with different ways of displaying the string values.

In fact we have a method in later work in the help notes, which would allow the list of objects to be shown once, and a click of the control change the way the list is displayed. We have lots of options if we do things the OOP way. For now, lets just get things to work.

Once you get the buzz of getting results, it is much easier to move on to code and technique refinements.


Further Ways to Acquire Data

Well, now that we have made a start lets us move on to the easy part, and do some things which seem far more advanced.

Yes, I said better application results, but easier coding. Got to be good news.

Above is a nicely displayed table of results to our select query statement. Lets us see what the Vulcan syntax looks like which generated such an output.

Well, in fact as you will see below, it is actually simpler than what we have done previously :-

==============================================================================================

We are still using a provider specific class in SqlDataAdapter, but the general coding will be the same when we try and write in a non-specific fashion later.

All we have had to do is instantiate a DataSet and 'Fill' it will a table of our own naming. We have called in 'CustomersHeader', but could have equally as easily called it "NiceDisplay". As long as we then use the same table name to provide a data source for the DataGridView control.

Now then, the 'Even Better' news.

The table returned to us, and now in our table, has been strongly typed and also given appropriate column names. This, as you will see, makes our life a lot easier, and we will visit this aspect again in later sections.


Displaying Tables Returned

Now that we know we can easily display tables in a data grid view control on our forms, we can even take advantage of the situation to improve the functionality of our applications.

For example it may be useful to have three or four related tables all in one data set, and swap between them for viewing - this could be done by some sort of event, like clicking radio buttons. We could switch between customer viewing, orders, products, and invoices. And only have one control on our screen.


Stored Procedures, a Start

While we are studying our 'trial' project, let's look at an example of using a stored procedure.

As part of my learning experience with the AB series video's I used MS to enter and test a Stored Procedure. This should be the one in the earlier notes for you to have a go yourself with MS.

Below is how it looks in MS at this very moment - it is seen under "Programmability, Stored Procedures' :-

=================================================================================================================================================================================

The code I am about to show you is actually running live and using this SP in order to create a new record in the Customers table.

As you will be able to see, it is just saying, " execute 'CustomerInsert' " and supplying some date, which of course needs to be in the correct order for what is expected.

========================================================================================================================

Notice that since we are not expecting any data to be returned we can use the method called 'ExecuteNonQuery'.

Obviously, if we want some sort of feed back we need to write some extra code to do this.

This is probably the point to mention that if you insert a new record into a table with a Primary Key in use, it is possible to ask for the last PK value issued, but this must be only for the last value issued. The Function @@Identity is what is used, look out for this is later examples, we use it and also the @@Error function too.


CRUD - Create, Update & Delete

Inserting New Information :

Now that we can send queries to the server, and get back returned data sets in various ways, let us see some basic ways to change the state of the data in the database.

Let us start with INSERT. This straight forward approach provides the query statement and the data for the new record, at the same time.

The stored procedure in the last sub-section is probably a better approach, but we need to know how to do it this way first I feel.

=====================================================================================================

You will notice that the 'ExecuteNonQuery()' method is used since no data is being returned. However, I have left in my first attempts to first attempts to display in a table the last few records to see that changes have taken place, in line with what was required.

This approach seems a bit 'simple' now I know more, but it did work, and it did prompt me to find out a much better way :-)

In a later section you will see how to get back the primary key last issued by the server so that may be used to get back just the record recently created. Look out for the @@Identity function in section 3.

You may notice how easy it is to use the returned data as a DataTable and attach it as a data source to the grid control. I like this a lot.

Updating Information :

Remember that the SqlDataAdapter is Microsoft specific, and that we will see DbDataAdapter used later in the sub-sections to follow.

Also in the code lines to follow below, there are hard coded variables provided, just to allow a test update. Again, there are better ways of doing this, or probably it should be said more flexible ways.

This listed method will work in a number of useful places however, so don't dismiss it :-

==========================================================================================================================

The 'ExecuteNonQuery()' method was used to send the SQL statement, and once again we used a very simple approach to checking that we had been successful.

Finally Deleting or Removing a Record :

Although a fair bit of this code is repeating earlier stuff, I do find it necessary to keep it altogether for at least one working example. This seemed to be the case with Alison Balter too, in her book that I have been following.

I would recommend you get hold of a copy. However, she does get confused somewhat in example 15.14 & 15.15 ... something goes funny, BUT, it did get me going and I got everything to work in the end. She didn't have the benefit of Help and Manual 5 as I have here - with such wonderful and easy screen and image capture and insert.

Well, lets see some code to remove table rows :-

=====================================================================================================

From now on we ought to be able to cut down on the duplication of such repetitive code and just be able to focus on the 'crunchy' bits ;-)


Making and Removing a Table

Contra to what you may expect, lets look at the way to remove a table before we look at how to create one !

Simply, one we know the SQL query syntax to use, it is just a matter of sending it to the server with the 'ExecuteNonQuery' method.

Lets see what this looks like :-

=========================================================================================

Please note that the 'GO' command is part of the MS 'front-end' and is not to be sent to the server. Also remember that we ought to be adding a semi colon " ; " to the end of out statements, as this is recommended by Paul Nielsen, and is necessary in certain places - like just before some Common Table Expressions (CTE).

Fine, now we can remove a table, we have a clean slate on which to work, so let's now create a table with a structure I have made up for testing purposes. It is not a copy of a Company or Adventure table.

=====================================================================================

The slightly more tricky thing is to make tables with primary and foreign keys, and to set both seed and increment values. However, there is a nice and easy trick ;-)

Take a table similar to what you require, one in MS, and then right click over the name in the object explorer. The option 'Script Table as ..' then 'Drop and Create to .. ' this will provide suitable code to be copied as and where you require it. Just check this way to see how to make a Primary Key by copying the code for the Customers table.

You should find it to be :-

CREATE TABLE [dbo].[Customers](

[CustomerID] [int] IDENTITY(1,1) NOT NULL,

[FirstName] [varchar](50) NOT NULL,

......................................

and this is code copied and pasted as suggested earlier. I think the IDENITY(x,y) order is seed and increment so x is seed, and y increment. try it for yourself.

Before we move on we need to make a note that we will need to get used to using "IF EXISTS" a lot more in future to check what is stored in the database. This goes for stored procedures, and much more.


Customised Objects from Tables

Enter topic text here.


Letting .NET do the Work

Enter topic text here.


Making an SQL Table from a DBF

In this small section we use the SQL table we created in the earlier sub-section called 'Making and Removing a Table'.

This is based on a real-life example, and simple customer records from my VO invoices application are to be transferred to the new SQL table.

The VO application has been going some years now and is based on a couple of DBFs which old invoice and customer records respectively.

First let us look at the method which opens and reads in the DBF records, and after each one calls on a 'transfer' method to 'push' the data into the table.

METHOD DoDBFtoSQL() AS VOID

RDDSetDefault("DBFCDX")

LOCAL oServerC AS DBServer

LOCAL cFilePath AS STRING

cFilePath := "C:\users\BEST\SQLdatabases\customers.dbf"

IF file.Exists(cFilePath)

//MessageBox.Show("Yes, file is found for use !!")

ELSE

MessageBox.Show(SELF,"NO ... can't find file ??","Problem - ABORT!")

RETURN

ENDIF

//------------------------------------

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

oServerC := DBServer{cFilePath}

oServerC:GoTop()

LOCAL nRecs AS DWORD

//nRecs := oServerC:RecCount

//MessageBox.Show(nRecs:ToString())

DO WHILE !oServerC:EOF

SELF:TransferRecordToSQLTable(oServerC)

oServerC:Skip(+1)

ENDDO

//-----------------------------

oServerC:Commit()

oServerC:Close()

oServerC := NULL_OBJECT

SELF:DoForceDisconnect()

RETURN

Because this is a 'one-off' operation we do not need to worry about efficiency of code, or anything like that, and can focus on some clarity in our coding.

You will notice that after each new DBF record is read the server object is passed into the TransferRecordToSQLTable() method.

Let us now look at this method to see what it does to send the correct information to the SQL table :-

METHOD TransferRecordToSQLTable(oS AS DBServer) AS VOID

LOCAL cDetails AS STRING

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

LOCAL nParamValue AS INT

nParamValue := (INT)oS:FieldGet("custrefno")

aParamValues := STRING[]{12}

aParamValues[1] := ""

aParamValues[2] := (STRING)oS:FieldGet("person")

aParamValues[3] := (STRING)oS:FieldGet("company")

aParamValues[4] := (STRING)oS:FieldGet("add1")

aParamValues[5] := (STRING)oS:FieldGet("add2")

aParamValues[6] := (STRING)oS:FieldGet("add3")

aParamValues[7] := (STRING)oS:FieldGet("postcode")

aParamValues[8] := (STRING)oS:FieldGet("phone")

aParamValues[9] := (STRING)oS:FieldGet("web")

aParamValues[10] := (STRING)oS:FieldGet("email")

aParamValues[11] := (STRING)oS:FieldGet("fax")

cDetails := ALLTRIM(oS:FieldGet("details1"))+" "+ALLTRIM(oS:FieldGet("details2"))

aParamValues[12] := cDetails

//----- set the 'CommandText' PROPERTY -----

LOCAL cText AS STRING

cText := "INSERT INTO [Customers] ([customerID],[person],[company],[add1],[add2],[add3],"

cText += "[postcode],[phone],[web],[email],[fax],[details])"

cText += "VALUES (@cID,@pr,@cm,@a1,@a2,@a3,@pc,@ph,@wb,@em,@fx,@dt)"

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of SqlCommand -----

oCommand:Connection := oConn

//----- add parameters and set their value -----

//----- post and country in diff order to DB! -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@cID", SqlDbType.SmallInt}

oParam:Value := nParamValue

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@pr", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[2]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@cm", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[3]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@a1", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[4]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@a2", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[5]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@a3", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[6]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@pc", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[7]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ph", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[8]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@wb", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[9]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@em", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[10]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@fx", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[11]

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@dt", SqlDbType.VarChar}

oParam:Value := SELF:aParamValues[12]

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [customers] ORDER BY customerID"

LOCAL oDA AS SqlDataAdapter

oDA := SqlDataAdapter{cSQL, SELF:cStrConn}

LOCAL oDS AS DataSet

oDS := DataSet{}

oDA:Fill(oDS, "InsertedAllDBFrecords")

//----- display contents of the data set table in the grid -----

odgvSQLtrial:DataSource := oDS:Tables["InsertedAllDBFrecords"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

RETURN

In fact there is nothing too clever at all about this transfer process, but importantly, it does seem to work, and work well !

Since it was my first attempt at a transfer of DBF data there are probably many ways to make this more developer friendly, so that many more tables can be constructed, and probably more easily.

I have resisted using third part tools for this chore, as it seems to me that there will be an opportunity for many of us to do a big restructuring and normalizing job at the same time as we move over to SQL.

So I for one am looking forward to doing things in a much more hands on way than if using a third party tool.

I for one also think that use of DateTime data types is far better than keeping with old style data and time as we did in DBF times.

There is certainly some normalization to be done on a few large 'order' files I know of.

These files are at least fourteen years old and in the early days the Clipper approach of the Data Processing Manager, meant we have order item lines in order files, and lots of empty space.

About time something was done, so I look forward to the chance to make changes.

If I was to pinpoint one simple place to improve the above, it would be to automate in some way the combining of a DBF date and time field so as to make a single DateTimeStamp or the equivalent.

Most of my old DBF files have a 'makdate' and 'maktime' field which is a 'stamp' of when the record was created.

Maybe a static method will be the answer to this, and if so I will put the code here later.

PHH - July 2008


Full Working Code

Enter topic text here.


Section 2 - A 'working' project

Let us dive right in and see what our first real application looks like :-

In this 'simple but effective' working application we will try our hand at most of the SQL techniques required for larger applications. However, the app is based on just one table.

This means that to study more complex 'set-based' queries, using JOINs and the like, we will need to study the workings of Section 3, where we have a fully relational database example to study.

In Section 3 we get both INNER and OUTER JOINs to work for us.

[ See Appendix B - Locating the Databases. ]


Describing the Project

This simple project, the first of many we hope, is based on the old early Windows 'Card File' tool.

Basically, we have a single table called 'Ultra File 2008' with a number of columns shown below. The image is taken from MS where the database has been attached.

One record in the table is equivalent to a one 'card' in the box of cards (or set). As well as maintaining the card set, with new cards, and updating older ones, we will see a full set of searching tools to help us locate the data we require.

The results from a search will be displayed in the DataViewGrid control. This is actually very easy to do indeed, although it still looks rather impressive to me - and I have seen it in action quite a number of times.

The structure of the SQL query statement lends particularly well to being constructed by Vulcan code - depending on what user inputs have been provided.

How Searching Works :

Before we card discuss the details of the searching tools, we need to view the 'structure' of a card in our box set.

Below is an image of the card add / edit screen which reflects the structure :-

In order to search the 'card set' for matching details, we need to supply a text 'string' to search for (see relevant image below), and select also which parts of the card we wish to search.

The card above shows a number of possible areas to search, like body, extra details, and main and sub headers - as well as others.

Let us now look at the portion of the main form which deals with searching and filtering :-

The left most 'green' area allows the user to provide a search 'string' and to select whereabouts to search on the cards.

The 'blue' area, in the middle of the main form, allows the user to set filter conditions, and importantly whether these are to cause cards to be included or excluded.

If the 'Filter On' check box is left unchecked, then no filtering takes place at all, and only searching conditions apply.

At the moment I have to get the dates scan search to work correctly - watch this space !

Notice that the filter process only works because there are Boolean fields in the table which can have true or false values set by user categorization.

In order to see details of how Vulcan code gets the SQL Server to do the search, see sub-section 'Searching the Box' later in this section.

Obviously, when the search results are found, they are returned in a data set by the Server and then displayed in the DataGridView control.

PHH - July 2008.


Making a Connection

Since this topic is needed to be dealt with in each SQL application, it has been covered in full detail in appendix A.

The details in appendix A are based on what was developed here for the 'Ultra Card 2008' project.

There may have been some modifications to move towards 'provider agnostic' code.

However, it should be enough to say the image below is a live capture of what is working in the project 2 application at the moment.

To see all the necessary details visit appendix A.


Seeing the Cards in the Box

It needs to be realized that when I developed the Vulcan code for this app. I had not discovered the way of creating and maintaining an error / exception table within the database.

For this reason the TRY / CATCH / END TRY structure is simpler than in parts of project three.

To find out more about saving runtime exceptions 'related data' within database tables, see section 'Saving and Viewing Exceptions' in the next main section (3).

Below is the code to find all the cards in the box with a reference of over zero, and display the whole set of columns in the grid :-

METHOD DoViewBox() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

SELF:FillListWithAllCards()

SELF:DoForceDisconnect()

SELF:DoSetConnectionString()

TRY

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [UltraFileMain] WHERE [CardID] >= 1;"

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := SELF:oDConn

oDComm:CommandText := cSQL

LOCAL oDDA AS DbDataAdapter

oDDA := oDFactory:CreateDataAdapter()

oDDA:SelectCommand := oDComm

LOCAL oDS AS DataSet

oDS := DataSet{}

oDDA:Fill(oDS, "ViewBox")

IF oDS:Tables:Count > 0

//----- display contents of the data set table in the grid -----

odgvSearchResults:DataSource := oDS:Tables["ViewBox"]

ENDIF

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

It ought to be realize too, that if we have a method to fill the grid, we need to have one to clear it - or empty the grid.

Let us look at how easy that is :-

METHOD pbClearResultsClick(o AS OBJECT , e AS EventArgs) AS VOID

odgvSearchResults:DataSource := DataTable{}

olbFound:TEXT := ".. no search .."

RETURN

Obviously, the list box text is being changed too, now that no results are showing.

Generally, we are going to wish to clear the grid of search results - hence the button name, and the method name too.


Adding a New Card

Adding a new card is reasonably straight forward.

We need to instantiate a new object of the fmUltrsAddEdid class and pass in 'SELF'.

METHOD pbAddNewClick(o AS OBJECT , e AS EventArgs) AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

oFocusCard := UltraCard{}

oFocusCard:newcard := TRUE

LOCAL oUAE AS fmUltraAddEdit

oUAE := fmUltraAddEdit{}

oUAE:ShowDialog(SELF)

oUAE := NULL_OBJECT

RETURN

This produces a form like that shown three sub-sections ago, in describing the project.

After user input and changes to the fields on the form, the 'save changes' button does as shown below :-

METHOD DoRefreshTable() AS VOID

SELF:RePopulateFocusCard()

IF oParent:oFocusCard:newcard

SELF:oParent:DoInsertNewCardObject()

ELSE

SELF:oParent:DoUpdateNewCardObject()

ENDIF

SELF:oParent:FillListWithAllCards()

SELF:Close()

RETURN

So quite simply, the SQL query statement is built up and used from within the 'DoInsertNewCardObject' method or the 'update' version, depending on whether the card existed - and therefore had an ID reference relating to the primary key.

Let us now look at the Insert version of the method :-

METHOD DoInsertNewCardObject() AS VOID

//----- use FocusCard object to populate ------

IF oFocusCard == NULL_OBJECT

//----- WARN -----

RETURN

ENDIF

//======================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [UltraFileMain] ([datetimestamp],[mainhead],[subhead],[body],[extradetails],[phone],"

cText += "[mobile],[email],[website],[company],[person],[assocdate],[cashamount],[work],"

cText += "[personal],[finance],[technical],[software],[vulcanvo],[IT],[hardware],[government])"

cText += "VALUES (@dts,@mh,@sh,@bd,@xd,@ph,@mb,@em,@ws,@cp,@ps,@ad,"

cText += "@ca,@wk,@psl,@fn,@tch,@sft,@vvo,@it,@hw,@gv);"

MessageBox.Show(cText)

//====================================================

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oDConn

//----- add parameters and set their value -----

//----- post and country in diff order to DB! -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@dts", SqlDbType.SmallDateTime}

oParam:Value := oFocusCard:datetimestamp

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@mh", SqlDbType.VarChar}

oParam:Value := oFocusCard:mainhead

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@sh", SqlDbType.VarChar}

oParam:Value := oFocusCard:subhead

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@bd", SqlDbType.VarChar}

oParam:Value := oFocusCard:body

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@xd", SqlDbType.VarChar}

oParam:Value := oFocusCard:extras

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ph", SqlDbType.VarChar}

oParam:Value := oFocusCard:phone

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@mb", SqlDbType.VarChar}

oParam:Value := oFocusCard:mobile

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@em", SqlDbType.VarChar}

oParam:Value := oFocusCard:email

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ws", SqlDbType.VarChar}

oParam:Value := oFocusCard:web

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@cp", SqlDbType.VarChar}

oParam:Value := oFocusCard:company

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ps", SqlDbType.VarChar}

oParam:Value := oFocusCard:person

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ad", SqlDbType.SmallDateTime}

//oParam:Value := SELF:aParamValues[11]

oParam:Value := oFocusCard:AssocDate

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ca", SqlDbType.Money}

//oParam:Value := SELF:aParamValues[12]

oParam:Value := oFocusCard:CashAmount

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@wk", SqlDbType.VarChar}

oParam:Value := oFocusCard:work

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@psl", SqlDbType.VarChar}

oParam:Value := oFocusCard:personal

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@fn", SqlDbType.VarChar}

oParam:Value := oFocusCard:finance

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tch", SqlDbType.VarChar}

oParam:Value := oFocusCard:technical

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@sft", SqlDbType.VarChar}

oParam:Value := oFocusCard:software

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@vvo", SqlDbType.VarChar}

oParam:Value := oFocusCard:vulcanvo

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@it", SqlDbType.VarChar}

oParam:Value := oFocusCard:it

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@hw", SqlDbType.VarChar}

oParam:Value := oFocusCard:hardware

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@gv", SqlDbType.VarChar}

oParam:Value := oFocusCard:government

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [UltraFileMain] WHERE [CardID] >= 4;"

LOCAL oDDA AS DbDataAdapter

oDDA := oDFactory:CreateDataAdapter()

oCommand:CommandText := cSQL

oDDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

oDDA:Fill(oDS, "InsertedUltraCardInfo")

//----- display contents of the data set table in the grid -----

odgvSearchResults:DataSource := oDS:Tables["InsertedUltraCardInfo"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

Okay, we are going to have to get used to the Vulcan code dealing with 'setting and sending' the query statement being 'long winded' - but that's the nature of the game, till we make more code as Stored Procedures.

And even then that does not shorten things dramatically when there are a number of fields to be filled in the table record, and variables to be set and sent.


Editing an Existing card

Since the complete code as it is working at the moment, is included in the final sub-section of this section - called "Full Working Code" - let us just view the update part of the second method - see below :-

This is because it is essentially very similar to that for inserting a new card, which is covered in detail in the last sub-section.

METHOD DoUpdateNewCardObject() AS VOID

//----- use FocusCard object to populate ------

IF oFocusCard == NULL_OBJECT

//----- WARN -----

RETURN

ENDIF

//======================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "UPDATE [UltraFileMain] SET "

cText += "[mainhead] = @mh,"

cText += "[subhead] = @sh,"

cText += "[body] = @bd,"

cText += "[extradetails] = @xd,"

cText += "[phone] = @ph,"

cText += "[mobile] = @mb,"

cText += "[email] = @em,"

cText += "[website] = @ws,"

cText += "[company] = @cp,"

cText += "[person] = @ps,"

cText += "[assocdate] = @ad,"

cText += "[cashamount] = @ca,"

cText += "[work] = @wk,"

cText += "[personal] = @psl,"

cText += "[finance] = @fn,"

cText += "[technical] = @tch,"

cText += "[software] = @sft,"

cText += "[vulcanvo] = @vvo,"

cText += "[it] = @it,"

cText += "[hardware] = @hw,"

cText += "[government] = @gv"

cText += " WHERE [CardID] = @CardRef;"

//MessageBox.Show(cText)

//====================================================

SELF:DoSetConnectionString()

The part shown above is different in structure to the INSERT statement of the last sub-section.

However, as usual with SQL query statements - not complicated to get right, just long and needing care by the coder.


Removing / Deleting a Card

So far so good, we are getting a working app together.

Now let us look at deleting, or removing a card from the set.

Since this is a very simple example as far as the database goes - no related tables, just the one main table - we can see DELETE in it simple 'glory'.

What I mean by this is that we have no complications - no enforced integrity problems. No other table record, in any other table, can require that the card remains, as this other record is related to it by key constraints etc. etc.

First lets look at the event handler to the push button click :-

METHOD pbRemoveFocusCardClick(o AS OBJECT , e AS EventArgs) AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

SELF:DoRemoveFocusCard()

SELF:FillListWithAllCards()

SELF:DoViewBox()

RETURN

Notice that we attempt to remove the card, and then refill the list box and grid to reflect any changes in the card 'set'. If all goes well there should be one card less.

Now lets run our eyes over the code to tell the Server to DELETE. You will notice that most of it is User Interface stuff, to give a sensible and positive feed-back to the user :-

METHOD DoRemoveFocusCard() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

LOCAL lRemoved AS Boolean

lRemoved := FALSE

LOCAL cText AS STRING

cText := "Card to be removed is :- "+CRLF

LOCAL nLen AS DWORD

nLen := (DWORD)oFocusCard:mainhead:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:mainhead:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:company:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:company:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:person:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:person:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:body:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:body:Substring(0,(INT)nLen)+CRLF

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Hand

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageboxDefaultButton

oDef := MessageboxDefaultButton.Button2

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF,cText,"Details :",oMBB,oIcon,oDef)

IF oDR = DialogResult.No

MessageBox.Show(SELF,"Card still remains!!","CANCEL :")

RETURN

ENDIF

//================================================================

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oDConn

//----- set the 'CommandText' property -----

cText := "DELETE FROM [UltraFileMain] WHERE [cardID] = @CardRef;"

MessageBox.Show(cText)

oCommand:CommandText := cText

//----- add a parameter and set its value -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@CardRef", SqlDbType.INT}

oParam:Value := nID //----- INT -----

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

lRemoved := TRUE

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

IF lRemoved

MessageBox.Show(SELF,"Card has been removed!","Success :")

ELSE

MessageBox.Show(SELF,"Card still remains!!","Failure :")

ENDIF

RETURN


Searching - Building Complex Queries

Basically, the search query is simple in structure :-

SELECT * FROM [UltraFileMain] WHERE .... compound conditions ....

What does the search query mean ?

Simply, take the whole table of data, restrict the rows according to the complex condition, send back all columns, and don't put the rows in any sorted order.

This means we are asking to get back all columns from the card table ( named 'UltraFileMain' ) where the rows are to be restricted by our compound, or complex condition.

We will come back to the detail of the complex condition in a moment. For now lets look at the Vulcan code to ask the query of the Server :-

===================================================================

Remember we are still using the very Microsoft syntax at present, and will move over to 'provider agnostic' stuff in the next section when dealing with project 3.

Notice that the complex query statement is given to the data adapter, as also is the connection string.

In return we get back a results set which is put into the DataSet object as a table called "UltraSearch".

This strongly type data table is given to the DataGridView as a data source - and the display is seen. All very easy, and nice.

Now we need to see the 'messy part', as this is how it looks - just one of those things. However, it was quite trouble free to get the syntax correct and make results happen.

It may help to realize that the compound condition can be written like ( ( cond1 OR cond2 OR cond3 ........ ) AND ... AND ... AND ..... )

First of all lets look at the Vulcan code to cover the OR clause :-

LOCAL cStatement AS STRING

cStatement := "SELECT * FROM [UltraFileMain] WHERE ( "

DO CASE

CASE orbHeader:Checked

IF orbAnywhere:Checked

cStatement += "[mainhead] LIKE '%"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mainhead] LIKE '"+cSearch+"%'" //----- just at start of line -----

cStatement += " OR [subhead] LIKE '"+cSearch+"%'"

ENDIF

CASE orbCompany:Checked

IF orbAnywhere:Checked

cStatement += "[company] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[company] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbContact:Checked

IF orbAnywhere:Checked

cStatement += "[person] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[person] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbPhone:Checked

IF orbAnywhere:Checked

cStatement += "[phone] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[phone] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbBody:Checked

IF orbAnywhere:Checked

cStatement += "[body] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[body] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbMobile:Checked

IF orbAnywhere:Checked

cStatement += "[mobile] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mobile] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbEmail:Checked

IF orbAnywhere:Checked

cStatement += "[email] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[email] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbDates:Checked

IF orbAnywhere:Checked

cStatement += "[AssocDate] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[AssocDate] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbAll:Checked

IF orbAnywhere:Checked

cStatement += "[mainhead] LIKE '%"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[company] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[person] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[phone] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[body] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[mobile] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[email] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mainhead] LIKE '"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[company] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[person] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[phone] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[body] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[mobile] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[email] LIKE '"+cSearch+"%'"

ENDIF

ENDCASE

cStatement += " )"

Notice that in some ways we have made life hard for ourselves by adding the complexity that we wish to have our search phrase either at the start of the target, or anywhere within it.

Similarly, by adding the ALL option, that adds to the complexity too - BUT - what are computers for ? To make life easy ;-)

So finally, lets look at the AND clause which deals with the filtering for the categories :-

IF SELF:ocbFilterOn:Checked

IF orbInclude:Checked

IF ocbWork:Checked

cStatement += " AND [work] = 1"

ENDIF

IF ocbPersonal:Checked

cStatement += " AND [personal] = 1"

ENDIF

IF ocbFinance:Checked

cStatement += " AND [finance] = 1"

ENDIF

IF ocbTechnical:Checked

cStatement += " AND [Technical] = 1"

ENDIF

IF ocbSoftware:Checked

cStatement += " AND [Software] = 1"

ENDIF

IF ocbVulcanVO:Checked

cStatement += " AND [VulcanVO] = 1"

ENDIF

IF ocbIT:Checked

cStatement += " AND [IT] = 1"

ENDIF

IF ocbHardware:Checked

cStatement += " AND [Hardware] = 1"

ENDIF

IF ocbGovernment:Checked

cStatement += " AND [Government] = 1"

ENDIF

ELSE

IF ocbWork:Checked

cStatement += " AND [work] = 0"

ENDIF

IF ocbPersonal:Checked

cStatement += " AND [personal] = 0"

ENDIF

IF ocbFinance:Checked

cStatement += " AND [finance] = 0"

ENDIF

IF ocbTechnical:Checked

cStatement += " AND [Technical] = 0"

ENDIF

IF ocbSoftware:Checked

cStatement += " AND [Software] = 0"

ENDIF

IF ocbVulcanVO:Checked

cStatement += " AND [VulcanVO] = 0"

ENDIF

IF ocbIT:Checked

cStatement += " AND [IT] = 0"

ENDIF

IF ocbHardware:Checked

cStatement += " AND [Hardware] = 0"

ENDIF

IF ocbGovernment:Checked

cStatement += " AND [Government] = 0"

ENDIF

ENDIF

ENDIF

Well, that's is - messy I did say - but work it certainly does :-)

Needless to say it may well be possible to compress this code - BUT - I am after easy to read and easy to maintain code - not 'squashed' code.


SQL - Full Text Search

When I started this simple project, I had the grand idea that I may branch out into using the Full-Text Search available to SQL Server.

Since watching the videos I have realized that in fact this is unrealistic and that the whole aspect of full text searching is more complex and needs to be looked at in great detail before any move is made in that direction.

Also, you need a REAL project and problem on which to apply the FTS.

So basically, scrap this idea ;-)

I do encourage you to see the video material, and read any books about it. That way at least we know what it is.


Where to Go from Here

Well folks, the app as it is works (apart from the date searching), and needs a thorough test by adding lots of cards and data.

As far as what it did for me - well it gave a real app to work on without having to worry about complex JOINs to access data sets.

In order to move our skills on a pace, we need to look at the next section (section 3) in which we develop a more worldly app which has many types of report queries, and some with complex INNER and OUTER JOINs.

As with the whole aspect of SQL learning, you need to take one step at a time. That way we will all be successful.

See you in the next section.

PHH - July 2008.


Full Working Code

#USING System.Windows.Forms

#USING System.Drawing

#using System.Data.Common

#using System.IO

#using System.Data.SqlClient

#using System.Data

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml

#using System.Diagnostics

CLASS fmUltraFile2008 INHERIT Form

PROTECT opbDoSearch AS Button

PROTECT ogbSearch AS GroupBox

PROTECT orbContact AS RadioButton

PROTECT orbCompany AS RadioButton

PROTECT orbEmail AS RadioButton

PROTECT orbAll AS RadioButton

PROTECT orbDates AS RadioButton

PROTECT orbMobile AS RadioButton

PROTECT orbPhone AS RadioButton

PROTECT orbBody AS RadioButton

PROTECT orbHeader AS RadioButton

PROTECT odgvSearchResults AS DataGridView

PROTECT opbClearFilter AS Button

PROTECT ogbFilter AS GroupBox

PROTECT ocbWork AS CheckBox

PROTECT ocbSoftware AS CheckBox

PROTECT ocbGovernment AS CheckBox

PROTECT ocbHardware AS CheckBox

PROTECT ocbIT AS CheckBox

PROTECT ocbVulcanVO AS CheckBox

PROTECT ocbTechnical AS CheckBox

PROTECT ocbFinance AS CheckBox

PROTECT ocbPersonal AS CheckBox

PROTECT opbConnection AS Button

PROTECT opbViewBox AS Button

PROTECT opbViewFocusCard AS Button

PROTECT opbHelp AS Button

PROTECT opbRemoveFocusCard AS Button

PROTECT opbEditFocusCard AS Button

PROTECT olbxFile AS ListBox

PROTECT opbClearResults AS Button

PROTECT ogbApplyMode AS GroupBox

PROTECT orbExclude AS RadioButton

PROTECT orbInclude AS RadioButton

PROTECT olbCategories AS Label

PROTECT ogbMatching AS GroupBox

PROTECT orbAnywhere AS RadioButton

PROTECT orbAtLeft AS RadioButton

PROTECT olbToFind AS Label

PROTECT ocbFilterOn AS CheckBox

PROTECT olbFound AS Label

PROTECT otbSearch AS TextBox

PROTECT opbAddNew AS Button

PROTECT oOKButton AS Button

// User code starts here (DO NOT remove this line) ##USER##

PROTECT cProviderInvariantName AS STRING

PROTECT oDPF AS DbProviderFactory

EXPORT oFocusCard AS UltraCard

PROTECT cFile, cPath AS STRING

PROTECT cFilePath AS STRING

PROTECT cStrConn AS STRING

PROTECT oConn AS SqlConnection

PROTECT aParamValues AS STRING[]

PROTECT nIndex AS INT

PROTECT alCards AS ArrayList

PROTECT nID AS INT

PROTECT cSearch AS STRING

EXPORT oConnDetails AS ConnectionDetails

EXPORT oDFactory AS DbProviderFactory

EXPORT oDConn AS DbConnection

EXPORT lConnectionTestedOK AS Boolean

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:Initialize()

SELF:DoForceDisconnect()

SELF:oFocusCard := UltraCard{}

IF SELF:ReadSerialXmlFile()

SELF:DoSetConnectionString()

IF SELF:DoTryToConnect()

//SELF:FillListWithAllCards()

SELF:DoViewBox()

ENDIF

ENDIF

RETURN

METHOD Initialize() AS VOID

SELF:oConnDetails := ConnectionDetails{}

SELF:MinimumSize := SELF:Size

nIndex := -1

orbAll:Checked := TRUE

orbAnywhere:Checked := TRUE

ocbFilterOn:Checked := FALSE

orbInclude:Checked := TRUE

olbFound:Text := ".. no search .."

SELF:cPath := "C:\Users\BEST\"

SELF:cFile := "Ultra File 2008.mdf"

cFilePath := cPath:Trim()+cFile:Trim()

RETURN

METHOD ReadSelectedIntoFocus() AS VOID

IF nID < 0

RETURN

ENDIF

//MessageBox.Show("card to read :- "+nID:ToString())

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

oCommand:Connection := SELF:oDConn

LOCAL oDr AS DbDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT "

cStatement += "[mainhead],"

cStatement += "[subhead],"

cStatement += "[company],"

cStatement += "[person],"

cStatement += "[body],"

cStatement += "[extradetails],"

cStatement += "[phone],"

cStatement += "[mobile],"

cStatement += "[email],"

cStatement += "[website],"

cStatement += "[assocdate],"

cStatement += "[cashamount],"

cStatement += "[work],"

cStatement += "[personal],"

cStatement += "[finance],"

cStatement += "[technical],"

cStatement += "[software],"

cStatement += "[vulcanvo],"

cStatement += "[it],"

cStatement += "[hardware],"

cStatement += "[government],"

cStatement += "[cardid],"

cStatement += "[datetimestamp]"

cStatement += " FROM [UltraFileMain] WHERE [CardID] = "+nID:ToString()

oCommand:CommandText := cStatement

oCommand:CommandType := Data.CommandType.Text

oDr := oCommand:ExecuteReader()

LOCAL cText AS STRING

IF ! oDr == NULL

oDr:Read()

oFocusCard := UltraCard{}

oFocusCard:mainhead := oDr:GetString(0)

oFocusCard:subhead := oDr:GetString(1)

oFocusCard:company := oDr:GetString(2)

oFocusCard:person := oDr:GetString(3)

oFocusCard:body := oDr:GetString(4)

oFocusCard:extras := oDr:GetString(5)

oFocusCard:phone := oDr:GetString(6)

oFocusCard:mobile := oDr:GetString(7)

oFocusCard:email := oDr:GetString(8)

oFocusCard:web := oDr:GetString(9)

oFocusCard:AssocDate := oDr:GetDateTime(10)

//oFocusCard:CashAmount := oDr:GetSqlMoney(11)

//cText := oDr:GetSqlMoney(11):ToString()

cText := oDr:GetValue(11):ToString()

oFocusCard:CashAmount := (REAL4)Conversion.Val(cText)

oFocusCard:work := oDr:GetBoolean(12)

oFocusCard:personal := oDr:GetBoolean(13)

oFocusCard:finance := oDr:GetBoolean(14)

oFocusCard:technical := oDr:GetBoolean(15)

oFocusCard:software := oDr:GetBoolean(16)

oFocusCard:vulcanvo := oDr:GetBoolean(17)

oFocusCard:it := oDr:GetBoolean(18)

oFocusCard:hardware := oDr:GetBoolean(19)

oFocusCard:government := oDr:GetBoolean(20)

oFocusCard:cardID := oDr:GetInt32(21)

oFocusCard:datetimestamp := oDr:GetDateTime(22)

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS System.Exception

MessageBox.Show(ex:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD FillListWithAllCards() AS VOID

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := SELF:oDConn

LOCAL oDr AS DbDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT [mainhead],[cardID] FROM [UltraFileMain] ORDER BY [mainhead]"

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.Text

oDr := oDComm:ExecuteReader()

olbxFile:Items:Clear() //----- empty the list initially -----

alCards := ArrayList{}

LOCAL cFullName AS STRING

LOCAL nID AS INT

IF ! oDr == NULL

DO WHILE oDr:Read()

cFullName := oDr:GetString(0)

olbxFile:Items:Add(cFullName)

nID := oDr:GetInt32(1)

alCards:Add(nID)

ENDDO

olbxFile:SelectedIndex := 0

SELF:nIndex := 0

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS System.Exception

MessageBox.Show(ex:Message)

END TRY

SELF:DoForceDisconnect()

nIndex := olbxFile:SelectedIndex

SELF:nID := (INT)alCards[nIndex]

RETURN

METHOD ReMakeCardTable() AS VOID

SELF:RemoveAnyExistingTable()

SELF:DoMakeTable()

RETURN

METHOD ShowDetails() AS VOID

LOCAL cText AS STRING

cText := ""

cText += cFile+CRLF

cText += cPath+CRLF+CRLF

IF File.Exists(cFilePath)

cText += "Yes, database file exists!"+CRLF+cFilePath

ELSE

cText += "No, can't find the data file?!"+CRLF+cFilePath

ENDIF

SELF:DisplayText(cText)

RETURN

METHOD DoSetConnectionString() AS VOID

IF String.IsNullOrEmpty(SELF:oConnDetails:cProvider)

MessageBox.Show(SELF,"Provider string NOT available !","WARNING")

RETURN

ENDIF

//====================================================================

IF SELF:oConnDetails:cUserName:IndexOf("<") > -1 .OR. SELF:oConnDetails:cUserName:IndexOf(">") > -1

SELF:oConnDetails:cUserName := ""

ENDIF

IF SELF:oConnDetails:cPassWord:IndexOf("<") > -1 .OR. SELF:oConnDetails:cPassWord:IndexOf(">") > -1

SELF:oConnDetails:cPassWord := ""

ENDIF

//----- get the factory for the provider we require -----

SELF:oDFactory := DbProviderFactories.GetFactory("System.Data."+SELF:oConnDetails:cProvider)

//----- create a connection STRING ------

oDConn := oDFactory:CreateConnection()

//====================================================

SELF:cStrConn := ""

LOCAL cSource, cAttach, cSecurity, cTime, cInst AS STRING

//------------ Hard Wired to get working ------------

//cSource := "Data Source=BEST-V64QUAD;"

//cAttach := "AttachDbFilename="+cFilePath+";;"

//cSecurity := "Integrated Security=SSPI;"

//cTime := "Connect Timeout=13;"

//cTime := "Connect Timeout=0;" //----- Willie said this would leave it always open -----

//cInst := ""

cSource := "Data Source="+SELF:oConnDetails:cServerName+";"

cAttach := "AttachDbFilename="+SELF:oConnDetails:cFilePath+";;"

cSecurity := "Integrated Security="+SELF:oConnDetails:cSecurity+";"

cTime := "Connect Timeout="+SELF:oConnDetails:nTimeOut:ToString()+";" //----- Willie said this would leave it always open -----

cInst := ""

cStrConn := cSource + cAttach + cSecurity + cTime +cInst

oDConn:ConnectionString := cStrConn

//MessageBox.Show(oDConn:ConnectionString:ToString())

oConnDetails:cConnectionString := cStrConn

RETURN

METHOD DoTryToConnect() AS LOGIC

LOCAL lResult AS Boolean

lResult := FALSE

TRY

oDConn:Open()

SELF:Text := "Vulcan : Ultra File 2008 : << Can Connect! >>"

lResult := TRUE

CATCH sqlEx AS DbException

MessageBox.Show(sqlEx:Message)

END TRY

RETURN lResult

METHOD DisplayText(cText AS STRING) AS VOID

MessageBox.Show(cText:Trim())

RETURN

METHOD RemoveAnyExistingTable() AS VOID

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

//----- set the 'CommandText' PROPERTY -----

LOCAL cText, cTableName AS STRING

cTableName := "UltraFileMain"

cText := "IF EXISTS(SELECT name FROM sys.tables WHERE name = '"+cTableName+"')"

cText += "DROP TABLE "+cTableName

//cText += " GO" //----- don't use here >> throws an exception! -----

MessageBox.Show(cText)

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of SqlCommand -----

oCommand:Connection := oConn

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD DoMakeTable() AS VOID

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

//----- set the 'CommandText' PROPERTY -----

LOCAL cText, cTableName AS STRING

cTableName := "UltraFileMain"

cText := "CREATE TABLE ["+cTableName:Trim()+"] ("

cText += "[CardID] [int] IDENTITY(1,1) NOT NULL,"

cText += "[datetimestamp] [smalldatetime] NULL,"

cText += "[MainHead] [varchar](130) NOT NULL,"

cText += "[SubHead] [varchar](130) NULL,"

cText += "[Body] [varchar](690) NULL,"

cText += "[ExtraDetails] [varchar](260) NULL,"

cText += "[phone] [varchar](24) NULL,"

cText += "[mobile] [varchar](24) NULL,"

cText += "[email] [varchar](70) NULL,"

cText += "[website] [varchar](80) NULL,"

cText += "[Company] [varchar](50) NULL,"

cText += "[Person] [varchar](50) NULL,"

cText += "[AssocDate] [smalldatetime] NULL,"

cText += "[CashAmount] [money] NULL,"

cText += "[work] [bit] NULL,"

cText += "[personal] [bit] NULL,"

cText += "[finance] [bit] NULL,"

cText += "[technical] [bit] NULL,"

cText += "[software] [bit] NULL,"

cText += "[vulcanvo] [bit] NULL,"

cText += "[IT] [bit] NULL,"

cText += "[hardware] [bit] NULL,"

cText += "[government] [bit] NULL,"

cText += "CONSTRAINT [PK_"+cTableName+"_1] PRIMARY KEY CLUSTERED"

cText += "("

cText += "[CardID] ASC"

cText += ")WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY"

cText += "= OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]"

cText += ")ON [PRIMARY]"

MessageBox.Show(cText)

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of SqlCommand -----

oCommand:Connection := oConn

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD DoForceDisconnect() AS VOID

LOCAL cText AS STRING

IF ! oDConn = NULL_OBJECT

oDConn:Close()

cText := oDConn:ConnectionString+" - State = "+ oDConn:State:ToString()

oDConn := NULL_OBJECT

ENDIF

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

SELF:Name := "fmUltraFile2008"

SELF:SuspendLayout()

SELF:Location := Point{100,100}

SELF:ClientSize := Size{818,478}

SELF:Text := "Vulcan : Ultra File 2008 :"

SELF:Load += EventHandler{ SELF , @fmUltraFile2008Load() }

SELF:opbDoSearch := Button{}

SELF:opbDoSearch:Name := "pbDoSearch"

SELF:opbDoSearch:Location := Point{ 176 , 168 }

SELF:opbDoSearch:Size := Size{ 72 , 23 }

SELF:opbDoSearch:Text := "Do Search"

SELF:opbDoSearch:Click += EventHandler{ SELF , @pbDoSearchClick() }

SELF:opbDoSearch:TabIndex := 6

SELF:Controls:Add(SELF:opbDoSearch)

SELF:ogbSearch := GroupBox{}

SELF:ogbSearch:Name := "gbSearch"

SELF:ogbSearch:SuspendLayout()

SELF:ogbSearch:Location := Point{ 8 , 8 }

SELF:ogbSearch:Size := Size{ 128 , 248 }

SELF:ogbSearch:Text := "Search In :"

SELF:ogbSearch:ForeColor := Color.FromArgb( 0,128,0 )

SELF:ogbSearch:TabIndex := 4

SELF:Controls:Add(SELF:ogbSearch)

SELF:orbContact := RadioButton{}

SELF:orbContact:Name := "rbContact"

SELF:orbContact:Location := Point{ 24 , 144 }

SELF:orbContact:Size := Size{ 96 , 24 }

SELF:orbContact:Text := "Contact :"

SELF:orbContact:TabIndex := 5

SELF:ogbSearch:Controls:Add(SELF:orbContact)

SELF:orbCompany := RadioButton{}

SELF:orbCompany:Name := "rbCompany"

SELF:orbCompany:Location := Point{ 24 , 168 }

SELF:orbCompany:Size := Size{ 96 , 24 }

SELF:orbCompany:Text := "Company :"

SELF:orbCompany:TabIndex := 6

SELF:ogbSearch:Controls:Add(SELF:orbCompany)

SELF:orbEmail := RadioButton{}

SELF:orbEmail:Name := "rbEmail"

SELF:orbEmail:Location := Point{ 24 , 96 }

SELF:orbEmail:Size := Size{ 88 , 24 }

SELF:orbEmail:Text := "email :"

SELF:orbEmail:TabIndex := 3

SELF:ogbSearch:Controls:Add(SELF:orbEmail)

SELF:orbAll := RadioButton{}

SELF:orbAll:Name := "rbAll"

SELF:orbAll:Location := Point{ 24 , 216 }

SELF:orbAll:Size := Size{ 96 , 24 }

SELF:orbAll:Text := "All :"

SELF:orbAll:TabIndex := 8

SELF:ogbSearch:Controls:Add(SELF:orbAll)

SELF:orbDates := RadioButton{}

SELF:orbDates:Name := "rbDates"

SELF:orbDates:Location := Point{ 24 , 192 }

SELF:orbDates:Size := Size{ 96 , 24 }

SELF:orbDates:Text := "Dates :"

SELF:orbDates:TabIndex := 7

SELF:ogbSearch:Controls:Add(SELF:orbDates)

SELF:orbMobile := RadioButton{}

SELF:orbMobile:Name := "rbMobile"

SELF:orbMobile:Location := Point{ 24 , 120 }

SELF:orbMobile:Size := Size{ 88 , 24 }

SELF:orbMobile:Text := "mobile :"

SELF:orbMobile:TabIndex := 4

SELF:ogbSearch:Controls:Add(SELF:orbMobile)

SELF:orbPhone := RadioButton{}

SELF:orbPhone:Name := "rbPhone"

SELF:orbPhone:Location := Point{ 24 , 72 }

SELF:orbPhone:Size := Size{ 96 , 24 }

SELF:orbPhone:Text := "Phone :"

SELF:orbPhone:TabIndex := 2

SELF:ogbSearch:Controls:Add(SELF:orbPhone)

SELF:orbBody := RadioButton{}

SELF:orbBody:Name := "rbBody"

SELF:orbBody:Location := Point{ 24 , 48 }

SELF:orbBody:Size := Size{ 96 , 24 }

SELF:orbBody:Text := "Body :"

SELF:orbBody:TabIndex := 1

SELF:ogbSearch:Controls:Add(SELF:orbBody)

SELF:orbHeader := RadioButton{}

SELF:orbHeader:Name := "rbHeader"

SELF:orbHeader:Location := Point{ 24 , 24 }

SELF:orbHeader:Size := Size{ 88 , 24 }

SELF:orbHeader:Text := "Header :"

SELF:orbHeader:TabIndex := 0

SELF:ogbSearch:Controls:Add(SELF:orbHeader)

SELF:odgvSearchResults := DataGridView{}

SELF:odgvSearchResults:Name := "dgvSearchResults"

SELF:odgvSearchResults:Location := Point{ 0 , 264 }

SELF:odgvSearchResults:Size := Size{ 816 , 176 }

SELF:odgvSearchResults:TabIndex := 3

SELF:odgvSearchResults:Anchor := AnchorStyles.Top + AnchorStyles.Bottom + AnchorStyles.Left + AnchorStyles.Right

SELF:odgvSearchResults:MouseClick += MouseEventHandler{ SELF , @dgvSearchResultsMouseClick() }

SELF:odgvSearchResults:MouseDoubleClick += MouseEventHandler{ SELF , @dgvSearchResultsMouseDoubleClick() }

SELF:Controls:Add(SELF:odgvSearchResults)

SELF:opbClearFilter := Button{}

SELF:opbClearFilter:Name := "pbClearFilter"

SELF:opbClearFilter:Location := Point{ 456 , 232 }

SELF:opbClearFilter:Size := Size{ 72 , 23 }

SELF:opbClearFilter:Text := "Clear Filter"

SELF:opbClearFilter:ForeColor := Color.FromArgb( 0,0,213 )

SELF:opbClearFilter:Click += EventHandler{ SELF , @pbClearFilterClick() }

SELF:opbClearFilter:TabIndex := 10

SELF:Controls:Add(SELF:opbClearFilter)

SELF:ogbFilter := GroupBox{}

SELF:ogbFilter:Name := "gbFilter"

SELF:ogbFilter:SuspendLayout()

SELF:ogbFilter:Location := Point{ 288 , 8 }

SELF:ogbFilter:Size := Size{ 160 , 248 }

SELF:ogbFilter:Text := "Set Filter :"

SELF:ogbFilter:ForeColor := Color.FromArgb( 0,0,213 )

SELF:ogbFilter:TabIndex := 7

SELF:Controls:Add(SELF:ogbFilter)

SELF:ocbWork := CheckBox{}

SELF:ocbWork:Name := "cbWork"

SELF:ocbWork:Location := Point{ 32 , 24 }

SELF:ocbWork:Size := Size{ 104 , 24 }

SELF:ocbWork:Text := "Work :"

SELF:ocbWork:TabIndex := 0

SELF:ogbFilter:Controls:Add(SELF:ocbWork)

SELF:ocbSoftware := CheckBox{}

SELF:ocbSoftware:Name := "cbSoftware"

SELF:ocbSoftware:Location := Point{ 32 , 120 }

SELF:ocbSoftware:Size := Size{ 104 , 24 }

SELF:ocbSoftware:Text := "Softare :"

SELF:ocbSoftware:TabIndex := 4

SELF:ogbFilter:Controls:Add(SELF:ocbSoftware)

SELF:ocbGovernment := CheckBox{}

SELF:ocbGovernment:Name := "cbGovernment"

SELF:ocbGovernment:Location := Point{ 32 , 216 }

SELF:ocbGovernment:Size := Size{ 104 , 24 }

SELF:ocbGovernment:Text := "Government :"

SELF:ocbGovernment:TabIndex := 8

SELF:ogbFilter:Controls:Add(SELF:ocbGovernment)

SELF:ocbHardware := CheckBox{}

SELF:ocbHardware:Name := "cbHardware"

SELF:ocbHardware:Location := Point{ 32 , 192 }

SELF:ocbHardware:Size := Size{ 104 , 24 }

SELF:ocbHardware:Text := "Hardware :"

SELF:ocbHardware:TabIndex := 7

SELF:ogbFilter:Controls:Add(SELF:ocbHardware)

SELF:ocbIT := CheckBox{}

SELF:ocbIT:Name := "cbIT"

SELF:ocbIT:Location := Point{ 32 , 168 }

SELF:ocbIT:Size := Size{ 104 , 24 }

SELF:ocbIT:Text := "IT :"

SELF:ocbIT:TabIndex := 6

SELF:ogbFilter:Controls:Add(SELF:ocbIT)

SELF:ocbVulcanVO := CheckBox{}

SELF:ocbVulcanVO:Name := "cbVulcanVO"

SELF:ocbVulcanVO:Location := Point{ 32 , 144 }

SELF:ocbVulcanVO:Size := Size{ 104 , 24 }

SELF:ocbVulcanVO:Text := "Vulcan VO :"

SELF:ocbVulcanVO:TabIndex := 5

SELF:ogbFilter:Controls:Add(SELF:ocbVulcanVO)

SELF:ocbTechnical := CheckBox{}

SELF:ocbTechnical:Name := "cbTechnical"

SELF:ocbTechnical:Location := Point{ 32 , 96 }

SELF:ocbTechnical:Size := Size{ 104 , 24 }

SELF:ocbTechnical:Text := "Technical :"

SELF:ocbTechnical:TabIndex := 3

SELF:ogbFilter:Controls:Add(SELF:ocbTechnical)

SELF:ocbFinance := CheckBox{}

SELF:ocbFinance:Name := "cbFinance"

SELF:ocbFinance:Location := Point{ 32 , 72 }

SELF:ocbFinance:Size := Size{ 104 , 24 }

SELF:ocbFinance:Text := "Finance :"

SELF:ocbFinance:TabIndex := 2

SELF:ogbFilter:Controls:Add(SELF:ocbFinance)

SELF:ocbPersonal := CheckBox{}

SELF:ocbPersonal:Name := "cbPersonal"

SELF:ocbPersonal:Location := Point{ 32 , 48 }

SELF:ocbPersonal:Size := Size{ 104 , 24 }

SELF:ocbPersonal:Text := "Personal :"

SELF:ocbPersonal:TabIndex := 1

SELF:ogbFilter:Controls:Add(SELF:ocbPersonal)

SELF:opbConnection := Button{}

SELF:opbConnection:Name := "pbConnection"

SELF:opbConnection:Location := Point{ 8 , 448 }

SELF:opbConnection:Size := Size{ 75 , 23 }

SELF:opbConnection:Text := "Connection"

SELF:opbConnection:ForeColor := Color.FromArgb( 0,0,128 )

SELF:opbConnection:BackColor := Color.FromArgb( 0,255,0 )

SELF:opbConnection:Click += EventHandler{ SELF , @pbConnectionClick() }

SELF:opbConnection:TabIndex := 21

SELF:Controls:Add(SELF:opbConnection)

SELF:opbViewBox := Button{}

SELF:opbViewBox:Name := "pbViewBox"

SELF:opbViewBox:Location := Point{ 528 , 448 }

SELF:opbViewBox:Size := Size{ 75 , 23 }

SELF:opbViewBox:Text := "View Box"

SELF:opbViewBox:Click += EventHandler{ SELF , @pbViewBoxClick() }

SELF:opbViewBox:TabIndex := 15

SELF:opbViewBox:Anchor := AnchorStyles.Bottom + AnchorStyles.Left

SELF:Controls:Add(SELF:opbViewBox)

SELF:opbViewFocusCard := Button{}

SELF:opbViewFocusCard:Name := "pbViewFocusCard"

SELF:opbViewFocusCard:Location := Point{ 416 , 448 }

SELF:opbViewFocusCard:Size := Size{ 104 , 23 }

SELF:opbViewFocusCard:Text := "View Focus Card"

SELF:opbViewFocusCard:Click += EventHandler{ SELF , @pbViewFocusCardClick() }

SELF:opbViewFocusCard:TabIndex := 12

SELF:opbViewFocusCard:Anchor := AnchorStyles.Bottom + AnchorStyles.Left

SELF:Controls:Add(SELF:opbViewFocusCard)

SELF:opbHelp := Button{}

SELF:opbHelp:Name := "pbHelp"

SELF:opbHelp:Location := Point{ 232 , 448 }

SELF:opbHelp:Size := Size{ 64 , 23 }

SELF:opbHelp:Text := "Help"

SELF:opbHelp:Click += EventHandler{ SELF , @pbHelpClick() }

SELF:opbHelp:TabIndex := 8

SELF:opbHelp:Anchor := AnchorStyles.Bottom + AnchorStyles.Left

SELF:Controls:Add(SELF:opbHelp)

SELF:opbRemoveFocusCard := Button{}

SELF:opbRemoveFocusCard:Name := "pbRemoveFocusCard"

SELF:opbRemoveFocusCard:Location := Point{ 104 , 448 }

SELF:opbRemoveFocusCard:Size := Size{ 120 , 23 }

SELF:opbRemoveFocusCard:Text := "Remove Focus Card"

SELF:opbRemoveFocusCard:Click += EventHandler{ SELF , @pbRemoveFocusCardClick() }

SELF:opbRemoveFocusCard:TabIndex := 13

SELF:opbRemoveFocusCard:Anchor := AnchorStyles.Bottom + AnchorStyles.Left

SELF:Controls:Add(SELF:opbRemoveFocusCard)

SELF:opbEditFocusCard := Button{}

SELF:opbEditFocusCard:Name := "pbEditFocusCard"

SELF:opbEditFocusCard:Location := Point{ 304 , 448 }

SELF:opbEditFocusCard:Size := Size{ 104 , 23 }

SELF:opbEditFocusCard:Text := "Edit Focus Card"

SELF:opbEditFocusCard:Click += EventHandler{ SELF , @pbEditFocusCardClick() }

SELF:opbEditFocusCard:TabIndex := 14

SELF:opbEditFocusCard:Anchor := AnchorStyles.Bottom + AnchorStyles.Left

SELF:Controls:Add(SELF:opbEditFocusCard)

SELF:olbxFile := ListBox{}

SELF:olbxFile:Name := "lbxFile"

SELF:olbxFile:Location := Point{ 600 , 8 }

SELF:olbxFile:Size := Size{ 216 , 248 }

SELF:olbxFile:TabIndex := 2

SELF:olbxFile:Anchor := AnchorStyles.Top + AnchorStyles.Right

SELF:olbxFile:MouseDoubleClick += MouseEventHandler{ SELF , @lbxFileMouseDoubleClick() }

SELF:olbxFile:MouseDown += MouseEventHandler{ SELF , @lbxFileMouseDown() }

SELF:Controls:Add(SELF:olbxFile)

SELF:opbClearResults := Button{}

SELF:opbClearResults:Name := "pbClearResults"

SELF:opbClearResults:Location := Point{ 144 , 232 }

SELF:opbClearResults:Size := Size{ 80 , 23 }

SELF:opbClearResults:Text := "Clear Results"

SELF:opbClearResults:ForeColor := Color.FromArgb( 0,128,0 )

SELF:opbClearResults:Click += EventHandler{ SELF , @pbClearResultsClick() }

SELF:opbClearResults:TabIndex := 9

SELF:Controls:Add(SELF:opbClearResults)

SELF:ogbApplyMode := GroupBox{}

SELF:ogbApplyMode:Name := "gbApplyMode"

SELF:ogbApplyMode:SuspendLayout()

SELF:ogbApplyMode:Location := Point{ 456 , 64 }

SELF:ogbApplyMode:Size := Size{ 136 , 80 }

SELF:ogbApplyMode:Text := "Apply Mode :"

SELF:ogbApplyMode:ForeColor := Color.FromArgb( 0,0,213 )

SELF:ogbApplyMode:TabIndex := 19

SELF:Controls:Add(SELF:ogbApplyMode)

SELF:orbExclude := RadioButton{}

SELF:orbExclude:Name := "rbExclude"

SELF:orbExclude:Location := Point{ 24 , 40 }

SELF:orbExclude:Size := Size{ 96 , 24 }

SELF:orbExclude:Text := "Exclude :"

SELF:orbExclude:TabIndex := 1

SELF:ogbApplyMode:Controls:Add(SELF:orbExclude)

SELF:orbInclude := RadioButton{}

SELF:orbInclude:Name := "rbInclude"

SELF:orbInclude:Location := Point{ 24 , 16 }

SELF:orbInclude:Size := Size{ 88 , 24 }

SELF:orbInclude:Text := "Include :"

SELF:orbInclude:TabIndex := 0

SELF:ogbApplyMode:Controls:Add(SELF:orbInclude)

SELF:olbCategories := Label{}

SELF:olbCategories:Name := "lbCategories"

SELF:olbCategories:Location := Point{ 464 , 8 }

SELF:olbCategories:Size := Size{ 100 , 23 }

SELF:olbCategories:Text := "Categories :"

SELF:olbCategories:ForeColor := Color.FromArgb( 0,0,213 )

SELF:olbCategories:TabIndex := 20

SELF:Controls:Add(SELF:olbCategories)

SELF:ogbMatching := GroupBox{}

SELF:ogbMatching:Name := "gbMatching"

SELF:ogbMatching:SuspendLayout()

SELF:ogbMatching:Location := Point{ 144 , 56 }

SELF:ogbMatching:Size := Size{ 136 , 80 }

SELF:ogbMatching:Text := "Matching :"

SELF:ogbMatching:ForeColor := Color.FromArgb( 0,128,0 )

SELF:ogbMatching:TabIndex := 17

SELF:Controls:Add(SELF:ogbMatching)

SELF:orbAnywhere := RadioButton{}

SELF:orbAnywhere:Name := "rbAnywhere"

SELF:orbAnywhere:Location := Point{ 24 , 40 }

SELF:orbAnywhere:Size := Size{ 88 , 24 }

SELF:orbAnywhere:Text := "Anywhere :"

SELF:orbAnywhere:TabIndex := 1

SELF:ogbMatching:Controls:Add(SELF:orbAnywhere)

SELF:orbAtLeft := RadioButton{}

SELF:orbAtLeft:Name := "rbAtLeft"

SELF:orbAtLeft:Location := Point{ 24 , 16 }

SELF:orbAtLeft:Size := Size{ 80 , 24 }

SELF:orbAtLeft:Text := "At Left :"

SELF:orbAtLeft:TabIndex := 0

SELF:ogbMatching:Controls:Add(SELF:orbAtLeft)

SELF:olbToFind := Label{}

SELF:olbToFind:Name := "lbToFind"

SELF:olbToFind:Location := Point{ 144 , 8 }

SELF:olbToFind:Size := Size{ 100 , 23 }

SELF:olbToFind:Text := "To Find :"

SELF:olbToFind:ForeColor := Color.FromArgb( 0,128,0 )

SELF:olbToFind:TabIndex := 16

SELF:Controls:Add(SELF:olbToFind)

SELF:ocbFilterOn := CheckBox{}

SELF:ocbFilterOn:Name := "cbFilterOn"

SELF:ocbFilterOn:Location := Point{ 472 , 32 }

SELF:ocbFilterOn:Size := Size{ 80 , 24 }

SELF:ocbFilterOn:Text := "Filter On :"

SELF:ocbFilterOn:TabIndex := 18

SELF:Controls:Add(SELF:ocbFilterOn)

SELF:olbFound := Label{}

SELF:olbFound:Name := "lbFound"

SELF:olbFound:Location := Point{ 144 , 200 }

SELF:olbFound:Size := Size{ 136 , 23 }

SELF:olbFound:BorderStyle := BorderStyle.Fixed3D

SELF:olbFound:Text := ".. found .."

SELF:olbFound:ForeColor := Color.Maroon

SELF:olbFound:BackColor := Color.BlanchedAlmond

SELF:olbFound:TabIndex := 11

SELF:Controls:Add(SELF:olbFound)

SELF:otbSearch := TextBox{}

SELF:otbSearch:Name := "tbSearch"

SELF:otbSearch:Location := Point{ 144 , 32 }

SELF:otbSearch:Size := Size{ 136 , 20 }

SELF:otbSearch:TabIndex := 5

SELF:Controls:Add(SELF:otbSearch)

SELF:opbAddNew := Button{}

SELF:opbAddNew:Name := "pbAddNew"

SELF:opbAddNew:Location := Point{ 656 , 448 }

SELF:opbAddNew:Size := Size{ 75 , 23 }

SELF:opbAddNew:Text := "Add New "

SELF:opbAddNew:Click += EventHandler{ SELF , @pbAddNewClick() }

SELF:opbAddNew:TabIndex := 1

SELF:opbAddNew:Anchor := AnchorStyles.Bottom + AnchorStyles.Right

SELF:Controls:Add(SELF:opbAddNew)

SELF:oOKButton := Button{}

SELF:oOKButton:Name := "OKButton"

SELF:oOKButton:Location := Point{ 768 , 448 }

SELF:oOKButton:Size := Size{ 48 , 24 }

SELF:oOKButton:Text := "&OK"

SELF:oOKButton:Click += EventHandler{ SELF , @OKButtonClick() }

SELF:oOKButton:TabIndex := 0

SELF:oOKButton:Anchor := AnchorStyles.Bottom + AnchorStyles.Right

SELF:Controls:Add(SELF:oOKButton)

SELF:ogbMatching:ResumeLayout()

SELF:ogbApplyMode:ResumeLayout()

SELF:ogbFilter:ResumeLayout()

SELF:ogbSearch:ResumeLayout()

SELF:ResumeLayout()

SELF:AcceptButton := SELF:opbDoSearch

RETURN

METHOD dgvSearchResultsMouseClick(o AS OBJECT , e AS MouseEventArgs) AS VOID

LOCAL oDGV AS DataGridView

LOCAL oDGVR AS DataGridViewRow

LOCAL nCardID AS INT

oDGV := (DataGridView)o

IF oDGV:SelectedRows:Count < 1

RETURN

ENDIF

oDGVR := oDGV:SelectedRows[0]

nCardID := (INT)oDGVR:Cells[0]:Value

SELF:nID := nCardID

SELF:ReadSelectedIntoFocus()

RETURN

METHOD pbConnectionClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoConnection()

RETURN

METHOD DoConnection() AS VOID

LOCAL oConnection AS fmConnection

oConnection := fmConnection{}

oConnection:ShowDialog(SELF)

oConnection := NULL_OBJECT

RETURN

METHOD dgvSearchResultsMouseDoubleClick(o AS OBJECT , e AS MouseEventArgs) AS VOID

SELF:DoSearchResultsSelectCard(o, e)

RETURN

METHOD DoSearchResultsSelectCard(o AS OBJECT , e AS MouseEventArgs) AS VOID

LOCAL oDGV AS DataGridView

LOCAL oDGVR AS DataGridViewRow

LOCAL nCardID AS INT

oDGV := (DataGridView)o

IF oDGV:SelectedRows:Count < 1

RETURN

ENDIF

oDGVR := oDGV:SelectedRows[0]

nCardID := (INT)oDGVR:Cells[0]:Value

SELF:nID := nCardID

SELF:ReadSelectedIntoFocus()

SELF:DoViewFocusCard()

RETURN

METHOD fmUltraFile2008Load(o AS OBJECT , e AS EventArgs) AS VOID

//------ not required as yet -----

RETURN

METHOD ReadSerialXmlFile() AS LOGIC

LOCAL serial AS Serialization.XmlSerializer

LOCAL oSR AS StreamReader

LOCAL cFile, cText, cHead AS STRING

LOCAL lResult AS Boolean

lResult := FALSE

lConnectionTestedOK := FALSE

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "Existing file will be read!"

cHead := "XML file :"

//MessageBox.Show(SELF,cText,cHead)

//----- read object from XML file -----

serial := Serialization.XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFile}

SELF:oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

oSR := NULL_OBJECT

serial := NULL_OBJECT

lResult := TRUE

lConnectionTestedOK := TRUE

ELSE

cHead := "XML file :"

cText := "File can't be found !?"+CRLF

cText := "Go to Connection and create a string."+CRLF

MessageBox.Show(SELF,cText,cHead)

ENDIF

RETURN lResult

METHOD lbxFileMouseDoubleClick(o AS OBJECT , e AS MouseEventArgs) AS VOID

SELF:DoFileMouseDoubleClick()

RETURN

METHOD DoFileMouseDoubleClick() AS VOID

nIndex := olbxFile:SelectedIndex

SELF:nID := (INT)alCards[nIndex]

SELF:ReadSelectedIntoFocus()

SELF:DoViewFocusCard()

RETURN

METHOD lbxFileMouseDown(o AS OBJECT , e AS MouseEventArgs) AS VOID

SELF:DoFileMouseDown()

RETURN

METHOD DoFileMouseDown() AS VOID

nIndex := olbxFile:SelectedIndex

//MessageBox.Show(nIndex:ToString(),"Index :")

SELF:nID := (INT)alCards[nIndex]

//MessageBox.Show(nID:ToString(),"Card ID :")

SELF:ReadSelectedIntoFocus()

RETURN

METHOD pbViewBoxClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoViewBox()

RETURN

METHOD DoViewBox() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

SELF:FillListWithAllCards()

SELF:DoForceDisconnect()

SELF:DoSetConnectionString()

TRY

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [UltraFileMain] WHERE [CardID] >= 1;"

//cSQL := "SELECT [mainhead] FROM [UltraFileMain] WHERE [CardID] >= 1"

//cSQL := "SELECT [mainhead],[subhead],[phone],[email],[cashamount],[work] FROM [UltraFileMain] "

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := SELF:oDConn

oDComm:CommandText := cSQL

LOCAL oDDA AS DbDataAdapter

oDDA := oDFactory:CreateDataAdapter()

oDDA:SelectCommand := oDComm

LOCAL oDS AS DataSet

oDS := DataSet{}

oDDA:Fill(oDS, "InsertedUltraCardInfo")

IF oDS:Tables:Count > 0

//----- display contents of the data set table in the grid -----

odgvSearchResults:DataSource := oDS:Tables["InsertedUltraCardInfo"]

ENDIF

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD pbEditFocusCardClick(o AS OBJECT , e AS EventArgs) AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

oFocusCard:newcard := FALSE

LOCAL oUAE AS fmUltraAddEdit

oUAE := fmUltraAddEdit{}

oUAE:ShowDialog(SELF)

oUAE := NULL_OBJECT

RETURN

METHOD pbRemoveFocusCardClick(o AS OBJECT , e AS EventArgs) AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

SELF:DoRemoveFocusCard()

SELF:FillListWithAllCards()

SELF:DoViewBox()

RETURN

METHOD DoRemoveFocusCard() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

LOCAL lRemoved AS Boolean

lRemoved := FALSE

LOCAL cText AS STRING

cText := "Card to be removed is :- "+CRLF

LOCAL nLen AS DWORD

nLen := (DWORD)oFocusCard:mainhead:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:mainhead:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:company:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:company:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:person:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:person:Substring(0,(INT)nLen)+CRLF

nLen := (DWORD)oFocusCard:body:Length

IF nLen > 39

nLen := 39

ENDIF

cText += oFocusCard:body:Substring(0,(INT)nLen)+CRLF

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Hand

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageboxDefaultButton

oDef := MessageboxDefaultButton.Button2

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF,cText,"Details :",oMBB,oIcon,oDef)

IF oDR = DialogResult.No

MessageBox.Show(SELF,"Card still remains!!","CANCEL :")

RETURN

ENDIF

//================================================================

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oDConn

//----- set the 'CommandText' property -----

cText := "DELETE FROM [UltraFileMain] WHERE [cardID] = @CardRef;"

MessageBox.Show(cText)

oCommand:CommandText := cText

//----- add a parameter and set its value -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@CardRef", SqlDbType.Int}

oParam:Value := nID //----- INT -----

oCommand:Parameters:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

lRemoved := TRUE

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

IF lRemoved

MessageBox.Show(SELF,"Card has been removed!","Success :")

ELSE

MessageBox.Show(SELF,"Card still remains!!","Failure :")

ENDIF

RETURN

METHOD pbViewFocusCardClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoViewFocusCard()

RETURN

METHOD DoViewFocusCard() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

oFocusCard:newcard := FALSE

LOCAL oUAE AS fmUltraAddEdit

oUAE := fmUltraAddEdit{}

oUAE:ViewModeOnly()

oUAE:ShowDialog(SELF)

oUAE := NULL_OBJECT

RETURN

METHOD pbHelpClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoShowHelpDetails()

RETURN

METHOD DoShowHelpDetails() AS VOID

LOCAL cText AS STRING

cText := ""

cText += "All cards are called the 'Box' !"+CRLF

cText += "'View Box' to see the full set."+CRLF

cText += "Click in ListBox to make a card 'Focus'."+CRLF

cText += "Double click in listbox to view the card."+CRLF

cText += "Click the LHS of a row in DGV to make it Focus."+CRLF

cText += "Double click the LHS of a row in DGV to view the card."+CRLF

cText += "Edit, Update and Remove operate on the Focus card."+CRLF

cText += "Supply at least one character to search."+CRLF

cText += "Expand the form to see more of the DGV."+CRLF

cText += "Form can expand from default but not shrink."+CRLF

cText += "For important SQL operations the statement is show. "+CRLF+CRLF

cText += "Searching 'Dates' only works with a year at the moment."+CRLF

cText += "Dates are not included in search ALL!"+CRLF

MessageBox.Show(SELF,cText,"HELP - Information : Ultra Card 2008")

RETURN

METHOD pbClearResultsClick(o AS OBJECT , e AS EventArgs) AS VOID

odgvSearchResults:DataSource := DataTable{}

olbFound:Text := ".. no search .."

RETURN

METHOD pbClearFilterClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoClearFilter()

RETURN

METHOD DoClearFilter() AS VOID

ocbFilterOn:Checked := FALSE

orbInclude:Checked := TRUE

ocbSoftware:Checked := FALSE

ocbGovernment:Checked := FALSE

ocbHardware:Checked := FALSE

ocbIT:Checked := FALSE

ocbVulcanVO:Checked := FALSE

ocbTechnical:Checked := FALSE

ocbFinance:Checked := FALSE

ocbPersonal:Checked := FALSE

ocbWork:Checked := FALSE

RETURN

METHOD pbDoSearchClick(o AS OBJECT , e AS EventArgs) AS VOID

SELF:DoSearch()

RETURN

METHOD DoSearch() AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

SELF:cSearch := otbSearch:Text:Trim()

IF cSearch == ""

//----- WARN -----

RETURN

ENDIF

SELF:DoSetConnectionString()

LOCAL cStatement AS STRING

cStatement := "SELECT * FROM [UltraFileMain] WHERE ( "

DO CASE

CASE orbHeader:Checked

IF orbAnywhere:Checked

cStatement += "[mainhead] LIKE '%"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mainhead] LIKE '"+cSearch+"%'" //----- just at start of line -----

cStatement += " OR [subhead] LIKE '"+cSearch+"%'"

ENDIF

CASE orbCompany:Checked

IF orbAnywhere:Checked

cStatement += "[company] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[company] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbContact:Checked

IF orbAnywhere:Checked

cStatement += "[person] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[person] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbPhone:Checked

IF orbAnywhere:Checked

cStatement += "[phone] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[phone] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbBody:Checked

IF orbAnywhere:Checked

cStatement += "[body] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[body] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbMobile:Checked

IF orbAnywhere:Checked

cStatement += "[mobile] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mobile] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbEmail:Checked

IF orbAnywhere:Checked

cStatement += "[email] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[email] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbDates:Checked

IF orbAnywhere:Checked

cStatement += "[AssocDate] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[AssocDate] LIKE '"+cSearch+"%'" //----- just at start of line -----

ENDIF

CASE orbAll:Checked

IF orbAnywhere:Checked

cStatement += "[mainhead] LIKE '%"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[company] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[person] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[phone] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[body] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[mobile] LIKE '%"+cSearch+"%'"

cStatement += " OR "

cStatement += "[email] LIKE '%"+cSearch+"%'"

ELSE

cStatement += "[mainhead] LIKE '"+cSearch+"%'"

cStatement += " OR [subhead] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[company] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[person] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[phone] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[body] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[mobile] LIKE '"+cSearch+"%'"

cStatement += " OR "

cStatement += "[email] LIKE '"+cSearch+"%'"

ENDIF

ENDCASE

cStatement += " )"

IF SELF:ocbFilterOn:Checked

IF orbInclude:Checked

IF ocbWork:Checked

cStatement += " AND [work] = 1"

ENDIF

IF ocbPersonal:Checked

cStatement += " AND [personal] = 1"

ENDIF

IF ocbFinance:Checked

cStatement += " AND [finance] = 1"

ENDIF

IF ocbTechnical:Checked

cStatement += " AND [Technical] = 1"

ENDIF

IF ocbSoftware:Checked

cStatement += " AND [Software] = 1"

ENDIF

IF ocbVulcanVO:Checked

cStatement += " AND [VulcanVO] = 1"

ENDIF

IF ocbIT:Checked

cStatement += " AND [IT] = 1"

ENDIF

IF ocbHardware:Checked

cStatement += " AND [Hardware] = 1"

ENDIF

IF ocbGovernment:Checked

cStatement += " AND [Government] = 1"

ENDIF

ELSE

IF ocbWork:Checked

cStatement += " AND [work] = 0"

ENDIF

IF ocbPersonal:Checked

cStatement += " AND [personal] = 0"

ENDIF

IF ocbFinance:Checked

cStatement += " AND [finance] = 0"

ENDIF

IF ocbTechnical:Checked

cStatement += " AND [Technical] = 0"

ENDIF

IF ocbSoftware:Checked

cStatement += " AND [Software] = 0"

ENDIF

IF ocbVulcanVO:Checked

cStatement += " AND [VulcanVO] = 0"

ENDIF

IF ocbIT:Checked

cStatement += " AND [IT] = 0"

ENDIF

IF ocbHardware:Checked

cStatement += " AND [Hardware] = 0"

ENDIF

IF ocbGovernment:Checked

cStatement += " AND [Government] = 0"

ENDIF

ENDIF

ENDIF

//MessageBox.Show(cStatement)

TRY

LOCAL oDA AS SqlDataAdapter

oDA := SqlDataAdapter{cStatement, SELF:cStrConn}

LOCAL oDS AS DataSet

oDS := DataSet{}

oDA:Fill(oDS, "UltraSearch")

odgvSearchResults:DataSource := oDS:Tables["UltraSearch"]

CATCH ex AS System.Exception

MessageBox.Show(ex:Message)

END TRY

oDS:Dispose()

oDA:Dispose()

SELF:DoForceDisconnect()

LOCAL nLen AS DWORD

nLen := (DWORD)odgvSearchResults:RowCount-1

olbFound:Text := " Cards Found >> "+nLen:ToString()

RETURN

METHOD pbAddNewClick(o AS OBJECT , e AS EventArgs) AS VOID

IF !lConnectionTestedOK

RETURN

ENDIF

oFocusCard := UltraCard{}

oFocusCard:newcard := TRUE

LOCAL oUAE AS fmUltraAddEdit

oUAE := fmUltraAddEdit{}

oUAE:ShowDialog(SELF)

oUAE := NULL_OBJECT

RETURN

METHOD DoInsertNewCardObject() AS VOID

//----- use FocusCard object to populate ------

IF oFocusCard == NULL_OBJECT

//----- WARN -----

RETURN

ENDIF

//======================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [UltraFileMain] ([datetimestamp],[mainhead],[subhead],[body],[extradetails],[phone],"

cText += "[mobile],[email],[website],[company],[person],[assocdate],[cashamount],[work],"

cText += "[personal],[finance],[technical],[software],[vulcanvo],[IT],[hardware],[government])"

cText += "VALUES (@dts,@mh,@sh,@bd,@xd,@ph,@mb,@em,@ws,@cp,@ps,@ad,"

cText += "@ca,@wk,@psl,@fn,@tch,@sft,@vvo,@it,@hw,@gv);"

MessageBox.Show(cText)

//====================================================

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oDConn

//----- add parameters and set their value -----

//----- post and country in diff order to DB! -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@dts", SqlDbType.SmallDateTime}

oParam:Value := oFocusCard:datetimestamp

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@mh", SqlDbType.VarChar}

oParam:Value := oFocusCard:mainhead

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@sh", SqlDbType.VarChar}

oParam:Value := oFocusCard:subhead

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@bd", SqlDbType.VarChar}

oParam:Value := oFocusCard:body

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@xd", SqlDbType.VarChar}

oParam:Value := oFocusCard:extras

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ph", SqlDbType.VarChar}

oParam:Value := oFocusCard:phone

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@mb", SqlDbType.VarChar}

oParam:Value := oFocusCard:mobile

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@em", SqlDbType.VarChar}

oParam:Value := oFocusCard:email

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ws", SqlDbType.VarChar}

oParam:Value := oFocusCard:web

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@cp", SqlDbType.VarChar}

oParam:Value := oFocusCard:company

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ps", SqlDbType.VarChar}

oParam:Value := oFocusCard:person

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ad", SqlDbType.SmallDateTime}

//oParam:Value := SELF:aParamValues[11]

oParam:Value := oFocusCard:AssocDate

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ca", SqlDbType.Money}

//oParam:Value := SELF:aParamValues[12]

oParam:Value := oFocusCard:CashAmount

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@wk", SqlDbType.VarChar}

oParam:Value := oFocusCard:work

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@psl", SqlDbType.VarChar}

oParam:Value := oFocusCard:personal

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@fn", SqlDbType.VarChar}

oParam:Value := oFocusCard:finance

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@tch", SqlDbType.VarChar}

oParam:Value := oFocusCard:technical

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@sft", SqlDbType.VarChar}

oParam:Value := oFocusCard:software

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@vvo", SqlDbType.VarChar}

oParam:Value := oFocusCard:vulcanvo

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@it", SqlDbType.VarChar}

oParam:Value := oFocusCard:it

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@hw", SqlDbType.VarChar}

oParam:Value := oFocusCard:hardware

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@gv", SqlDbType.VarChar}

oParam:Value := oFocusCard:government

oCommand:Parameters:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [UltraFileMain] WHERE [CardID] >= 4;"

LOCAL oDDA AS DbDataAdapter

oDDA := oDFactory:CreateDataAdapter()

oCommand:CommandText := cSQL

oDDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

oDDA:Fill(oDS, "InsertedUltraCardInfo")

//----- display contents of the data set table in the grid -----

odgvSearchResults:DataSource := oDS:Tables["InsertedUltraCardInfo"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD DoUpdateNewCardObject() AS VOID

//----- use FocusCard object to populate ------

IF oFocusCard == NULL_OBJECT

//----- WARN -----

RETURN

ENDIF

//======================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "UPDATE [UltraFileMain] SET "

cText += "[mainhead] = @mh,"

cText += "[subhead] = @sh,"

cText += "[body] = @bd,"

cText += "[extradetails] = @xd,"

cText += "[phone] = @ph,"

cText += "[mobile] = @mb,"

cText += "[email] = @em,"

cText += "[website] = @ws,"

cText += "[company] = @cp,"

cText += "[person] = @ps,"

cText += "[assocdate] = @ad,"

cText += "[cashamount] = @ca,"

cText += "[work] = @wk,"

cText += "[personal] = @psl,"

cText += "[finance] = @fn,"

cText += "[technical] = @tch,"

cText += "[software] = @sft,"

cText += "[vulcanvo] = @vvo,"

cText += "[it] = @it,"

cText += "[hardware] = @hw,"

cText += "[government] = @gv"

cText += " WHERE [CardID] = @CardRef;"

//MessageBox.Show(cText)

//====================================================

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oDConn

//----- add parameters and set their value -----

//----- post and country in diff order to DB! -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@CardRef", SqlDbType.Int}

oParam:Value := SELF:nID //----- INT -----

oCommand:Parameters:Add(oParam)

//=====================================================

oParam := SqlParameter{"@dts", SqlDbType.SmallDateTime}

oParam:Value := oFocusCard:datetimestamp

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@mh", SqlDbType.VarChar}

oParam:Value := oFocusCard:mainhead

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@sh", SqlDbType.VarChar}

oParam:Value := oFocusCard:subhead

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@bd", SqlDbType.VarChar}

oParam:Value := oFocusCard:body

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@xd", SqlDbType.VarChar}

oParam:Value := oFocusCard:extras

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ph", SqlDbType.VarChar}

oParam:Value := oFocusCard:phone

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@mb", SqlDbType.VarChar}

oParam:Value := oFocusCard:mobile

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@em", SqlDbType.VarChar}

oParam:Value := oFocusCard:email

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ws", SqlDbType.VarChar}

oParam:Value := oFocusCard:web

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@cp", SqlDbType.VarChar}

oParam:Value := oFocusCard:company

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ps", SqlDbType.VarChar}

oParam:Value := oFocusCard:person

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ad", SqlDbType.SmallDateTime}

//oParam:Value := SELF:aParamValues[11]

oParam:Value := oFocusCard:AssocDate

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@ca", SqlDbType.Money}

//oParam:Value := SELF:aParamValues[12]

oParam:Value := oFocusCard:CashAmount

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@wk", SqlDbType.VarChar}

oParam:Value := oFocusCard:work

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@psl", SqlDbType.VarChar}

oParam:Value := oFocusCard:personal

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@fn", SqlDbType.VarChar}

oParam:Value := oFocusCard:finance

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@tch", SqlDbType.VarChar}

oParam:Value := oFocusCard:technical

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@sft", SqlDbType.VarChar}

oParam:Value := oFocusCard:software

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@vvo", SqlDbType.VarChar}

oParam:Value := oFocusCard:vulcanvo

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@it", SqlDbType.VarChar}

oParam:Value := oFocusCard:it

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@hw", SqlDbType.VarChar}

oParam:Value := oFocusCard:hardware

oCommand:Parameters:Add(oParam)

oParam := SqlParameter{"@gv", SqlDbType.VarChar}

oParam:Value := oFocusCard:government

oCommand:Parameters:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [UltraFileMain] WHERE [CardID] = "+SELF:nID:ToString()+";"

LOCAL oDDA AS DbDataAdapter

oDDA := oDFactory:CreateDataAdapter()

oCommand:CommandText := cSQL

oDDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

oDDA:Fill(oDS, "InsertedUltraCardInfo")

//----- display contents of the data set table in the grid -----

odgvSearchResults:DataSource := oDS:Tables["InsertedUltraCardInfo"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

METHOD OKButtonClick(oControl AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoForceDisconnect()

SELF:Close()

RETURN

END CLASS

CLASS fmUltraAddEdit INHERIT System.Windows.Forms.Form

PROTECT opbRefreshTable AS System.Windows.Forms.Button

PROTECT olbCashAmount AS System.Windows.Forms.Label

PROTECT omtbCashAmmount AS System.Windows.Forms.MaskedTextBox

PROTECT ortbDetails AS System.Windows.Forms.RichTextBox

PROTECT ortbBody AS System.Windows.Forms.RichTextBox

PROTECT odtpUltraAddEdit AS System.Windows.Forms.DateTimePicker

PROTECT otbWeb AS System.Windows.Forms.TextBox

PROTECT otbEmail AS System.Windows.Forms.TextBox

PROTECT otbMobile AS System.Windows.Forms.TextBox

PROTECT otbPerson AS System.Windows.Forms.TextBox

PROTECT otbCompany AS System.Windows.Forms.TextBox

PROTECT otbPhone AS System.Windows.Forms.TextBox

PROTECT otbSubHead AS System.Windows.Forms.TextBox

PROTECT otbMainHead AS System.Windows.Forms.TextBox

PROTECT ogbCategories AS System.Windows.Forms.GroupBox

PROTECT ocbGovernment AS System.Windows.Forms.CheckBox

PROTECT ocbHardware AS System.Windows.Forms.CheckBox

PROTECT ocbIT AS System.Windows.Forms.CheckBox

PROTECT ocbVulcanVO AS System.Windows.Forms.CheckBox

PROTECT ocbSoftware AS System.Windows.Forms.CheckBox

PROTECT ocbTechnical AS System.Windows.Forms.CheckBox

PROTECT ocbFinance AS System.Windows.Forms.CheckBox

PROTECT ocbPersonal AS System.Windows.Forms.CheckBox

PROTECT ocbWork AS System.Windows.Forms.CheckBox

PROTECT opbClear AS System.Windows.Forms.Button

PROTECT olbAssocDate AS System.Windows.Forms.Label

PROTECT olbBody AS System.Windows.Forms.Label

PROTECT olbExtraDetails AS System.Windows.Forms.Label

PROTECT olbWeb AS System.Windows.Forms.Label

PROTECT olbPhone AS System.Windows.Forms.Label

PROTECT olbMobile AS System.Windows.Forms.Label

PROTECT olbEmail AS System.Windows.Forms.Label

PROTECT olbPerson AS System.Windows.Forms.Label

PROTECT olbCompany AS System.Windows.Forms.Label

PROTECT olbSubHead AS System.Windows.Forms.Label

PROTECT olbMainHead AS System.Windows.Forms.Label

PROTECT opbOK AS System.Windows.Forms.Button

// User code starts here (DO NOT remove this line) ##USER##

PROTECT oParent AS fmUltraFile2008

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

SELF:MinimumSize := SELF:Size

SELF:MaximumSize := SELF:Size

RETURN

METHOD PopulateFieldsFromCard() AS VOID

omtbCashAmmount:Text := oParent:oFocusCard:CashAmount:ToString()

ortbDetails:Text := oParent:oFocusCard:extras

ortbBody:Text := oParent:oFocusCard:body

odtpUltraAddEdit:Value := oParent:oFocusCard:AssocDate

otbWeb:Text := oParent:oFocusCard:web

otbEmail:Text := oParent:oFocusCard:email

otbMobile:Text := oParent:oFocusCard:mobile

otbPerson:Text := oParent:oFocusCard:person

otbCompany:Text := oParent:oFocusCard:company

otbPhone:Text := oParent:oFocusCard:phone

otbSubHead:Text := oParent:oFocusCard:subhead

otbMainHead:Text := oParent:oFocusCard:mainhead

ocbGovernment:Checked := oParent:oFocusCard:government

ocbHardware:Checked := oParent:oFocusCard:hardware

ocbIT:Checked := oParent:oFocusCard:it

ocbVulcanVO:Checked := oParent:oFocusCard:vulcanvo

ocbSoftware:Checked := oParent:oFocusCard:software

ocbTechnical:Checked := oParent:oFocusCard:technical

ocbFinance:Checked := oParent:oFocusCard:finance

ocbPersonal:Checked := oParent:oFocusCard:personal

ocbWork:Checked := oParent:oFocusCard:work

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

SELF:Name := "fmUltraAddEdit"

SELF:SuspendLayout()

SELF:Location := System.Drawing.Point{100,100}

SELF:ClientSize := System.Drawing.Size{674,438}

SELF:Text := "Ultra File 2008 : Add / Edit :"

SELF:Load += System.EventHandler{ SELF , @fmUltraAddEditLoad() }

SELF:opbRefreshTable := System.Windows.Forms.Button{}

SELF:opbRefreshTable:Name := "pbRefreshTable"

SELF:opbRefreshTable:Location := System.Drawing.Point{ 480 , 408 }

SELF:opbRefreshTable:Size := System.Drawing.Size{ 88 , 23 }

SELF:opbRefreshTable:Text := "Save Card "

SELF:opbRefreshTable:Click += System.EventHandler{ SELF , @pbRefreshTableClick() }

SELF:opbRefreshTable:TabIndex := 4

SELF:Controls:Add(SELF:opbRefreshTable)

SELF:olbCashAmount := System.Windows.Forms.Label{}

SELF:olbCashAmount:Name := "lbCashAmount"

SELF:olbCashAmount:Location := System.Drawing.Point{ 24 , 224 }

SELF:olbCashAmount:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbCashAmount:Text := "cash amount :"

SELF:olbCashAmount:TabIndex := 26

SELF:Controls:Add(SELF:olbCashAmount)

SELF:omtbCashAmmount := System.Windows.Forms.MaskedTextBox{}

SELF:omtbCashAmmount:Name := "mtbCashAmmount"

SELF:omtbCashAmmount:Location := System.Drawing.Point{ 112 , 224 }

SELF:omtbCashAmmount:Size := System.Drawing.Size{ 100 , 20 }

SELF:omtbCashAmmount:Text := "£00.00"

SELF:omtbCashAmmount:TabIndex := 25

SELF:Controls:Add(SELF:omtbCashAmmount)

SELF:ortbDetails := System.Windows.Forms.RichTextBox{}

SELF:ortbDetails:Name := "rtbDetails"

SELF:ortbDetails:Location := System.Drawing.Point{ 112 , 352 }

SELF:ortbDetails:Size := System.Drawing.Size{ 280 , 40 }

SELF:ortbDetails:Text := "... extra details ..."

SELF:ortbDetails:TextChanged += System.EventHandler{ SELF , @rtbDetailsTextChanged() }

SELF:ortbDetails:TabIndex := 20

SELF:Controls:Add(SELF:ortbDetails)

SELF:ortbBody := System.Windows.Forms.RichTextBox{}

SELF:ortbBody:Name := "rtbBody"

SELF:ortbBody:Location := System.Drawing.Point{ 112 , 248 }

SELF:ortbBody:Size := System.Drawing.Size{ 280 , 96 }

SELF:ortbBody:Text := "... body of card ..."

SELF:ortbBody:TextChanged += System.EventHandler{ SELF , @rtbBodyTextChanged() }

SELF:ortbBody:TabIndex := 18

SELF:Controls:Add(SELF:ortbBody)

SELF:odtpUltraAddEdit := System.Windows.Forms.DateTimePicker{}

SELF:odtpUltraAddEdit:Name := "dtpUltraAddEdit"

SELF:odtpUltraAddEdit:Location := System.Drawing.Point{ 112 , 200 }

SELF:odtpUltraAddEdit:Size := System.Drawing.Size{ 200 , 20 }

SELF:odtpUltraAddEdit:TextChanged += System.EventHandler{ SELF , @dtpUltraAddEditTextChanged() }

SELF:odtpUltraAddEdit:TabIndex := 22

SELF:Controls:Add(SELF:odtpUltraAddEdit)

SELF:otbWeb := System.Windows.Forms.TextBox{}

SELF:otbWeb:Name := "tbWeb"

SELF:otbWeb:Location := System.Drawing.Point{ 112 , 176 }

SELF:otbWeb:Size := System.Drawing.Size{ 280 , 20 }

SELF:otbWeb:Text := "... web site ..."

SELF:otbWeb:TextChanged += System.EventHandler{ SELF , @tbWebTextChanged() }

SELF:otbWeb:TabIndex := 16

SELF:Controls:Add(SELF:otbWeb)

SELF:otbEmail := System.Windows.Forms.TextBox{}

SELF:otbEmail:Name := "tbEmail"

SELF:otbEmail:Location := System.Drawing.Point{ 112 , 152 }

SELF:otbEmail:Size := System.Drawing.Size{ 280 , 20 }

SELF:otbEmail:Text := "... email address ..."

SELF:otbEmail:TextChanged += System.EventHandler{ SELF , @tbEmailTextChanged() }

SELF:otbEmail:TabIndex := 14

SELF:Controls:Add(SELF:otbEmail)

SELF:otbMobile := System.Windows.Forms.TextBox{}

SELF:otbMobile:Name := "tbMobile"

SELF:otbMobile:Location := System.Drawing.Point{ 112 , 128 }

SELF:otbMobile:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbMobile:Text := "... mobile ..."

SELF:otbMobile:TextChanged += System.EventHandler{ SELF , @tbMobileTextChanged() }

SELF:otbMobile:TabIndex := 12

SELF:Controls:Add(SELF:otbMobile)

SELF:otbPerson := System.Windows.Forms.TextBox{}

SELF:otbPerson:Name := "tbPerson"

SELF:otbPerson:Location := System.Drawing.Point{ 112 , 104 }

SELF:otbPerson:Size := System.Drawing.Size{ 208 , 20 }

SELF:otbPerson:Text := "... person ..."

SELF:otbPerson:TextChanged += System.EventHandler{ SELF , @tbPersonTextChanged() }

SELF:otbPerson:TabIndex := 10

SELF:Controls:Add(SELF:otbPerson)

SELF:otbCompany := System.Windows.Forms.TextBox{}

SELF:otbCompany:Name := "tbCompany"

SELF:otbCompany:Location := System.Drawing.Point{ 112 , 80 }

SELF:otbCompany:Size := System.Drawing.Size{ 208 , 20 }

SELF:otbCompany:Text := "... company ..."

SELF:otbCompany:TextChanged += System.EventHandler{ SELF , @tbCompanyTextChanged() }

SELF:otbCompany:TabIndex := 8

SELF:Controls:Add(SELF:otbCompany)

SELF:otbPhone := System.Windows.Forms.TextBox{}

SELF:otbPhone:Name := "tbPhone"

SELF:otbPhone:Location := System.Drawing.Point{ 112 , 56 }

SELF:otbPhone:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbPhone:Text := " 12345432"

SELF:otbPhone:TextChanged += System.EventHandler{ SELF , @tbPhoneTextChanged() }

SELF:otbPhone:TabIndex := 3

SELF:Controls:Add(SELF:otbPhone)

SELF:otbSubHead := System.Windows.Forms.TextBox{}

SELF:otbSubHead:Name := "tbSubHead"

SELF:otbSubHead:Location := System.Drawing.Point{ 112 , 32 }

SELF:otbSubHead:Size := System.Drawing.Size{ 280 , 20 }

SELF:otbSubHead:Text := "... sub head ..."

SELF:otbSubHead:TextChanged += System.EventHandler{ SELF , @tbSubHeadTextChanged() }

SELF:otbSubHead:TabIndex := 2

SELF:Controls:Add(SELF:otbSubHead)

SELF:otbMainHead := System.Windows.Forms.TextBox{}

SELF:otbMainHead:Name := "tbMainHead"

SELF:otbMainHead:Location := System.Drawing.Point{ 112 , 8 }

SELF:otbMainHead:Size := System.Drawing.Size{ 280 , 20 }

SELF:otbMainHead:Text := "... MainHead ..."

SELF:otbMainHead:TextChanged += System.EventHandler{ SELF , @tbMainHeadTextChanged() }

SELF:otbMainHead:TabIndex := 1

SELF:Controls:Add(SELF:otbMainHead)

SELF:ogbCategories := System.Windows.Forms.GroupBox{}

SELF:ogbCategories:Name := "gbCategories"

SELF:ogbCategories:SuspendLayout()

SELF:ogbCategories:Location := System.Drawing.Point{ 480 , 8 }

SELF:ogbCategories:Size := System.Drawing.Size{ 192 , 280 }

SELF:ogbCategories:Text := "Categories :"

SELF:ogbCategories:TabIndex := 24

SELF:Controls:Add(SELF:ogbCategories)

SELF:ocbGovernment := System.Windows.Forms.CheckBox{}

SELF:ocbGovernment:Name := "cbGovernment"

SELF:ocbGovernment:Location := System.Drawing.Point{ 40 , 216 }

SELF:ocbGovernment:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbGovernment:Text := "Government :"

SELF:ocbGovernment:CheckedChanged += System.EventHandler{ SELF , @cbGovernmentCheckedChanged() }

SELF:ocbGovernment:TabIndex := 8

SELF:ogbCategories:Controls:Add(SELF:ocbGovernment)

SELF:ocbHardware := System.Windows.Forms.CheckBox{}

SELF:ocbHardware:Name := "cbHardware"

SELF:ocbHardware:Location := System.Drawing.Point{ 40 , 192 }

SELF:ocbHardware:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbHardware:Text := "Hardware :"

SELF:ocbHardware:CheckedChanged += System.EventHandler{ SELF , @cbHardwareCheckedChanged() }

SELF:ocbHardware:TabIndex := 7

SELF:ogbCategories:Controls:Add(SELF:ocbHardware)

SELF:ocbIT := System.Windows.Forms.CheckBox{}

SELF:ocbIT:Name := "cbIT"

SELF:ocbIT:Location := System.Drawing.Point{ 40 , 168 }

SELF:ocbIT:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbIT:Text := "IT :"

SELF:ocbIT:CheckedChanged += System.EventHandler{ SELF , @cbITCheckedChanged() }

SELF:ocbIT:TabIndex := 6

SELF:ogbCategories:Controls:Add(SELF:ocbIT)

SELF:ocbVulcanVO := System.Windows.Forms.CheckBox{}

SELF:ocbVulcanVO:Name := "cbVulcanVO"

SELF:ocbVulcanVO:Location := System.Drawing.Point{ 40 , 144 }

SELF:ocbVulcanVO:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbVulcanVO:Text := "Vulcan VO :"

SELF:ocbVulcanVO:CheckedChanged += System.EventHandler{ SELF , @cbVulcanVOCheckedChanged() }

SELF:ocbVulcanVO:TabIndex := 5

SELF:ogbCategories:Controls:Add(SELF:ocbVulcanVO)

SELF:ocbSoftware := System.Windows.Forms.CheckBox{}

SELF:ocbSoftware:Name := "cbSoftware"

SELF:ocbSoftware:Location := System.Drawing.Point{ 40 , 120 }

SELF:ocbSoftware:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbSoftware:Text := "Software :"

SELF:ocbSoftware:CheckedChanged += System.EventHandler{ SELF , @cbSoftwareCheckedChanged() }

SELF:ocbSoftware:TabIndex := 4

SELF:ogbCategories:Controls:Add(SELF:ocbSoftware)

SELF:ocbTechnical := System.Windows.Forms.CheckBox{}

SELF:ocbTechnical:Name := "cbTechnical"

SELF:ocbTechnical:Location := System.Drawing.Point{ 40 , 96 }

SELF:ocbTechnical:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbTechnical:Text := "Technical :"

SELF:ocbTechnical:CheckedChanged += System.EventHandler{ SELF , @cbTechnicalCheckedChanged() }

SELF:ocbTechnical:TabIndex := 3

SELF:ogbCategories:Controls:Add(SELF:ocbTechnical)

SELF:ocbFinance := System.Windows.Forms.CheckBox{}

SELF:ocbFinance:Name := "cbFinance"

SELF:ocbFinance:Location := System.Drawing.Point{ 40 , 72 }

SELF:ocbFinance:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbFinance:Text := "Finance :"

SELF:ocbFinance:CheckedChanged += System.EventHandler{ SELF , @cbFinanceCheckedChanged() }

SELF:ocbFinance:TabIndex := 2

SELF:ogbCategories:Controls:Add(SELF:ocbFinance)

SELF:ocbPersonal := System.Windows.Forms.CheckBox{}

SELF:ocbPersonal:Name := "cbPersonal"

SELF:ocbPersonal:Location := System.Drawing.Point{ 40 , 48 }

SELF:ocbPersonal:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbPersonal:Text := "Personal :"

SELF:ocbPersonal:CheckedChanged += System.EventHandler{ SELF , @cbPersonalCheckedChanged() }

SELF:ocbPersonal:TabIndex := 1

SELF:ogbCategories:Controls:Add(SELF:ocbPersonal)

SELF:ocbWork := System.Windows.Forms.CheckBox{}

SELF:ocbWork:Name := "cbWork"

SELF:ocbWork:Location := System.Drawing.Point{ 40 , 24 }

SELF:ocbWork:Size := System.Drawing.Size{ 104 , 24 }

SELF:ocbWork:Text := "Work :"

SELF:ocbWork:CheckedChanged += System.EventHandler{ SELF , @cbWorkCheckedChanged() }

SELF:ocbWork:TabIndex := 0

SELF:ogbCategories:Controls:Add(SELF:ocbWork)

SELF:opbClear := System.Windows.Forms.Button{}

SELF:opbClear:Name := "pbClear"

SELF:opbClear:Location := System.Drawing.Point{ 136 , 248 }

SELF:opbClear:Size := System.Drawing.Size{ 51 , 23 }

SELF:opbClear:Text := "Clear"

SELF:opbClear:Click += System.EventHandler{ SELF , @pbClearClick() }

SELF:opbClear:TabIndex := 9

SELF:ogbCategories:Controls:Add(SELF:opbClear)

SELF:olbAssocDate := System.Windows.Forms.Label{}

SELF:olbAssocDate:Name := "lbAssocDate"

SELF:olbAssocDate:Location := System.Drawing.Point{ 24 , 200 }

SELF:olbAssocDate:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbAssocDate:Text := "Assoc. Date :"

SELF:olbAssocDate:TabIndex := 23

SELF:Controls:Add(SELF:olbAssocDate)

SELF:olbBody := System.Windows.Forms.Label{}

SELF:olbBody:Name := "lbBody"

SELF:olbBody:Location := System.Drawing.Point{ 24 , 248 }

SELF:olbBody:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbBody:Text := "Body :"

SELF:olbBody:TabIndex := 19

SELF:Controls:Add(SELF:olbBody)

SELF:olbExtraDetails := System.Windows.Forms.Label{}

SELF:olbExtraDetails:Name := "lbExtraDetails"

SELF:olbExtraDetails:Location := System.Drawing.Point{ 24 , 352 }

SELF:olbExtraDetails:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbExtraDetails:Text := "Extra Details :"

SELF:olbExtraDetails:TabIndex := 21

SELF:Controls:Add(SELF:olbExtraDetails)

SELF:olbWeb := System.Windows.Forms.Label{}

SELF:olbWeb:Name := "lbWeb"

SELF:olbWeb:Location := System.Drawing.Point{ 24 , 176 }

SELF:olbWeb:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbWeb:Text := "web :"

SELF:olbWeb:TabIndex := 17

SELF:Controls:Add(SELF:olbWeb)

SELF:olbPhone := System.Windows.Forms.Label{}

SELF:olbPhone:Name := "lbPhone"

SELF:olbPhone:Location := System.Drawing.Point{ 24 , 56 }

SELF:olbPhone:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbPhone:Text := "phone :"

SELF:olbPhone:TabIndex := 7

SELF:Controls:Add(SELF:olbPhone)

SELF:olbMobile := System.Windows.Forms.Label{}

SELF:olbMobile:Name := "lbMobile"

SELF:olbMobile:Location := System.Drawing.Point{ 24 , 128 }

SELF:olbMobile:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbMobile:Text := "mobile :"

SELF:olbMobile:TabIndex := 13

SELF:Controls:Add(SELF:olbMobile)

SELF:olbEmail := System.Windows.Forms.Label{}

SELF:olbEmail:Name := "lbEmail"

SELF:olbEmail:Location := System.Drawing.Point{ 24 , 152 }

SELF:olbEmail:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbEmail:Text := "email :"

SELF:olbEmail:TabIndex := 15

SELF:Controls:Add(SELF:olbEmail)

SELF:olbPerson := System.Windows.Forms.Label{}

SELF:olbPerson:Name := "lbPerson"

SELF:olbPerson:Location := System.Drawing.Point{ 24 , 104 }

SELF:olbPerson:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbPerson:Text := "Person :"

SELF:olbPerson:TabIndex := 11

SELF:Controls:Add(SELF:olbPerson)

SELF:olbCompany := System.Windows.Forms.Label{}

SELF:olbCompany:Name := "lbCompany"

SELF:olbCompany:Location := System.Drawing.Point{ 24 , 80 }

SELF:olbCompany:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbCompany:Text := "Company :"

SELF:olbCompany:TabIndex := 9

SELF:Controls:Add(SELF:olbCompany)

SELF:olbSubHead := System.Windows.Forms.Label{}

SELF:olbSubHead:Name := "lbSubHead"

SELF:olbSubHead:Location := System.Drawing.Point{ 24 , 32 }

SELF:olbSubHead:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbSubHead:Text := "Sub Head :"

SELF:olbSubHead:TabIndex := 6

SELF:Controls:Add(SELF:olbSubHead)

SELF:olbMainHead := System.Windows.Forms.Label{}

SELF:olbMainHead:Name := "lbMainHead"

SELF:olbMainHead:Location := System.Drawing.Point{ 24 , 8 }

SELF:olbMainHead:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbMainHead:Text := "Main Head :"

SELF:olbMainHead:TabIndex := 5

SELF:Controls:Add(SELF:olbMainHead)

SELF:opbOK := System.Windows.Forms.Button{}

SELF:opbOK:Name := "pbOK"

SELF:opbOK:Location := System.Drawing.Point{ 616 , 408 }

SELF:opbOK:Size := System.Drawing.Size{ 51 , 23 }

SELF:opbOK:Text := "OK"

SELF:opbOK:Click += System.EventHandler{ SELF , @pbOKClick() }

SELF:opbOK:TabIndex := 0

SELF:Controls:Add(SELF:opbOK)

SELF:ogbCategories:ResumeLayout()

SELF:ResumeLayout()

RETURN

METHOD fmUltraAddEditLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

oParent:= (fmUltraFile2008)SELF:Owner

//MessageBox.Show(oParent:ToString())

SELF:PopulateFieldsFromCard()

RETURN

METHOD ViewModeOnly() AS VOID

SELF:Text := "Ultra File 2008 : View Mode :"

SELF:opbRefreshTable:Hide()

SELF:opbClear:Hide()

SELF:otbMainHead:Enabled := FALSE

SELF:otbSubHead:Enabled := FALSE

SELF:otbPerson:Enabled := FALSE

SELF:otbCompany:Enabled := FALSE

SELF:otbPhone:Enabled := FALSE

SELF:otbWeb:Enabled := FALSE

SELF:otbEmail:Enabled := FALSE

SELF:otbMobile:Enabled := FALSE

SELF:ortbDetails:Enabled := FALSE

SELF:ortbBody:Enabled := FALSE

SELF:omtbCashAmmount:Enabled := FALSE

SELF:odtpUltraAddEdit:Enabled := FALSE

SELF:ocbGovernment:Enabled := FALSE

SELF:ocbHardware:Enabled := FALSE

SELF:ocbIT:Enabled := FALSE

SELF:ocbVulcanVO:Enabled := FALSE

SELF:ocbSoftware:Enabled := FALSE

SELF:ocbTechnical:Enabled := FALSE

SELF:ocbFinance:Enabled := FALSE

SELF:ocbPersonal:Enabled := FALSE

SELF:ocbWork:Enabled := FALSE

RETURN

METHOD cbGovernmentCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbHardwareCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbITCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbVulcanVOCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbSoftwareCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbTechnicalCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbFinanceCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbPersonalCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD cbWorkCheckedChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

// Add event handling code here

RETURN

METHOD rtbDetailsTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD rtbBodyTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD dtpUltraAddEditTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbWebTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbEmailTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbMobileTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbPersonTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbCompanyTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbPhoneTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbSubHeadTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD tbMainHeadTextChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoEditChanges()

RETURN

METHOD DoEditChanges() AS VOID

RETURN

METHOD pbClearClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoClearCategories()

RETURN

METHOD DoClearCategories() AS VOID

ocbSoftware:Checked := FALSE

ocbGovernment:Checked := FALSE

ocbHardware:Checked := FALSE

ocbIT:Checked := FALSE

ocbVulcanVO:Checked := FALSE

ocbTechnical:Checked := FALSE

ocbFinance:Checked := FALSE

ocbPersonal:Checked := FALSE

ocbWork:Checked := FALSE

RETURN

METHOD pbRefreshTableClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoRefreshTable()

RETURN

METHOD DoRefreshTable() AS VOID

SELF:RePopulateFocusCard()

IF oParent:oFocusCard:newcard

SELF:oParent:DoInsertNewCardObject()

ELSE

SELF:oParent:DoUpdateNewCardObject()

ENDIF

SELF:oParent:FillListWithAllCards()

SELF:Close()

RETURN

METHOD RePopulateFocusCard() AS VOID

SELF:oParent:oFocusCard:datetimestamp := DateTime.Now

SELF:oParent:oFocusCard:mainhead := SELF:otbMainHead:Text:Trim()

SELF:oParent:oFocusCard:subhead := SELF:otbSubHead:Text:Trim()

SELF:oParent:oFocusCard:body := SELF:ortbBody:Text:Trim()

SELF:oParent:oFocusCard:extras := SELF:ortbDetails:Text:Trim()

SELF:oParent:oFocusCard:phone := SELF:otbPhone:Text:Trim()

SELF:oParent:oFocusCard:mobile := SELF:otbMobile:Text:Trim()

SELF:oParent:oFocusCard:email := SELF:otbEmail:Text:Trim()

SELF:oParent:oFocusCard:web := SELF:otbWeb:Text:Trim()

SELF:oParent:oFocusCard:company := SELF:otbCompany:Text:Trim()

SELF:oParent:oFocusCard:person := SELF:otbPerson:Text:Trim()

SELF:oParent:oFocusCard:government := ocbGovernment:Checked

SELF:oParent:oFocusCard:hardware := ocbHardware:Checked

SELF:oParent:oFocusCard:it := ocbIT:Checked

SELF:oParent:oFocusCard:vulcanvo := ocbVulcanVO:Checked

SELF:oParent:oFocusCard:software := ocbSoftware:Checked

SELF:oParent:oFocusCard:technical := ocbTechnical:Checked

SELF:oParent:oFocusCard:finance := ocbFinance:Checked

SELF:oParent:oFocusCard:personal := ocbPersonal:Checked

SELF:oParent:oFocusCard:work := ocbWork:Checked

SELF:oParent:oFocusCard:AssocDate := odtpUltraAddEdit:Value

SELF:oParent:oFocusCard:CashAmount := (REAL4)conversion.val(omtbCashAmmount:Text)

RETURN

METHOD pbOKClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:Close()

RETURN

END CLASS

CLASS UltraCard

EXPORT cardID AS INT

EXPORT datetimestamp AS datetime

//---------- now use defined -----------

EXPORT mainhead AS STRING

EXPORT subhead AS STRING

EXPORT phone AS STRING

EXPORT company AS STRING

EXPORT person AS STRING

EXPORT mobile AS STRING

EXPORT email AS STRING

EXPORT web AS STRING

EXPORT AssocDate AS DateTime

EXPORT CashAmount AS REAL4

EXPORT body AS STRING

EXPORT extras AS STRING

EXPORT work AS Boolean

EXPORT personal AS Boolean

EXPORT finance AS Boolean

EXPORT technical AS Boolean

EXPORT software AS Boolean

EXPORT vulcanvo AS Boolean

EXPORT it AS Boolean

EXPORT hardware AS Boolean

EXPORT government AS Boolean

EXPORT editchange AS Boolean

EXPORT newcard AS Boolean

CONSTRUCTOR()

SUPER()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

cardID := 9999

datetimestamp := DateTime.Now

mainhead := ""

subhead := ""

phone := ""

company := ""

person := ""

mobile := ""

email := ""

web := ""

assocdate := DateTime.Now

cashamount := (REAL4)0.00

body := ""

extras := ""

work := FALSE

personal := FALSE

finance := FALSE

technical := FALSE

software := FALSE

vulcanvo := FALSE

it := FALSE

hardware := FALSE

government := FALSE

editchange := FALSE

newcard := FALSE

RETURN

END CLASS

CLASS fmConnection INHERIT System.Windows.Forms.Form

PROTECT odgvConnection AS System.Windows.Forms.DataGridView

PROTECT ogbServerDetails AS System.Windows.Forms.GroupBox

PROTECT ocbTimeOut AS System.Windows.Forms.ComboBox

PROTECT ocbIntSecurity AS System.Windows.Forms.ComboBox

PROTECT ocbProviders AS System.Windows.Forms.ComboBox

PROTECT ocbWindowsUserAccess AS System.Windows.Forms.CheckBox

PROTECT olbInstanceName AS System.Windows.Forms.Label

PROTECT olbTimeout AS System.Windows.Forms.Label

PROTECT olbIntSecurity AS System.Windows.Forms.Label

PROTECT olbPassword AS System.Windows.Forms.Label

PROTECT olbUserName AS System.Windows.Forms.Label

PROTECT olbServerName AS System.Windows.Forms.Label

PROTECT olbProvider AS System.Windows.Forms.Label

PROTECT otbInstanceName AS System.Windows.Forms.TextBox

PROTECT otbPassWord AS System.Windows.Forms.TextBox

PROTECT otbUserName AS System.Windows.Forms.TextBox

PROTECT otbServerName AS System.Windows.Forms.TextBox

PROTECT olbFolder AS System.Windows.Forms.Label

PROTECT olbFilename AS System.Windows.Forms.Label

PROTECT opbViewConnectionString AS System.Windows.Forms.Button

PROTECT opbTextConnection AS System.Windows.Forms.Button

PROTECT opbSerializeToFile AS System.Windows.Forms.Button

PROTECT opbLoadFromXML AS System.Windows.Forms.Button

PROTECT opbViewXMLfile AS System.Windows.Forms.Button

PROTECT opbCheckFile AS System.Windows.Forms.Button

PROTECT opbSelectFolder AS System.Windows.Forms.Button

PROTECT otbFolder AS System.Windows.Forms.TextBox

PROTECT otbFilename AS System.Windows.Forms.TextBox

PROTECT ortbConnection AS System.Windows.Forms.RichTextBox

PROTECT opbOK AS System.Windows.Forms.Button

PROTECT ogbDatabase AS System.Windows.Forms.GroupBox

// User code starts here (DO NOT remove this line) ##USER##

PROTECT cPath, cFile AS STRING

PROTECT oParent AS fmUltraFile2008

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

ocbWindowsUserAccess:Checked := TRUE

otbPassWord:Enabled := FALSE

otbUserName:Enabled := FALSE

SELF:DisableServerDetails()

odgvConnection:DataSource := DbProviderFactories.GetFactoryClasses()

LOCAL nLen, i, nPointer AS DWORD

nLen := (DWORD)odgvConnection:RowCount-1

LOCAL aProviders AS STRING[]

aProviders := STRING[]{(INT)nLen}

nPointer := 0

FOR i := 0 UPTO nLen-1

aProviders[i+1] := (STRING)odgvConnection["name",(INT)i]:Value

IF "SqlClient Data Provider" = aProviders[i+1]

nPointer := i

ENDIF

NEXT

LOCAL nIndex AS INT

LOCAL cprovider AS STRING

FOR i := 1 UPTO nLen

cProvider := aProviders[i]:Trim()

nIndex := cProvider:IndexOf(" Data ")

cProvider := cProvider:Substring(0,nIndex):Trim()

aProviders[i] := cProvider

NEXT

ocbProviders:Items:Clear()

ocbProviders:Items:AddRange(aProviders)

ocbProviders:SelectedIndex := (INT)nPointer

LOCAL aTO AS INT[]

aTO := <INT>{0,5,10,15,20,25,30}

ocbTimeOut:Items:AddRange(aTO)

ocbTimeOut:SelectedIndex := 0

LOCAL aIS AS STRING[]

aIS := <STRING>{"True","SSPI"}

ocbIntSecurity:Items:AddRange(aIS)

ocbIntSecurity:SelectedIndex := 1

RETURN

METHOD DisableServerDetails() AS VOID

RETURN

METHOD EnableServerDetails() AS VOID

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

SELF:Name := "fmConnection"

SELF:SuspendLayout()

SELF:Location := System.Drawing.Point{100,100}

SELF:ClientSize := System.Drawing.Size{498,382}

SELF:Text := "Ultra Card 2008 : Connection Details :"

SELF:Load += System.EventHandler{ SELF , @fmConnectionLoad() }

SELF:odgvConnection := System.Windows.Forms.DataGridView{}

SELF:odgvConnection:Name := "dgvConnection"

SELF:odgvConnection:Location := System.Drawing.Point{ 8 , 376 }

SELF:odgvConnection:Size := System.Drawing.Size{ 488 , 8 }

SELF:odgvConnection:TabIndex := 11

SELF:odgvConnection:Visible := FALSE

SELF:odgvConnection:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:odgvConnection)

SELF:ogbServerDetails := System.Windows.Forms.GroupBox{}

SELF:ogbServerDetails:Name := "gbServerDetails"

SELF:ogbServerDetails:SuspendLayout()

SELF:ogbServerDetails:Location := System.Drawing.Point{ 8 , 104 }

SELF:ogbServerDetails:Size := System.Drawing.Size{ 368 , 216 }

SELF:ogbServerDetails:Text := "Server Details :"

SELF:ogbServerDetails:TabIndex := 10

SELF:Controls:Add(SELF:ogbServerDetails)

SELF:ocbTimeOut := System.Windows.Forms.ComboBox{}

SELF:ocbTimeOut:Name := "cbTimeOut"

SELF:ocbTimeOut:Location := System.Drawing.Point{ 280 , 152 }

SELF:ocbTimeOut:Size := System.Drawing.Size{ 72 , 21 }

SELF:ocbTimeOut:TabIndex := 10

SELF:ogbServerDetails:Controls:Add(SELF:ocbTimeOut)

SELF:ocbIntSecurity := System.Windows.Forms.ComboBox{}

SELF:ocbIntSecurity:Name := "cbIntSecurity"

SELF:ocbIntSecurity:Location := System.Drawing.Point{ 104 , 152 }

SELF:ocbIntSecurity:Size := System.Drawing.Size{ 72 , 21 }

SELF:ocbIntSecurity:TabIndex := 8

SELF:ogbServerDetails:Controls:Add(SELF:ocbIntSecurity)

SELF:ocbProviders := System.Windows.Forms.ComboBox{}

SELF:ocbProviders:Name := "cbProviders"

SELF:ocbProviders:Location := System.Drawing.Point{ 104 , 24 }

SELF:ocbProviders:Size := System.Drawing.Size{ 248 , 21 }

SELF:ocbProviders:TabIndex := 11

SELF:ogbServerDetails:Controls:Add(SELF:ocbProviders)

SELF:ocbWindowsUserAccess := System.Windows.Forms.CheckBox{}

SELF:ocbWindowsUserAccess:Name := "cbWindowsUserAccess"

SELF:ocbWindowsUserAccess:Location := System.Drawing.Point{ 104 , 80 }

SELF:ocbWindowsUserAccess:Size := System.Drawing.Size{ 144 , 24 }

SELF:ocbWindowsUserAccess:Text := "Windows User Access :"

SELF:ocbWindowsUserAccess:TabIndex := 14

SELF:ogbServerDetails:Controls:Add(SELF:ocbWindowsUserAccess)

SELF:olbInstanceName := System.Windows.Forms.Label{}

SELF:olbInstanceName:Name := "lbInstanceName"

SELF:olbInstanceName:Location := System.Drawing.Point{ 16 , 184 }

SELF:olbInstanceName:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbInstanceName:Text := "Instance name :"

SELF:olbInstanceName:TabIndex := 13

SELF:ogbServerDetails:Controls:Add(SELF:olbInstanceName)

SELF:olbTimeout := System.Windows.Forms.Label{}

SELF:olbTimeout:Name := "lbTimeout"

SELF:olbTimeout:Location := System.Drawing.Point{ 216 , 152 }

SELF:olbTimeout:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbTimeout:Text := "Timeout :"

SELF:olbTimeout:TabIndex := 11

SELF:ogbServerDetails:Controls:Add(SELF:olbTimeout)

SELF:olbIntSecurity := System.Windows.Forms.Label{}

SELF:olbIntSecurity:Name := "lbIntSecurity"

SELF:olbIntSecurity:Location := System.Drawing.Point{ 16 , 152 }

SELF:olbIntSecurity:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbIntSecurity:Text := "Int. Security :"

SELF:olbIntSecurity:TabIndex := 9

SELF:ogbServerDetails:Controls:Add(SELF:olbIntSecurity)

SELF:olbPassword := System.Windows.Forms.Label{}

SELF:olbPassword:Name := "lbPassword"

SELF:olbPassword:Location := System.Drawing.Point{ 16 , 128 }

SELF:olbPassword:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbPassword:Text := "Password :"

SELF:olbPassword:TabIndex := 7

SELF:ogbServerDetails:Controls:Add(SELF:olbPassword)

SELF:olbUserName := System.Windows.Forms.Label{}

SELF:olbUserName:Name := "lbUserName"

SELF:olbUserName:Location := System.Drawing.Point{ 16 , 104 }

SELF:olbUserName:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbUserName:Text := "User name :"

SELF:olbUserName:TabIndex := 5

SELF:ogbServerDetails:Controls:Add(SELF:olbUserName)

SELF:olbServerName := System.Windows.Forms.Label{}

SELF:olbServerName:Name := "lbServerName"

SELF:olbServerName:Location := System.Drawing.Point{ 16 , 56 }

SELF:olbServerName:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbServerName:Text := "Server name :"

SELF:olbServerName:TabIndex := 0

SELF:ogbServerDetails:Controls:Add(SELF:olbServerName)

SELF:olbProvider := System.Windows.Forms.Label{}

SELF:olbProvider:Name := "lbProvider"

SELF:olbProvider:Location := System.Drawing.Point{ 16 , 24 }

SELF:olbProvider:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbProvider:Text := "Provider :"

SELF:olbProvider:TabIndex := 12

SELF:ogbServerDetails:Controls:Add(SELF:olbProvider)

SELF:otbInstanceName := System.Windows.Forms.TextBox{}

SELF:otbInstanceName:Name := "tbInstanceName"

SELF:otbInstanceName:Location := System.Drawing.Point{ 104 , 184 }

SELF:otbInstanceName:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbInstanceName:Text := "... << instance >> ..."

SELF:otbInstanceName:TabIndex := 12

SELF:ogbServerDetails:Controls:Add(SELF:otbInstanceName)

SELF:otbPassWord := System.Windows.Forms.TextBox{}

SELF:otbPassWord:Name := "tbPassWord"

SELF:otbPassWord:Location := System.Drawing.Point{ 104 , 128 }

SELF:otbPassWord:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbPassWord:Text := "... << pass word >> ..."

SELF:otbPassWord:TabIndex := 6

SELF:ogbServerDetails:Controls:Add(SELF:otbPassWord)

SELF:otbUserName := System.Windows.Forms.TextBox{}

SELF:otbUserName:Name := "tbUserName"

SELF:otbUserName:Location := System.Drawing.Point{ 104 , 104 }

SELF:otbUserName:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbUserName:Text := "... << user name >> ..."

SELF:otbUserName:TabIndex := 4

SELF:ogbServerDetails:Controls:Add(SELF:otbUserName)

SELF:otbServerName := System.Windows.Forms.TextBox{}

SELF:otbServerName:Name := "tbServerName"

SELF:otbServerName:Location := System.Drawing.Point{ 104 , 56 }

SELF:otbServerName:Size := System.Drawing.Size{ 136 , 20 }

SELF:otbServerName:Text := "... << server name >> ..."

SELF:otbServerName:TabIndex := 8

SELF:ogbServerDetails:Controls:Add(SELF:otbServerName)

SELF:olbFolder := System.Windows.Forms.Label{}

SELF:olbFolder:Name := "lbFolder"

SELF:olbFolder:Location := System.Drawing.Point{ 24 , 56 }

SELF:olbFolder:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbFolder:Text := "Folder :"

SELF:olbFolder:TabIndex := 7

SELF:Controls:Add(SELF:olbFolder)

SELF:olbFilename := System.Windows.Forms.Label{}

SELF:olbFilename:Name := "lbFilename"

SELF:olbFilename:Location := System.Drawing.Point{ 24 , 24 }

SELF:olbFilename:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbFilename:Text := "Filename :"

SELF:olbFilename:TabIndex := 6

SELF:Controls:Add(SELF:olbFilename)

SELF:opbViewConnectionString := System.Windows.Forms.Button{}

SELF:opbViewConnectionString:Name := "pbViewConnectionString"

SELF:opbViewConnectionString:Location := System.Drawing.Point{ 128 , 344 }

SELF:opbViewConnectionString:Size := System.Drawing.Size{ 152 , 23 }

SELF:opbViewConnectionString:Text := "View Connection String"

SELF:opbViewConnectionString:Click += System.EventHandler{ SELF , @pbViewConnectionStringClick() }

SELF:opbViewConnectionString:TabIndex := 2

SELF:opbViewConnectionString:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left

SELF:Controls:Add(SELF:opbViewConnectionString)

SELF:opbTextConnection := System.Windows.Forms.Button{}

SELF:opbTextConnection:Name := "pbTextConnection"

SELF:opbTextConnection:Location := System.Drawing.Point{ 8 , 344 }

SELF:opbTextConnection:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbTextConnection:Text := "Test Connection"

SELF:opbTextConnection:Click += System.EventHandler{ SELF , @pbTextConnectionClick() }

SELF:opbTextConnection:TabIndex := 3

SELF:opbTextConnection:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left

SELF:Controls:Add(SELF:opbTextConnection)

SELF:opbSerializeToFile := System.Windows.Forms.Button{}

SELF:opbSerializeToFile:Name := "pbSerializeToFile"

SELF:opbSerializeToFile:Location := System.Drawing.Point{ 384 , 296 }

SELF:opbSerializeToFile:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbSerializeToFile:Text := "Serialize to File"

SELF:opbSerializeToFile:Click += System.EventHandler{ SELF , @pbSerializeToFileClick() }

SELF:opbSerializeToFile:TabIndex := 12

SELF:opbSerializeToFile:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbSerializeToFile)

SELF:opbLoadFromXML := System.Windows.Forms.Button{}

SELF:opbLoadFromXML:Name := "pbLoadFromXML"

SELF:opbLoadFromXML:Location := System.Drawing.Point{ 384 , 264 }

SELF:opbLoadFromXML:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbLoadFromXML:Text := "Load from XML"

SELF:opbLoadFromXML:Click += System.EventHandler{ SELF , @pbLoadFromXMLClick() }

SELF:opbLoadFromXML:TabIndex := 14

SELF:opbLoadFromXML:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbLoadFromXML)

SELF:opbViewXMLfile := System.Windows.Forms.Button{}

SELF:opbViewXMLfile:Name := "pbViewXMLfile"

SELF:opbViewXMLfile:Location := System.Drawing.Point{ 384 , 232 }

SELF:opbViewXMLfile:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbViewXMLfile:Text := "View XML File"

SELF:opbViewXMLfile:Click += System.EventHandler{ SELF , @pbViewXMLfileClick() }

SELF:opbViewXMLfile:TabIndex := 13

SELF:opbViewXMLfile:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbViewXMLfile)

SELF:opbCheckFile := System.Windows.Forms.Button{}

SELF:opbCheckFile:Name := "pbCheckFile"

SELF:opbCheckFile:Location := System.Drawing.Point{ 392 , 88 }

SELF:opbCheckFile:Size := System.Drawing.Size{ 88 , 23 }

SELF:opbCheckFile:Text := "Check File"

SELF:opbCheckFile:Click += System.EventHandler{ SELF , @pbCheckFileClick() }

SELF:opbCheckFile:TabIndex := 1

SELF:opbCheckFile:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbCheckFile)

SELF:opbSelectFolder := System.Windows.Forms.Button{}

SELF:opbSelectFolder:Name := "pbSelectFolder"

SELF:opbSelectFolder:Location := System.Drawing.Point{ 392 , 56 }

SELF:opbSelectFolder:Size := System.Drawing.Size{ 88 , 23 }

SELF:opbSelectFolder:Text := "Select Folder"

SELF:opbSelectFolder:Click += System.EventHandler{ SELF , @pbSelectFolderClick() }

SELF:opbSelectFolder:TabIndex := 11

SELF:opbSelectFolder:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbSelectFolder)

SELF:otbFolder := System.Windows.Forms.TextBox{}

SELF:otbFolder:Name := "tbFolder"

SELF:otbFolder:Location := System.Drawing.Point{ 120 , 56 }

SELF:otbFolder:Size := System.Drawing.Size{ 240 , 20 }

SELF:otbFolder:Text := "... << folder string >> ..."

SELF:otbFolder:TabIndex := 5

SELF:Controls:Add(SELF:otbFolder)

SELF:otbFilename := System.Windows.Forms.TextBox{}

SELF:otbFilename:Name := "tbFilename"

SELF:otbFilename:Location := System.Drawing.Point{ 120 , 24 }

SELF:otbFilename:Size := System.Drawing.Size{ 120 , 20 }

SELF:otbFilename:Text := "... << file name >> ..."

SELF:otbFilename:TabIndex := 4

SELF:Controls:Add(SELF:otbFilename)

SELF:ortbConnection := System.Windows.Forms.RichTextBox{}

SELF:ortbConnection:Name := "rtbConnection"

SELF:ortbConnection:Location := System.Drawing.Point{ 384 , 120 }

SELF:ortbConnection:Size := System.Drawing.Size{ 112 , 104 }

SELF:ortbConnection:TabIndex := 15

SELF:ortbConnection:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:ortbConnection)

SELF:opbOK := System.Windows.Forms.Button{}

SELF:opbOK:Name := "pbOK"

SELF:opbOK:Location := System.Drawing.Point{ 440 , 344 }

SELF:opbOK:Size := System.Drawing.Size{ 51 , 23 }

SELF:opbOK:Text := "OK"

SELF:opbOK:Click += System.EventHandler{ SELF , @pbOKClick() }

SELF:opbOK:TabIndex := 0

SELF:opbOK:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbOK)

SELF:ogbDatabase := System.Windows.Forms.GroupBox{}

SELF:ogbDatabase:Name := "gbDatabase"

SELF:ogbDatabase:SuspendLayout()

SELF:ogbDatabase:Location := System.Drawing.Point{ 8 , 8 }

SELF:ogbDatabase:Size := System.Drawing.Size{ 368 , 83 }

SELF:ogbDatabase:Text := "Database :"

SELF:ogbDatabase:TabIndex := 9

SELF:Controls:Add(SELF:ogbDatabase)

SELF:ogbDatabase:ResumeLayout()

SELF:ogbServerDetails:ResumeLayout()

SELF:ResumeLayout()

RETURN

METHOD pbLoadFromXMLClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoLoadFromXML()

SELF:PopulateDetailsFromObject()

RETURN

METHOD PopulateDetailsFromObject() AS VOID

otbFilename:Text := SELF:oParent:oConnDetails:cFile

otbFolder:Text := SELF:oParent:oConnDetails:cFolder

ocbProviders:Text := SELF:oParent:oConnDetails:cProvider

otbServerName:Text := SELF:oParent:oConnDetails:cServerName

ocbIntSecurity:Text := SELF:oParent:oConnDetails:cSecurity

ocbTimeOut:Text := SELF:oParent:oConnDetails:nTimeOut:ToString()

otbUserName:Text := SELF:oParent:oConnDetails:cUserName

otbPassWord:Text := SELF:oParent:oConnDetails:cPassWord

ocbWindowsUserAccess:Checked := SELF:oParent:oConnDetails:lWindowsUserAccess

otbInstanceName:Text := SELF:oParent:oConnDetails:cInstance

RETURN

METHOD DoLoadFromXML()AS VOID

LOCAL serial AS Serialization.XmlSerializer

LOCAL oSR AS StreamReader

LOCAL cFile, cText, cHead AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "Existing file will be read!"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

//----- read object from XML file -----

serial := Serialization.XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFile}

SELF:oParent:oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

oSR := NULL_OBJECT

serial := NULL_OBJECT

ELSE

cText := "File can't be found !?"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

ENDIF

RETURN

METHOD fmConnectionLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent := (fmUltraFile2008)SELF:Owner

SELF:PopulateDetailsFromObject()

RETURN

METHOD pbViewXMLfileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cFile AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF !File.Exists(cFile)

RETURN

ENDIF

//============================

/*LOCAL oProcess AS Process

oProcess := Process{}

oProcess:StartInfo:FileName := cFile

oProcess:StartInfo:WorkingDirectory := ""

oProcess:StartInfo:UseShellExecute := TRUE

oProcess:Start()

//MessageBox.Show("Close this to terminate the Shell Execute !")

IF ! oProcess:HasExited

oProcess:CloseMainWindow()

oProcess:Close()

ENDIF

oProcess := NULL_OBJECT */

LOCAL cXML, cLine AS STRING

cXML := ""

LOCAL oSR AS StreamReader

oSR := StreamReader{cFile}

DO WHILE .T.

cLine := oSR:Readline()

IF cLine = NULL_OBJECT

EXIT

ENDIF

cXML += cLine+CRLF

ENDDO

oSR:Close()

oSR:Dispose()

oSR := NULL_OBJECT

SELF:ortbConnection:Text := cXML

RETURN

METHOD pbSerializeToFileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoSerialization2File()

RETURN

METHOD DoSerialization2File() AS VOID

SELF:DoForm2Object()

LOCAL serial AS Serialization.XmlSerializer

LOCAL oSW AS StreamWriter

LOCAL cFile, cText, cHead AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "File exists already!"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

ENDIF

//----- save object to XML file -----

serial := Serialization.XmlSerializer{typeof(ConnectionDetails)}

oSW := StreamWriter{cFile}

serial:Serialize(oSW, SELF:oParent:oConnDetails)

oSW:Close()

oSW := NULL_OBJECT

serial := NULL_OBJECT

RETURN

METHOD DoForm2Object() AS VOID

SELF:oParent:oConnDetails:cFile := otbFilename:Text:Trim()

SELF:oParent:oConnDetails:cFolder := otbFolder:Text:Trim()

SELF:oParent:oConnDetails:cFilePath := otbFolder:Text:Trim()+otbFilename:Text:Trim()

SELF:oParent:oConnDetails:cProvider := ocbProviders:Text:Trim()

SELF:oParent:oConnDetails:cServerName := otbServerName:Text:Trim()

SELF:oParent:oConnDetails:cSecurity := ocbIntSecurity:Text:Trim()

SELF:oParent:oConnDetails:nTimeOut := (INT)Conversion.Val(ocbTimeOut:Text)

SELF:oParent:oConnDetails:cUserName := otbUserName:Text:Trim()

SELF:oParent:oConnDetails:cPassWord := otbPassWord:Text:Trim()

SELF:oParent:oConnDetails:lWindowsUserAccess := ocbWindowsUserAccess:Checked

SELF:oParent:oConnDetails:cInstance := otbInstanceName:Text:Trim()

//SELF:oParent:oConnDetails:cConnectionString

RETURN

METHOD pbSelectFolderClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoSelectFolder()

RETURN

METHOD DoSelectFolder() AS VOID

LOCAL oFBD AS FolderBrowserDialog

oFBD := FolderBrowserDialog{}

oFBD:ShowNewFolderButton := FALSE

oFBD:RootFolder := Environment.SpecialFolder.Desktop

oFBD:ShowDialog()

SELF:cPath := oFBD:SelectedPath+"\"

otbFolder:Text := cPath

oFBD := NULL_OBJECT

RETURN

METHOD pbViewConnectionStringClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cText AS STRING

cText := "Test the Connection FIRST, the try again !"

IF SELF:oParent:oConnDetails:cConnectionString:Trim():Length < 13

MessageBox.Show(SELF,cText,"WARNING :")

ELSE

MessageBox.Show(SELF:oParent:oConnDetails:cConnectionString)

ENDIF

RETURN

METHOD pbTextConnectionClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent:DoSetConnectionString()

LOCAL cResult AS STRING

IF SELF:oParent:DoTryToConnect()

cResult := "Connection : - State = Open !"

SELF:oParent:lConnectionTestedOK := TRUE

ELSE

cResult := "Connection : - State = Closed ??"

SELF:oParent:lConnectionTestedOK := FALSE

ENDIF

MessageBox.Show(SELF,cResult,"Attempt to Connect :")

SELF:oParent:DoForceDisconnect()

RETURN

METHOD pbCheckFileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cFile, cText, cHead AS STRING

//cFile := SELF:oParent:oConnDetails:cFilePath:Trim()

cFile := otbFolder:Text:Trim()+otbFilename:Text:Trim()

IF File.Exists(cFile)

cText := "Yes, file exists !"+CRLF

cText += cFile+CRLF

cHead := "File Check - Ultra :"

MessageBox.Show(SELF,cText,cHead)

ENDIF

RETURN

METHOD pbOKClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:Close()

RETURN

END CLASS

[Serializable]CLASS ConnectionDetails

EXPORT cFile AS STRING

EXPORT cFolder AS STRING

EXPORT cFilePath AS STRING

EXPORT cProvider AS STRING

EXPORT cServerName AS STRING

EXPORT cSecurity AS STRING

EXPORT nTimeOut AS INT

EXPORT cUserName AS STRING

EXPORT cPassWord AS STRING

EXPORT lWindowsUserAccess AS Boolean

EXPORT cInstance AS STRING

EXPORT cConnectionString AS STRING

CONSTRUCTOR()

SUPER()

cFile := ""

cFolder := ""

cFilePath := ""

cProvider := ""

cServerName := ""

cSecurity := ""

nTimeOut := 0

cUserName := ""

cPassWord := ""

lWindowsUserAccess := TRUE

cInstance := ""

cConnectionString := ""

RETURN

END CLASS


Section 3 - A 'full' application

This project will show a full working project. What I mean by this is that it is a project named 'BEST company' which is based on a number of related tables in the database, and it fully functions as a small application.

The code within the application shows about as much variety and scope in the SQL techniques applied, as it is possible to incorporate into one small project / application.

At all turns I have tried to use the dotNet classes to good effect, and also incorporate as wide a range of Form 2.0 controls as possible. So you will see in use, list boxes, combo boxes, ListView controls and DataGridView controls - all showing data being displayed after retrieval from the SQL server. OOP as been used whenever, and wherever, possible.

For many cases the SQL statement 'text' is within the Vulcan code in the application itself, rather than being stored in the database as 'Stored Procedures' (SP). When I have used a stored procedure, I have included a method to create and update the SP, so you may see in full the SQL statement text used.

We can worry about streamlining things, and building better techniques, once we have the basics.

This application - called 'BEST company - uses each of the CRUD operations within it, as well as at least one Stored Procedure.

Different forms of the SELECT statement are coded and used, and a feature I have 'built-in' is for the user to be able to 'see' the query statement just before it is sent to the server.

On each form there is a check box which allows the user to turn this feature off if they so wish - see below for an example :-


Describing the Project

When I started out on the SQL path to learning, some five weeks ago now, I sat through the Bob Tabor video sessions and picked up a great deal. It seemed sensible to me therefore, that the Company database which he used to explore SQL and MS in the videos, should be used as a basis of a small, but fully working application written in Vulcan code.

I appreciate that to take it as far as I have, or as I wanted to, means the original table structure is not really good enough - but we can get some useful mileage out of using it, as it will be very familiar to all who watch the videos.

I have taken this free download database and renamed it 'BEST company', at the same time as including a few extra details to make the application more realistic. However, I have taken great care to not break backward compatibility, so that all the queries he does, will still work on the database I provide with 'ClickStart SQL'.

I have included an extra table called 'DeliveryAddresses' and added an extra column in the Orders table so that the DeliveryAddressID is stored in the order record, if an alternative delivery address is required.

Because of wishing to alter as little as possible, I have made the Invoice and Dispatch Note features work on the order details, and no extra table exists for them after creation and printing. So we don't actually record the fact that a print out has been made. Not very true to real life on that one :-)

However, you will be pleased to know that a colour print out is possible, as well as a print preview. Note however, it is simple (but effective) approach to printing. If you want more then get ClickStartPnP when it is available - 'Printers and Printing'.

The image shown below, is of the 'front screen' of this application, and you will see that the options show a close 'mapping' to the database used by Bob in the videos.

The most obvious 'extras' are Connection, Delivery Addresses, Create an Order, and Business Reports.

Oh! as well as the HELP which is behind the green question mark. Either clicking the push buttons, or double clicking the picture box areas will launch the sub-form to manage and maintain the related table(s).

When Bob Tabor made an order he used the MS shell, but we need a full GUI to handle the order items as well as the basic order details. We will see more of that later.

So to finish this introduction, the project is running a small company database system. From customers, and delivery addresses, to products and monthly reports. Invoices and dispatch notes may be printed for any order created.

I have taken the opportunity in the Business Reports section to look at some interesting extension to what may be done, and used later in other projects.

By the time we have got to this project we have developed a standard 'provider agnostic' general coding approach, and have a Connection GUI which may be used in all future projects.


Making a Connection

Since this topic is needed to be dealt with in each SQL application, it has been covered in full detail in appendix A.

The details in appendix A are all taken from this 'BEST company' project.

There is no need to say anything more.

:0)


Selecting the Database

Although this project is based on the 'Company' database which comes with the free download from Microsoft in the AB series videos, certain modifications have been made to allow the project to be more realistic.

The modified database call 'BESTcompany' is provided along with the full code and these help notes and is available from www.ClickStartVulcan.net

[ Databases and other support files may well be zipped up separately to the ClickStartSQL help file itself. ]

Any modifications have been done to improve the working of the project generally, and backwards compatibility should still be intact.

This means that all the video examples that Bob Tabor does with 'Company', will still work with 'BESTCompany'. However, we now have delivery address facilities, and any supplied and chosen delivery address is tied in with the order when created.

So, to minimize problems and errors, the 'BESTCompany' data files must be found and installed, if the Vulcan project is to work correctly.

Look at the web site show above when the ClickStartSQL notes are near to completion - this should be before the end of July 2008.

Take into account what was said in earlier parts about the folder placement of database files. Make note of you folder path and make sure you provide this in the connection string via the Connection GUI.

To test it first use the attach facility in MS and make some successful queries.

PHH - July 2008.


Working with Products

The 'BEST company' application has a separate screen for maintaining the product data file.

From here new products may be entered and exiting product data changed. It is also possible to remove (or delete) a product if referential integrity is not broken.

However, at the moment any product referred to in existing orders by the ProductID means that 'used' or previously 'sold' products will not be allowed to be removed by the SQL Server.

See the product GUI below :-

You should also realize that product types are in a separate table and each product must have an existing product type associated with it.

Obviously, product types associated with products will have to remain too, in order to keep referential integrity.

The code is shown below and please note it is different to that shown in earlier sections, as now even the parameter is also created in the DbFactory way :-

METHOD DoAddNewProduct() AS VOID

IF SELF:AnyEmptyFields()

MessageBox.Show(SELF,"At least one field needs filling!","WARNING - Data Missing ?")

RETURN

ENDIF

//============================

SELF:MakeProductObject()

//=====================================

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Question

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageBoxDefaultButton

oDef := MessageBoxDefaultButton.Button2

LOCAL cMess, cHead AS STRING

cMess := "Create a new record in the 'Products' SQL table ?"

cHead := "CONFIRM - Create !?"

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF, cMess, cHead, oMBB, oIcon, oDef)

IF oDR = DialogResult.No

RETURN

ENDIF

//=================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [Products] ([ProductName],[ProductPrice],"

cText += "[ProductDescription],[ProductShippingOunces],[ProductTypeID])"

cText += "VALUES (@pn,@pp,@pd,@pso,@ptid);"

IF ocbDisplaySQLStatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.STRING

oParam:ParameterName := "@pn"

oParam:Value := oProd:ProductName

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@pp"

oParam:Value := oProd:ProductPrice

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.STRING

oParam:ParameterName := "@pd"

oParam:Value := oProd:ProductDescription

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@pso"

oParam:Value := oProd:ProductShippingOunces

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@ptid"

oParam:Value := oProd:ProductTypeID

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- prepare the UI again -----

SELF:oParent:DoForceDisconnect()

//----- refill the list box-----

SELF:FillProductsList()

SELF:DoResetFields()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN

We will not look in detail at the UPDATE code behind the 'save changes' button as it will be similar to that for Customers - so see that sub-section for detail if required.

What we will look at is the code to attempt to remove the product item. See the details below :-

METHOD DoAttemptRemoveProduct() AS VOID

LOCAL nRef AS INT

nRef := (INT)Conversion.VAL(otbProdID:TEXT)

IF nRef < 1

MessageBox.Show(SELF,"You need an existing product in focus !!","WARING - Reference?")

RETURN

ENDIF

MessageBox.Show(SELF,"Only products without orders may be removed !!","LIMITED - Application!")

//========================================

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Question

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageBoxDefaultButton

oDef := MessageBoxDefaultButton.Button2

LOCAL cMess, cHead AS STRING

cMess := "Delete product in focus, from the SQL table ?"+CRLF

cMess += "[ Product ID is :- "+nRef:ToString()+" ]"+CRLF

cHead := "CONFIRM - Delete !?"

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF, cMess, cHead, oMBB, oIcon, oDef)

IF oDR = DialogResult.No

RETURN

ENDIF

//=================================

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "DELETE FROM [Products] WHERE [ProductID] = @pID;"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@pID"

oParam:Value := nRef

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

SELF:oParent:DoForceDisconnect()

//----- refill the list box-----

SELF:FillProductsList()

SELF:DoResetFields()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN

You will notice that the CATCH does not do anything but the basic operation to stop the code 'failing'.

This may be a good place to add the approach developed in the 'Reports' module, where we store the details in the new 'Exception' table.

Again notice that the code used is of the 'provider agnostic' style.

Below is the relevant section from the 'Reports' module, which finds an error number and stores that along with the message in the 'exception' table.

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

//MessageBox.Show(nError:ToString())

//MessageBox.Show(DbEx:Message)

LOCAL oSED AS StoreExceptionDetails

oSED := StoreExceptionDetails{SELF:oParent}

oSED:oDbExcept := DbEx

oSED:ErrorNumber := nError

oSED:RecordInTable()

oSED := NULL_OBJECT

END TRY

Look out the full details of the error recording system in a later sub-section called 'Saving and Viewing Exceptions'.


Working with Customers


Working with Product Types

Enter topic text here.


Delivery Addresses

Enter topic text here.


Creating New Orders

The GUI form to accept and make a new order is the most complex of the forms that was required to make the 'BEST company' project realistic.

It needed to have a section which dealt with the general order detail, as well as being able to select any suitable delivery address (if applicable), and also allow interactive input of product, at an item line and quantity level.

The screen shown below copes with all this, and automatically calculates item lines and order totals as the order is put together (being raised).

Since all the values are updated in response to events and their handlers, the red 'Calculate Totals' button is not really necessary - but I like it :O)

A class was created called 'NewOrder' which facilitates the putting together of the order as well as calculating totals etc.

As with all the other screens, it is possible to check the check box control so that SQL statements are seen as they are about to be sent to the Server. However, for this screen the default is unchecked as there can be quite a number of such 'sends and displays' as a result of clicking the 'Add New' button. Annoying if you are wishing to make orders without testing what is going on :-)

Once the order is complete, the 'Add New' button causes a few things to happen. A new record is placed in the 'Orders' table and if successful the new identity is sent back to our application, and this then allows one or up to six item line records to be added to the 'Order Items' table. So for the order shown five SQL statements would be sent to the Server if no exceptions were thrown.

If a separate delivery address was selected, for delivery to a place other than the customer address, then the Delivery Address ID would be saved in the relevant field in the new orders record. (This is an extra field to the Microsoft free download of the Company / MyCompany databse.)

You should be familiar with the rest of this process after seeing the AB series videos from Microsoft.

One thing I had to build in myself was a way of generating some meaningful values for the Tax and Shipping costs. I simply made values based on a fixed proportion scale. So the heavier the total order, the more the Shipping cost. Also the Tax was done on a sliding scale too. Finally, I made the grand order total the sum of Costs, Tax and Shipping.

Obviously, at screen opening time an SQL statement is sent to the Server in order to get all the product items which are currently available for purchase. The 'formload' event triggers the method to do this which is part of the main form class, and therefore available to all sub-forms in the project application. Below is the code for 'GetAllProducts' :-

METHOD GetAllProducts() AS ArrayList

LOCAL alProducts AS ArrayList

SELF:oParent:DoSetConnectionString()

SELF:oParent:DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := SELF:oParent:oDFactory:CreateCommand()

oDComm:Connection := SELF:oParent:oDConn

LOCAL oDr AS DbDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT [ProductName],[ProductID],[ProductPrice],"

cStatement += "[ProductDescription],[ProductShippingOunces],[ProductTypeID]"

cStatement += " FROM [Products] ORDER BY [ProductID];"

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.TEXT

oDr := oDComm:ExecuteReader()

alProducts := ArrayList{}

LOCAL cFullName AS STRING

LOCAL oProd AS Product

LOCAL oSM AS SqlMoney

LOCAL cSM AS STRING

IF ! oDr == NULL

DO WHILE oDr:Read()

oProd := Product{}

cFullName := oDr:GetString(0)

cFullName := cFullName:TRIM()

IF cFullName:Length < 1

cFullName := "<< .. empty .. >>"

ENDIF

oProd:ProductName := cFullName

oProd:ProductID := oDr:GetInt32(1)

cSM := oDr:GetValue(2):ToString()

oProd:ProductPrice := Conversion.VAL(cSM)

oProd:ProductDescription := oDr:GetString(3)

oProd:ProductShippingOunces := oDr:GetInt16(4)

oProd:ProductTypeID := oDr:GetInt32(5)

alProducts:Add(oProd)

ENDDO

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS DbException

MessageBox.Show(ex:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN alProducts

And now we ought to look at the 'calling' code which causes the products to be found, and then gets them displayed in all six drop down list controls :-

METHOD fmCreateAnOrderLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent := (fmBESTcompany)SELF:Owner

LOCAL oPR AS ProductRange

oPR := ProductRange{SELF:oParent}

alProducts := oPR:GetAllProducts() //--- zero based ---

oPR := NULL_OBJECT

aProds := (Product[])alProducts:ToArray(typeof(Product))

ocbProductLineOne:Items:AddRange(aProds)

ocbProductLineTwo:Items:AddRange(aProds)

ocbProductLineThree:Items:AddRange(aProds)

ocbProductLineFour:Items:AddRange(aProds)

ocbProductLineFive:Items:AddRange(aProds)

ocbProductLineSix:Items:AddRange(aProds)

SELF:nProducts := aProds:Length

LOCAL oAC AS AllCustomers

oAC := AllCustomers{SELF:oParent}

alCustomers := oAC:GetAllCustomers() //--- zero based ---

LOCAL aCust AS Customer[]

aCust := (Customer[])alCustomers:ToArray(typeof(Customer))

ocbCustomers:Items:AddRange(aCust)

ocbCustomers:SelectedIndex := 0

RETURN

Okay then, now we have seen how the form is set up ready for a new order, let us seen what is actually done once the 'Add New' button is clicked.

Obviously, we need a few checks before any 'crunchy' bits start, like checking that there is a customer in focus, and that there is at least one item line with product and quantity selected.

A check to ask the user if they actually wish to create an order, also never goes amiss. So lets look at this confirmation part first :-

METHOD DoAddNewOrder() AS VOID

IF nCustomerID < 1

MessageBox.Show(SELF,"First select a customer from the list.","CARE - Select !")

RETURN

ENDIF

//========================

IF !oNewOrd:GTotalCost > 0

MessageBox.Show(SELF,"Select at least one item.","CARE - Select !")

RETURN

ENDIF

//=====================================

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Question

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageBoxDefaultButton

oDef := MessageBoxDefaultButton.Button2

LOCAL cMess, cHead AS STRING

cMess := "Create a new Order & Order Items in the SQL tables ?"

cHead := "CONFIRM - Create !?"

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF, cMess, cHead, oMBB, oIcon, oDef)

IF oDR = DialogResult.No

RETURN

ENDIF

//=================================

SELF:DoCalculateTotals()

SELF:CreateNewOrder()

RETURN

Okay then, the keen eyed will say, lets just get on and see the 'DoCalculateTotals' and 'CreateNewOrder' methods as they do the interesting stuff .

You will notice in the code below that the CalculateTotals method is called just before sending data to the Server.

This ensures that the totals are actually for ALL the selected product lines :-

METHOD DoCalculateTotals() AS VOID

oNewOrd:CalculateTotals()

RETURN

METHOD CalculateTotals() AS VOID

GTotalWeight := 0

GTotalCost := 0

TotalTax := 0

TotalShipping := 0

LOCAL i AS INT

LOCAL oOIL AS OrderItemLine

FOR i := 1 UPTO 6

oOIL := aItemLines[i]

GTotalWeight += oOIL:LineWeight

GTotalCost += oOIL:Cost

NEXT

TotalTax := GTotalCost*0.13

//TotalShipping := GTotalWeight*0.085

TotalShipping := GTotalWeight*0.165

oParent:otbTax:TEXT := TotalTax:ToString()

oParent:otbShipping:TEXT := TotalShipping:ToString()

oParent:otbTotalCost:TEXT := GTotalCost:ToString()

oParent:otbWeight:TEXT := GTotalWeight:ToString()

GTotalAmount := GTotalCost +TotalTax+ TotalShipping

oParent:otbTotalAmount:TEXT := GTotalAmount:ToString()

RETURN

Now that we have 'set' all the properties in the 'NewOrder' object as we wish them to be, lets see how we use this object to send the data to the Server.

Notice that we have a Select part to the overall statement, and that it comes directly after the INSERT part of the statement.

Because we are using the @@Identity function immediately after the statement which adds a record to the table, then if successful, a meaningful identity value will be returned for use later :-

METHOD CreateNewOrder() AS VOID

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [Orders] ([CustomerID],[OrderDateTime],[Tax],[Shipping])"

cText += "VALUES (@cid,@odt,@tx,@sp) "

cText += "SELECT @@Identity ;"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL dtStamp AS DateTime

dtStamp := DateTime.Now

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@cid"

oParam:Value := oNewOrd:CustomerID

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@odt"

oParam:Value := dtStamp

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@tx"

oParam:Value := oNewOrd:TotalTax

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@sp"

oParam:Value := oNewOrd:TotalShipping

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

LOCAL nOrderID AS INT

LOCAL cOrderID AS STRING

cOrderID := oCommand:ExecuteScalar():ToString()

nOrderID := (INT)Conversion.VAL(cOrderID)

//MessageBox.Show(SELF,nOrderID:ToString(),"New Identity :")

//----- MUST set an orderID in the Object -----

//----- for use with creating order lines -----

IF nOrderID > 0

oNewOrd:OrderID := nOrderID

LOCAL i AS INT

FOR i := 1 UPTO 6

IF oNewOrd:aItemLines[i]:Cost > 0

SELF:CreateNewOrderLine(i)

ENDIF

NEXT

SELF:ClearLastOrder()

ELSE

//----- WARN -----

MessageBox.Show(SELF,"No valid order ID returned from SQL engine ?","WARN !!!")

ENDIF

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN

Notice that we only go on to insert new records into the 'order items' table if there is a meaningful identity valued returned.

Take good notice of this way of knowing the identity (for Primary Key) of what you have just added (INSERTed). This is a very common thing to want to do - BUT - I did not find it in the material I was following.

Thanks must go to my friend Mike Pitcher, one of my 'guiding hands'.

The code takes into account that some item lines may be blank, or incomplete, while later ones are OK. So it scans all six lines for completed entries, before sending all completed ones to be used by the 'CreateNewOrderLine' method which we can see below :-

METHOD CreateNewOrderLine(nLine AS INT) AS VOID

//MessageBox.Show("in create new order lines method "+oNewOrd:OrderID:ToString())

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [OrderItems] ([OrderID],[ProductID],[Quantity])"

cText += "VALUES (@oid,@pid,@qt);"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@oid"

oParam:Value := oNewOrd:OrderID

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@pid"

oParam:Value := oNewOrd:aItemLines[nLine]:ProductID

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@qt"

oParam:Value := oNewOrd:aItemLines[nLine]:Qty

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

RETURN

You will notice that both the 'order' and 'order items' new record creation does not have our new Exception and Error 'capturing and table storage system' in place.

This is simply because I found out about it after doing and testing this code section, while doing Business Reports later.

I think it would be good for you to try and adapt this existing code so that any problems at order creation time are kept in the exceptions table for later viewing and inspection.

This is a good extension exercise for you to do :-)

Notice all order and order items SQL statements have been communicated to the Sever using the DbFactory approach.

Before we leave the order creation process we may like to see how the code responds to item line selections.

Any of the combo boxes for product and quantity, trigger the same event handler when they have a selection changed event take place.

.........................

.........................

METHOD cbProductLineTwoSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

..............

..............

METHOD cbQtyLineOneSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

........................

........................

METHOD DoItemLineCheck(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL oCB, oCB2 AS ComboBox

oCB := (ComboBox)o

LOCAL cName, cItem, cType AS STRING

LOCAL i, nIndex, nNum AS INT

nIndex := oCB:SelectedIndex+1

cName := oCB:Name

cItem := ".. empty .."

cType := ""

IF cName:IndexOf("Qty") >=0

nSelectQ := nIndex //--- only if Qtys. go from one and increment in one too ! -----

cType := "Q"

nNum := nSelectQ

ELSE

LOCAL oP AS Product

oP := aProds[nIndex]

nSelectProd := oP:ProductID

cType := "P"

nNum := nSelectProd

ENDIF

LOCAL nItemLine AS INT

//----- we need the item line (1 - 6) -----

IF cName:IndexOf("One") >= 0

nItemLine := 1

ENDIF

IF cName:IndexOf("Two") >= 0

nItemLine := 2

ENDIF

IF cName:IndexOf("Three") >= 0

nItemLine := 3

ENDIF

IF cName:IndexOf("Four") >= 0

nItemLine := 4

ENDIF

IF cName:IndexOf("Five") >= 0

nItemLine := 5

ENDIF

IF cName:IndexOf("Six") >= 0

nItemLine := 6

ENDIF

//----- update the specific item line in the New Order object -----

SELF:oNewOrd:UpdateItemLines(SELF, nItemLine, cType, nNum)

RETURN

Simply the object 'o' lets us pick up which combo box it is that has been used for the changed selection, and then a simple index approach is used to allow data to be prepared ready to send into the method of the 'NewOrder' class so that the 'UpdateItemLines' can be carried out. Lets look at that method now :-

METHOD UpdateItemLines(oOwner AS fmCreateAnOrder, nItemLine AS INT, cType AS STRING, nNum AS INT) AS VOID

oParent := oOwner

//----- cType is either P or Q indicating the obvious -----

//----- nNum is either a ProdID or a Qty -----

LOCAL oOIL AS OrderItemLine

oOIL := aItemLines[nItemLine]

IF cType == "P"

oOIL:UpdateItemLineProd(oOwner,nNum)

ELSE

oOIL:UpdateItemLineQty(oOwner,nNum)

ENDIF

SELF:CalculateTotals()

oParent:otbCost1:TEXT := aItemLines[1]:Cost:ToString()

oParent:otbCost2:TEXT := aItemLines[2]:Cost:ToString()

oParent:otbCost3:TEXT := aItemLines[3]:Cost:ToString()

oParent:otbCost4:TEXT := aItemLines[4]:Cost:ToString()

oParent:otbCost5:TEXT := aItemLines[5]:Cost:ToString()

oParent:otbCost6:TEXT := aItemLines[6]:Cost:ToString()

RETURN

You will notice that the relevant 'OrderItemLine' object is located and then either the 'UpdateItemLinePord' or 'UpdateItemLineQty' method code is run.

Since this is just standard sort of Vulcan coding you may refer to it in the working code section of in the PRGs which will be provided for download when 'ClickStartSQL' is completed.

Anyway, if you wish to view any new order details, you can look at the table details from the Business Reports form.

Options for viewing in the Reports form include 'All Orders' and 'All Order Items', which when displayed you can resort using the column header click sort, to get the latest at the top of the displayed list.

This final screen shot shows the latest order items being displayed, after re-sorting, and the orders about to be displayed after selection from the beige (pale yellow) list control.

So you don't have to use MS for everything, its only a 'layer' over the top of the database and Server, like our application. No different really.

PHH - July 2008.


Invoices and Dispatch Notes

Enter topic text here.


Viewing Reports

The idea for this GUI was quite simple. I wanted a way to be able to send a whole range of queries to the Server and have the results data set displayed in a grid.

I wanted the form to be a learning tool, which could also be changed and updated by another user. For this reason the user may see the query statement just before it is sent to the Server, unless the user un-checks the check box at top left of screen.

I also had an idea of moving towards 'Reporting Services' (RS) so that reports already created for a query and data set, could be seen and printed physically.

The RS idea is probably going to have to be dealt with in a separate ClickStartRS set of notes. I think it is just too much to include here for a number of reasons.

So, let us keep the Reports screen to finding a whole range of results data sets, and displaying then in the grid. I think it is also very nice to be able to see the column data types at a right muse click too :O)

We will see how to gain access to the strongly type column data later in this section.

Okay then, what does the 'Run Query' button do for us ? :-

Obviously, before a query can be run it needs to be selected, so the 'Run Query' button works with an 'In Focus' query. This simply means that a query is first selected form the list box, this makes it the 'focus' query, so the button is now able to be used.

The code behind the button is :-

METHOD DoRunQuery() AS Boolean

LOCAL lResult AS Boolean

lResult := TRUE

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := cFocusSelect

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

lResult := FALSE

RETURN lResult

ENDIF

//===============================================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oParent:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS, "Reports")

//----- display in DGV control -----

odgvReports:DataSource := oDS:Tables["Reports"]

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

LOCAL oSED AS StoreExceptionDetails

oSED := StoreExceptionDetails{SELF:oParent}

oSED:oDbExcept := DbEx

oSED:ErrorNumber := nError

oSED:RecordInTable()

oSED := NULL_OBJECT

END TRY

SELF:oParent:DoForceDisconnect()

RETURN lResult

Notice that any exceptions and error numbers are stored in the Exceptions table, and the SQL Server access code is 'provider agnostic'.

You may be interested to know that as I added more query statements to the list and tested them, I actually got some errors now and then, and these were added quite genuinely to the 'exceptions' table. These are those you see shown in the grid in the sub-section called 'Saving and Viewing Exceptions'.

Please realize that we could not do this without the flexibility of using the 'Fill()' method of the DbDataAdapter class. By using that approach we can display a table in the grid without knowing anything about its structure, column names, or data types.

To accommodate many columns the form can be expanded horizontally.

If we wish however, we can gain all this afterwards by processing the DataTable in the correct manner.

Lets see a typical report results set displayed :-

When the image was captured with the H&M5 camera facility, the 'ProcuctPrice' column had previously had its header clicked with the right mouse button.

You will see that the the Server has returned the 'smallmoney' data type as System.Decimal. We will look at data types, nulls, DateTime and other issues in another section, probably the appendices.

What I can say is that when I was using a data reader approach and had to manually code the casting, it was a nightmare to get this conversion / casting right ;-) This is much easier!

In the next image shot below, we can see that it is possible to have a single scalar value displayed and also what the screen looks like after the mouse has been double clicked in the list box :-

Notice that the Paul Nielsen recommended DateTime code snippets have been included in the report query list. Check out appendix D on UDF (user defined functions) to learn more of these.

Also notice that the the list box is displaying the actual query text and not the description. let us now take a look to see how this may be done :-

METHOD lbxStandardStatementsDoubleClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoChangeListBoxToString()

RETURN

METHOD DoChangeListBoxToString() AS VOID

//MessageBox.Show("Now in double click !")

IF cDisplayMode = "T"

cDisplayMode := "D"

ELSE

cDisplayMode := "T"

ENDIF

SELF:FillList()

RETURN

Well, as you can see we have not had to do a lot. But there must be something in the FillList() method which is doing more than we think ?

METHOD FillList() AS VOID

aStatements := Statement[]{22}

LOCAL oState AS Statement

LOCAL cSwitch AS STRING

//cSwitch := "T"

//cSwitch := "D"

cSwitch := cDisplayMode

.............................................

.............................................

//----------------------------------------------------

olbxStandardStatements:Items:Clear()

olbxStandardStatements:Items:AddRange(aStatements)

olbxStandardStatements:SelectedIndex := 0

LOCAL oS AS Statement

oS := aStatements[olbxStandardStatements:SelectedIndex+1]

cFocusSelect := oS:TEXT

RETURN

Well, does not seem to be much there, only clearing and refilling the list box with Statement objects, so it must have to do with the statement objects themselves :-

CLASS Statement

EXPORT Text AS STRING

EXPORT Description AS STRING

EXPORT Type AS STRING

//----- C for Customers, O for Orders, P for products -----

EXPORT Values AS STRING

EXPORT SwitchString AS STRING

CONSTRUCTOR()

SUPER()

SELF:Initialize()

RETURN

CONSTRUCTOR(cSwitch AS STRING)

SUPER()

SELF:Initialize()

IF cSwitch = "T"

SELF:SwitchString := "T"

ENDIF

RETURN

METHOD Initialize() AS VOID

SELF:Text := ""

SELF:SwitchString := "D"

RETURN

METHOD ToString() AS STRING

LOCAL cResult AS STRING

IF SELF:SwitchString == "T"

cResult := SELF:Text:Trim()

ELSE

cResult := SELF:Description:Trim()

ENDIF

RETURN cResult

METHOD SwitchDisplay() AS VOID

IF SELF:SwitchString == "T"

SwitchString := "D"

ELSE

SwitchString := "T"

ENDIF

RETURN

END CLASS

Well fancy that, it would seem that the 'ToString()' method of the 'Statement' class controls what is displayed in the list, depending on how 'cSwitch' is set and this comes in as a variable in one of the overloads for the class.

It would appear that we could use such a 'trick' to do some quite useful changing of list box displays, on other forms and in other projects :-)

Okay then lets finally look at how we display the column name and data type after a right mouse click in the column header :-

METHOD dgvReportsColumnHeaderMouseClick(o AS OBJECT , e AS System.Windows.Forms.DataGridViewCellMouseEventArgs) AS VOID

SELF:DoShowColumnDataType(o,e)

RETURN

METHOD DoShowColumnDataType(o AS OBJECT , e AS System.Windows.Forms.DataGridViewCellMouseEventArgs) AS VOID

IF e:Button = MouseButtons.RIGHT

LOCAL nIndex AS INT

nIndex := e:ColumnIndex

//MessageBox.Show(nIndex:Tostring())

LOCAL cText, cName AS STRING

cText := odgvReports:Columns[nIndex]:ValueType:ToString()

//MessageBox.Show(cText)

cName := odgvReports:Columns[nIndex]:Name:ToString()

olbColumnDataType:Text := "Col"+(nIndex+1):ToString()+" : '"+cName+"' : "+cText

ENDIF

RETURN

Basically, it is two or three lines of dotNet code in Vulcan syntax. Once you get used to using the DataGridView control you will find that it feels quite straight forward. Yes, it is a bit strange at first, but then many new things do.

PHH - July 2008.


Working with Orders

Enter topic text here.


Saving and Viewing Exceptions

One very useful spin-off to come out of the 'BEST company' project is a potential method for capturing and storing details for when things go wrong !

We used to call it an error log - I suppose we ought to now call in an exception table :-)

Look at some output below to see MS information regarding a table called 'Exceptions'. This was created after the rest of the working project :-

===========================================================================================================================================

From the above information we can see that some basic and useful information is being stored in the exception table, like description or message, a date time stamp of the occurrence, and the error number provided by the Server itself. Column provision is present for multi user deployment, so that workstation and user ID could be captured too.

We can discuss later how this general approach may be extended to be of real use to an administrator for any working network application.

First of all lets look at the code which is doing the job at the moment, and then we can worry about improving it with T-SQL code later.

TRY

//----- execute the select statement -----

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

//MessageBox.Show(nError:ToString())

//MessageBox.Show(DbEx:Message)

LOCAL oSED AS StoreExceptionDetails

oSED := StoreExceptionDetails{SELF:oParent}

oSED:oDbExcept := DbEx

oSED:ErrorNumber := nError

oSED:RecordInTable()

oSED := NULL_OBJECT

END TRY

Now in order to see everything which is taking place we need to look at the class 'StoreExceptionDetails'.

Look at the code below to see what this class does when use as above :-

CLASS StoreExceptionDetails

EXPORT Message AS STRING

EXPORT ErrorNumber AS INT

EXPORT User AS STRING

EXPORT WorkStation AS STRING

EXPORT DateTimeStamp AS DateTime

EXPORT oDbExcept AS DbException

PROTECT oParent AS fmBESTcompany

CONSTRUCTOR(oOwner AS fmBESTcompany)

SUPER()

oParent := (fmBESTcompany)oOwner

SELF:Initialize()

ErrorNumber := 0

RETURN

METHOD Initialize() AS VOID

Message := ""

User := ""

WorkStation := ""

DateTimeStamp := DateTime.Now

RETURN

METHOD RefreshFields() AS VOID

SELF:Initialize()

Message := oDbExcept:Message

RETURN

METHOD RecordInTable() AS VOID

SELF:RefreshFields()

//----- check message is not empty -----

IF SELF:Message == ""

MessageBox.Show("Exception message is empty !?","WARNING - Message!")

ENDIF

//======================================

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Question

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageBoxDefaultButton

oDef := MessageBoxDefaultButton.Button2

LOCAL cMess, cHead AS STRING

cMess := "Create a new record in the 'Exception' table ?"

cHead := "CONFIRM - Save Error Message !?"

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(oParent, cMess, cHead, oMBB, oIcon, oDef)

IF oDR = DialogResult.No

RETURN

ENDIF

//=================================

//----- set the 'CommandText' PROPERTY -----

LOCAL cText AS STRING

cText := "INSERT INTO [Exceptions] ([DateTimeStamp],[Message],[ErrorNumber],[WorkStation],[User])"

cText += "VALUES (@dts, @mess, @en, @ws, @us);"

/*IF ocbDisplayStatement:Checked

MessageBox.Show(cText)

ENDIF */

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

//----- add parameters and set their value -----

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@dts"

oParam:Value := DateTime.Now

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.STRING

oParam:ParameterName := "@mess"

oParam:Value := SELF:Message

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@en"

oParam:Value := SELF:ErrorNumber

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.STRING

oParam:ParameterName := "@ws"

oParam:Value := SELF:WorkStation

oCommand:PARAMETERS:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.STRING

oParam:ParameterName := "@us"

oParam:Value := SELF:User

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN

END CLASS

Although this is a simple and straight forward approach as it stands, it does work OK.

However, it has a limitation in that it can only get an error code for the last statement executed in the Server before the exception occurs.

If we look to T-SQL to help us out we can take this further, and capture details of things going wrong during a batch of statements to be executed.

Look at the appendices for further developments on this front.

Finally, we need to realize that since the details are stored in just another SQL table, we can have a report query to view our table, and see the exception details in the grid on the Reports form.

Below is an example output from our exception table after just such a query :-

It should be possible for you to extend this approach to categorize the different types of error / exception and to allow selected viewing depending on the category required for viewing.

In fact it would be a good idea for a 'real life' application, to have a error / exception screen which as all this functionality built in and more :-)

Good error gathering !

PHH - July 2008.


Returned Data Sets to Objects

Let us now focus in more detail, on both what exactly we can get returned by the Server, and also on what we do with it.

Below shows a example from our working project. This 'display full details' button causes the server to be asked to return three 'lots' of information.

These three are related to Customer details, delivery address (if different), and also the detail of the order item lines.

Some of this can be seen in the rich text box control below :-

If we now look at both how we asked for the data of the Server, and what we did when it was returned, we will start to see how we may improve things, a step at a time.

=========================================================================================

The above code is quite straight forward, and should be getting familiar to us by now.

It asks the Server to return a table with five columns and only for delivery address 'ID' values of that specified in out Vulcan code.

Okay, we know that the table will have only one row, but it is still a table. Notice the DataReader object is passed to an 'extra' method which is shown below :-

=============================================================================

You can see that the data is extracted from the record in the data reader by reference to an ordinal value, that is the number in the braces.

It needs to be realized that while the one record is in 'focus' as it were, the data items could be 'extracted' in any ordinal order, as long as the correct value is used for the piece of data required.

This works, and works fine. However, it does look a little old fashioned, as we have become accustomed to named references rather than positional ones.

To see how this may be improved lets look at the item line details data we need for the task of printing invoices and dispatch notes :-

=====================================================================================

And what we do with this when it is returned :-

===================================================================================================================

Because the data reader 'Get' methods have only one overload, which requires an ordinal value, we have to generate one using another method of the class, called 'GetOrdinal'.

So looking at our Vulcan code we see that we are now referring to our data items by name as such, even if it is a bit unwieldy in the syntax used.

How could we improve this further ? Not just the syntax, but say the fact that we don't wish to provide either the data type of the items being extracted, or their 'reference' name !

Yes, we seem to be asking quite a lot, but it can be done, and quite easily too.

Strongly Typed 'DataTable' :

In the Business Reports screen in or project, we have a way of displaying any data which is returned by the query chosen from the drop down list.

Because at the time of writing I did not know either the number of columns and their data types, or indeed their column or field names, I chose to use a data adapter which in turn attaches the returned data as a data table to an instantiated 'data set' object.

========================================================

Again, this code should be starting to look familiar to you by now. Notice the DataSet object appears to have just one table and that is called "Reports".

The name of the table has been provided by us the developer, through our code.

However, the rest of the information about the data returned, can be found from the DataTable object itself. Which has been returned 'strongly typed'.

You will notice that in order for us to see the returned data items, we actually need to know nothing at the point of viewing, we just need to provide then DataSet as a source of data for the DataGridView object.

This does make life easy for us all.

It would appear that the 'Fill()' method is doing an awful lot for us.

In this particular form I didn't wish to do more than view the data, however, in order to learn more, let us see how we can write a generalized approach to handle all returned data.

Let us start with adapting our Reports form so that when we right click with the mouse in any column header the data type and name of that column is displayed in the 'ice blue' label near the top of the form.

The image below shows that the second column header has been right clicked :-

This seems to work quite nicely so lets take a look at the code I just had to add to make this work :-

SELF:odgvReports:ColumnHeaderMouseClick += System.Windows.Forms.DataGridViewCellMouseEventHandler{ SELF , @dgvReportsColumnHeaderMouseClick () }

....................................

METHOD dgvReportsColumnHeaderMouseClick(o AS OBJECT , e AS System.Windows.Forms.DataGridViewCellMouseEventArgs) AS VOID

SELF:DoShowColumnDataType(o,e)

RETURN

METHOD DoShowColumnDataType(o AS OBJECT , e AS System.Windows.Forms.DataGridViewCellMouseEventArgs) AS VOID

IF e:Button = MouseButtons.Right

LOCAL nIndex AS INT

nIndex := e:ColumnIndex

//MessageBox.Show(nIndex:Tostring())

LOCAL cText, cName AS STRING

cText := odgvReports:Columns[nIndex]:ValueType:ToString()

//MessageBox.Show(cText)

cName := odgvReports:Columns[nIndex]:Name:ToString()

olbColumnDataType:Text := "Col"+(nIndex+1):ToString()+" : '"+cName+"' : "+cText

ENDIF

RETURN

I like to separate the event from the action, as far as my code lines are concerned, so there are more lines here than essential to make things work.

Also I have left in the message box lines to demonstrate how I go about building up such a behaviour. I started with just printing in a message box as a response to a double click. before I changed, and honed things, so that a single right mouse click caused the label to have its text property changed.

If you build in small but important steps you get few errors (and none you can't handle) and you also don't need to resort to debuggers :-)

Finally, for this section, we ought to look at how to make a specific object from each table or grid row.

Refer to section 1 - Customized Objects from Tables for this information.


Some dotNet Techniques Used

ListView : Using with Objects :

One useful 'tool' to the form designer and code developer, is the use of the ListView (LV) control to display groups of objects.

In the SQL server querying 'area', we can get back our results set and convert straight to customized objects which may then be placed into a ListView control.

Let us look at the general approach first, of displaying objects in a LV control.

For our example lets we will display customer objects, so let us take a look at the 'customer' class :-

=====================================================

We now need to set up the LV control so that it has columns for some or all of the these properties shown above :-

So for the Initialize() method of the form with the LV control we need the following Vulcan code :-

==================================================================

You can see that eight columns have been created with suitable names, and approximate widths. Note the width figure is in pixels and needs to be larger than you first think.

Okay then, not far now.

The LV control is made up from a list of ListViewItems (LVI) so we now need to convert our objects into such items.

The code below shows an array of customer objects being used to create LVIs and then these being added to the LV control itself :-

===============================================================

Notice we are clearing the LV control of any previous contents, in the very first line.

Let us now see what this will look like in our live project :-

To see how customized (developer) objects may be created from SQL 'returned data sets' see the relevant sub-section.

The sub-section is entitled 'Returned Data Sets to Objects'.

Finally, for this part we need to see how to gain access to our object data if the LV control is clicked for example.

===================================================================================================================

This is just one way of doing it, and it uses the fact that we already have an array of the customer objects, and these have a unique ID.

Column seven is the reference ID and this is extracted after mouse click selection, to be used to look down the array of customer objects.

This method would still work if a sort routine was applied to the LV control - the array and the LV would have different orders, but since the reference ID is being used it does not matter.

===================================================

include ListView in Customers

also listbox - in reports - how to change over the display details

interactive dates

ordinal use in data reader - replace with GetOrdinal("name")

and compare with DataTable / DataSets

create proc - DateLive !?

also use proc.


Stored Procedures

Let us now look in detail at a report from a query which can be sent 'in total' to the Server, or as a much shorter entity which then runs a stored procedure held within the database itself.

We will examine the code for 'long winded' way of sending the query, as well as code to create the query in the Server, and also the code to just take the start and finish dates and send these as data to run the stored procedure by sending a much shorter query. Finally we will see the query as seen form MS.

Let us see the Reports screen as it looks when we select a start and finish date a month apart, for the data I have at the moment :-

The above screen capture was done as the full select statement shown was about to be sent to the Server. By a little trick we can also see what information the query produces in the results set, and this is see displayed in the grid.

METHOD DoInteractiveDates() AS VOID

LOCAL cText AS STRING

LOCAL cFirst, cLast AS STRING

LOCAL dtFirst, dtLast AS DateTime

dtFirst := odtpStart:Value

dtLast := odtpFinish:Value

//----- format required is year/month/day (there are others) -----

cFirst := SELF:ConvertDateTimeYMD(dtFirst)

cLast := SELF:ConvertDateTimeYMD(dtLast)

cText := "SELECT [ProductID],[Quantity] FROM [OrderItems] "

cText += "WHERE [OrderID] IN (SELECT [OrderID] FROM [Orders] WHERE [OrderDateTime] BETWEEN '"

cText += cFirst

cText += "' AND '"

cText += cLast

cText += "');"

cFocusSelect := cText

SELF:DoRunQuery()

RETURN

Because we have the 'DoRunQuery' code separated out it does not seem so 'long winded'. However, notice in order to send the query we have had to do some variable string conversion and concatenation with the query, which does seem a little 'dated' or old fashioned ;-)

Before we view the stored procedure (SP) lets see how the query code looks when used :-

=========================================================================================================

Okay, so we are still concatenating but it does look a bit neater, and clearer to understand etc.

Let us now view the information in the database, as seen from the 'programmability' section of the MS object explorer :-

===============================================================================================================================

You will notice a correspondence with the full query code we sent at the top of this sub-section.

Finally, lets look at what our program code looks like which created the SP in the database in the first place :-

METHOD DoCreateProcDateLive() AS VOID

LOCAL cText AS STRING

cText := "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[DateLive]') ) DROP PROCEDURE [DateLive];"

cFocusSelect := cText

IF !SELF:DoRunQuery()

RETURN

ENDIF

cText := "SET ANSI_NULLS ON;"

cText += "SET QUOTED_IDENTIFIER ON;"

cFocusSelect := cText

IF !SELF:DoRunQuery()

RETURN

ENDIF

cText := "CREATE PROCEDURE DateLive @start varchar(13), @finish varchar(13) "

cText += "AS "

cText += "BEGIN "

cText += "select [ProductID],[Quantity] from [OrderItems] "

cText += "where [OrderID] in (select [OrderID] from [Orders] "

cText += "where [OrderDateTime] between @start and @Finish); "

cText += "END ; "

cFocusSelect := cText

IF !SELF:DoRunQuery()

RETURN

ENDIF

RETURN

Now, just before we leave this sub-section we can look at the detail of another 9second) SP which is used in this project, and it is one which gets the orders which have been generated in a set period.

Lets look at the MS detailed information, as the calling query is almost identical to that above.

Notice we have used an 'inner join' to extract the information in which we are interested :-

======================================================================================================================================

Make sure you go to the orders screen (GUI) to try out this SP, simply by changing the dates, and the Refilling the Lists. The resultant information goes into the drop down lists.

Hope this prompts you to designing and using a few more SPs of your own.

PHH - July 2008.


Connection

// Application : BESTcompany

// Connection.prg , Created : 25/06/2008 06:44:14 AM

#USING System.Windows.Forms

#USING System.Drawing

#using System.Data.Common

#using System.IO

#using System.Data.SqlClient

#using System.Data

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml

#using System.Diagnostics

CLASS fmConnection INHERIT System.Windows.Forms.Form

PROTECT opbSerializeToFile AS System.Windows.Forms.Button

PROTECT ocbWindowsUserAccess AS System.Windows.Forms.CheckBox

PROTECT ortbConnection AS System.Windows.Forms.RichTextBox

PROTECT opbSelectFolder AS System.Windows.Forms.Button

PROTECT opbCheckFile AS System.Windows.Forms.Button

PROTECT odgvConnection AS System.Windows.Forms.DataGridView

PROTECT opbTextConnection AS System.Windows.Forms.Button

PROTECT opbViewConnectionString AS System.Windows.Forms.Button

PROTECT opbViewXMLfile AS System.Windows.Forms.Button

PROTECT opbLoadFromXML AS System.Windows.Forms.Button

PROTECT opbOK AS System.Windows.Forms.Button

PROTECT otbInstanceName AS System.Windows.Forms.TextBox

PROTECT olbInstanceName AS System.Windows.Forms.Label

PROTECT olbPassword AS System.Windows.Forms.Label

PROTECT olbIntSecurity AS System.Windows.Forms.Label

PROTECT olbUserName AS System.Windows.Forms.Label

PROTECT olbProvider AS System.Windows.Forms.Label

PROTECT olbServerName AS System.Windows.Forms.Label

PROTECT olbFilename AS System.Windows.Forms.Label

PROTECT olbFolder AS System.Windows.Forms.Label

PROTECT olbTimeout AS System.Windows.Forms.Label

PROTECT ocbProviders AS System.Windows.Forms.ComboBox

PROTECT ocbTimeOut AS System.Windows.Forms.ComboBox

PROTECT ocbIntSecurity AS System.Windows.Forms.ComboBox

PROTECT otbPassWord AS System.Windows.Forms.TextBox

PROTECT otbUserName AS System.Windows.Forms.TextBox

PROTECT otbServerName AS System.Windows.Forms.TextBox

PROTECT otbFolder AS System.Windows.Forms.TextBox

PROTECT otbFilename AS System.Windows.Forms.TextBox

PROTECT ogbDatabase AS System.Windows.Forms.GroupBox

PROTECT ogbServerDetails AS System.Windows.Forms.GroupBox

PROTECT opbxConnection AS System.Windows.Forms.PictureBox

// User code starts here (DO NOT remove this line) ##USER##

PROTECT cPath, cFile AS STRING

PROTECT oParent AS fmBESTcompany

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:Initialize()

SELF:odgvConnection:Hide()

SELF:MinimumSize := SELF:Size

opbxConnection:SizeMode := PictureBoxSizeMode.StretchImage

RETURN

METHOD Initialize() AS VOID

ocbWindowsUserAccess:Checked := TRUE

otbPassWord:Enabled := FALSE

otbUserName:Enabled := FALSE

SELF:DisableServerDetails()

odgvConnection:DataSource := DbProviderFactories.GetFactoryClasses()

LOCAL nLen, i, nPointer AS DWORD

nLen := (DWORD)odgvConnection:RowCount-1

LOCAL aProviders AS STRING[]

aProviders := STRING[]{(INT)nLen}

nPointer := 0

FOR i := 0 UPTO nLen-1

aProviders[i+1] := (STRING)odgvConnection["name",(INT)i]:Value

IF "SqlClient Data Provider" = aProviders[i+1]

nPointer := i

ENDIF

NEXT

LOCAL nIndex AS INT

LOCAL cprovider AS STRING

FOR i := 1 UPTO nLen

cProvider := aProviders[i]:Trim()

nIndex := cProvider:IndexOf(" Data ")

cProvider := cProvider:Substring(0,nIndex):Trim()

aProviders[i] := cProvider

NEXT

ocbProviders:Items:Clear()

ocbProviders:Items:AddRange(aProviders)

ocbProviders:SelectedIndex := (INT)nPointer

LOCAL aTO AS INT[]

aTO := <INT>{0,5,10,15,20,25,30}

ocbTimeOut:Items:AddRange(aTO)

ocbTimeOut:SelectedIndex := 0

LOCAL aIS AS STRING[]

aIS := <STRING>{"True","SSPI"}

ocbIntSecurity:Items:AddRange(aIS)

ocbIntSecurity:SelectedIndex := 1

RETURN

METHOD DisableServerDetails() AS VOID

RETURN

METHOD EnableServerDetails() AS VOID

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

LOCAL oResourceManager AS System.Resources.ResourceManager

oResourceManager := System.Resources.ResourceManager{ "Designers" , System.Reflection.Assembly.GetExecutingAssembly() }

SELF:Name := "fmConnection"

SELF:SuspendLayout()

SELF:Location := System.Drawing.Point{100,100}

SELF:ClientSize := System.Drawing.Size{490,438}

SELF:Text := "BEST company : Connection Details :"

SELF:Load += System.EventHandler{ SELF , @fmConnectionLoad() }

SELF:opbSerializeToFile := System.Windows.Forms.Button{}

SELF:opbSerializeToFile:Name := "pbSerializeToFile"

SELF:opbSerializeToFile:Location := System.Drawing.Point{ 376 , 352 }

SELF:opbSerializeToFile:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbSerializeToFile:Text := "Serialize to File"

SELF:opbSerializeToFile:BackColor := System.Drawing.Color.Thistle

SELF:opbSerializeToFile:Click += System.EventHandler{ SELF , @pbSerializeToFileClick() }

SELF:opbSerializeToFile:TabIndex := 16

SELF:opbSerializeToFile:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbSerializeToFile)

SELF:ocbWindowsUserAccess := System.Windows.Forms.CheckBox{}

SELF:ocbWindowsUserAccess:Name := "cbWindowsUserAccess"

SELF:ocbWindowsUserAccess:Location := System.Drawing.Point{ 112 , 200 }

SELF:ocbWindowsUserAccess:Size := System.Drawing.Size{ 120 , 24 }

SELF:ocbWindowsUserAccess:Text := "Win.User Access :"

SELF:ocbWindowsUserAccess:TabIndex := 11

SELF:Controls:Add(SELF:ocbWindowsUserAccess)

SELF:ortbConnection := System.Windows.Forms.RichTextBox{}

SELF:ortbConnection:Name := "rtbConnection"

SELF:ortbConnection:Location := System.Drawing.Point{ 376 , 152 }

SELF:ortbConnection:Size := System.Drawing.Size{ 112 , 104 }

SELF:ortbConnection:TabIndex := 1

SELF:ortbConnection:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:ortbConnection)

SELF:opbSelectFolder := System.Windows.Forms.Button{}

SELF:opbSelectFolder:Name := "pbSelectFolder"

SELF:opbSelectFolder:Location := System.Drawing.Point{ 376 , 56 }

SELF:opbSelectFolder:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbSelectFolder:Text := "Select Folder"

SELF:opbSelectFolder:Click += System.EventHandler{ SELF , @pbSelectFolderClick() }

SELF:opbSelectFolder:TabIndex := 12

SELF:opbSelectFolder:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbSelectFolder)

SELF:opbCheckFile := System.Windows.Forms.Button{}

SELF:opbCheckFile:Name := "pbCheckFile"

SELF:opbCheckFile:Location := System.Drawing.Point{ 376 , 96 }

SELF:opbCheckFile:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbCheckFile:Text := "Check File"

SELF:opbCheckFile:Click += System.EventHandler{ SELF , @pbCheckFileClick() }

SELF:opbCheckFile:TabIndex := 13

SELF:opbCheckFile:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbCheckFile)

SELF:odgvConnection := System.Windows.Forms.DataGridView{}

SELF:odgvConnection:Name := "dgvConnection"

SELF:odgvConnection:Location := System.Drawing.Point{ 0 , 432 }

SELF:odgvConnection:Size := System.Drawing.Size{ 488 , 8 }

SELF:odgvConnection:TabIndex := 30

SELF:Controls:Add(SELF:odgvConnection)

SELF:opbTextConnection := System.Windows.Forms.Button{}

SELF:opbTextConnection:Name := "pbTextConnection"

SELF:opbTextConnection:Location := System.Drawing.Point{ 8 , 408 }

SELF:opbTextConnection:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbTextConnection:Text := "Test Connection"

SELF:opbTextConnection:Click += System.EventHandler{ SELF , @pbTextConnectionClick() }

SELF:opbTextConnection:TabIndex := 17

SELF:opbTextConnection:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left

SELF:Controls:Add(SELF:opbTextConnection)

SELF:opbViewConnectionString := System.Windows.Forms.Button{}

SELF:opbViewConnectionString:Name := "pbViewConnectionString"

SELF:opbViewConnectionString:Location := System.Drawing.Point{ 136 , 408 }

SELF:opbViewConnectionString:Size := System.Drawing.Size{ 144 , 23 }

SELF:opbViewConnectionString:Text := "View Connection String"

SELF:opbViewConnectionString:Click += System.EventHandler{ SELF , @pbViewConnectionStringClick() }

SELF:opbViewConnectionString:TabIndex := 18

SELF:opbViewConnectionString:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left

SELF:Controls:Add(SELF:opbViewConnectionString)

SELF:opbViewXMLfile := System.Windows.Forms.Button{}

SELF:opbViewXMLfile:Name := "pbViewXMLfile"

SELF:opbViewXMLfile:Location := System.Drawing.Point{ 376 , 272 }

SELF:opbViewXMLfile:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbViewXMLfile:Text := "View XML File"

SELF:opbViewXMLfile:Click += System.EventHandler{ SELF , @pbViewXMLfileClick() }

SELF:opbViewXMLfile:TabIndex := 14

SELF:opbViewXMLfile:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbViewXMLfile)

SELF:opbLoadFromXML := System.Windows.Forms.Button{}

SELF:opbLoadFromXML:Name := "pbLoadFromXML"

SELF:opbLoadFromXML:Location := System.Drawing.Point{ 376 , 312 }

SELF:opbLoadFromXML:Size := System.Drawing.Size{ 112 , 23 }

SELF:opbLoadFromXML:Text := "Load from XML"

SELF:opbLoadFromXML:Click += System.EventHandler{ SELF , @pbLoadFromXMLClick() }

SELF:opbLoadFromXML:TabIndex := 15

SELF:opbLoadFromXML:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbLoadFromXML)

SELF:opbOK := System.Windows.Forms.Button{}

SELF:opbOK:Name := "pbOK"

SELF:opbOK:Location := System.Drawing.Point{ 440 , 408 }

SELF:opbOK:Size := System.Drawing.Size{ 48 , 23 }

SELF:opbOK:Text := "OK"

SELF:opbOK:Click += System.EventHandler{ SELF , @pbOKClick() }

SELF:opbOK:TabIndex := 0

SELF:opbOK:Anchor := System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbOK)

SELF:otbInstanceName := System.Windows.Forms.TextBox{}

SELF:otbInstanceName:Name := "tbInstanceName"

SELF:otbInstanceName:Location := System.Drawing.Point{ 112 , 320 }

SELF:otbInstanceName:Size := System.Drawing.Size{ 128 , 20 }

SELF:otbInstanceName:Text := "... << instance >> ..."

SELF:otbInstanceName:TabIndex := 7

SELF:Controls:Add(SELF:otbInstanceName)

SELF:olbInstanceName := System.Windows.Forms.Label{}

SELF:olbInstanceName:Name := "lbInstanceName"

SELF:olbInstanceName:Location := System.Drawing.Point{ 24 , 320 }

SELF:olbInstanceName:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbInstanceName:Text := "Instance name :"

SELF:olbInstanceName:TabIndex := 27

SELF:Controls:Add(SELF:olbInstanceName)

SELF:olbPassword := System.Windows.Forms.Label{}

SELF:olbPassword:Name := "lbPassword"

SELF:olbPassword:Location := System.Drawing.Point{ 24 , 256 }

SELF:olbPassword:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbPassword:Text := "Password :"

SELF:olbPassword:TabIndex := 24

SELF:Controls:Add(SELF:olbPassword)

SELF:olbIntSecurity := System.Windows.Forms.Label{}

SELF:olbIntSecurity:Name := "lbIntSecurity"

SELF:olbIntSecurity:Location := System.Drawing.Point{ 24 , 288 }

SELF:olbIntSecurity:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbIntSecurity:Text := "Int. Security :"

SELF:olbIntSecurity:TabIndex := 25

SELF:Controls:Add(SELF:olbIntSecurity)

SELF:olbUserName := System.Windows.Forms.Label{}

SELF:olbUserName:Name := "lbUserName"

SELF:olbUserName:Location := System.Drawing.Point{ 24 , 232 }

SELF:olbUserName:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbUserName:Text := "User name :"

SELF:olbUserName:TabIndex := 23

SELF:Controls:Add(SELF:olbUserName)

SELF:olbProvider := System.Windows.Forms.Label{}

SELF:olbProvider:Name := "lbProvider"

SELF:olbProvider:Location := System.Drawing.Point{ 24 , 144 }

SELF:olbProvider:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbProvider:Text := "Provider :"

SELF:olbProvider:TabIndex := 21

SELF:Controls:Add(SELF:olbProvider)

SELF:olbServerName := System.Windows.Forms.Label{}

SELF:olbServerName:Name := "lbServerName"

SELF:olbServerName:Location := System.Drawing.Point{ 24 , 176 }

SELF:olbServerName:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbServerName:Text := "Server name :"

SELF:olbServerName:TabIndex := 22

SELF:Controls:Add(SELF:olbServerName)

SELF:olbFilename := System.Windows.Forms.Label{}

SELF:olbFilename:Name := "lbFilename"

SELF:olbFilename:Location := System.Drawing.Point{ 24 , 24 }

SELF:olbFilename:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbFilename:Text := "Filename :"

SELF:olbFilename:TabIndex := 19

SELF:Controls:Add(SELF:olbFilename)

SELF:olbFolder := System.Windows.Forms.Label{}

SELF:olbFolder:Name := "lbFolder"

SELF:olbFolder:Location := System.Drawing.Point{ 24 , 56 }

SELF:olbFolder:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbFolder:Text := "Folder :"

SELF:olbFolder:TabIndex := 20

SELF:Controls:Add(SELF:olbFolder)

SELF:olbTimeout := System.Windows.Forms.Label{}

SELF:olbTimeout:Name := "lbTimeout"

SELF:olbTimeout:Location := System.Drawing.Point{ 192 , 288 }

SELF:olbTimeout:Size := System.Drawing.Size{ 72 , 23 }

SELF:olbTimeout:Text := "Timeout :"

SELF:olbTimeout:TabIndex := 26

SELF:Controls:Add(SELF:olbTimeout)

SELF:ocbProviders := System.Windows.Forms.ComboBox{}

SELF:ocbProviders:Name := "cbProviders"

SELF:ocbProviders:Location := System.Drawing.Point{ 112 , 144 }

SELF:ocbProviders:Size := System.Drawing.Size{ 232 , 21 }

SELF:ocbProviders:TabIndex := 8

SELF:Controls:Add(SELF:ocbProviders)

SELF:ocbTimeOut := System.Windows.Forms.ComboBox{}

SELF:ocbTimeOut:Name := "cbTimeOut"

SELF:ocbTimeOut:Location := System.Drawing.Point{ 264 , 288 }

SELF:ocbTimeOut:Size := System.Drawing.Size{ 65 , 21 }

SELF:ocbTimeOut:TabIndex := 10

SELF:Controls:Add(SELF:ocbTimeOut)

SELF:ocbIntSecurity := System.Windows.Forms.ComboBox{}

SELF:ocbIntSecurity:Name := "cbIntSecurity"

SELF:ocbIntSecurity:Location := System.Drawing.Point{ 112 , 288 }

SELF:ocbIntSecurity:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbIntSecurity:TabIndex := 9

SELF:Controls:Add(SELF:ocbIntSecurity)

SELF:otbPassWord := System.Windows.Forms.TextBox{}

SELF:otbPassWord:Name := "tbPassWord"

SELF:otbPassWord:Location := System.Drawing.Point{ 112 , 256 }

SELF:otbPassWord:Size := System.Drawing.Size{ 128 , 20 }

SELF:otbPassWord:Text := "... << pass word >> ..."

SELF:otbPassWord:TabIndex := 6

SELF:Controls:Add(SELF:otbPassWord)

SELF:otbUserName := System.Windows.Forms.TextBox{}

SELF:otbUserName:Name := "tbUserName"

SELF:otbUserName:Location := System.Drawing.Point{ 112 , 232 }

SELF:otbUserName:Size := System.Drawing.Size{ 128 , 20 }

SELF:otbUserName:Text := "... << user name >> ..."

SELF:otbUserName:TabIndex := 5

SELF:Controls:Add(SELF:otbUserName)

SELF:otbServerName := System.Windows.Forms.TextBox{}

SELF:otbServerName:Name := "tbServerName"

SELF:otbServerName:Location := System.Drawing.Point{ 112 , 176 }

SELF:otbServerName:Size := System.Drawing.Size{ 128 , 20 }

SELF:otbServerName:Text := "... << server name >> ..."

SELF:otbServerName:TabIndex := 4

SELF:Controls:Add(SELF:otbServerName)

SELF:otbFolder := System.Windows.Forms.TextBox{}

SELF:otbFolder:Name := "tbFolder"

SELF:otbFolder:Location := System.Drawing.Point{ 112 , 56 }

SELF:otbFolder:Size := System.Drawing.Size{ 232 , 20 }

SELF:otbFolder:Text := "... << folder string >> ..."

SELF:otbFolder:TabIndex := 3

SELF:Controls:Add(SELF:otbFolder)

SELF:otbFilename := System.Windows.Forms.TextBox{}

SELF:otbFilename:Name := "tbFilename"

SELF:otbFilename:Location := System.Drawing.Point{ 112 , 24 }

SELF:otbFilename:Size := System.Drawing.Size{ 122 , 20 }

SELF:otbFilename:Text := "... << filename >> ..."

SELF:otbFilename:TabIndex := 2

SELF:Controls:Add(SELF:otbFilename)

SELF:ogbDatabase := System.Windows.Forms.GroupBox{}

SELF:ogbDatabase:Name := "gbDatabase"

SELF:ogbDatabase:SuspendLayout()

SELF:ogbDatabase:Location := System.Drawing.Point{ 8 , 8 }

SELF:ogbDatabase:Size := System.Drawing.Size{ 361 , 90 }

SELF:ogbDatabase:Text := "Database :"

SELF:ogbDatabase:TabIndex := 28

SELF:Controls:Add(SELF:ogbDatabase)

SELF:ogbServerDetails := System.Windows.Forms.GroupBox{}

SELF:ogbServerDetails:Name := "gbServerDetails"

SELF:ogbServerDetails:SuspendLayout()

SELF:ogbServerDetails:Location := System.Drawing.Point{ 8 , 112 }

SELF:ogbServerDetails:Size := System.Drawing.Size{ 360 , 241 }

SELF:ogbServerDetails:Text := "Server Details :"

SELF:ogbServerDetails:TabIndex := 29

SELF:Controls:Add(SELF:ogbServerDetails)

SELF:opbxConnection := System.Windows.Forms.PictureBox{}

SELF:opbxConnection:Name := "pbxConnection"

SELF:opbxConnection:Location := System.Drawing.Point{ 240 , 64 }

SELF:opbxConnection:Size := System.Drawing.Size{ 112 , 104 }

SELF:opbxConnection:BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D

SELF:opbxConnection:Image := (System.Drawing.Bitmap)oResourceManager:GetObject( "connection04.jpg" )

SELF:opbxConnection:TabIndex := 0

SELF:ogbServerDetails:Controls:Add(SELF:opbxConnection)

SELF:ogbServerDetails:ResumeLayout()

SELF:ogbDatabase:ResumeLayout()

SELF:ResumeLayout()

RETURN

METHOD pbLoadFromXMLClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoLoadFromXML()

SELF:PopulateDetailsFromObject()

RETURN

METHOD PopulateDetailsFromObject() AS VOID

otbFilename:Text := SELF:oParent:oConnDetails:cFile

otbFolder:Text := SELF:oParent:oConnDetails:cFolder

ocbProviders:Text := SELF:oParent:oConnDetails:cProvider

otbServerName:Text := SELF:oParent:oConnDetails:cServerName

ocbIntSecurity:Text := SELF:oParent:oConnDetails:cSecurity

ocbTimeOut:Text := SELF:oParent:oConnDetails:nTimeOut:ToString()

otbUserName:Text := SELF:oParent:oConnDetails:cUserName

otbPassWord:Text := SELF:oParent:oConnDetails:cPassWord

ocbWindowsUserAccess:Checked := SELF:oParent:oConnDetails:lWindowsUserAccess

otbInstanceName:Text := SELF:oParent:oConnDetails:cInstance

RETURN

METHOD DoLoadFromXML()AS VOID

LOCAL serial AS Serialization.XmlSerializer

LOCAL oSR AS StreamReader

LOCAL cFile, cText, cHead AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "Existing file will be read!"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

//----- read object from XML file -----

serial := Serialization.XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFile}

SELF:oParent:oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

oSR := NULL_OBJECT

serial := NULL_OBJECT

ELSE

cText := "File can't be found !?"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

ENDIF

RETURN

METHOD fmConnectionLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent := (fmBESTcompany)SELF:Owner

SELF:PopulateDetailsFromObject()

RETURN

METHOD pbViewXMLfileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cFile AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF !File.Exists(cFile)

RETURN

ENDIF

//============================

/*LOCAL oProcess AS Process

oProcess := Process{}

oProcess:StartInfo:FileName := cFile

oProcess:StartInfo:WorkingDirectory := ""

oProcess:StartInfo:UseShellExecute := TRUE

oProcess:Start()

//MessageBox.Show("Close this to terminate the Shell Execute !")

IF ! oProcess:HasExited

oProcess:CloseMainWindow()

oProcess:Close()

ENDIF

oProcess := NULL_OBJECT */

LOCAL cXML, cLine AS STRING

cXML := ""

LOCAL oSR AS StreamReader

oSR := StreamReader{cFile}

DO WHILE .T.

cLine := oSR:Readline()

IF cLine = NULL_OBJECT

EXIT

ENDIF

cXML += cLine+CRLF

ENDDO

oSR:Close()

oSR:Dispose()

oSR := NULL_OBJECT

SELF:ortbConnection:Text := cXML

RETURN

METHOD pbSerializeToFileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoSerialization2File()

RETURN

METHOD DoSerialization2File() AS VOID

SELF:DoForm2Object()

LOCAL serial AS Serialization.XmlSerializer

LOCAL oSW AS StreamWriter

LOCAL cFile, cText, cHead AS STRING

cFile := Application.StartupPath+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "File exists already!"

cHead := "XML file :"

MessageBox.Show(SELF,cText,cHead)

ENDIF

//----- save object to XML file -----

serial := Serialization.XmlSerializer{typeof(ConnectionDetails)}

oSW := StreamWriter{cFile}

serial:Serialize(oSW, SELF:oParent:oConnDetails)

oSW:Close()

oSW := NULL_OBJECT

serial := NULL_OBJECT

RETURN

METHOD DoForm2Object() AS VOID

SELF:oParent:oConnDetails:cFile := otbFilename:Text:Trim()

SELF:oParent:oConnDetails:cFolder := otbFolder:Text:Trim()

SELF:oParent:oConnDetails:cFilePath := otbFolder:Text:Trim()+otbFilename:Text:Trim()

SELF:oParent:oConnDetails:cProvider := ocbProviders:Text:Trim()

SELF:oParent:oConnDetails:cServerName := otbServerName:Text:Trim()

SELF:oParent:oConnDetails:cSecurity := ocbIntSecurity:Text:Trim()

SELF:oParent:oConnDetails:nTimeOut := (INT)Conversion.Val(ocbTimeOut:Text)

SELF:oParent:oConnDetails:cUserName := otbUserName:Text:Trim()

SELF:oParent:oConnDetails:cPassWord := otbPassWord:Text:Trim()

SELF:oParent:oConnDetails:lWindowsUserAccess := ocbWindowsUserAccess:Checked

SELF:oParent:oConnDetails:cInstance := otbInstanceName:Text:Trim()

SELF:oParent:cStrConn := ""

LOCAL cSource, cAttach, cSecurity, cTime, cInst AS STRING

//------------ Hard Wired to get working ------------

//cSource := "Data Source=BEST-V64QUAD;"

//cAttach := "AttachDbFilename="+cFilePath+";;"

//cSecurity := "Integrated Security=SSPI;"

//cTime := "Connect Timeout=13;"

//cTime := "Connect Timeout=0;" //----- Willie said this would leave it always open -----

//cInst := ""

cSource := "Data Source="+SELF:oParent:oConnDetails:cServerName+";"

cAttach := "AttachDbFilename="+SELF:oParent:oConnDetails:cFilePath+";;"

cSecurity := "Integrated Security="+SELF:oParent:oConnDetails:cSecurity+";"

cTime := "Connect Timeout="+SELF:oParent:oConnDetails:nTimeOut:ToString()+";" //----- Willie said this would leave it always open -----

cInst := ""

SELF:oParent:cStrConn := cSource + cAttach + cSecurity + cTime +cInst

SELF:oParent:oConnDetails:cConnectionString := SELF:oParent:cStrConn

RETURN

METHOD pbSelectFolderClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoSelectFolder()

RETURN

METHOD DoSelectFolder() AS VOID

LOCAL oFBD AS FolderBrowserDialog

oFBD := FolderBrowserDialog{}

oFBD:ShowNewFolderButton := FALSE

oFBD:RootFolder := Environment.SpecialFolder.Desktop

oFBD:ShowDialog()

SELF:cPath := oFBD:SelectedPath+"\"

otbFolder:Text := cPath

oFBD := NULL_OBJECT

RETURN

METHOD pbViewConnectionStringClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cText, cHead AS STRING

cHead := "Connection String is :- "

cText := "Test the Connection FIRST, the try again !"

IF SELF:oParent:oConnDetails:cConnectionString:Trim():Length < 13

MessageBox.Show(SELF,cText,"WARNING :")

ELSE

MessageBox.Show(SELF:oParent:oConnDetails:cConnectionString,cHead)

ENDIF

RETURN

METHOD pbTextConnectionClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent:DoSetConnectionString()

LOCAL cResult AS STRING

IF SELF:oParent:DoTryToConnect()

cResult := "Connection : - State = Open !"

SELF:oParent:lConnectionTestedOK := TRUE

ELSE

cResult := "Connection : - State = Closed ??"

SELF:oParent:lConnectionTestedOK := FALSE

ENDIF

MessageBox.Show(SELF,cResult,"Attempt to Connect :")

SELF:oParent:DoForceDisconnect()

RETURN

METHOD pbCheckFileClick(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL cFile, cText, cHead AS STRING

//cFile := SELF:oParent:oConnDetails:cFilePath:Trim()

cFile := otbFolder:Text:Trim()+otbFilename:Text:Trim()

IF File.Exists(cFile)

cText := "Yes, file exists !"+CRLF

cText += cFile+CRLF

ELSE

cText := "No, file can't be found ?"+CRLF

cText += cFile+CRLF

ENDIF

cHead := "File Check - Connection :"

MessageBox.Show(SELF,cText,cHead)

RETURN

METHOD pbOKClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:Close()

END CLASS

[Serializable]CLASS ConnectionDetails

EXPORT cFile AS STRING

EXPORT cFolder AS STRING

EXPORT cFilePath AS STRING

EXPORT cProvider AS STRING

EXPORT cServerName AS STRING

EXPORT cSecurity AS STRING

EXPORT nTimeOut AS INT

EXPORT cUserName AS STRING

EXPORT cPassWord AS STRING

EXPORT lWindowsUserAccess AS Boolean

EXPORT cInstance AS STRING

EXPORT cConnectionString AS STRING

CONSTRUCTOR()

SUPER()

cFile := ""

cFolder := ""

cFilePath := ""

cProvider := ""

cServerName := ""

cSecurity := ""

nTimeOut := 0

cUserName := ""

cPassWord := ""

lWindowsUserAccess := TRUE

cInstance := ""

cConnectionString := ""

RETURN

END CLASS


Reports

// Application : BESTcompany

// Reports.prg , Created : 29/06/2008 09:17:38 AM

#USING System.Windows.Forms

#USING System.Drawing

#using System.Data.Common

#using System.IO

#using System.Data.SqlClient

#using System.Data

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml

#using System.Diagnostics

#using system.globalization

#using System.Threading

CLASS fmReports INHERIT System.Windows.Forms.Form

PROTECT opbUseProcWIthDates AS System.Windows.Forms.Button

PROTECT opbCreateProcDateLive AS System.Windows.Forms.Button

PROTECT ortbSelectStatement AS System.Windows.Forms.RichTextBox

PROTECT olbxStandardStatements AS System.Windows.Forms.ListBox

PROTECT opbInteractiveDates AS System.Windows.Forms.Button

PROTECT opbRunQuery AS System.Windows.Forms.Button

PROTECT ocbDisplaySQLstatement AS System.Windows.Forms.CheckBox

PROTECT opbxReports AS System.Windows.Forms.PictureBox

PROTECT ogbReportType AS System.Windows.Forms.GroupBox

PROTECT orbCustomer AS System.Windows.Forms.RadioButton

PROTECT orbSales AS System.Windows.Forms.RadioButton

PROTECT orbProduct AS System.Windows.Forms.RadioButton

PROTECT orbOther AS System.Windows.Forms.RadioButton

PROTECT orbOrder AS System.Windows.Forms.RadioButton

PROTECT orbError AS System.Windows.Forms.RadioButton

PROTECT odgvReports AS System.Windows.Forms.DataGridView

PROTECT opbOK AS System.Windows.Forms.Button

PROTECT olbTo AS System.Windows.Forms.Label

PROTECT olbFrom AS System.Windows.Forms.Label

PROTECT odtpFinish AS System.Windows.Forms.DateTimePicker

PROTECT odtpStart AS System.Windows.Forms.DateTimePicker

// User code starts here (DO NOT remove this line) ##USER##

PROTECT oParent AS fmBESTcompany

PROTECT aStatements AS Statement[]

PROTECT cFocusSelect AS STRING

PROTECT oCI AS CultureInfo

PROTECT cDisplayMode AS STRING

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:MinimumSize := SELF:Size

//SELF:MaximumSize := SELF:Size

LOCAL oSize AS Size

oSize := SELF:Size

oSize:Width += 1300

SELF:MaximumSize := oSize

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

ocbDisplaySQLstatement:Checked := TRUE

SELF:cDisplayMode := "D"

SELF:FillList()

opbxReports:SizeMode := PictureBoxSizeMode.StretchImage

/*oCI := Application.CurrentCulture

MessageBox.Show(oCI:DateTimeFormat:ShortDatePattern)*/

RETURN

METHOD FillList() AS VOID

aStatements := Statement[]{22}

LOCAL oState AS Statement

LOCAL cSwitch AS STRING

//cSwitch := "T"

//cSwitch := "D"

cSwitch := cDisplayMode

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Customers];"

oState:Description := "All Customers"

oState:Type := "C"

aStatements[1] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Orders];"

oState:Description := "All Orders"

oState:Type := "O"

aStatements[2] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Products];"

oState:Description := "All Products"

oState:Type := "P"

aStatements[3] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [DeliveryAddresses];"

oState:Description := "All Delivery Addresses"

oState:Type := "DA"

aStatements[4] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [OrderItems];"

oState:Description := "All Order Items"

oState:Type := "O"

aStatements[5] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [ProductTypes];"

oState:Description := "All Product Types"

oState:Type := "P"

aStatements[6] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Exceptions];"

oState:Description := "All Exceptions"

oState:Type := "EX"

aStatements[7] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Patients];"

oState:Description := "Test error throw as no such table exists!"

oState:Type := "PT"

aStatements[8] := oState

oState := Statement{cSwitch}

LOCAL cSubQ AS STRING

cSubQ := "(SELECT DISTINCT [OrderID] from [OrderItems] WHERE [ProductID] = 3)"

oState:Text := cSubQ+";"

oState:Description := "Testing distint use!!"

oState:Type := "S"

aStatements[9] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT * FROM [Orders] WHERE [OrderID] IN ("+cSubQ+");"

oState:Description := "Checking out a sub-query"

oState:Type := "S"

aStatements[10] := oState

oState := Statement{cSwitch}

//----- return datetime - date only / midnight time -----

oState:Text := "SELECT DATEADD(day, DATEDIFF(day, '20040101', CURRENT_TIMESTAMP), '20040101');"

oState:Description := "Paul Nielsen -return datetime - date only / midnight time- "

oState:Type := "Q"

aStatements[11] := oState

oState := Statement{cSwitch}

//----- extract date only as string -----

oState:Text := "SELECT Convert(CHAR(10), CURRENT_TIMESTAMP, 101);"

oState:Description := "Paul Nielsen -extract date only as string- "

oState:Type := "Q"

aStatements[12] := oState

oState := Statement{cSwitch}

//----- extract time only as string (with seconds) -----

oState:Text := "SELECT substring(Convert(CHAR(19), CURRENT_TIMESTAMP, 120),12,8);"

oState:Description := "Paul Nielsen -extract time only as string (with seconds)- "

oState:Type := "Q"

aStatements[13] := oState

oState := Statement{cSwitch}

//----- extract time only as string (without seconds) -----

oState:Text := "SELECT substring(Convert(CHAR(19), CURRENT_TIMESTAMP, 120),12,5);"

oState:Description := "Paul Nielsen -extract time only as string (without seconds)- "

oState:Type := "Q"

aStatements[14] := oState

oState := Statement{cSwitch}

oState:Text := "select [ProductID],[Quantity] from [OrderItems] "

oState:Text += "where [OrderID] in (select [OrderID] from [Orders] where [OrderDateTime] between '20080101' and '20090101');"

oState:Description := "Trying between dates to reduce rows."

oState:Type := "S"

aStatements[15] := oState

oState := Statement{cSwitch}

oState:Text := "select [ProductID],[Quantity] from [OrderItems] "

oState:Text += "where [OrderID] in (select [OrderID] from [Orders] where [OrderDateTime] between '01/13/2008' and '01/13/2009');"

oState:Description := "Trying between dates with a more familiar date format."

oState:Type := "S"

aStatements[16] := oState

/*LOCAL dtNow AS DateTime

dtNow := DateTime.Now

MessageBox.Show(dtNow:ToShortDateString()) */

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID, [Orders].OrderDateTime "

oState:Text += "FROM [Customers] LEFT OUTER JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "ORDER BY [LastName];"

oState:Description := "Checking out a simple Left Outer Join"

oState:Type := "S"

aStatements[17] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID, [Orders].OrderDateTime "

oState:Text += "FROM [Customers] LEFT OUTER JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "WHERE [Orders].OrderDateTime < '01/01/2007'"

oState:Text += "ORDER BY [LastName];"

oState:Description := "Checking Left Outer Join with a Where clause."

oState:Type := "S"

aStatements[18] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID, [Orders].OrderDateTime "

oState:Text += "FROM [Customers] INNER JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "WHERE [Orders].OrderDateTime > '01/01/2008'"

oState:Text += "ORDER BY [LastName];"

oState:Description := "Trying an Inner Join."

oState:Type := "S"

aStatements[19] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID, [Orders].OrderDateTime "

oState:Text += "FROM [Customers] LEFT OUTER JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "WHERE [Orders].OrderID IS NULL "

oState:Text += "ORDER BY [LastName];"

oState:Description := "More Join testing."

oState:Type := "S"

aStatements[20] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID,"

oState:Text += " [Orders].OrderDateTime, [OrderItems].Quantity, [Products].ProductName "

oState:Text += "FROM [Customers] "

oState:Text += "JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "JOIN [OrderItems] "

oState:Text += "ON [Orders].OrderID = [OrderItems].OrderID "

oState:Text += "JOIN [Products] "

oState:Text += "ON [OrderItems].ProductID = [Products].ProductID "

oState:Text += "WHERE [Orders].OrderDateTime > '01/01/2008'"

oState:Text += "ORDER BY [ProductName];"

oState:Description := "Getting Join happy - more complex!"

oState:Type := "S"

aStatements[21] := oState

oState := Statement{cSwitch}

oState:Text := "SELECT [Customers].CustomerID, [FirstName], [LastName], [Orders].OrderID,"

oState:Text += " [Orders].OrderDateTime, [OrderItems].Quantity, [Products].ProductName,[ProductTypes].ProductTypeName "

oState:Text += "FROM [Customers] "

oState:Text += "JOIN [Orders] "

oState:Text += "ON [Orders].CustomerID = [Customers].CustomerID "

oState:Text += "JOIN [OrderItems] "

oState:Text += "ON [Orders].OrderID = [OrderItems].OrderID "

oState:Text += "JOIN [Products] "

oState:Text += "ON [OrderItems].ProductID = [Products].ProductID "

oState:Text += "JOIN [ProductTypes] "

oState:Text += "ON [Products].ProductTypeID = [ProductTypes].ProductTypeID "

oState:Text += "WHERE [Orders].OrderDateTime > '01/01/2008'"

oState:Text += "ORDER BY [OrderID];"

oState:Description := "Finally, a full test of Joins for this example DB."

oState:Type := "S"

aStatements[22] := oState

//----------------------------------------------------

olbxStandardStatements:Items:Clear()

olbxStandardStatements:Items:AddRange(aStatements)

olbxStandardStatements:SelectedIndex := 0

LOCAL oS AS Statement

oS := aStatements[olbxStandardStatements:SelectedIndex+1]

cFocusSelect := oS:Text

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

LOCAL oResourceManager AS System.Resources.ResourceManager

oResourceManager := System.Resources.ResourceManager{ "Designers" , System.Reflection.Assembly.GetExecutingAssembly() }

SELF:Name := "fmReports"

SELF:SuspendLayout()

SELF:Location := System.Drawing.Point{100,100}

SELF:ClientSize := System.Drawing.Size{826,430}

SELF:Text := "BEST company : Reports :"

SELF:Load += System.EventHandler{ SELF , @fmReportsLoad() }

SELF:opbUseProcWIthDates := System.Windows.Forms.Button{}

SELF:opbUseProcWIthDates:Name := "pbUseProcWIthDates"

SELF:opbUseProcWIthDates:Location := System.Drawing.Point{ 464 , 72 }

SELF:opbUseProcWIthDates:Size := System.Drawing.Size{ 128 , 23 }

SELF:opbUseProcWIthDates:Text := "Use Proc with Dates"

SELF:opbUseProcWIthDates:Click += System.EventHandler{ SELF , @pbUseProcWIthDatesClick() }

SELF:opbUseProcWIthDates:TabIndex := 14

SELF:Controls:Add(SELF:opbUseProcWIthDates)

SELF:opbCreateProcDateLive := System.Windows.Forms.Button{}

SELF:opbCreateProcDateLive:Name := "pbCreateProcDateLive"

SELF:opbCreateProcDateLive:Location := System.Drawing.Point{ 464 , 40 }

SELF:opbCreateProcDateLive:Size := System.Drawing.Size{ 128 , 23 }

SELF:opbCreateProcDateLive:Text := "Create Proc 'DateLive'"

SELF:opbCreateProcDateLive:Click += System.EventHandler{ SELF , @pbCreateProcDateLiveClick() }

SELF:opbCreateProcDateLive:TabIndex := 13

SELF:Controls:Add(SELF:opbCreateProcDateLive)

SELF:ortbSelectStatement := System.Windows.Forms.RichTextBox{}

SELF:ortbSelectStatement:Name := "rtbSelectStatement"

SELF:ortbSelectStatement:Location := System.Drawing.Point{ 0 , 200 }

SELF:ortbSelectStatement:Size := System.Drawing.Size{ 760 , 64 }

SELF:ortbSelectStatement:BackColor := System.Drawing.Color.AntiqueWhite

SELF:ortbSelectStatement:TabIndex := 8

SELF:Controls:Add(SELF:ortbSelectStatement)

SELF:olbxStandardStatements := System.Windows.Forms.ListBox{}

SELF:olbxStandardStatements:Name := "lbxStandardStatements"

SELF:olbxStandardStatements:Location := System.Drawing.Point{ 0 , 104 }

SELF:olbxStandardStatements:Size := System.Drawing.Size{ 616 , 96 }

SELF:olbxStandardStatements:BackColor := System.Drawing.Color.Beige

SELF:olbxStandardStatements:DoubleClick += System.EventHandler{ SELF , @lbxStandardStatementsDoubleClick() }

SELF:olbxStandardStatements:SelectedIndexChanged += System.EventHandler{ SELF , @lbxStandardStatementsSelectedIndexChanged() }

SELF:olbxStandardStatements:TabIndex := 9

SELF:Controls:Add(SELF:olbxStandardStatements)

SELF:opbInteractiveDates := System.Windows.Forms.Button{}

SELF:opbInteractiveDates:Name := "pbInteractiveDates"

SELF:opbInteractiveDates:Location := System.Drawing.Point{ 464 , 8 }

SELF:opbInteractiveDates:Size := System.Drawing.Size{ 128 , 23 }

SELF:opbInteractiveDates:Text := "Interactive Dates "

SELF:opbInteractiveDates:Click += System.EventHandler{ SELF , @pbInteractiveDatesClick() }

SELF:opbInteractiveDates:TabIndex := 12

SELF:Controls:Add(SELF:opbInteractiveDates)

SELF:opbRunQuery := System.Windows.Forms.Button{}

SELF:opbRunQuery:Name := "pbRunQuery"

SELF:opbRunQuery:Location := System.Drawing.Point{ 128 , 24 }

SELF:opbRunQuery:Size := System.Drawing.Size{ 88 , 23 }

SELF:opbRunQuery:Text := "Run Query"

SELF:opbRunQuery:Click += System.EventHandler{ SELF , @pbTypeOneClick() }

SELF:opbRunQuery:TabIndex := 2

SELF:Controls:Add(SELF:opbRunQuery)

SELF:ocbDisplaySQLstatement := System.Windows.Forms.CheckBox{}

SELF:ocbDisplaySQLstatement:Name := "cbDisplaySQLstatement"

SELF:ocbDisplaySQLstatement:Location := System.Drawing.Point{ 128 , 0 }

SELF:ocbDisplaySQLstatement:Size := System.Drawing.Size{ 152 , 24 }

SELF:ocbDisplaySQLstatement:Text := "Display SQL statement :"

SELF:ocbDisplaySQLstatement:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:ocbDisplaySQLstatement:TabIndex := 7

SELF:Controls:Add(SELF:ocbDisplaySQLstatement)

SELF:opbxReports := System.Windows.Forms.PictureBox{}

SELF:opbxReports:Name := "pbxReports"

SELF:opbxReports:Location := System.Drawing.Point{ 0 , 0 }

SELF:opbxReports:Size := System.Drawing.Size{ 120 , 104 }

SELF:opbxReports:BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D

SELF:opbxReports:Image := (System.Drawing.Bitmap)oResourceManager:GetObject( "Reports01.jpg" )

SELF:opbxReports:TabIndex := 11

SELF:Controls:Add(SELF:opbxReports)

SELF:ogbReportType := System.Windows.Forms.GroupBox{}

SELF:ogbReportType:Name := "gbReportType"

SELF:ogbReportType:SuspendLayout()

SELF:ogbReportType:Location := System.Drawing.Point{ 624 , 72 }

SELF:ogbReportType:Size := System.Drawing.Size{ 200 , 120 }

SELF:ogbReportType:Text := "Report Type :"

SELF:ogbReportType:TabIndex := 10

SELF:Controls:Add(SELF:ogbReportType)

SELF:orbCustomer := System.Windows.Forms.RadioButton{}

SELF:orbCustomer:Name := "rbCustomer"

SELF:orbCustomer:Location := System.Drawing.Point{ 104 , 24 }

SELF:orbCustomer:Size := System.Drawing.Size{ 88 , 24 }

SELF:orbCustomer:Text := "Customer :"

SELF:orbCustomer:TabIndex := 1

SELF:ogbReportType:Controls:Add(SELF:orbCustomer)

SELF:orbSales := System.Windows.Forms.RadioButton{}

SELF:orbSales:Name := "rbSales"

SELF:orbSales:Location := System.Drawing.Point{ 104 , 72 }

SELF:orbSales:Size := System.Drawing.Size{ 88 , 24 }

SELF:orbSales:Text := "Sales"

SELF:orbSales:TabIndex := 5

SELF:ogbReportType:Controls:Add(SELF:orbSales)

SELF:orbProduct := System.Windows.Forms.RadioButton{}

SELF:orbProduct:Name := "rbProduct"

SELF:orbProduct:Location := System.Drawing.Point{ 104 , 48 }

SELF:orbProduct:Size := System.Drawing.Size{ 88 , 24 }

SELF:orbProduct:Text := "Product :"

SELF:orbProduct:TabIndex := 3

SELF:ogbReportType:Controls:Add(SELF:orbProduct)

SELF:orbOther := System.Windows.Forms.RadioButton{}

SELF:orbOther:Name := "rbOther"

SELF:orbOther:Location := System.Drawing.Point{ 16 , 72 }

SELF:orbOther:Size := System.Drawing.Size{ 88 , 24 }

SELF:orbOther:Text := "Other :"

SELF:orbOther:TabIndex := 4

SELF:ogbReportType:Controls:Add(SELF:orbOther)

SELF:orbOrder := System.Windows.Forms.RadioButton{}

SELF:orbOrder:Name := "rbOrder"

SELF:orbOrder:Location := System.Drawing.Point{ 16 , 24 }

SELF:orbOrder:Size := System.Drawing.Size{ 64 , 24 }

SELF:orbOrder:Text := "Order :"

SELF:orbOrder:TabIndex := 0

SELF:ogbReportType:Controls:Add(SELF:orbOrder)

SELF:orbError := System.Windows.Forms.RadioButton{}

SELF:orbError:Name := "rbError"

SELF:orbError:Location := System.Drawing.Point{ 16 , 48 }

SELF:orbError:Size := System.Drawing.Size{ 72 , 24 }

SELF:orbError:Text := "Error :"

SELF:orbError:TabIndex := 2

SELF:ogbReportType:Controls:Add(SELF:orbError)

SELF:odgvReports := System.Windows.Forms.DataGridView{}

SELF:odgvReports:Name := "dgvReports"

SELF:odgvReports:Location := System.Drawing.Point{ 0 , 264 }

SELF:odgvReports:Size := System.Drawing.Size{ 824 , 166 }

SELF:odgvReports:BackColor := System.Drawing.Color.CadetBlue

SELF:odgvReports:TabIndex := 1

SELF:odgvReports:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Bottom + System.Windows.Forms.AnchorStyles.Left + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:odgvReports)

SELF:opbOK := System.Windows.Forms.Button{}

SELF:opbOK:Name := "pbOK"

SELF:opbOK:Location := System.Drawing.Point{ 768 , 232 }

SELF:opbOK:Size := System.Drawing.Size{ 51 , 23 }

SELF:opbOK:Text := "OK"

SELF:opbOK:ForeColor := System.Drawing.Color.Purple

SELF:opbOK:BackColor := System.Drawing.Color.Thistle

SELF:opbOK:Click += System.EventHandler{ SELF , @pbOKClick() }

SELF:opbOK:TabIndex := 0

SELF:opbOK:Anchor := System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:opbOK)

SELF:olbTo := System.Windows.Forms.Label{}

SELF:olbTo:Name := "lbTo"

SELF:olbTo:Location := System.Drawing.Point{ 608 , 40 }

SELF:olbTo:Size := System.Drawing.Size{ 40 , 23 }

SELF:olbTo:Text := "To :"

SELF:olbTo:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbTo:TabIndex := 6

SELF:olbTo:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:olbTo)

SELF:olbFrom := System.Windows.Forms.Label{}

SELF:olbFrom:Name := "lbFrom"

SELF:olbFrom:Location := System.Drawing.Point{ 608 , 8 }

SELF:olbFrom:Size := System.Drawing.Size{ 48 , 23 }

SELF:olbFrom:Text := "From :"

SELF:olbFrom:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbFrom:TabIndex := 5

SELF:olbFrom:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:olbFrom)

SELF:odtpFinish := System.Windows.Forms.DateTimePicker{}

SELF:odtpFinish:Name := "dtpFinish"

SELF:odtpFinish:Location := System.Drawing.Point{ 656 , 40 }

SELF:odtpFinish:Size := System.Drawing.Size{ 168 , 20 }

SELF:odtpFinish:TabIndex := 3

SELF:odtpFinish:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:odtpFinish)

SELF:odtpStart := System.Windows.Forms.DateTimePicker{}

SELF:odtpStart:Name := "dtpStart"

SELF:odtpStart:Location := System.Drawing.Point{ 656 , 8 }

SELF:odtpStart:Size := System.Drawing.Size{ 168 , 20 }

SELF:odtpStart:TabIndex := 4

SELF:odtpStart:Anchor := System.Windows.Forms.AnchorStyles.Top + System.Windows.Forms.AnchorStyles.Right

SELF:Controls:Add(SELF:odtpStart)

SELF:ogbReportType:ResumeLayout()

SELF:ResumeLayout()

RETURN

METHOD pbUseProcWIthDatesClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoUseProcWithDTPdates()

RETURN

METHOD DoUseProcWithDTPdates() AS VOID

//execute DateLive '2008/01/01', '2009/01/01'

LOCAL cText AS STRING

LOCAL cFirst, cLast AS STRING

LOCAL dtFirst, dtLast AS DateTime

dtFirst := odtpStart:Value

dtLast := odtpFinish:Value

//----- format required is year/month/day (there are others) -----

cFirst := SELF:ConvertDateTimeYMD(dtFirst)

cLast := SELF:ConvertDateTimeYMD(dtLast)

cText := "EXECUTE DateLive '" + cFirst + "', '"+ cLast + "';"

cFocusSelect := cText

//MessageBox.Show(SELF,cText,"Proc string :")

SELF:DoRunQuery()

RETURN

METHOD pbCreateProcDateLiveClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoCreateProcDateLive()

RETURN

METHOD DoCreateProcDateLive() AS VOID

LOCAL cText AS STRING

cText := "IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[DateLive]') ) DROP PROCEDURE [DateLive];"

cFocusSelect := cText

//MessageBox.Show(SELF,cText,"Proc string :")

IF !SELF:DoRunQuery()

RETURN

ENDIF

cText := "SET ANSI_NULLS ON;"

cText += "SET QUOTED_IDENTIFIER ON;"

cFocusSelect := cText

//MessageBox.Show(SELF,cText,"Proc string :")

IF !SELF:DoRunQuery()

RETURN

ENDIF

cText := "CREATE PROCEDURE DateLive @start varchar(13), @finish varchar(13) "

cText += "AS "

cText += "BEGIN "

cText += "select [ProductID],[Quantity] from [OrderItems] "

cText += "where [OrderID] in (select [OrderID] from [Orders] "

cText += "where [OrderDateTime] between @start and @Finish); "

cText += "END ; "

cFocusSelect := cText

//MessageBox.Show(SELF,cText,"Proc string :")

IF !SELF:DoRunQuery()

RETURN

ENDIF

/*

***** Object: StoredProcedure [dbo].[DateLive] Script Date: 07/02/2008 08:42:07 *****

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DateLive]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[DateLive]

GO

*/

RETURN

METHOD pbInteractiveDatesClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoInteractiveDates()

RETURN

METHOD DoInteractiveDates() AS VOID

LOCAL cText AS STRING

LOCAL cFirst, cLast AS STRING

LOCAL dtFirst, dtLast AS DateTime

dtFirst := odtpStart:Value

dtLast := odtpFinish:Value

//----- format required is year/month/day (there are others) -----

cFirst := SELF:ConvertDateTimeYMD(dtFirst)

cLast := SELF:ConvertDateTimeYMD(dtLast)

cText := "select [ProductID],[Quantity] from [OrderItems] "

cText += "where [OrderID] in (select [OrderID] from [Orders] where [OrderDateTime] between '"

cText += cFirst

cText += "' and '"

cText += cLast

cText += "');"

cFocusSelect := cText

SELF:DoRunQuery()

RETURN

METHOD ConvertDateTimeYMD(dtVal AS DateTime) AS STRING

LOCAL cResult AS STRING

cResult := ""

cResult += dtVal:Year:ToString()+"/"

cResult += dtVal:Month:ToString()+"/"

cResult += dtVal:day:ToString()

RETURN cResult

METHOD lbxStandardStatementsDoubleClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoChangeListBoxToString()

RETURN

METHOD DoChangeListBoxToString() AS VOID

//MessageBox.Show("Now in double click !")

IF cDisplayMode = "T"

cDisplayMode := "D"

ELSE

cDisplayMode := "T"

ENDIF

SELF:FillList()

RETURN

METHOD lbxStandardStatementsSelectedIndexChanged(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoSelectedStatement()

RETURN

METHOD DoSelectedStatement() AS VOID

LOCAL oS AS Statement

oS := aStatements[olbxStandardStatements:SelectedIndex+1]

SELF:cFocusSelect := oS:Text

ortbSelectStatement:Text := cFocusSelect

RETURN

METHOD fmReportsLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent := (fmBESTcompany)SELF:Owner

RETURN

METHOD pbTypeOneClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoRunQuery()

RETURN

METHOD DoRunQuery() AS Boolean

LOCAL lResult AS Boolean

lResult := TRUE

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := cFocusSelect

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

lResult := FALSE

RETURN lResult

ENDIF

//===============================================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oParent:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS, "Reports")

//----- display in DGV control -----

odgvReports:DataSource := oDS:Tables["Reports"]

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

//MessageBox.Show(nError:ToString())

//MessageBox.Show(DbEx:Message)

LOCAL oSED AS StoreExceptionDetails

oSED := StoreExceptionDetails{SELF:oParent}

oSED:oDbExcept := DbEx

oSED:ErrorNumber := nError

oSED:RecordInTable()

oSED := NULL_OBJECT

END TRY

SELF:oParent:DoForceDisconnect()

RETURN lResult

METHOD pbOKClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:Close()

RETURN

END CLASS

CLASS Statement

EXPORT Text AS STRING

EXPORT Description AS STRING

EXPORT Type AS STRING

//----- C for Customers, O for Orders, P for products -----

EXPORT Values AS STRING

EXPORT SwitchString AS STRING

CONSTRUCTOR()

SUPER()

SELF:Initialize()

RETURN

CONSTRUCTOR(cSwitch AS STRING)

SUPER()

SELF:Initialize()

IF cSwitch = "T"

SELF:SwitchString := "T"

ENDIF

RETURN

METHOD Initialize() AS VOID

SELF:Text := ""

SELF:SwitchString := "D"

RETURN

METHOD ToString() AS STRING

LOCAL cResult AS STRING

IF SELF:SwitchString == "T"

cResult := SELF:Text:Trim()

ELSE

cResult := SELF:Description:Trim()

ENDIF

RETURN cResult

METHOD SwitchDisplay() AS VOID

IF SELF:SwitchString == "T"

SwitchString := "D"

ELSE

SwitchString := "T"

ENDIF

RETURN

END CLASS


Creating Orders

// Application : BESTcompany

// OrdersAndCreation.prg , Created : 25/06/2008 12:46:34 PM

#USING System.Windows.Forms

#USING System.Drawing

#using System.Data.Common

#using System.IO

#using System.Data.SqlClient

#using System.Data

#using System.Data.SqlTypes

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml

#using System.Diagnostics

#USING System.Drawing.Printing

CLASS fmCreateAnOrder INHERIT System.Windows.Forms.Form

EXPORT olbDeliverTo AS System.Windows.Forms.Label

EXPORT olbCost AS System.Windows.Forms.Label

EXPORT olbDeliveryDetails AS System.Windows.Forms.Label

EXPORT ocbDeliveryAddress AS System.Windows.Forms.ComboBox

EXPORT ocbCustomers AS System.Windows.Forms.ComboBox

EXPORT olbDeliveryAddress AS System.Windows.Forms.Label

EXPORT opbResetOrder AS System.Windows.Forms.Button

EXPORT ocbDisplaySQLstatement AS System.Windows.Forms.CheckBox

EXPORT opbAddNew AS System.Windows.Forms.Button

EXPORT opbCalculateTotals AS System.Windows.Forms.Button

EXPORT otbTotalAmount AS System.Windows.Forms.TextBox

EXPORT otbTotalCost AS System.Windows.Forms.TextBox

EXPORT otbCost1 AS System.Windows.Forms.TextBox

EXPORT otbCost6 AS System.Windows.Forms.TextBox

EXPORT otbCost5 AS System.Windows.Forms.TextBox

EXPORT otbCost4 AS System.Windows.Forms.TextBox

EXPORT otbCost3 AS System.Windows.Forms.TextBox

EXPORT otbCost2 AS System.Windows.Forms.TextBox

EXPORT olbQty AS System.Windows.Forms.Label

EXPORT ocbQtyLineSix AS System.Windows.Forms.ComboBox

EXPORT otbWeight AS System.Windows.Forms.TextBox

EXPORT otbTax AS System.Windows.Forms.TextBox

EXPORT ocbProductLineOne AS System.Windows.Forms.ComboBox

EXPORT ocbQtyLineTwo AS System.Windows.Forms.ComboBox

EXPORT ocbQtyLineThree AS System.Windows.Forms.ComboBox

EXPORT ocbProductLineSix AS System.Windows.Forms.ComboBox

EXPORT ocbQtyLineFive AS System.Windows.Forms.ComboBox

EXPORT ocbProductLineFive AS System.Windows.Forms.ComboBox

EXPORT ocbQtyLineFour AS System.Windows.Forms.ComboBox

EXPORT ocbProductLineFour AS System.Windows.Forms.ComboBox

EXPORT ocbProductLineThree AS System.Windows.Forms.ComboBox

EXPORT ocbProductLineTwo AS System.Windows.Forms.ComboBox

EXPORT ocbQtyLineOne AS System.Windows.Forms.ComboBox

EXPORT olbProductToSend AS System.Windows.Forms.Label

EXPORT olbTotalWeight AS System.Windows.Forms.Label

EXPORT olbTotalAmount AS System.Windows.Forms.Label

EXPORT olbOrderLines AS System.Windows.Forms.Label

EXPORT olbShipping AS System.Windows.Forms.Label

EXPORT olbTax AS System.Windows.Forms.Label

EXPORT otbShipping AS System.Windows.Forms.TextBox

EXPORT olbPostalCode AS System.Windows.Forms.Label

EXPORT olbAddress AS System.Windows.Forms.Label

EXPORT olbFirstName AS System.Windows.Forms.Label

EXPORT olbLastName AS System.Windows.Forms.Label

EXPORT olbSix AS System.Windows.Forms.Label

EXPORT olbFive AS System.Windows.Forms.Label

EXPORT olbFour AS System.Windows.Forms.Label

EXPORT olbThree AS System.Windows.Forms.Label

EXPORT olbTwo AS System.Windows.Forms.Label

EXPORT olbOne AS System.Windows.Forms.Label

EXPORT olbCustomerRef AS System.Windows.Forms.Label

EXPORT otbPostalCode AS System.Windows.Forms.TextBox

EXPORT otbAddress AS System.Windows.Forms.TextBox

EXPORT otbFirstName AS System.Windows.Forms.TextBox

EXPORT otbLastName AS System.Windows.Forms.TextBox

EXPORT olbSelectACustomer AS System.Windows.Forms.Label

EXPORT opbxCreateOrder AS System.Windows.Forms.PictureBox

EXPORT opbOK AS System.Windows.Forms.Button

// User code starts here (DO NOT remove this line) ##USER##

PROTECT oParent AS fmBESTcompany

PROTECT aQtys AS STRING[]

PROTECT alProducts AS ArrayList

PROTECT alCustomers AS ArrayList

PROTECT aQtyCombo AS ComboBox[]

PROTECT aProdCombo AS ComboBox[]

EXPORT aCosts AS TextBox[]

EXPORT nProducts AS INT

PROTECT nQtys, nSelectQ AS INT

PROTECT nSelectProd AS INT

EXPORT aProds AS Product[]

EXPORT oNewOrd AS NewOrder

PROTECT nCustomerID AS INT

PROTECT aDAD AS DeliveryAddressDetail[]

PROTECT aDADtypes AS Type[]

CONSTRUCTOR()

SUPER()

SELF:InitializeForm()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

opbxCreateOrder:SizeMode := PictureBoxSizeMode.StretchImage

aQtys := <STRING>{"1","2","3","4","5","6","7","8","9"}

nQtys := aQtys:Length

ocbQtyLineSix:Items:AddRange(aQtys)

ocbQtyLineFive:Items:AddRange(aQtys)

ocbQtyLineFour:Items:AddRange(aQtys)

ocbQtyLineThree:Items:AddRange(aQtys)

ocbQtyLineTwo:Items:AddRange(aQtys)

ocbQtyLineOne:Items:AddRange(aQtys)

aQtyCombo := ComboBox[]{6}

aQtyCombo[1] := ocbQtyLineOne

aQtyCombo[2] := ocbQtyLineTwo

aQtyCombo[3] := ocbQtyLineThree

aQtyCombo[4] := ocbQtyLineFour

aQtyCombo[5] := ocbQtyLineFive

aQtyCombo[6] := ocbQtyLineSix

aProdCombo := ComboBox[]{6}

aProdCombo[1] := ocbProductLineOne

aProdCombo[2] := ocbProductLineTwo

aProdCombo[3] := ocbProductLineThree

aProdCombo[4] := ocbProductLineFour

aProdCombo[5] := ocbProductLineFive

aProdCombo[6] := ocbProductLineSix

aCosts := Textbox[]{6}

aCosts[1] := otbCost1

aCosts[2] := otbCost2

aCosts[3] := otbCost3

aCosts[4] := otbCost4

aCosts[5] := otbCost5

aCosts[6] := otbCost6

oNewOrd := NewOrder{}

nCustomerID := 0

ocbDisplaySQLstatement:Checked := FALSE

RETURN

METHOD InitializeForm() AS VOID

// IDE generated code (please DO NOT modify)

LOCAL oResourceManager AS System.Resources.ResourceManager

oResourceManager := System.Resources.ResourceManager{ "Designers" , System.Reflection.Assembly.GetExecutingAssembly() }

SELF:Name := "fmCreateAnOrder"

SELF:SuspendLayout()

SELF:Location := System.Drawing.Point{100,100}

SELF:ClientSize := System.Drawing.Size{706,454}

SELF:Text := "BEST company : Create an Order :"

SELF:Load += System.EventHandler{ SELF , @fmCreateAnOrderLoad() }

SELF:olbDeliverTo := System.Windows.Forms.Label{}

SELF:olbDeliverTo:Name := "lbDeliverTo"

SELF:olbDeliverTo:Location := System.Drawing.Point{ 440 , 152 }

SELF:olbDeliverTo:Size := System.Drawing.Size{ 100 , 23 }

SELF:olbDeliverTo:Text := "Deliver To :"

SELF:olbDeliverTo:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbDeliverTo:TabIndex := 57

SELF:Controls:Add(SELF:olbDeliverTo)

SELF:olbCost := System.Windows.Forms.Label{}

SELF:olbCost:Name := "lbCost"

SELF:olbCost:Location := System.Drawing.Point{ 376 , 152 }

SELF:olbCost:Size := System.Drawing.Size{ 48 , 23 }

SELF:olbCost:Text := "Cost :"

SELF:olbCost:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbCost:TabIndex := 45

SELF:Controls:Add(SELF:olbCost)

SELF:olbDeliveryDetails := System.Windows.Forms.Label{}

SELF:olbDeliveryDetails:Name := "lbDeliveryDetails"

SELF:olbDeliveryDetails:Location := System.Drawing.Point{ 440 , 176 }

SELF:olbDeliveryDetails:Size := System.Drawing.Size{ 264 , 112 }

SELF:olbDeliveryDetails:BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D

SELF:olbDeliveryDetails:Text := "Delivery Details :-"

SELF:olbDeliveryDetails:BackColor := System.Drawing.Color.Beige

SELF:olbDeliveryDetails:TabIndex := 56

SELF:Controls:Add(SELF:olbDeliveryDetails)

SELF:ocbDeliveryAddress := System.Windows.Forms.ComboBox{}

SELF:ocbDeliveryAddress:Name := "cbDeliveryAddress"

SELF:ocbDeliveryAddress:Location := System.Drawing.Point{ 480 , 72 }

SELF:ocbDeliveryAddress:Size := System.Drawing.Size{ 56 , 21 }

SELF:ocbDeliveryAddress:SelectionChangeCommitted += System.EventHandler{ SELF , @cbDeliveryAddressSelectionChangeCommitted() }

SELF:ocbDeliveryAddress:TabIndex := 54

SELF:Controls:Add(SELF:ocbDeliveryAddress)

SELF:ocbCustomers := System.Windows.Forms.ComboBox{}

SELF:ocbCustomers:Name := "cbCustomers"

SELF:ocbCustomers:Location := System.Drawing.Point{ 360 , 32 }

SELF:ocbCustomers:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbCustomers:SelectionChangeCommitted += System.EventHandler{ SELF , @cbCustomersSelectionChangeCommitted() }

SELF:ocbCustomers:TabIndex := 3

SELF:Controls:Add(SELF:ocbCustomers)

SELF:olbDeliveryAddress := System.Windows.Forms.Label{}

SELF:olbDeliveryAddress:Name := "lbDeliveryAddress"

SELF:olbDeliveryAddress:Location := System.Drawing.Point{ 376 , 72 }

SELF:olbDeliveryAddress:Size := System.Drawing.Size{ 104 , 23 }

SELF:olbDeliveryAddress:Text := "Delivery Address :"

SELF:olbDeliveryAddress:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbDeliveryAddress:TabIndex := 55

SELF:Controls:Add(SELF:olbDeliveryAddress)

SELF:opbResetOrder := System.Windows.Forms.Button{}

SELF:opbResetOrder:Name := "pbResetOrder"

SELF:opbResetOrder:Location := System.Drawing.Point{ 600 , 336 }

SELF:opbResetOrder:Size := System.Drawing.Size{ 96 , 23 }

SELF:opbResetOrder:Text := "Reset Order"

SELF:opbResetOrder:BackColor := System.Drawing.Color.BurlyWood

SELF:opbResetOrder:Click += System.EventHandler{ SELF , @pbResetOrderClick() }

SELF:opbResetOrder:TabIndex := 53

SELF:Controls:Add(SELF:opbResetOrder)

SELF:ocbDisplaySQLstatement := System.Windows.Forms.CheckBox{}

SELF:ocbDisplaySQLstatement:Name := "cbDisplaySQLstatement"

SELF:ocbDisplaySQLstatement:Location := System.Drawing.Point{ 456 , 376 }

SELF:ocbDisplaySQLstatement:Size := System.Drawing.Size{ 152 , 24 }

SELF:ocbDisplaySQLstatement:Text := "Display SQL Statement :"

SELF:ocbDisplaySQLstatement:TabIndex := 52

SELF:Controls:Add(SELF:ocbDisplaySQLstatement)

SELF:opbAddNew := System.Windows.Forms.Button{}

SELF:opbAddNew:Name := "pbAddNew"

SELF:opbAddNew:Location := System.Drawing.Point{ 456 , 424 }

SELF:opbAddNew:Size := System.Drawing.Size{ 88 , 23 }

SELF:opbAddNew:Text := "Add New"

SELF:opbAddNew:ForeColor := System.Drawing.Color.FromArgb( 0,0,0 )

SELF:opbAddNew:BackColor := System.Drawing.Color.FromArgb( 0,213,0 )

SELF:opbAddNew:Click += System.EventHandler{ SELF , @pbAddNewClick() }

SELF:opbAddNew:TabIndex := 2

SELF:Controls:Add(SELF:opbAddNew)

SELF:opbCalculateTotals := System.Windows.Forms.Button{}

SELF:opbCalculateTotals:Name := "pbCalculateTotals"

SELF:opbCalculateTotals:Location := System.Drawing.Point{ 256 , 424 }

SELF:opbCalculateTotals:Size := System.Drawing.Size{ 104 , 23 }

SELF:opbCalculateTotals:Text := "Calculate Totals"

SELF:opbCalculateTotals:BackColor := System.Drawing.Color.Salmon

SELF:opbCalculateTotals:Click += System.EventHandler{ SELF , @pbCalculateTotalsClick() }

SELF:opbCalculateTotals:TabIndex := 43

SELF:Controls:Add(SELF:opbCalculateTotals)

SELF:otbTotalAmount := System.Windows.Forms.TextBox{}

SELF:otbTotalAmount:Name := "tbTotalAmount"

SELF:otbTotalAmount:Location := System.Drawing.Point{ 112 , 424 }

SELF:otbTotalAmount:Size := System.Drawing.Size{ 80 , 20 }

SELF:otbTotalAmount:TabIndex := 37

SELF:Controls:Add(SELF:otbTotalAmount)

SELF:otbTotalCost := System.Windows.Forms.TextBox{}

SELF:otbTotalCost:Name := "tbTotalCost"

SELF:otbTotalCost:Location := System.Drawing.Point{ 376 , 376 }

SELF:otbTotalCost:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbTotalCost:TabIndex := 51

SELF:Controls:Add(SELF:otbTotalCost)

SELF:otbCost1 := System.Windows.Forms.TextBox{}

SELF:otbCost1:Name := "tbCost1"

SELF:otbCost1:Location := System.Drawing.Point{ 376 , 176 }

SELF:otbCost1:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost1:TabIndex := 44

SELF:Controls:Add(SELF:otbCost1)

SELF:otbCost6 := System.Windows.Forms.TextBox{}

SELF:otbCost6:Name := "tbCost6"

SELF:otbCost6:Location := System.Drawing.Point{ 376 , 336 }

SELF:otbCost6:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost6:TabIndex := 50

SELF:Controls:Add(SELF:otbCost6)

SELF:otbCost5 := System.Windows.Forms.TextBox{}

SELF:otbCost5:Name := "tbCost5"

SELF:otbCost5:Location := System.Drawing.Point{ 376 , 304 }

SELF:otbCost5:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost5:TabIndex := 49

SELF:Controls:Add(SELF:otbCost5)

SELF:otbCost4 := System.Windows.Forms.TextBox{}

SELF:otbCost4:Name := "tbCost4"

SELF:otbCost4:Location := System.Drawing.Point{ 376 , 272 }

SELF:otbCost4:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost4:TabIndex := 48

SELF:Controls:Add(SELF:otbCost4)

SELF:otbCost3 := System.Windows.Forms.TextBox{}

SELF:otbCost3:Name := "tbCost3"

SELF:otbCost3:Location := System.Drawing.Point{ 376 , 240 }

SELF:otbCost3:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost3:TabIndex := 47

SELF:Controls:Add(SELF:otbCost3)

SELF:otbCost2 := System.Windows.Forms.TextBox{}

SELF:otbCost2:Name := "tbCost2"

SELF:otbCost2:Location := System.Drawing.Point{ 376 , 208 }

SELF:otbCost2:Size := System.Drawing.Size{ 48 , 20 }

SELF:otbCost2:TabIndex := 46

SELF:Controls:Add(SELF:otbCost2)

SELF:olbQty := System.Windows.Forms.Label{}

SELF:olbQty:Name := "lbQty"

SELF:olbQty:Location := System.Drawing.Point{ 296 , 152 }

SELF:olbQty:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbQty:Text := "Qty :"

SELF:olbQty:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbQty:TabIndex := 42

SELF:Controls:Add(SELF:olbQty)

SELF:ocbQtyLineSix := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineSix:Name := "cbQtyLineSix"

SELF:ocbQtyLineSix:Location := System.Drawing.Point{ 296 , 336 }

SELF:ocbQtyLineSix:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineSix:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineSixSelectionChangeCommitted() }

SELF:ocbQtyLineSix:TabIndex := 25

SELF:Controls:Add(SELF:ocbQtyLineSix)

SELF:otbWeight := System.Windows.Forms.TextBox{}

SELF:otbWeight:Name := "tbWeight"

SELF:otbWeight:Location := System.Drawing.Point{ 296 , 376 }

SELF:otbWeight:Size := System.Drawing.Size{ 64 , 20 }

SELF:otbWeight:TabIndex := 39

SELF:Controls:Add(SELF:otbWeight)

SELF:otbTax := System.Windows.Forms.TextBox{}

SELF:otbTax:Name := "tbTax"

SELF:otbTax:Location := System.Drawing.Point{ 112 , 376 }

SELF:otbTax:Size := System.Drawing.Size{ 80 , 20 }

SELF:otbTax:TabIndex := 33

SELF:Controls:Add(SELF:otbTax)

SELF:ocbProductLineOne := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineOne:Name := "cbProductLineOne"

SELF:ocbProductLineOne:Location := System.Drawing.Point{ 112 , 176 }

SELF:ocbProductLineOne:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineOne:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineOneSelectionChangeCommitted() }

SELF:ocbProductLineOne:TabIndex := 15

SELF:Controls:Add(SELF:ocbProductLineOne)

SELF:ocbQtyLineTwo := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineTwo:Name := "cbQtyLineTwo"

SELF:ocbQtyLineTwo:Location := System.Drawing.Point{ 296 , 208 }

SELF:ocbQtyLineTwo:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineTwo:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineTwoSelectionChangeCommitted() }

SELF:ocbQtyLineTwo:TabIndex := 17

SELF:Controls:Add(SELF:ocbQtyLineTwo)

SELF:ocbQtyLineThree := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineThree:Name := "cbQtyLineThree"

SELF:ocbQtyLineThree:Location := System.Drawing.Point{ 296 , 240 }

SELF:ocbQtyLineThree:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineThree:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineThreeSelectionChangeCommitted() }

SELF:ocbQtyLineThree:TabIndex := 19

SELF:Controls:Add(SELF:ocbQtyLineThree)

SELF:ocbProductLineSix := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineSix:Name := "cbProductLineSix"

SELF:ocbProductLineSix:Location := System.Drawing.Point{ 112 , 336 }

SELF:ocbProductLineSix:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineSix:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineSixSelectionChangeCommitted() }

SELF:ocbProductLineSix:TabIndex := 26

SELF:Controls:Add(SELF:ocbProductLineSix)

SELF:ocbQtyLineFive := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineFive:Name := "cbQtyLineFive"

SELF:ocbQtyLineFive:Location := System.Drawing.Point{ 296 , 304 }

SELF:ocbQtyLineFive:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineFive:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineFiveSelectionChangeCommitted() }

SELF:ocbQtyLineFive:TabIndex := 23

SELF:Controls:Add(SELF:ocbQtyLineFive)

SELF:ocbProductLineFive := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineFive:Name := "cbProductLineFive"

SELF:ocbProductLineFive:Location := System.Drawing.Point{ 112 , 304 }

SELF:ocbProductLineFive:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineFive:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineFiveSelectionChangeCommitted() }

SELF:ocbProductLineFive:TabIndex := 24

SELF:Controls:Add(SELF:ocbProductLineFive)

SELF:ocbQtyLineFour := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineFour:Name := "cbQtyLineFour"

SELF:ocbQtyLineFour:Location := System.Drawing.Point{ 296 , 272 }

SELF:ocbQtyLineFour:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineFour:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineFourSelectionChangeCommitted() }

SELF:ocbQtyLineFour:TabIndex := 21

SELF:Controls:Add(SELF:ocbQtyLineFour)

SELF:ocbProductLineFour := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineFour:Name := "cbProductLineFour"

SELF:ocbProductLineFour:Location := System.Drawing.Point{ 112 , 272 }

SELF:ocbProductLineFour:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineFour:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineFourSelectionChangeCommitted() }

SELF:ocbProductLineFour:TabIndex := 22

SELF:Controls:Add(SELF:ocbProductLineFour)

SELF:ocbProductLineThree := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineThree:Name := "cbProductLineThree"

SELF:ocbProductLineThree:Location := System.Drawing.Point{ 112 , 240 }

SELF:ocbProductLineThree:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineThree:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineThreeSelectionChangeCommitted() }

SELF:ocbProductLineThree:TabIndex := 20

SELF:Controls:Add(SELF:ocbProductLineThree)

SELF:ocbProductLineTwo := System.Windows.Forms.ComboBox{}

SELF:ocbProductLineTwo:Name := "cbProductLineTwo"

SELF:ocbProductLineTwo:Location := System.Drawing.Point{ 112 , 208 }

SELF:ocbProductLineTwo:Size := System.Drawing.Size{ 176 , 21 }

SELF:ocbProductLineTwo:SelectionChangeCommitted += System.EventHandler{ SELF , @cbProductLineTwoSelectionChangeCommitted() }

SELF:ocbProductLineTwo:TabIndex := 18

SELF:Controls:Add(SELF:ocbProductLineTwo)

SELF:ocbQtyLineOne := System.Windows.Forms.ComboBox{}

SELF:ocbQtyLineOne:Name := "cbQtyLineOne"

SELF:ocbQtyLineOne:Location := System.Drawing.Point{ 296 , 176 }

SELF:ocbQtyLineOne:Size := System.Drawing.Size{ 64 , 21 }

SELF:ocbQtyLineOne:SelectionChangeCommitted += System.EventHandler{ SELF , @cbQtyLineOneSelectionChangeCommitted() }

SELF:ocbQtyLineOne:TabIndex := 16

SELF:Controls:Add(SELF:ocbQtyLineOne)

SELF:olbProductToSend := System.Windows.Forms.Label{}

SELF:olbProductToSend:Name := "lbProductToSend"

SELF:olbProductToSend:Location := System.Drawing.Point{ 112 , 152 }

SELF:olbProductToSend:Size := System.Drawing.Size{ 100 , 23 }

SELF:olbProductToSend:Text := "Product to Send :"

SELF:olbProductToSend:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbProductToSend:TabIndex := 41

SELF:Controls:Add(SELF:olbProductToSend)

SELF:olbTotalWeight := System.Windows.Forms.Label{}

SELF:olbTotalWeight:Name := "lbTotalWeight"

SELF:olbTotalWeight:Location := System.Drawing.Point{ 208 , 376 }

SELF:olbTotalWeight:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbTotalWeight:Text := "Total Weight :"

SELF:olbTotalWeight:ForeColor := System.Drawing.Color.FromArgb( 213,0,0 )

SELF:olbTotalWeight:TabIndex := 40

SELF:Controls:Add(SELF:olbTotalWeight)

SELF:olbTotalAmount := System.Windows.Forms.Label{}

SELF:olbTotalAmount:Name := "lbTotalAmount"

SELF:olbTotalAmount:Location := System.Drawing.Point{ 16 , 424 }

SELF:olbTotalAmount:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbTotalAmount:Text := "Total Amount :"

SELF:olbTotalAmount:ForeColor := System.Drawing.Color.FromArgb( 213,0,0 )

SELF:olbTotalAmount:TabIndex := 38

SELF:Controls:Add(SELF:olbTotalAmount)

SELF:olbOrderLines := System.Windows.Forms.Label{}

SELF:olbOrderLines:Name := "lbOrderLines"

SELF:olbOrderLines:Location := System.Drawing.Point{ 16 , 152 }

SELF:olbOrderLines:Size := System.Drawing.Size{ 88 , 23 }

SELF:olbOrderLines:Text := "Order Lines :"

SELF:olbOrderLines:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbOrderLines:TabIndex := 14

SELF:Controls:Add(SELF:olbOrderLines)

SELF:olbShipping := System.Windows.Forms.Label{}

SELF:olbShipping:Name := "lbShipping"

SELF:olbShipping:Location := System.Drawing.Point{ 16 , 400 }

SELF:olbShipping:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbShipping:Text := "Shipping :"

SELF:olbShipping:ForeColor := System.Drawing.Color.FromArgb( 213,0,0 )

SELF:olbShipping:TabIndex := 36

SELF:Controls:Add(SELF:olbShipping)

SELF:olbTax := System.Windows.Forms.Label{}

SELF:olbTax:Name := "lbTax"

SELF:olbTax:Location := System.Drawing.Point{ 16 , 376 }

SELF:olbTax:Size := System.Drawing.Size{ 80 , 23 }

SELF:olbTax:Text := "Tax :"

SELF:olbTax:ForeColor := System.Drawing.Color.FromArgb( 213,0,0 )

SELF:olbTax:TabIndex := 34

SELF:Controls:Add(SELF:olbTax)

SELF:otbShipping := System.Windows.Forms.TextBox{}

SELF:otbShipping:Name := "tbShipping"

SELF:otbShipping:Location := System.Drawing.Point{ 112 , 400 }

SELF:otbShipping:Size := System.Drawing.Size{ 80 , 20 }

SELF:otbShipping:TabIndex := 35

SELF:Controls:Add(SELF:otbShipping)

SELF:olbPostalCode := System.Windows.Forms.Label{}

SELF:olbPostalCode:Name := "lbPostalCode"

SELF:olbPostalCode:Location := System.Drawing.Point{ 16 , 104 }

SELF:olbPostalCode:Size := System.Drawing.Size{ 96 , 23 }

SELF:olbPostalCode:Text := "Postal Code :"

SELF:olbPostalCode:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbPostalCode:TabIndex := 12

SELF:Controls:Add(SELF:olbPostalCode)

SELF:olbAddress := System.Windows.Forms.Label{}

SELF:olbAddress:Name := "lbAddress"

SELF:olbAddress:Location := System.Drawing.Point{ 16 , 72 }

SELF:olbAddress:Size := System.Drawing.Size{ 96 , 23 }

SELF:olbAddress:Text := "Address :"

SELF:olbAddress:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbAddress:TabIndex := 10

SELF:Controls:Add(SELF:olbAddress)

SELF:olbFirstName := System.Windows.Forms.Label{}

SELF:olbFirstName:Name := "lbFirstName"

SELF:olbFirstName:Location := System.Drawing.Point{ 16 , 40 }

SELF:olbFirstName:Size := System.Drawing.Size{ 96 , 23 }

SELF:olbFirstName:Text := "First Name :"

SELF:olbFirstName:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbFirstName:TabIndex := 8

SELF:Controls:Add(SELF:olbFirstName)

SELF:olbLastName := System.Windows.Forms.Label{}

SELF:olbLastName:Name := "lbLastName"

SELF:olbLastName:Location := System.Drawing.Point{ 16 , 8 }

SELF:olbLastName:Size := System.Drawing.Size{ 96 , 23 }

SELF:olbLastName:Text := "Last Name :"

SELF:olbLastName:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbLastName:TabIndex := 6

SELF:Controls:Add(SELF:olbLastName)

SELF:olbSix := System.Windows.Forms.Label{}

SELF:olbSix:Name := "lbSix"

SELF:olbSix:Location := System.Drawing.Point{ 40 , 336 }

SELF:olbSix:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbSix:Text := "6. >>"

SELF:olbSix:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbSix:TabIndex := 32

SELF:Controls:Add(SELF:olbSix)

SELF:olbFive := System.Windows.Forms.Label{}

SELF:olbFive:Name := "lbFive"

SELF:olbFive:Location := System.Drawing.Point{ 40 , 304 }

SELF:olbFive:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbFive:Text := "5. >>"

SELF:olbFive:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbFive:TabIndex := 31

SELF:Controls:Add(SELF:olbFive)

SELF:olbFour := System.Windows.Forms.Label{}

SELF:olbFour:Name := "lbFour"

SELF:olbFour:Location := System.Drawing.Point{ 40 , 272 }

SELF:olbFour:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbFour:Text := "4. >>"

SELF:olbFour:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbFour:TabIndex := 30

SELF:Controls:Add(SELF:olbFour)

SELF:olbThree := System.Windows.Forms.Label{}

SELF:olbThree:Name := "lbThree"

SELF:olbThree:Location := System.Drawing.Point{ 40 , 240 }

SELF:olbThree:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbThree:Text := "3. >>"

SELF:olbThree:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbThree:TabIndex := 29

SELF:Controls:Add(SELF:olbThree)

SELF:olbTwo := System.Windows.Forms.Label{}

SELF:olbTwo:Name := "lbTwo"

SELF:olbTwo:Location := System.Drawing.Point{ 40 , 208 }

SELF:olbTwo:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbTwo:Text := "2. >>"

SELF:olbTwo:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbTwo:TabIndex := 28

SELF:Controls:Add(SELF:olbTwo)

SELF:olbOne := System.Windows.Forms.Label{}

SELF:olbOne:Name := "lbOne"

SELF:olbOne:Location := System.Drawing.Point{ 40 , 176 }

SELF:olbOne:Size := System.Drawing.Size{ 64 , 23 }

SELF:olbOne:Text := "1. >>"

SELF:olbOne:ForeColor := System.Drawing.Color.FromArgb( 0,0,255 )

SELF:olbOne:TabIndex := 27

SELF:Controls:Add(SELF:olbOne)

SELF:olbCustomerRef := System.Windows.Forms.Label{}

SELF:olbCustomerRef:Name := "lbCustomerRef"

SELF:olbCustomerRef:Location := System.Drawing.Point{ 232 , 104 }

SELF:olbCustomerRef:Size := System.Drawing.Size{ 128 , 23 }

SELF:olbCustomerRef:BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D

SELF:olbCustomerRef:Text := "Customer Ref :"

SELF:olbCustomerRef:ForeColor := System.Drawing.Color.Maroon

SELF:olbCustomerRef:BackColor := System.Drawing.Color.Bisque

SELF:olbCustomerRef:TabIndex := 13

SELF:Controls:Add(SELF:olbCustomerRef)

SELF:otbPostalCode := System.Windows.Forms.TextBox{}

SELF:otbPostalCode:Name := "tbPostalCode"

SELF:otbPostalCode:Location := System.Drawing.Point{ 112 , 104 }

SELF:otbPostalCode:Size := System.Drawing.Size{ 112 , 20 }

SELF:otbPostalCode:TabIndex := 11

SELF:Controls:Add(SELF:otbPostalCode)

SELF:otbAddress := System.Windows.Forms.TextBox{}

SELF:otbAddress:Name := "tbAddress"

SELF:otbAddress:Location := System.Drawing.Point{ 112 , 72 }

SELF:otbAddress:Size := System.Drawing.Size{ 248 , 20 }

SELF:otbAddress:TabIndex := 9

SELF:Controls:Add(SELF:otbAddress)

SELF:otbFirstName := System.Windows.Forms.TextBox{}

SELF:otbFirstName:Name := "tbFirstName"

SELF:otbFirstName:Location := System.Drawing.Point{ 112 , 40 }

SELF:otbFirstName:Size := System.Drawing.Size{ 112 , 20 }

SELF:otbFirstName:TabIndex := 7

SELF:Controls:Add(SELF:otbFirstName)

SELF:otbLastName := System.Windows.Forms.TextBox{}

SELF:otbLastName:Name := "tbLastName"

SELF:otbLastName:Location := System.Drawing.Point{ 112 , 8 }

SELF:otbLastName:Size := System.Drawing.Size{ 112 , 20 }

SELF:otbLastName:TabIndex := 5

SELF:Controls:Add(SELF:otbLastName)

SELF:olbSelectACustomer := System.Windows.Forms.Label{}

SELF:olbSelectACustomer:Name := "lbSelectACustomer"

SELF:olbSelectACustomer:Location := System.Drawing.Point{ 360 , 8 }

SELF:olbSelectACustomer:Size := System.Drawing.Size{ 120 , 23 }

SELF:olbSelectACustomer:Text := "Select a Customer :"

SELF:olbSelectACustomer:ForeColor := System.Drawing.Color.FromArgb( 0,170,0 )

SELF:olbSelectACustomer:TabIndex := 4

SELF:Controls:Add(SELF:olbSelectACustomer)

SELF:opbxCreateOrder := System.Windows.Forms.PictureBox{}

SELF:opbxCreateOrder:Name := "pbxCreateOrder"

SELF:opbxCreateOrder:Location := System.Drawing.Point{ 544 , 8 }

SELF:opbxCreateOrder:Size := System.Drawing.Size{ 160 , 160 }

SELF:opbxCreateOrder:BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D

SELF:opbxCreateOrder:Image := (System.Drawing.Bitmap)oResourceManager:GetObject( "Orders04.jpg" )

SELF:opbxCreateOrder:TabIndex := 1

SELF:Controls:Add(SELF:opbxCreateOrder)

SELF:opbOK := System.Windows.Forms.Button{}

SELF:opbOK:Name := "pbOK"

SELF:opbOK:Location := System.Drawing.Point{ 640 , 424 }

SELF:opbOK:Size := System.Drawing.Size{ 56 , 23 }

SELF:opbOK:Text := "OK"

SELF:opbOK:Click += System.EventHandler{ SELF , @pbOKClick() }

SELF:opbOK:TabIndex := 0

SELF:Controls:Add(SELF:opbOK)

SELF:ResumeLayout()

RETURN

METHOD cbDeliveryAddressSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoDisplayDeliveryDetails()

RETURN

METHOD DoDisplayDeliveryDetails() AS VOID

LOCAL nIndex AS INT

nIndex := ocbDeliveryAddress:SelectedIndex+1

LOCAL oDAD AS DeliveryAddressDetail

oDAD := aDAD[nIndex]

LOCAL cText AS STRING

cText := ""

cText += oDAD:ContactPerson+CRLF

cText += oDAD:Company+CRLF

cText += oDAD:Address+CRLF

cText += oDAD:City+CRLF

cText += oDAD:StateProvince+CRLF

cText += oDAD:Country+CRLF

cText += oDAD:PostalCode+CRLF

olbDeliveryDetails:Text := cText

RETURN

METHOD pbResetOrderClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:ClearLastOrder()

RETURN

METHOD pbCalculateTotalsClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoCalculateTotals()

RETURN

METHOD DoCalculateTotals() AS VOID

oNewOrd:CalculateTotals()

RETURN

METHOD cbQtyLineSixSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbProductLineSixSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbQtyLineFiveSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbProductLineFiveSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbQtyLineFourSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbProductLineFourSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbQtyLineThreeSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbProductLineThreeSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbQtyLineTwoSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD DoItemLineCheck(o AS OBJECT , e AS System.EventArgs) AS VOID

LOCAL oCB, oCB2 AS ComboBox

oCB := (ComboBox)o

LOCAL cName, cItem, cType AS STRING

LOCAL i, nIndex, nNum AS INT

nIndex := oCB:SelectedIndex+1

cName := oCB:Name

//MessageBox.Show( cName+" : "+nIndex:ToString())

cItem := ".. empty .."

cType := ""

IF cName:IndexOf("Qty") >=0

//MessageBox.Show("it is a Qty !")

nSelectQ := nIndex //--- only if Qtys. go from one and increment in one too ! -----

//nSelectQ := (INT)Conversion.Val(aQtys[nIndex])

//MessageBox.Show("quantity required :- "+nSelectQ:ToString())

cType := "Q"

nNum := nSelectQ

ELSE

//MessageBox.Show("it is a Prod !")

LOCAL oP AS Product

oP := aProds[nIndex]

nSelectProd := oP:ProductID

//MessageBox.Show("product ID required :- "+nSelectProd:ToString())

cType := "P"

nNum := nSelectProd

ENDIF

LOCAL nItemLine AS INT

//----- we need the item line (1 - 6) -----

IF cName:IndexOf("One") >= 0

nItemLine := 1

ENDIF

IF cName:IndexOf("Two") >= 0

nItemLine := 2

ENDIF

IF cName:IndexOf("Three") >= 0

nItemLine := 3

ENDIF

IF cName:IndexOf("Four") >= 0

nItemLine := 4

ENDIF

IF cName:IndexOf("Five") >= 0

nItemLine := 5

ENDIF

IF cName:IndexOf("Six") >= 0

nItemLine := 6

ENDIF

//MessageBox.Show("Item line is :- "+nItemLine:ToString())

//----- update the specific item line in the New Order object -----

SELF:oNewOrd:UpdateItemLines(SELF, nItemLine, cType, nNum)

RETURN

METHOD cbProductLineTwoSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbQtyLineOneSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbProductLineOneSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoItemLineCheck(o,e)

RETURN

METHOD cbCustomersSelectionChangeCommitted(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoCustomerSelected()

RETURN

METHOD DoCustomerSelected() AS VOID

LOCAL oCust AS Customer

oCust := (Customer)ocbCustomers:SelectedItem

otbFirstName:Text := oCust:FirstName

otbLastName:Text := oCust:LastName

otbAddress:Text := oCust:Address

otbPostalCode:Text := oCust:PostalCode

olbCustomerRef:Text := "Customer Ref :"+oCust:CustomerID:ToString()

nCustomerID := oCust:CustomerID

oNewOrd:CustomerID := oCust:CustomerID

SELF:FindAndFillDeliveryAddresses(nCustomerID)

RETURN

METHOD FindAndFillDeliveryAddresses(nCustomerID AS INT) AS Boolean

LOCAL lResult AS Boolean

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "SELECT [DeliveryAddressID],[CustomerID],[ContactPerson],[Company],[Address],"

cText += "[City],[StateProvince],[PostalCode],[Country],[DateTimeStamp] "

cText += "FROM [DeliveryAddresses] "

cText += "WHERE [CustomerID] = "+nCustomerID:ToString()+" ORDER BY [DeliveryAddressID];"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

lResult := FALSE

RETURN lResult

ENDIF

//===============================================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oParent:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS, "Delivery")

SELF:GetDADtypes(oDS:Tables["Delivery"])

SELF:FillDropDown(oDS:Tables["Delivery"])

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

LOCAL oSED AS StoreExceptionDetails

oSED := StoreExceptionDetails{SELF:oParent}

oSED:oDbExcept := DbEx

oSED:ErrorNumber := nError

oSED:RecordInTable()

oSED := NULL_OBJECT

END TRY

SELF:oParent:DoForceDisconnect()

RETURN lResult

METHOD FillDropDown(oDT AS DataTable) AS VOID

LOCAL nCols, nRows, i AS INT

nCols := oDT:Columns:Count

nRows := oDT:Rows:Count

LOCAL oDR AS DataRow

aDAD := DeliveryAddressDetail[]{nRows}

FOR i := 1 UPTO nRows

oDR := oDT:Rows:Item[nRows-1]

aDAD[i] := SELF:MakeDADobject(oDR)

NEXT

ocbDeliveryAddress:Items:Clear()

ocbDeliveryAddress:Items:AddRange(aDAD)

RETURN

METHOD GetDADtypes(oDT AS DataTable) AS VOID

LOCAL cText AS STRING

LOCAL nCols, i AS INT

nCols := oDT:Columns:Count

aDADtypes := Type[]{nCols}

cText := ""

FOR i := 1 UPTO nCols

aDADtypes[i] := oDT:Columns:Item[i-1]:DataType

cText += aDADtypes[i]:ToString()+CRLF

NEXT

//MessageBox.Show(cText)

//MessageBox.Show(aDADtypes[1]:Name)

RETURN

METHOD MakeDADobject(oDR AS DataRow) AS DeliveryAddressDetail

LOCAL oDAD AS DeliveryAddressDetail

oDAD := DeliveryAddressDetail{}

/*LOCAL oType AS Type

oType := aDADtypes[1] */

oDAD:DeliveryAddressID := (INT)oDR:Item["DeliveryAddressID"]

oDAD:CustomerID := (INT)oDR:Item["CustomerID"]

oDAD:ContactPerson := (STRING)oDR:Item["ContactPerson"]

oDAD:Company := (STRING)oDR:Item["Company"]

oDAD:Address := (STRING)oDR:Item["Address"]

oDAD:City := (STRING)oDR:Item["City"]

oDAD:StateProvince := (STRING)oDR:Item["StateProvince"]

oDAD:PostalCode := (STRING)oDR:Item["PostalCode"]

oDAD:Country := (STRING)oDR:Item["Country"]

oDAD:DateTimeStamp := (DateTime)oDR:Item["DateTimeStamp"]

RETURN oDAD

METHOD pbAddNewClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:DoAddNewOrder()

RETURN

METHOD DoAddNewOrder() AS VOID

IF nCustomerID < 1

MessageBox.Show(SELF,"First select a customer from the list.","CARE - Select !")

RETURN

ENDIF

//========================

IF !oNewOrd:GTotalCost > 0

MessageBox.Show(SELF,"Select at least one item.","CARE - Select !")

RETURN

ENDIF

//=====================================

LOCAL oIcon AS MessageBoxIcon

oIcon := MessageBoxIcon.Question

LOCAL oMBB AS MessageBoxButtons

oMBB := MessageBoxButtons.YesNo

LOCAL oDef AS MessageBoxDefaultButton

oDef := MessageBoxDefaultButton.Button2

LOCAL cMess, cHead AS STRING

cMess := "Create a new Order & Order Items in the SQL tables ?"

cHead := "CONFIRM - Create !?"

LOCAL oDR AS DialogResult

oDR := MessageBox.Show(SELF, cMess, cHead, oMBB, oIcon, oDef)

IF oDR = DialogResult.No

RETURN

ENDIF

//=================================

SELF:DoCalculateTotals()

SELF:CreateNewOrder()

RETURN

METHOD ClearLastOrder() AS VOID

oNewOrd := NewOrder{}

nCustomerID := 0

SELF:EmptyTextBoxes()

SELF:ClearComboBoxes()

RETURN

METHOD EmptyTextBoxes() AS VOID

otbPostalCode:Text := ""

otbAddress:Text := ""

otbFirstName:Text := ""

otbLastName:Text := ""

SELF:olbCustomerRef:Text := "Customer Ref :"

otbTotalAmount:Text := ""

otbTotalCost:Text := ""

otbCost1:Text := ""

otbCost6:Text := ""

otbCost5:Text := ""

otbCost4:Text := ""

otbCost3:Text := ""

otbCost2:Text := ""

otbWeight:Text := ""

otbTax:Text := ""

otbShipping:Text := ""

RETURN

METHOD ClearComboBoxes() AS VOID

ocbCustomers:SelectedIndex := 0

ocbQtyLineOne:Text := ""

ocbQtyLineTwo:Text := ""

ocbQtyLineThree:Text := ""

ocbQtyLineFour:Text := ""

ocbQtyLineFive:Text := ""

ocbQtyLineSix:Text := ""

ocbProductLineOne:Text := ""

ocbProductLineTwo:Text := ""

ocbProductLineThree:Text := ""

ocbProductLineFour:Text := ""

ocbProductLineFive:Text := ""

ocbProductLineSix:Text := ""

RETURN

METHOD CreateNewOrder() AS VOID

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [Orders] ([CustomerID],[OrderDateTime],[Tax],[Shipping])"

cText += "VALUES (@cid,@odt,@tx,@sp) "

cText += "SELECT @@Identity ;"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL dtStamp AS DateTime

dtStamp := DateTime.Now

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@cid"

oParam:Value := oNewOrd:CustomerID

oCommand:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@odt"

oParam:Value := dtStamp

oCommand:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@tx"

oParam:Value := oNewOrd:TotalTax

oCommand:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@sp"

oParam:Value := oNewOrd:TotalShipping

oCommand:Parameters:Add(oParam)

TRY

//----- execute the insert statement -----

LOCAL nOrderID AS INT

LOCAL cOrderID AS STRING

cOrderID := oCommand:ExecuteScalar():ToString()

nOrderID := (INT)Conversion.Val(cOrderID)

//MessageBox.Show(SELF,nOrderID:ToString(),"New Identity :")

/*oCommand:ExecuteNonQuery()

Application.DoEvents()

System.Threading.Thread.Sleep(130)

Application.DoEvents() */

//--------------------------------

//nOrderID := SELF:FindNewOrderID(dtStamp)

//----- MUST set an orderID in the Object -----

//----- for use with creating order lines -----

IF nOrderID > 0

oNewOrd:OrderID := nOrderID

LOCAL i AS INT

FOR i := 1 UPTO 6

IF oNewOrd:aItemLines[i]:Cost > 0

SELF:CreateNewOrderLine(i)

ENDIF

NEXT

SELF:ClearLastOrder()

ELSE

//----- WARN -----

MessageBox.Show(SELF,"No valid order ID returned from SQL engine ?","WARN !!!")

ENDIF

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN

METHOD FindNewOrderID(dtStamp AS DateTime) AS INT

//------ not required now @@Identity does the job fine -----

LOCAL dtCutOff AS DateTime

dtCutOff := dtStamp:AddMinutes(-3)

SELF:oParent:DoSetConnectionString()

SELF:oParent:DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := SELF:oParent:oDFactory:CreateCommand()

oDComm:Connection := SELF:oParent:oDConn

LOCAL cStatement AS STRING

cStatement := "SELECT [OrderID],[CustomerID], [OrderDateTime] FROM [Orders] "

cStatement += "WHERE [OrderDateTime] > @odt AND [CustomerID]=@cid;"

//cStatement += "WHERE [CustomerID]=@cid;"

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.Text

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cStatement)

ENDIF

//====================================================

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@odt"

oParam:Value := dtStamp

oDComm:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@cid"

oParam:Value := oNewOrd:CustomerID

oDComm:Parameters:Add(oParam)

LOCAL oDr AS DbDataReader

TRY

oDr := oDComm:ExecuteReader()

LOCAL nOrderID, nCustomerID AS INT

LOCAL dtValue AS DateTime

LOCAL nBigOrderID AS INT

nOrderID := 0

nBigOrderID := 0

IF ! oDr == NULL

DO WHILE oDr:Read()

//cFullName := (STRING)oDr:GetValue(0)

nOrderID := oDr:GetInt32(0)

IF nOrderID > nBigOrderID

nBigOrderID := nOrderID

ENDIF

nCustomerID := oDr:GetInt32(1)

dtValue := oDr:GetDateTime(2)

//MessageBox.Show(nOrderID:ToString())

ENDDO

//MessageBox.Show(nBigOrderID:ToString())

ELSE

MessageBox.Show("Nothing coming back from SQL DB engine!","WARNING !!!")

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS DbException

MessageBox.Show(ex:Message)

END TRY

//SELF:oParent:DoForceDisconnect()

RETURN nBigOrderID

METHOD CreateNewOrderLine(nLine AS INT) AS VOID

//MessageBox.Show("in create new order lines method "+oNewOrd:OrderID:ToString())

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [OrderItems] ([OrderID],[ProductID],[Quantity])"

cText += "VALUES (@oid,@pid,@qt);"

IF ocbDisplaySQLstatement:Checked

MessageBox.Show(cText)

ENDIF

//====================================================

SELF:oParent:DoSetConnectionString()

IF !SELF:oParent:DoTryToConnect()

RETURN

ENDIF

//==========================

LOCAL oCommand AS DbCommand

oCommand := SELF:oParent:oDFactory:CreateCommand()

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.Text

//----- set the connection property of DbCommand -----

oCommand:Connection := SELF:oParent:oDConn

LOCAL oParam AS DbParameter

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@oid"

oParam:Value := oNewOrd:OrderID

oCommand:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@pid"

oParam:Value := oNewOrd:aItemLines[nLine]:ProductID

oCommand:Parameters:Add(oParam)

oParam := SELF:oParent:oDFactory:CreateParameter()

oParam:DbType := DbType.Double

oParam:ParameterName := "@qt"

oParam:Value := oNewOrd:aItemLines[nLine]:Qty

oCommand:Parameters:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

RETURN

METHOD fmCreateAnOrderLoad(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:oParent := (fmBESTcompany)SELF:Owner

LOCAL oPR AS ProductRange

oPR := ProductRange{SELF:oParent}

alProducts := oPR:GetAllProducts() //--- zero based ---

oPR := NULL_OBJECT

aProds := (Product[])alProducts:ToArray(typeof(Product))

ocbProductLineOne:Items:AddRange(aProds)

ocbProductLineTwo:Items:AddRange(aProds)

ocbProductLineThree:Items:AddRange(aProds)

ocbProductLineFour:Items:AddRange(aProds)

ocbProductLineFive:Items:AddRange(aProds)

ocbProductLineSix:Items:AddRange(aProds)

SELF:nProducts := aProds:Length

LOCAL oAC AS AllCustomers

oAC := AllCustomers{SELF:oParent}

alCustomers := oAC:GetAllCustomers() //--- zero based ---

LOCAL aCust AS Customer[]

aCust := (Customer[])alCustomers:ToArray(typeof(Customer))

ocbCustomers:Items:AddRange(aCust)

ocbCustomers:SelectedIndex := 0

RETURN

METHOD pbOKClick(o AS OBJECT , e AS System.EventArgs) AS VOID

SELF:Close()

RETURN

END CLASS

CLASS Customer

EXPORT FirstName AS STRING

EXPORT LastName AS STRING

EXPORT Address AS STRING

EXPORT City AS STRING

EXPORT StateProvince AS STRING

EXPORT PostalCode AS STRING

EXPORT Country AS STRING

EXPORT CustomerID AS INT

CONSTRUCTOR()

SUPER()

RETURN

METHOD ToString() AS STRING

RETURN FirstName+" "+LastName

END CLASS

CLASS AllCustomers

EXPORT alCustomers AS ArrayList

EXPORT oParent AS fmBESTcompany

CONSTRUCTOR(oOwner AS fmBESTcompany)

SUPER()

SELF:Initialize()

SELF:oParent := oOwner

RETURN

METHOD Initialize() AS VOID

RETURN

METHOD GetAllCustomers() AS ArrayList

LOCAL alCustomers AS ArrayList

SELF:oParent:DoSetConnectionString()

SELF:oParent:DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := SELF:oParent:oDFactory:CreateCommand()

oDComm:Connection := SELF:oParent:oDConn

LOCAL oDr AS DbDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT [FirstName],[LastName],[Address],[City],"

cStatement += "[StateProvince],[PostalCode],[Country],[CustomerID]"

cStatement += " FROM [Customers] ORDER BY [CustomerID] DESC;"

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.Text

oDr := oDComm:ExecuteReader()

alCustomers := ArrayList{}

LOCAL cFirstName, cLastName AS STRING

LOCAL oCust AS Customer

IF ! oDr == NULL

DO WHILE oDr:Read()

oCust := Customer{}

cFirstName := oDr:GetString(0):Trim()

cLastName := oDr:GetString(1):Trim()

IF cFirstName:Length < 1

cFirstName := "<< .. empty .. >>"

ENDIF

IF cLastName:Length < 1

cLastName := "<< .. empty .. >>"

ENDIF

oCust:FirstName := cFirstName

oCust:LastName := cLastName

oCust:Address := oDr:GetString(2):Trim()

oCust:City := oDr:GetString(3):Trim()

oCust:StateProvince := oDr:GetString(4):Trim()

oCust:PostalCode := oDr:GetString(5):Trim()

oCust:Country := oDr:GetString(6):Trim()

oCust:CustomerID := oDr:GetInt32(7)

alCustomers:Add(oCust)

ENDDO

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS DbException

MessageBox.Show(ex:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN alCustomers

END CLASS

CLASS Product

EXPORT ProductName AS STRING

EXPORT ProductPrice AS Double

EXPORT ProductDescription AS STRING

EXPORT ProductShippingOunces AS INT

EXPORT ProductID AS INT

EXPORT ProductTypeID AS INT

CONSTRUCTOR()

SUPER()

RETURN

METHOD ToString() AS STRING

RETURN ProductName

END CLASS

CLASS ProductRange

EXPORT alProducts AS ArrayList

EXPORT oParent AS fmBESTcompany

CONSTRUCTOR(oOwner AS fmBESTcompany)

SUPER()

SELF:Initialize()

SELF:oParent := oOwner

RETURN

METHOD Initialize() AS VOID

RETURN

METHOD GetAllProducts() AS ArrayList

LOCAL alProducts AS ArrayList

SELF:oParent:DoSetConnectionString()

SELF:oParent:DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := SELF:oParent:oDFactory:CreateCommand()

oDComm:Connection := SELF:oParent:oDConn

LOCAL oDr AS DbDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT [ProductName],[ProductID],[ProductPrice],"

cStatement += "[ProductDescription],[ProductShippingOunces],[ProductTypeID]"

cStatement += " FROM [Products] ORDER BY [ProductID];"

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cStatement

oDComm:CommandType := Data.CommandType.Text

oDr := oDComm:ExecuteReader()

alProducts := ArrayList{}

LOCAL cFullName AS STRING

LOCAL oProd AS Product

LOCAL oSM AS SqlMoney

LOCAL cSM AS STRING

IF ! oDr == NULL

DO WHILE oDr:Read()

oProd := Product{}

cFullName := oDr:GetString(0)

cFullName := cFullName:Trim()

IF cFullName:Length < 1

cFullName := "<< .. empty .. >>"

ENDIF

oProd:ProductName := cFullName

oProd:ProductID := oDr:GetInt32(1)

cSM := oDr:GetValue(2):ToString()

oProd:ProductPrice := Conversion.Val(cSM)

oProd:ProductDescription := oDr:GetString(3)

oProd:ProductShippingOunces := oDr:GetInt16(4)

oProd:ProductTypeID := oDr:GetInt32(5)

alProducts:Add(oProd)

ENDDO

ENDIF

oDr:Close()

oDr := NULL_OBJECT

CATCH ex AS DbException

MessageBox.Show(ex:Message)

END TRY

SELF:oParent:DoForceDisconnect()

RETURN alProducts

END CLASS

CLASS NewOrder

EXPORT GTotalWeight AS Double

EXPORT GTotalCost AS Double

EXPORT GTotalAmount AS Double

EXPORT TotalTax AS Double

EXPORT TotalShipping AS Double

EXPORT CustomerID AS INT

EXPORT OrderID AS INT

EXPORT FilledLines AS INT

EXPORT aItemLines AS OrderItemLine[]

PROTECT oParent AS fmCreateAnOrder

CONSTRUCTOR()

SUPER()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

LOCAL i AS INT

LOCAL oOIL AS OrderItemLine

aItemLines := OrderItemLine[]{6}

FOR i := 1 UPTO 6

oOIL := OrderItemLine{}

aItemLines[i] := oOIL

NEXT

GTotalWeight := 0

GTotalCost := 0

TotalTax := 0

TotalShipping := 0

GTotalAmount := 0

OrderID := 0

CustomerID := 0

FilledLines := 0

RETURN

METHOD UpdateItemLines(oOwner AS fmCreateAnOrder, nItemLine AS INT, cType AS STRING, nNum AS INT) AS VOID

oParent := oOwner

//----- cType is either P or Q indicating the obvious -----

//----- nNum is either a ProdID or a Qty -----

LOCAL oOIL AS OrderItemLine

oOIL := aItemLines[nItemLine]

IF cType == "P"

oOIL:UpdateItemLineProd(oOwner,nNum)

ELSE

oOIL:UpdateItemLineQty(oOwner,nNum)

ENDIF

SELF:CalculateTotals()

//MessageBox.Show(aItemLines[1]:Cost:ToString())

oParent:otbCost1:Text := aItemLines[1]:Cost:ToString()

oParent:otbCost2:Text := aItemLines[2]:Cost:ToString()

oParent:otbCost3:Text := aItemLines[3]:Cost:ToString()

oParent:otbCost4:Text := aItemLines[4]:Cost:ToString()

oParent:otbCost5:Text := aItemLines[5]:Cost:ToString()

oParent:otbCost6:Text := aItemLines[6]:Cost:ToString()

RETURN

METHOD CalculateTotals() AS VOID

GTotalWeight := 0

GTotalCost := 0

TotalTax := 0

TotalShipping := 0

LOCAL i AS INT

LOCAL oOIL AS OrderItemLine

FOR i := 1 UPTO 6

oOIL := aItemLines[i]

GTotalWeight += oOIL:LineWeight

GTotalCost += oOIL:Cost

NEXT

TotalTax := GTotalCost*0.13

//TotalShipping := GTotalWeight*0.085

TotalShipping := GTotalWeight*0.165

oParent:otbTax:Text := TotalTax:ToString()

oParent:otbShipping:Text := TotalShipping:ToString()

oParent:otbTotalCost:Text := GTotalCost:ToString()

oParent:otbWeight:Text := GTotalWeight:ToString()

GTotalAmount := GTotalCost +TotalTax+ TotalShipping

oParent:otbTotalAmount:Text := GTotalAmount:ToString()

RETURN

END CLASS

CLASS OrderItemLine

EXPORT ProdIndex AS INT

EXPORT Qty AS INT

EXPORT Product AS STRING

EXPORT ProductID AS INT

EXPORT OrderID AS INT

EXPORT ItemPrice AS Double

EXPORT ItemWeight AS Double

EXPORT Cost AS Double

EXPORT LineWeight AS Double

CONSTRUCTOR()

SUPER()

SELF:Initialize()

RETURN

METHOD Initialize() AS VOID

ProdIndex := 0

Qty := 0

Product := ""

ProductID := 0

OrderID := 0

ItemPrice := 0.0

ItemWeight := 0.0

Cost := 0.0

LineWeight := 0.0

RETURN

METHOD UpdateItemLineProd(oOwner AS fmCreateAnOrder, nNum AS INT) AS VOID

LOCAL lFound AS Boolean

LOCAL i AS INT

LOCAL oP AS Product

lFound := FALSE

FOR i := 1 UPTO oOwner:nProducts

IF oOwner:aProds[i]:ProductID = nNum

oP := oOwner:aProds[i]

lFound := TRUE

EXIT

ENDIF

NEXT

IF !lFound

RETURN

ENDIF

//===============

Product := oP:ProductName

ProductID := oP:ProductID

ItemPrice := oP:ProductPrice

ItemWeight := oP:ProductShippingOunces

IF Qty > 0

LineWeight := ItemWeight*Qty

Cost := ItemPrice*Qty

ELSE

LineWeight := 0.0

Cost := 0.0

ENDIF

//MessageBox.Show(Cost:ToString(),"Prod Update :")

RETURN

METHOD UpdateItemLineQty(oOwner AS fmCreateAnOrder, nNum AS INT) AS VOID

SELF:Qty := nNum

IF ItemPrice > 0

Cost := ItemPrice*Qty

ENDIF

IF ItemWeight > 0

LineWeight := ItemWeight*Qty

ELSE

LineWeight := 0.0

ENDIF

//MessageBox.Show(Cost:ToString(), "Qty Update :")

RETURN

METHOD CalculateTotals() AS VOID

RETURN

END CLASS


Section 4

Issues with smallmoney and similar / possible MS data type in the tables - check out what is whose and the general ones to safely use

Reporting Service - another set of notes ClickStartRS


Appendix A - Serialize Connection Details

Once you have more than a test or trial app, you need to provide an interactive way to both 'set and test' your connection string details, as well as then storing them for automatic use next time the app runs.

In this appendix we will examine in detail a method which works quite effectively, and which can then be used in future projects. At least in a similar way to the way it works here at present.

The general idea is that there is a data file in XML format which contains the connection details.

When the app launches it looks for this file, and if found, reads the details back into an appropriate object.

Think of the XML file as just a 'dump' of the object to file. The term 'de-serialize' is used for the process of reading the XML file data back in and populating the object properties.

(Properties can be arrays and ArrayLists too, they don't have to be as simple as in this case.)

Lets us look at the object used in our section 3 project :-

#USING System.Xml

...............................

=============================================================

And also it may be useful to see the current working XML file as it stands :-

=====================================================================================

A glance down the XML text steam will show properties listed in the class above.

You will see that all but two, are string data types.

Okay then, if we have a object into which to read data, and some data to read in, shall we now look at the code to do the reading and populate the object properties :-

========================================================================================================

Above is the code section to read in the XML data, directly into the object itself.

Let us now look at what we do when the app first launches. The method called Initialize() is run at launch time and it mainly looks like this :-

METHOD Initialize() AS VOID

SELF:oConnDetails := ConnectionDetails{}

..............................................................

IF SELF:ReadSerialXmlFile()

SELF:DoSetConnectionString()

IF SELF:DoTryToConnect()

//MessageBox.Show("Connection String - GOOD !!")

ELSE

MessageBox.Show("CARE - you need to set your connection string !!!")

ENDIF

ENDIF

RETURN

Obviously, from the code above, you can see that we also need to view the methods called DoSetConnectionString() and DoTryToConnect() as well.

METHOD DoTryToConnect() AS LOGIC

LOCAL lResult AS Boolean

lResult := FALSE

TRY

oDConn:Open()

SELF:Text := "Vulcan.NET : BEST company : << Connected! >>"

lResult := TRUE

CATCH sqlEx AS DbException

MessageBox.Show(sqlEx:Message)

SELF:Text := "Vulcan.NET : BEST company : << NOT Connected! >>"

END TRY

RETURN lResult

METHOD DoSetConnectionString() AS VOID

IF String.IsNullOrEmpty(SELF:oConnDetails:cProvider)

MessageBox.Show(SELF,"Provider string NOT available !","WARNING")

RETURN

ENDIF

//====================================================================

IF SELF:oConnDetails:cUserName:IndexOf("<") > -1 .OR. SELF:oConnDetails:cUserName:IndexOf(">") > -1

SELF:oConnDetails:cUserName := ""

ENDIF

IF SELF:oConnDetails:cPassWord:IndexOf("<") > -1 .OR. SELF:oConnDetails:cPassWord:IndexOf(">") > -1

SELF:oConnDetails:cPassWord := ""

ENDIF

//----- get the factory for the provider we require -----

SELF:oDFactory := DbProviderFactories.GetFactory("System.Data."+SELF:oConnDetails:cProvider)

//----- create a connection STRING ------

oDConn := oDFactory:CreateConnection()

//====================================================

SELF:cStrConn := ""

LOCAL cSource, cAttach, cSecurity, cTime, cInst AS STRING

//------------ Hard Wired to get working ------------

//cSource := "Data Source=BEST-V64QUAD;"

//cAttach := "AttachDbFilename="+cFilePath+";;"

//cSecurity := "Integrated Security=SSPI;"

//cTime := "Connect Timeout=13;"

//cTime := "Connect Timeout=0;" //----- Willie said this would leave it always open -----

//cInst := ""

cSource := "Data Source="+SELF:oConnDetails:cServerName+";"

cAttach := "AttachDbFilename="+SELF:oConnDetails:cFilePath+";;"

cSecurity := "Integrated Security="+SELF:oConnDetails:cSecurity+";"

cTime := "Connect Timeout="+SELF:oConnDetails:nTimeOut:ToString()+";" //----- Willie said this would leave it always open -----

cInst := ""

cStrConn := cSource + cAttach + cSecurity + cTime +cInst

oDConn:ConnectionString := cStrConn

//MessageBox.Show(oDConn:ConnectionString:ToString())

oConnDetails:cConnectionString := cStrConn

RETURN

When you examine this is detail you will see that there is provision for you to have a password and user name, and also to provide an instance name of the server.

How you use the above code and general approach is up to you, and your specific computer platform - as well as the provider SQL server.

At least you have some solid, working code on which to base any changes you make.

Okay then, we are just about done. All we need to do now is be able to make - or remake - the XML file from any selected user inputs.

In other words we need a method to 'do the serialization' and produce the disk file :-

=========================================================================

You will notice that I have chosen (rightly or wrongly) to have the XML file placed in the same folder as the application, or EXE file.

Obviously, you may place it where you wish, but if you are not to 'hard wire' it you will have to provide 'config' type information.

I like this method for running on single platforms as it allows the EXE file to be placed in a variety of places and the code will still work.

You do whatever you see fits your situation - or your fancy. Lets take a final look at the connection screen :-

You can see as well as loading the XML file it is possible to view it. The form extends to the right to allow full viewing.

Once details are selected for a connection, a test is done, and if all is OK then the details serialized to file. It is the a good idea to read this back in and try it once more.

Good Luck with your connecting !

Remember, without a connection you can't go any further :O)

PHH - July 2008.


Appendix B - Locating the Databases

In order to run the three projects in these help notes, you need to have appropriate databases which have tables with structures that match the Vulcan code.


Appendix C - DateTime, Aspects and Issues

The DateTime data type is both powerful and flexible. However, to some it is also a bit of a nightmare.

I recommend that before you start using it in SQL tables, you make an effort to get a 'good working feel' for it by doing some test coding.

In having to write the small section in 'ClickStartVDN' I had to explore some of the depths of DateTime, and I am pleased I did. Without that work and effort I would be lost now.

Somewhere in 'ClickStartVDN' I alluded to the fact that there was no NULL for the DateTime data type. Well we need to revisit this topic here as table data entries 'muddy the water' somewhat on this issue.

There are many complex issues relating to DateTime and depending upon your needs, you can either ignore these, or you will have to find out a lot more from other suitable sources.

Don't forget the help in MS, and then there is Paul Nielsen's book.

Before we get bogged down by difficult issues, let us first start with some helpful advice from Paul Nielsen, which actually works :-)

When dealing with code within the Server itself, it is often useful, and necessary, to find the following :-

Remember we are using functions of SQL Server, not Vulcan or dotNet.

There are four helpful tips which as you can see he shows during the video lessons from Total Training.

He assures that these are the fastest ways to find what we are after, as he has made timed trials. (See more in his book.)

Once we know what these are doing for us, we can write UDFs (User Defined Functions) which will reside at the server end, and can be called by us at will. See the next appendix for this.

Below is the input to a query pane in MS, with the results shown in gray :-

select CURRENT_TIMESTAMP

2008-07-04 08:13:29.337

-- next trial / test --

select DATEDIFF(day,'2004/01/01', CURRENT_TIMESTAMP)

1646

-- next trial / test --

select DATEADD(day, 1646, '2004/01/01')

2008-07-04 00:00:00.000

-- next trial / test --

SELECT DATEADD(day, DATEDIFF(day,'2004/01/01', CURRENT_TIMESTAMP), '2004/01/01')

2008-07-04 00:00:00.000

-- next trial / test --

By working through what has been done we can see the following :-

Obviously, CURRENT_TIMESTAMP returns a DateTime of 'now' and then a fixed date (here he chose 01/01/2004) is subtracted and only the day difference returned.

The difference in the days is then added back to the same fixed date, with a 'days only' DATEADD. Not surprisingly therefore, we get a 'day only' version of today, now !

The next three examples are a bit simpler, and it is also necessary to look up in the MS help Index - CAST and CONVERT (Transact-SQL).

Above is a portion of what may be found in the MS Help. Notice 101 and 120 which is what we need to use in Paul's recommendations.

At this point it would be a good idea to check out the CONVERT function in some depth.


Appendix D - User Defined Functions

UDF Basics :

Alison Balter has this to say early on in her text :-

"User-defined functions are procedures that accept parameters, perform some sort of action, and return the result of that action as a value.

The return value can be either a single scalar value or a result set. A single scalar value is a simple value such as a number.

A result set refers to something such as an ADO.NET recordset."

She then goes on to say .....

"UDFs have many benefits. Like stored procedures, they allow modular programming, meaning that you can call them from anywhere in your program. If you need to modify the function, you can do so in one place. UDFs allow for faster execution. SQL Server caches them in memory so that they execute faster after the first execution."

In T-SQL we use CREATE FUNCTION :- (below are four lines from the SDK documentation for Framework 3.5)

This creates a user-defined function. This is a saved Transact-SQL or common language runtime (CLR) routine that returns a value.

UDFs cannot be used to perform actions that modify the database state.

UDFs, like system functions, can be invoked from a query.

Scalar functions can be executed by using an EXECUTE statement like stored procedures.

Okay then, now we have some idea of what we are addressing, lets see about making one or more of our own.

In the last section we looked at some useful recommendations from Paul Nielsen regarding DateTime. Lets us attempt to encapsulate the four recommendations into four separate UDFs.

Once we know how to make one, the other three should be easy.

Let us see just how much help we can get from the MS environment itself.

==================================================================================================================

Above you can see what we get from MS 2008, when we right click over the Scalar-valued Functions entry in the Object Explorer pane.

Have a look at this for yourself, using the MS available to you.

What we will do here, is take one of the PN recommendations, get some query code input and tested within the MS environment. And when this is working satisfactorily, code it into Vulcan and provide a way to create and update UDFs from our apps.

The first thing to do is have a new query tab in MS and type in the expression from the PN DVD lesson. The image below shows I have done this and tested it to see we get the correct result returned from the Server :-

=======================================================================================================================

* Note * we will not include the 'AS [Click Test]' in our UDF, this was just for MS output.

Okay then, just re-found why I am doing these notes - has taken quite a while to get the syntax right so that MS accepts the new UDF.

This is with a few good books and SDK documentation, and MS help etc. Anyway, lets have a look at what worked :-

=======================================================================================

So if we use the little trick / technique, to refresh the Object Explorer, and then locate the new scalar function, we can 'right click' and ask for 'script function as ...' then drop and create.

Which leaves us with the following :-

=====================================================================================================================

If we are to make some Vulcan code which creates this UDF we wish it to be 're-usable' and so we need to include the code to remove any exiting similarly named UDF before re-making it.

To test our newly made and stored UDF lets try two ways, using EXECUTE and the SELECT :-

==============================================================================================================

You can see I have run two statements from the MS query pane, and only got back 'results' from the latter.

Below shows what we get when we just execute the function :-

================================================================================================

Hopefully, now we have fought a few syntax monsters, and won :-) we can go on and make the other three UDFs related to Paul's DateTime recommendations.

Also we are much closer to creating and using Vulcan code.

Oh! by the way, it was ten minutes past eleven. And time for a fresh cup of tea.

While refreshing my tea the mailman (postman in the UK) arrived with a new book from Amazon. It is the 'large' book by Paul Nielsen, called the SQL 'Bible'. So from now on I should not have as much difficulty as I seem to have had this morning with UDF syntax.


Appendix E - NULLs and how to Handle

Okay folks. Now that we have had quite a few positive results as far as SQL and Vulcan coding is concerned, lets look at a topic which is a bit of a 'thorny' one as far as the developer is concerned.

I am sure it only looks 'thorny' for those who may not have addressed the issue thoroughly, and made modifications and changes, just to get things to work.

One experienced colleague had this advice :-

"I don't really see NULLs causing problems. NULL is another part of your
total solution, indicating that no value has been assigned to the particular
column. If you have a column where NULL is not wanted, such as a key column,
you would mark that column as not allowing NULL. If you are building queries
on a column with NULL you need to keep that fact in mind as you write the
query."

I think the gist of this remark is that if you go about things right, and with knowledge of what you are dealing with, then things should be OK. Also, that running away from nulls is not professionally acceptable, and I agree on this too.

However, this advice seems mainly directed at the SQL Server and the table data, but the same general attitude can be applied at the Vulcan coding end of things too.

Also, if this sounds a bit 'low key' remember experienced colleagues often don't see the problems we beginners do ;-)

We will use the sections of this appendix to examine in some detail this situation, and any issues involved. We will look at both the Server end of things, and, as we then move (and possibly convert) data to and from our business objects, the situation as far as our Vulcan code is concerned.

Let us start by getting ourselves a good 'test platform' :-

As you may realize by now, the fields in the data tables often contain NULL values. Yes, we can set the property of the table column such that NULL is not allowed when a new record is made, but we still have to deal with NULLs sometime, somewhere :-)

Also, we must not forget that 'null' is information in its own right. It means no information, and this can be very useful in the correct place.

If for example the field data type was bit (logic) and was related to say a check box on a form, then a null value would indicate that the check box had never been checked by the user. The knowledge as to whether the user wants chilly pickle with their Big Mac, is quite different to the fact they have never made a choice at all.

So let us see just what we may be dealing with on this issue.

Lets dive right in and make a test table for the sole purpose of finding out - we will call it "TestTable".

Below is shown a start to this table, it has three records and very few values :-

====================================

====================================================================================================================================================

As you can see, there is a reasonable range of data types, and this was done by design, so we can test those we are most likely to use frequently.

Notice that MS itself seems to handle the NULL situation and actually fills the cell with a string value of "NULL" to indicate to the user what is going on.

Also notice we have one record with all NULLs, you could call this a null record I suppose, and we can use this to good effect later.

Extracting the exact data types we have used from the MS Object Explorer pane :-

money,

smallmoney,

date,

datetime,

bit,

varchar(50),

float,

smalldatetime,

real,

decimal(18,0),

int,

smallint

If like me you are new to the world of SQL, the first thing I noticed when watching the AB videos, was that some of data types were quite a bit different.

Maybe this is one area which if handled correctly, would save us some grief ! I also think looking back, that this is why I had some difficulties (many) when using the data reader object and coding the casting of each field to my Vulcan variables. I have to admit, I did resort on a couple of occasions to going via a 'string' type :O(

Look at the next sub-section to see the dotNet special 'Sql' data types and how this may make things easier for Server 2005 / 2008 developers.

PHH - July 2008.


General Situation

Before we go much further we ought to read what the SDK documentation has to say about handling NULL values, so see below :-

======================================================================================================

So it would make good sense to also see some more detailed information about the SqlDataTypes so read that shown below.

However, before you do - think about this. If there had not been some sort of a problem, or issue, with nulls in the past, then why would Microsoft have made this new namespace with all the 'Sql' classes ?

More importantly, since these do exist, if we don't use them in our solution, then we will face the same problems as those the namespace classes were designed to overcome.

Or have I been writing program code for too long ;-) ???

====================================================================================================

Well, after reading the above information, it would seem that with care and application we can get over one 'thorny' issue.

One which I have heard lots said about over quite a few months. And that is the one about the DateTime structure not having a 'null' state.

Since all the 'Sql' data types have a 'null' state then SqlDateTime must have one where DateTime does not. That sounds like good news indeed.

Shall we map out our 'TestTable' fields with these new classes ? Lets try below :-

money - SqlMoney,

smallmoney - SqlMoney,

date - ???,

datetime - SqlDateTime,

bit - SqlBoolean,

varchar(50) - SqlString,

float - SqlDouble,

smalldatetime - SqlDateTime,

real - SqlSingle,

decimal(18,0) - SqlDecimal,

int - SqlInt32,

smallint - SqlInt16

So after a simple mapping process where a direct comparison of list was carried out, we see only one field which is in doubt.

We need to find out what the Server 'date' type is when we use 'Sql' data types.

My guess would be an SqlDateTime with the time part set at midnight. But since it is not in the above list as such, we need to check it out.

We ought to see what the SDK documentation has to say about SqlDateTime :-

===================================================================================================

Also it may be helpful to see what a Help search in MS 2008 has to offer :-

================================================================================================================================

Now we have a better idea of what we are dealing with at the Server end, and what data types are available to us in dotNet, we can see in the following sub-section about reading data into objects, just how some of this may help us.

In section "Reading Data from results to Objects" we will examine in more detail how to extra data from the results set sent back by the Server, and put it efficiently (and tidily) into our own business objects with dotNet data types best suited to our needs.

We will take our [TestTable] and see how to extract data from a data reader and from a data table.

Remember, we have good news already - if we can have an SqlDateTime property to our business object, it is possible to set the value to null, as well as test for null. The restriction of the standard DateTime not having such functionality was a cause for much 'grief' with quite a few developers.

PHH - July 2008.


Within the Server Itself

Let us now move on.

Before we go to examine what problems may be in store for us when we read and write data, from and to the SQL tables, we need to talk briefly about getting the Server to make row selections on the data set to be returned as the result of a query.

If we make part of our SELECT statement do comparisons on the values in row cells (or fields), we need to take advice from Paul Nielsen in his video lessons.

Study the queries below to see how 'IS NULL' and 'IS NOT NULL' should be used :-

================================================================================================================================================

For Server queries, do not use ' = NULL' as this does not do as you expect. Also you must be very much aware that a field value which is NULL, which has another value 'applied' to it will result in NULL.

What I am referring to is the sort of operation where say a value of one is added to all the column cells. If for any reason there is a NULL in any of the fields, the result of the operation will be a NULL.

See the following query and the results after execution :-

===============================================================================

However, a simple way to avoid this is with the following code :-

(I am aware there will be many ways to improve on this - here we are focusing on the is & is not - NULL syntax.)

=================================================================================

If you try this little test yourself in MS, be aware that by doing them in reverse order you will get a different set of values.

Once the values are not NULL then the +=13 operation will affect all rows in the [Cash] field.

Since Paul Nielsen likes CASE statements so much we ought to try and get one of those to work ;-) First we would need to see if a CASE may be used in an UPDATE statement.

Suffice it to say we have at least got a CASE to work in the SELECT statement as follows :-

=========================================================================

PHH - July 2008.


Returned Results into a Grid

Below we are going to examine what happens when we return as a results set the whole table we created for testing purposes - the 'TestTable' table.

Well, what will this do, or look like, if we just read the table and throw the results set at a grid ? Let's see :-

Take my word for it, the grid loads up fine, no exception or error messages, and there are two other columns out of view, which are of type INT and small INT.

So what can we observe ? The two values (in the money and small money fields) are just there to help identify we are looking at the correct table.

The table as displayed here does not show the 'NULL' string values as in MS, and the BIT column has check boxes shown. You will have to take it from me that if you check and uncheck these boxes they seem to have only one state as shown - meaning a checked and unchecked box looks just like one which comes in from the NULL value.

Since we don't wish to 'guess' at any stage of this investigation, and can't really say that the check boxes are two state only, we need a way to 'view' the cell contents as far as their value is concerned. So as far as these BIT values are concerned we would wish to know if the cell holds NULL, 0 or 1. It should be obvious when it is 1, you would hope :-)

METHOD dgvSQLtrialCellClick(o AS OBJECT , e AS DataGridViewCellEventArgs) AS VOID

SELF:DoCellContentCheck(o, e)

RETURN

METHOD DoCellContentCheck(o AS OBJECT , e AS DataGridViewCellEventArgs) AS VOID

LOCAL nIndex AS INT

nIndex := e:ColumnIndex

MessageBox.Show("Column : "+(1+nIndex):ToString())

RETURN

Okay then, we have now linked a 'cell click' event for the grid into finding and displaying the column value. From here we ought to be able to develop things so we can see the contents of each cell.

Since we need to use the row object to access the cell value we need to adjust our code to give us both row and column :-

METHOD DoCellContentCheck(o AS OBJECT , e AS DataGridViewCellEventArgs) AS VOID

LOCAL nRIndex, nCIndex AS INT

nCIndex := e:ColumnIndex

nRIndex := e:RowIndex

LOCAL cText AS STRING

cText := ""

cText += "Column : "+(1+nCIndex):ToString()+CRLF

cText += "Row : "+(1+nRIndex):ToString()+CRLF

MessageBox.Show(SELF,cText,"Cell Details : ")

RETURN

Do remember though, that we have added one to both the row and column index for 'human' reasons ;-)

[ sorry Don ! ]

Now we have our foot in the door we should be able to take this further, and to a point where it may start to become useful.

Examine the image below to see what we can find out for any grid cell clicked :-

Let us look at the code which does this for us :-

METHOD DoCellContentCheck(o AS OBJECT , e AS DataGridViewCellEventArgs) AS VOID

LOCAL nRIndex, nCIndex AS INT

nCIndex := e:ColumnIndex

nRIndex := e:RowIndex

LOCAL cText, cHead AS STRING

cText := ""

cText += "Column : "+(1+nCIndex):ToString()+CRLF

cText += "Row : "+(1+nRIndex):ToString()+CRLF

cText += "Fieldname : "+odgvSQLtrial:Columns[nCIndex]:Name+CRLF

cText += "Data Type : "+odgvSQLtrial:Columns[nCIndex]:ValueType:ToString()+CRLF

cHead := "Cell Details : "

LOCAL oC AS DataColumn

LOCAL oR AS DataRow

LOCAL oDS AS DataSet

LOCAL oDT AS DataTable

oDT := (DataTable)odgvSQLtrial:DataSource

oR := oDT:Rows[nRIndex]

oC := oDT:Columns[nCIndex]

cText += CRLF

IF oR:IsNull(nCIndex)

cText += "is Null !"

ELSE

cText += oR:Item[nCIndex]:ToString()

ENDIF

MessageBox.Show(SELF,cText,cHead)

RETURN

With the code as it is, we have to be careful about clicking outside the meaningful cell boundaries, particularly in the extra row which is placed at the bottom of the grid. I am pretty sure there is a property to turn this off (so to speak) but since we are doing some simple tests we just need to be careful where we click for now.

Okay then, it may prove a little 'sticky' the way the event handler seems to work - the way I have set the code up here - but it does seem to function as we would wish.

We can now detect that the values behind the check box are indeed 'three state'. Also the [LittleCash] column was changed to a value of 1.111 and this displayed correctly too.

Where does this all lead us ? Well, I think we are probably starting to see 'areas' which may be those on which we need to focus to prevent 'nasty things' happening when we don't wish them to.

Here I am talking about dealing with, and handling, nulls.

I have changed and tested out the event handler code, and it seems less 'sticky' to use the 'CellMouseClick' event of the grid, and also to trap when the right button has been clicked :-

METHOD dgvSQLtrialCellMouseClick(o AS OBJECT , e AS DataGridViewCellMouseEventArgs) AS VOID

SELF:DoCellContentCheck(o, e)

RETURN

METHOD DoCellContentCheck(o AS OBJECT , e AS DataGridViewCellMouseEventArgs) AS VOID

IF e:Button = MouseButtons.RIGHT

LOCAL nRIndex, nCIndex AS INT

nCIndex := e:ColumnIndex

nRIndex := e:RowIndex

LOCAL cText, cHead AS STRING

cText := ""

cText += "Column : "+(1+nCIndex):ToString()+CRLF

cText += "Row : "+(1+nRIndex):ToString()+CRLF

................................

................................

In making these changes shown above, I think I have also found the 'cause' of the 'not quite correct' behavior of the check boxes in the grid.

It would appear that you can make a 'tick' or 'check' appear with only a single click - BUT - this does not change the value behind them. You need to double click and then change the 'check'.

Then a right mouse button click does report the correct value 'behind' the check box.

I feel this is NOT as it should be, but I am telling you so you can be forewarned, if you choose to use a grid to display your returned data sets in the future.

PHH - July 2008.


Reading Data from Results to Objects

Reading Data from a 'DataTable' and a 'DataReader' into an Object :

Once we have data safely read into a 'DataTable' which is then displayed in the grid, we need to make our customized objects for our own use throughout the business logic of our solution.

We need to examine ways to do this and see if there are any pit-falls we are likely to encounter. Also in doing this we may also learn a few useful tips and tricks, just because we have pushed ourselves to read some of the huge amount of resource material available to us.

On top of the task as outlined above, we need also to apply our knowledge about 'Sql' data types to see if we can also make a better attempt at reading data from a DataReader object, directly into a business object.

Before we can attempt either of these task above, we need a suitable object into which we can read the returned data from the Server :-

#using System.Data.SqlTypes

............................................

............................................

CLASS TestSQLobject

EXPORT TTCash AS SqlMoney

EXPORT TTLittleCash AS SqlMoney

EXPORT TTMemDate AS SqlDateTime

EXPORT TTMemDateTime AS SqlDateTime

EXPORT TTLogic AS SqlBoolean

EXPORT TTTheirName50 AS SqlString

EXPORT TTTestFloat AS SqlDouble

EXPORT TTLittleDateTime AS SqlDateTime

EXPORT TTTeatReal AS SqlSingle

EXPORT TTTestDecimal18 AS SqlDecimal

EXPORT TTTestInt AS SqlInt32

EXPORT TTLittleTestInt AS SqlInt16

CONSTRUCTOR()

RETURN

METHOD Initialize() AS VOID

RETURN

END CLASS

This basic outline should be sufficient to test our code solutions for reading from both 'table' and 'reader'.

Now that I have started to read some of this material on SQL related data types, I see there are methods in the 'SqlDataReader' class which are not there for the 'DbDataReader' class. Or should I say they are not obviously available to this class. The list in the SDK documentation for Framework 3.5 is shown below :-

==============================================================================================================================================================

I have to say at this point that I have taken onboard the advice of colleagues, and shown the 'provider free' approach in the Vulcan coding so far, but since I can't see myself using anything other than Microsoft SQL Server 2005/2008, I fully intend to explore these specific methods to their full.

At least be me telling you that you need to check out for an alternative approach to the one I am taking - if you wish to be free of Microsoft specifics - then you can make your own choice.

Result of new Casting :

Well, that has proved a useful and interesting exercise. I have just tried to read the field data from the data reader object as show in the code below. And low and behold every filed except the 'Date' field worked straight away as worked out above.

This was the one we were unsure about, and which did not have a direct mapping. Look at the code to see what did work first go :-

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

LOCAL oDr AS SqlDataReader

TRY

LOCAL cStatement AS STRING

cStatement := "SELECT * FROM [TestTable];"

oCommand:CommandText := cStatement

oCommand:CommandType := Data.CommandType.TEXT

oCommand:Connection := oConn

oDr := oCommand:ExecuteReader()

LOCAL cText AS STRING

LOCAL oTSO AS TestSQLobject

oTSO := TestSQLobject{}

IF ! oDr == NULL

DO WHILE oDr:Read()

oTSO:TTCash := oDr:GetSqlMoney(0)

oTSO:TTLittleCash := oDr:GetSqlMoney(1)

//oTSO:TTMemDate := oDr:GetSqlDateTime(2)

cText := oDr:GetSqlValue(2):ToString()

oTSO:TTMemDateTime := oDr:GetSqlDateTime(3)

oTSO:TTLogic := oDr:GetSqlBoolean(4)

oTSO:TTTheirName50 := oDr:GetSqlString(5)

oTSO:TTTestFloat := oDr:GetSqlDouble(6)

oTSO:TTLittleDateTime := oDr:GetSqlDateTime(7)

oTSO:TTTeatReal := oDr:GetSqlSingle(8)

oTSO:TTTestDecimal18 := oDr:GetSqlDecimal(9)

oTSO:TTTestInt := oDr:GetSqlInt32(10)

oTSO:TTLittleTestInt := oDr:GetSqlInt16(11)

MessageBox.Show(cText)

ENDDO

ENDIF

oDr:Close()

CATCH ex AS System.Exception

MessageBox.Show(ex:Message)

END TRY

So I am still not yet sure what the Server 'Date' data type is (table field 'MemDate'), so I need to try out some further tests on that field. However, before that, we can add a full test record to the table - all fields with something meaningful within - and then try and display the object for that row.

Probably a useful method to add to the test object class is one called 'DisplayObjectFields', which will simply convert each property to a string and concatenate these to a string to show in a message box.

=============================================================================

Okay folks, we seem to be getting somewhere I feel.

Our new method displays the object it would appear, and the one shown is for the third row - which does have these values as seen when viewed in the MS tool.

Certainly, apart from the third field, it is looking and feeling 'good'.

=============================================================================================================

Finally, for this small section on casting the data reader to business object, the code has been adjusted to use the more meaningful GetOrdinal approach and the result for row one is shown above as live output from the code shown.

Now I think we have a solution to the Server 'Date' problem. Yes, I can cast it, and it gives the right results - but it isn't pretty :O(

Have a look at this, the code and a live output from the Vulcan code :-

==================================================================================

Yes, I know you can say we could have avoided the particular Server data type - BUT - the whole point was to learn what we could about using and converting (or casting) different data types. And then to see if we hit any problems.

When we compare this with how the data is read into the DataTable object automatically, we may yet learn how to keep away from these little nightmare problems.

Oh! just to complete this exercise above, the screen output for the row (third) with no value for MemDate shows a 'Null' in the message box as we may have hoped.

Below is the MS output of the data in the table at the point of the last couple of tests :-

===========================================================================================================

As a matter of interest, you can over-write a valued field for 'MemDate' with a NULL value in the MS shell, simply by clicking in the cell and typing 'NULL' in upper case.

Strange as it seems this Beta 2 version of MS does not accept the word 'null' in other, what me be considered obvious, formats - just all upper case.

Also when you try to enter Boolean or 'bit' values 'True' and 'False' work on this MS version, but it seems more friendly than 'null' as the upper and lower case is more flexible. However '0' and '1' seem to be no good and generate an exception message.

The whole point of all of this was to create a business object from our stored SQL table data, so here is row two with all fields containing information other than NULL. For simplicity I entered the data in MS with the edit top 200 option.

==================================================================================================

Reading Data from a 'DataTable' into an Object :

The interesting fact is that if we now look at the test data from our [TestTable] used in the above test, we still have not had to do anything other than thrown the table at the grid, and yet successful casting seems to have been done for us, as the table displays as shown below :-

So, if we consider the row of a DataTable to be a 'business object' (it is certainly and object in our Vulcan code), then we have had the data read in and cast for us, all in one automatic process.

When I check through the columns by mouse right clicking in the data area, I see that all the column (field) data types are what we would hope for, including the one we had so much trouble with when we did it ourselves from the data reader object :-

However, the automating casting has produced all column data types which are the 'standard' dotNet and not the 'Sql' versions we were using with the data reader.

The easiest way to see what I have observed and reported, is to get the code to generate a schema in XML format. See the code and output below :-

====================================================================================================================================

So armed with this knowledge we could take each row object and use it to populate the properties of a business object of our own. Also, if we wish, we can use the 'Sql' data types in our customized object, to gain the benefit of being able to set the value of each to NULL.

However, you may just choose to use SqlDateTime for this purpose.

I have to say that I do like the facility myself, of being able to set a previously 'valued' date time column back to NULL.

There is one trick you could always try, and that is to add a column to the table which is of data type 'SqlDateTime', and read in the data from the 'auto' column. You could hide the 'auto' column, and then you have the best of both worlds. I think then you could set the column field value to NULL interactively, which you can't otherwise.

Give this a try for yourself, it could be useful if it works. I have tried it and got so far, so below :-

I have managed to add an extra column with name 'NullAbleDT' as you can see, and the code for this is shown below.

However, I failed to get the same code (slightly modified) to work for the SqlDateTime structure. We will need to take advice on this :-

For some reason I get a runtime error which say that I do not have a object instantiated for oType when I try to print it in the message box.

I am sure there is still some mileage in this idea yet however :-)

At least from this attempt you have seen how to add another column to the table 'on the fly' as it were, and to choose its data type.

On top of these benefits we can also see how to move the new column to the required position by use of the following line of code :-

oDC := DataColumn{"NullAbleDT", oType}

oDS:Tables["NullTest"]:Columns:Add(oDC)

oDS:Tables["NullTest"]:Columns["NullAbleDT"]:SetOrdinal(2) //---- this is the line that matters (third column) -----

And finally, the line of code which causes the 'MemDate' column we are trying to replace as such, or at least hide and not use, can be hidden from view by the code line show below :-

odgvSQLtrial:DataSource := oDS:Tables["NullTest"]

odgvSQLtrial:Columns["MemDate"]:Visible := FALSE

Notice that it is a property of the DataGridView column and not the DataTable itself. In other words it is a grid property we are changing.

So, where does that leave us ? Well we just need help from colleagues on how to have an 'SqlDateTime' data type column.

Before we go onto the next sub-section about writing data back from our business objects into the SQL tables themselves, we need to do a final exercise.

This exercise involves taking row items from the automatically made DataTable (now in the grid) and create our own business objects from the field data. At this stage we can also choose to use SqlDataTypes if we so wish. In fact in this next exercise I will use all Sql types just to see how things go.

The business object we will use is the test will be our 'TestSQLobject' since it will optimize the range of field data types, as well as fully matching our [TestTable].

Okay then, what if we place a new button on our form which (when the grid is filled from the SQL table) will take the second row object, and use it to populate the properties of our newly instantiated business object.

=======================================================================================

Well, although we have coded it property for property - by inserting the data type for casting manually - it does work OK.

Remember the display is from a method of the instantiated object.

It would appear that we could simplify this process a whole step further by cutting out the need to supply the data type in our coding. Since the data type is already a property of the column, then we ought to be able to use that in place of our manual code.

I will look into this as a first try was good, but not that good ;-)

Now the whole point of Appendix E is to checkout NULL behaviour, so we ought to test our code above to see how it copes with a whole row of NULL values from the SQL table.

I will make such a row in MS and then do a live test (it will be the third row or Don's row '2' !) :-

===================================================================================

Yes, as I suspected, we had to handle the fact that the row values were seem as nulls, so I have adapted our earlier code to get results.

Once we can incorporate the fact we know the column name and data type, we ought to be able to loop through the list and make this process a lot simpler, and easier for the developer certainly.

Like with a lot of coding issue however, it is good to do it the long or hard way, to get a feel for what is working - before it all gets wrapped up in a loop and uses data which is unseen at coding time.

It may be an easier code style, but it is less easy to learn from initially, as you can't identify the columns being processed.

So there are benefits to us, and our learning, from having done it as above.

Okay, lets go on to writing our objects to SQL tables - once we have had a nice cup of tea :0)

PHH - July 2008.


Writing Data from Objects to Tables

Writing Data into SQL Tables :- (Handling NULL Values) :

Let us work out a plan for ourselves for this section, before we go any further.

If we can write code to pass the following tests then we ought to be a pretty good position as far as handling NULLs is concerned.

In our Vulcan.NET code we will create an object (with many and varied data types) and assign a value to each property which is not null.

We will then write the data from this business object, into a suitable SQL table, creating a new record.

We will then change the values in our existing business object, so that all properties are set to NULL.

And finally, we will update the same SQL table record with this all NULL object data.

If we are successful, and in MS the table looks correct, and we don't get any errors or exceptions thrown, we will have achieved what we set out to do a few days ago.

The target then being to be able to handle NULLs at all stages of our solution involving SQL databases - which contain tables with NULLable values of a range of data types.

Okay, roll your sleeves up and lets get to work !

For much of what we need to do we have code already and waiting to help.

The 'TestSQLobject' class can be used as our typical business object as it has a wide range of data types associated with its properties.

CLASS TestSQLobject

EXPORT TTCash AS SqlMoney

EXPORT TTLittleCash AS SqlMoney

EXPORT TTMemDate AS SqlDateTime

EXPORT TTMemDateTime AS SqlDateTime

EXPORT TTLogic AS SqlBoolean

EXPORT TTTheirName50 AS SqlString

EXPORT TTTestFloat AS SqlDouble

EXPORT TTLittleDateTime AS SqlDateTime

EXPORT TTTeatReal AS SqlSingle

EXPORT TTTestDecimal18 AS SqlDecimal

EXPORT TTTestInt AS SqlInt32

EXPORT TTLittleTestInt AS SqlInt16

Also, our [TestTable] in the MyCompany database is fine to use for inserting and updating the records we discussed above.

So lets simplify our plans, at the same time as taking them forward.

On our test form we have used so much to date, let us add four new buttons. One to create a fully populated business object, another to create a completely NULL business object.

We can then place buttons three and four, which 'insert' the business object in focus, and update the existing records in the table.

Because we are just using a few records of test data we can update all existing records without need to identify which one in particular. This was we can affect all fields, without having to keep any as primary key fields etc. Simple but quite effective ;-)

===========================================================================================

Right, we seem to be on our way, two buttons which work, they make the business object as planned, and display it to show us what we have done.

What we now need is a method to insert the business object in focus, into the[ TestTable].

Well, as you can see we succeeded, and did not have too much difficulty, just one problem. Guess which field that would be, yes, the 'Date' field - but now it is working as you will see in the code shown below :-

METHOD DoInsertIntoTestTable() AS VOID

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "INSERT INTO [TestTable] ([cash],[littlecash],[memdate],"

cText += "[memdatetime],[logic],[theirname50],[testfloat],"

cText += "[littledatetime],[teatreal],[testdecimal18],[testint],[littletestint] )"

cText += "VALUES (@c,@lc,@md,@mdt,@l,@tn50,@tf,@ldt,@tr,@td18,@ti,@lti) ;"

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of SqlCommand -----

oCommand:Connection := oConn

//----- add parameters and set their value -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@c", SqlDbType.Money}

oParam:Value := oTSO:TTCash

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@lc", SqlDbType.SmallMoney}

oParam:Value := oTSO:TTLittleCash

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@md", SqlDbType.DateTime}

oParam:Value := oTSO:TTMemDate

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@mdt", SqlDbType.DateTime}

oParam:Value := oTSO:TTMemDateTime

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@l", SqlDbType.Bit}

oParam:Value := oTSO:TTLogic

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tn50", SqlDbType.VarChar}

oParam:Value := oTSO:TTTheirName50

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tf", SqlDbType.FLOAT}

oParam:Value := oTSO:TTTestFloat

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ldt", SqlDbType.SmallDateTime}

oParam:Value := oTSO:TTLittleDateTime

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tr", SqlDbType.Real}

oParam:Value := oTSO:TTTeatReal

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@td18", SqlDbType.Decimal}

oParam:Value := oTSO:TTTestDecimal18

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ti", SqlDbType.INT}

oParam:Value := oTSO:TTTestInt

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@lti", SqlDbType.SmallInt}

oParam:Value := oTSO:TTLittleTestInt

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [TestTable] ;"

LOCAL oDA AS SqlDataAdapter

oDA := SqlDataAdapter{cSQL, SELF:cStrConn}

LOCAL oDS AS DataSet

oDS := DataSet{}

oDA:Fill(oDS, "TTinsert")

//----- display contents of the data set table in the grid -----

odgvSQLtrial:DataSource := oDS:Tables["TTinsert"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

The very good news is that the insert method also inserts a full NULL record without any trouble - here is the table now seen in MS :-

=====================================================================================================================

You can see the double, or duplicate row, from my testing, as well as the final all null row just added a minute or two ago.

My feelings are that things are looking good for the 'update' to work as we would wish, and for all nulls, especially DateTime variety, to be successfully handled.

Lets make a final method, one to make the update to all existing records :-

========================================================================================================

Sorry that the two images of the live capture are in some ways not very interesting ;-) However, they are exactly what re require.

Any field in the range of data types we have covered, may be inserted and updated, with non null values or NULLs themselves.

The code to do the updating is shown below and is very similar to that for inserting - no doubt some combination of coding could be achieved once you get a good idea of what you need to do.

The parameter and query sections are common, it is just the setting up of the query statement which differs as you will see when you examine the following code :-

METHOD DoUpdateToTestTable() AS VOID

SELF:DoSetConnectionString()

SELF:DoTryToConnect()

LOCAL oCommand AS SqlCommand

oCommand := oConn:CreateCommand()

//----- set the 'CommandText' property -----

LOCAL cText AS STRING

cText := "UPDATE [TestTable] "

cText += "SET "

cText += "[cash] = @c, "

cText += "[littlecash] = @lc, "

cText += "[memdate] = @md, "

cText += "[memdatetime] = @mdt, "

cText += "[logic] = @l, "

cText += "[theirname50] = @tn50, "

cText += "[testfloat] = @tf, "

cText += "[littledatetime] = @ldt, "

cText += "[teatreal] = @tr, "

cText += "[testdecimal18] = @td18, "

cText += "[testint] = @ti, "

cText += "[littletestint] = @lti ; "

oCommand:CommandText := cText

//----- designate the command type -----

oCommand:CommandType := Data.CommandType.TEXT

//----- set the connection property of SqlCommand -----

oCommand:Connection := oConn

//----- add parameters and set their value -----

LOCAL oParam AS SqlParameter

oParam := SqlParameter{"@c", SqlDbType.Money}

oParam:Value := oTSO:TTCash

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@lc", SqlDbType.SmallMoney}

oParam:Value := oTSO:TTLittleCash

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@md", SqlDbType.DateTime}

oParam:Value := oTSO:TTMemDate

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@mdt", SqlDbType.DateTime}

oParam:Value := oTSO:TTMemDateTime

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@l", SqlDbType.Bit}

oParam:Value := oTSO:TTLogic

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tn50", SqlDbType.VarChar}

oParam:Value := oTSO:TTTheirName50

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tf", SqlDbType.FLOAT}

oParam:Value := oTSO:TTTestFloat

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ldt", SqlDbType.SmallDateTime}

oParam:Value := oTSO:TTLittleDateTime

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@tr", SqlDbType.Real}

oParam:Value := oTSO:TTTeatReal

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@td18", SqlDbType.Decimal}

oParam:Value := oTSO:TTTestDecimal18

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@ti", SqlDbType.INT}

oParam:Value := oTSO:TTTestInt

oCommand:PARAMETERS:Add(oParam)

oParam := SqlParameter{"@lti", SqlDbType.SmallInt}

oParam:Value := oTSO:TTLittleTestInt

oCommand:PARAMETERS:Add(oParam)

TRY

//----- execute the insert statement -----

oCommand:ExecuteNonQuery()

//----- fill the data set with data from the inserted row -----

LOCAL cSQL AS STRING

cSQL := "SELECT * FROM [TestTable] ;"

LOCAL oDA AS SqlDataAdapter

oDA := SqlDataAdapter{cSQL, SELF:cStrConn}

LOCAL oDS AS DataSet

oDS := DataSet{}

oDA:Fill(oDS, "TTinsert")

//----- display contents of the data set table in the grid -----

odgvSQLtrial:DataSource := oDS:Tables["TTinsert"]

CATCH sqlEx AS SqlException

MessageBox.Show(sqlEx:Message)

END TRY

SELF:DoForceDisconnect()

RETURN

I think it is time for another cup of tea :O)

To make our exploration very complete, we need to see if we can do some, or all, of this in the 'Reading and Writing' sections, in a "provider non-specific" manner.

I will take a look at the DbProviderFactory related classes.

If nothing else, this appendix should at least make you well aware of what can go wrong, as well as many ways and ideas to actually - get things right !

Who said NULLs weren't fun ?

Good Luck ;O)

PHH - July 2008.


What to Include- notes to myself !

 Top Previous

- using SDK doc for framework 3.5 all the time now.

- statement terminator ';' encouraged in 2005 - Paul Nielsen says use it all the time - and some places it must be!

- use of MS to browse and supply SQL server names

- error table - keeping exception details - also error number from SQL server itself.

- reading data straight into objects - making object lists - viewing in list controls and setting the ToString() method.

- switching output using the ToString() method and a pointer from outside - different for each object, use contact and company fields as an example - if one blank then use the other.

- sample selects from Paul Nielsen on datetime issues - tips - explain what they are doing, by breaking them down

-

- some T-SQL stuff which also does TRY Catch better !?

- NULLs an issue - what to do

- DateTime - nulls and min value for many apps. Limited by no null at the dotNet end / objects etc.

-

- Once the ClickStartSQL is finished, provide a zipped form of EXE files, MDF files, and VIDE export files with PRGs.

These can be downloaded separately to the ClickStart.

make sure all forms can expand sensibly - or are locked.

include as an appendix the way to

reporting services - screen control - make in Report Builder type tool - Bus. Int. VS shell.

separate ClickStartRS entity.