The 'Small Trader' project

 Top Next

In the image above you see the opening screen of the 'Small Trader' application.

The project to create a real and working application, for use in a shop or small company, was born in about January 2010.

The initial app was targeted at version 3.5 of the .NET framework, but in very recent times (July 2011) this has been altered

to be version 4.0 - some comments on changing framework mid-way through a project are to be found in Section 'D' of these eNotes.

Aims, Objectives, and Purposes :

In creating the project it was hoped that I would discover how to use data binding in WPF forms, and learn both the dotNet code

required as well as finding out how to use XAML script, and learning how to set the necessary binding there, within the script.

Because some screens were designed and created well before others (over 15 months apart), we have both ways existing in

the app, and as such prove useful to new guys coming to learn about WPF and data binding.

Another purpose for writing this Small Trader app was so that I could explore the ways in which the ADO.NET code could be

improved from my first attempts (in the publicised 'BEST company' app) - particularly in respect to simplifying specific methods for

inserting and updating table records.

Although the ADO.NET approach to backend data source access is very easy, and just works, it also can be very long winded,

or verbose, if an attempt is not made to try and simplify the process the developer needs to apply when writing new methods for

handling business and custom objects.

PHH - July 17th. 2011


Introduction for VODC 2011 (Stuttgart)

 Top Previous Next

The Small Trader application was party designed and made with VODC 2011 in mind.

The nice thing about this application is that the 'Business Logic' layer does not really amount to very

much at all, some would say it was even missing altogether.

The application to accept and track jobs relating to PC and laptop repairs is very much a data input

and storage 'system', with occasional need to display data on screen, and even less often print out a

sheet for the customer and the accounting physical records - we all need to keep the accountants happy !

Because of this limited existence of the Business Logic layer, I can present the application is two separate

presentation sessions - one to focus on the 'Top Half' or "Input & Display" section, the other which focuses

on the 'Bottom Half' or the "Data Storage & Access" section.

Let us study below the schematic of how I see the Small Trader application :-

[ B.O. & BO, is 'Business Object', ST is Small Trader, MS is Management Studio ]

{ to be seen as 'layers' }

1. GUI - Form and its controls

2. GUI/B.O. - Binding BO to the controls

[ .. binding done mainly in XAML for ease of scripting ... point at data source in code .. ]

3. Business Logic - all the business 'stuff' !

4. B.O. - the object class definitions [ .. like 'Customer' and 'Invoice' .. ]

5. ADO.net / B.O. - STATIC class for all interfaces with business logic.

[ .. this layer has the Business SQL statements in it - no others .. ]

6. SQL / ADO.net - STATIC class to interface with SQL server

[ ... this layer has very limited SQL statements, only for 'Error Retrieval' and 'Backing Up' .. ]

7. Server (SQL) - 'out of the box'

8. Database (SQL) - tables designed for ST app and made in MS

*** Note *** layers 1,2 & 4 will be covered in one presentation, and layers 5,6 & 8 the other.

[ There is nothing much we need to say about layers 3 and 7. ]

Structure of Any Application :

It may interest you to know that I now see the above 8 layered structure as the overall for any application I

now design and create using WPF and Vulcan. This structure has a LOT going for it and makes life a

lot easier for the coder / developer, once they have the ideas firmly in their head.

For Your Information :

It is not till very recently that I finalised this eight layered structure, by writing (creating) those numbered 5 and 6.

The STATIC class numbered six (6) is the general class I can now use in any of my future apps, it does all the

standard 'stuff' with the SQL server. No SELECT, INSERT or UPDATE statements relating to the B.O.s are in

this layer.

I now realise too, that I will need to write the STATIC class for layer five (5) for each app that I design. This has

all members relating to the specific BO which are also designed specifically for the application in focus.

If we look at an image of the Solution Explorer for SM we see the following :-

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

The two PRG files are in the order 6 then 5 - and the STATIC class for the application specific members will always

tend to be named with that application related name - here 'SmallTrader.prg'.

Looking at this from another Angle :

If you wish, we could look at this from the other direction, so to speak. We could say that layers 8,5 & 4 need

to be considered as a whole - all at once!

While we are solving our development needs without new tools like E.F. (Entity Framework from MS), and are

coding everything ourselves in mainly Vulcan, we need to consider the structure of the data tables along with

our design of the business objects we require, and also the members we create in the fifth layer.

Remember the fifth layer is where we write specific query statements which relate to our table structures. So for

example, if our app does not involve invoicing, then we will NOT have an 'Invoice' table, nor an Invoice BO and also

therefore no STATIC members in layer 5 relating to storing and retrieving invoice data.

Benefits to the Developer :

So what do we gain by doing things in layers like shown above ?

Well, a few things, and one is speed of development, once we have a tried and tested layer 6.

Quite soon we will not get errors arising because of layer 6, it will be tried and tested - quite thoroughly.

Also, it will mean we get accustomed to writing our fifth layers (application specific) since we are then

more familiar with what layer 6 requires us to do in layer 5.

Already I am feeling a speed and ease factor because of the above.

Although the ADO.net code is not rocket science, or complicated, it can be a bit tricky. So the idea of removing

as much of its as possible to a 'one-off' layer is a GREAT idea - wish I had thought of it earlier.

So finally, the benefit is that for the majority of the time we as developers are concentrating on the business aspects

of the solution, and not the 'nuts and bolts' of making it work.

One final benefit, which I quite like, is that all the code is generated by myself, no third party stuff for me to misunderstand.

Also, as things stand for the Small Trader application, all but one class is written in Vulcan - say 98.5% of the code, or more.

I find writing code in VN2 is better and much faster for me than using C#, but I will when I need to, and feel that all of us

needs to be able to read C# so that we can adapt bits for our own use in VN2.

Summary :

So the outcome of all this 'layered' talk is that one presentation at VODC will look at business objects as bound to the GUI

controls, and how to make life easier by using data binding, and the other will look at the data access side of things. The

data access is done using the two STATIC class layers which interface with the same business objects as we have bound

to the GUI.

Now time to move on - see you in the next subsection ..........

PHH - August 30th. 2011

Addendum :

It needs to be realised that I started to code the Small Trader app around Christmas time 2009 and so although the

design was always WPF and data binding, not everything has been coded in the style I would use today.

This in itself can be helpful to see how things may be done in Vulcan code, as well as in the more recent XAML script.

I will attempt to point out when there are better, or more recent ways to do things.

In particular, there is one aspect I have kept in the application in order to show possibilities, and that is the printing of

invoices. The way invoice printing is done at the moment is by effectively passing an object from one EXE to another.

This shows how to use older and existing code (the printing EXE is based on WinForms) with a newer program module.

This use of an existing printing module also demonstrates nicely the use of 'Serialization' of an in-memory object.

Finally, on this topic of old and new styles, or coding approaches to a working solution, there are a on-show in the app

a few ways of coding a business object for use with data binding. Data binding in WPF uses Dependency Properties (DP)

and the more fully compliant your BO with DP technology the better.

Sometimes binding works OK, for what the app requires, with less of a 'full' DP approach, and sometimes we even require

a class written in C#. Luckily for us, the need to dip into C# to work around the current slight VN deficiency using events

will soon be a thing of the past. And even by the time we attend VODC in November 2011.

PHH - August 30th. 2011


More details of some layers

It may be helpful to you if I expand some on what was said previously about the 'layers' that we see

in the make-up of the Small Trader application.

Layer 1. - The GUI :

How much you do for this layer depends on your experience.

It is possible to use the Visual Studio 2010 designer in a simple fashion, by just pacing Toolbox objects on the surface

as you did in WinForms. The XAML script is made automatically for you.

If you have more skills then use placement elements to help you, like grids with rows and columns, as well as others

designed to improve layout and arrangement.

There are elements and techniques available in WPF which are far superior to those we had in WinForms - it may

take a while to discover them, it did for me, and I am still learning.

One personal tip, I suggest you edit the XAML script which is made automatically for you, simply to provide names for

controls which will be more meaningful to you and your dotNet code than the default values.

For example, I would change 'textBox4' into 'tbxAdd3', and 'listBox2' into 'lbxCustomers'. These are then available in

the Vulcan PRG files as 'self:tbxAdd3' and also 'self:lbxCustomers'.

Layer 2. - GUI and B.O.

I strongly suggest binding to GUI controls as often as possible - always even !

Another thing I prefer at the moment is not specifying in the XAML script the source of the binding, I do that in the PRG

file using a 'DataContext' property assignment on the control(s) needing to be bound. This makes it easier at the XAML

end of things, even though it is possible to script it.

Layer 3. - Business Logic

Nothing much needs to be said about this layer - except don't do anything here which should be elsewhere according

to the overall plan of things.

Don't be tempted to 'do a quick fix' and place a method in here which should be with those of layer 5 or 6.

Most often you will use in your 'Business Logic' (BL) the members you create in layer 5.

Depending on your application type your BL could be small or HUGE !! It all depends on your problem.

Layer 4. - Business Objects (BO)

If you can, place your business, or custom objects, in a Class Library. You may make the library after first testing them

in the main body of your application.

Now that I have the benefit of hindsight, I am sure I will always make my main BOs dependency class objects. By that

I mean that I would code them in Vulcan such that they would successfully take part in 'TwoWay' binding if required.

Certainly this would be my approach for BOs like 'Customer', 'JobSheet', and 'Invoice'

Now that I have working examples of DP classes in dotNet and Vulcan, I simply need to carefully copy and adapt.

Making sure that I had the correct data type selected and a suitable default value. See the section on Data Binding

to understand what I mean by this.

Layer 5, - ADO.net / BO [ STATIC class ]

This will take a bit of hard work for each application you develop.

You will need to create methods for each of your BOs - for database / table INSERT and UPDATE operations, as well as quite

a few specific or specialised aspects - like finding the oldest order, or customers not ordering, unpaid invoices, and a host more

stuff like that. Since each of these can be scripted into an SQL query, then a static member can be written.

You will find yourself working in Management Studio a lot while developing and coding layer 5.

These specific members will be used in your Layer 3 - the business logic.

Layer 6. - SQL / ADO.net [ STATIC class ]

Once the standard members of our general STATIC class have been originally created, and then thoroughly tested, there

will be little need to change any code - just use the members as you require them from layer 5.

Layer 7. - SQL Server

Nothing needs to be said about the server, it just works, and works well !!

Choose the most recent and up-to-date if you can, and Microsoft's flavour if at all possible. This will make life easier for

yourself while developing your solution.

Layer 8. - Database, mainly SQL Tables

Before we get the use of any further technologies from Microsoft (like EF - Entity Framework) we will need to make our

own data tables.

Our data table structures will VERY often mirror the BOs (Business Objects) we need to use in our application.

And often the BO will be mirrored in the GUI form itself. Sometimes a GUI may have groups of controls bound to more

than one type of business object. For instance it is possible to have the top left of a Job screen bound to some of the

properties of a related 'Customer' object.

Where will all the work take place ? :

Simple to answer. Layer 7, 4, 1 (with a bit of 2), and 6 - and probably in that order too.

I suppose there is some work in layer 3 to make it all hang together, but the detail in layer 6 will be crucial to the

success of your application.

Most of your problem solving can be done in layer 5 with a bit of thought. Just in the same way that creating reports

with Reporting Services (MSSQL server) requires that 90% of the work be put into designing the query to return the

most suitable data set.

Oh! - Should have said !

It may be obvious to some of you - BUT - it needs to be said, only Layers 1 through 6 will appear in the VS solution.

Also I am 'exploring' having folders in the S.E. pane which are part named 'Layer1_' and so on.

So there will be a group of sub-folders in the SE pane which come under 'Layer1_' and are named after the WPF form

classes - like 'Customer', Invoice, 'Job' etc..

The S.E. Pane - a 'layered approach' :

Take a good look at the image shown below, it reflects this new layered structure :-

In the above image you see what I am trying out, or exploring. A new structure for me of an application

in the Solution Explorer (SE) pane.

Notice that layers numbered 7 and 8 are not present, as these are SQL entities on HDD and not therefore

within our VS file and folder structure.

Let us expand a few sections to see how they look with this new structure :-

And then expanding one of the GUI related form folders we see this :-

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

Each of the folders in the 'GuiBinding' layer will have an XMAL designer file.

Now let us look at another folder, that of the Business Logic - check this out :-

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

There will be a folder for each of the WPF form classes (screens) as well as a couple more,

like 'Printing' which do not have a screen for display purposes.

Note that there is a folder for 'Window1' even though the WPF form is at an application root

level in the structure. See the following image and locate the main form at the bottom - it

is still called 'Window1'.

Since I had a bit of a S.E. disaster in the past when I tried to relocate this XAML file in a sub-folder,

I have left it well alone, as it works fine where it is ;-)

Now checkout the Business Classes layer, and see that we do not appear to have a folder for

Invoices - we can see three others below :-

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

This is simple, the 'InvoiceCS' class we use in the Small Trader application is a C# coded class and

is held in the class library referenced in the References folder - see below :-

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

Supporting Classes :

As yet I have not completed the tidying up of my code and files, in the near future I will find each Enumeration,

Structure and Class which is used - and is NOT a major BO class - and place them in the same folder, within

a similar namespace to that required by any scoping conditions.

For this application most will probably be placed in the 'SmallTrader' namespace.

Okay then, after some time away tidying the files, looking for extra classes, and moving them around, we see

the following in the supporting classes folder and SmallTrader namespace :-

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

The S.E. pane now looks like this :-

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

So the extra classes, enumerations and structures are in a file called 'Windows1_OtherClasses.prg'.

Okay then you guys, enough of tidying and reorganising, time to move on, the structure seems to

be good enough to try for real.

See you in the next subsection ............

PHH - 31st. August 2011.


How you should use it

Copy and Adapt, Test and Improve :

Once again, a simple answer.

You should use it as I will, as a working model on which to base my future SQL applications in WPF.

Select the code and script parts which suit your needs best, then take a copy of those sections and adapt

them to your needs.

It is very difficult, or almost impossible, to code off the top of our heads as we so often did it the past. We

MUST have examples to refer back to these days, and the skills we require now are those of being able to

select and adapt suitable code, to suit our needs. And make it work of course.

Be aware however that some parts are older than others, and although everything is basically dotNet 4.0

and WPF, there are some techniques which are more 'modern' and those I would prefer to use myself

over others that are in the Small Trader application.

I think I make it clear in my notes, while discussing each section, those techniques I prefer and recommend.

Getting into WPF :

This is easier than you think. If you follow through many of my working Vulcan examples, in both these eNotes and

the ones in the ClickStartXAML and ClickStartWPF, then you will be well on your way to using WPF technologies.

Remember WPF is really just the modern Windows GUI, and as such replaces the WinForm technology which is now

decades old, and shows it.

Once you get into WPF and get some sucess for yourself, then you will NEVER wish to go back to older GUI technologies.

I know, it happened to me, and I see it happening to VOUG members of my local group based in Bristol UK.

Starting SQL database use :

If you have not got a installed 'instance' of MS SQL Server to use, then install your own.

Use the later sections of these notes to find out how to download an Express version of a recent version of SQL Server.

Then also follow the notes in this eVolume to show you how to install your own instance of SQL Server.

Make sure you get as far as making Management Studio work for you.

Try to copy my supplied database (DB) which comes with Small Trader (ST) - it is a "2008 R2" compatible one, so do not

try to use it with SQL Server 2005 or "Denali".

My advice is to find yourself a copy of Express 2008 R2 and install that.

Once you have the ST data showing in MS you are ready to try and get the Small Trader app to run for you, and also to

start writing your own Vulcan ADO.NET code.

Getting into WPF Collections :

The ST app was done by me n order to get me into WPF and some early data binding, so I have not done a lot within it

regarding collections - as such.

You will on the other hand get a lot more from these eNotes themselves, and the little demo app called 'VODCdataBinding'

will also guide you in the right direction.

"Section K" in these notes is where you can learn a 'heap' of useful stuff on collections.

Start off by reading and studying the 'Ten points about Collections'.

Collections are important in WPF and binding, so make the effort right from the start.

PHH - July 17th. 2011 >>> 2nd. November 2011.


Useful support material

I have several web sites and addresses you may try for locating useful eNote information.

The following are listed in order or most recent first :-

www.WinClickStartVulcan2.net

www.ClickStartVulcan2.net

www.ClickStartVulcan.net

However there is another general site which is hoped to be a WordPress site and which needs much developing

and this is found at the following URL :-

www.ClickStartLearning.net

eVolumes to consult :

'ClickStartTSQL' T-SQL - learn some more in-depth SQL scripting and enhance your Queries !

ClickStartSQL' Starting SQL Server use with ADO.Net :

'ClickStartXAML ' Starting XAML scripting for easier WPF input

'ClickStartWPF 'Starting WPF application development, painlessly ! :

The Future and 'ClickStart' :

It is hoped that after Germany - VODC Stuttgart 2011 - I will make some big changes to the newest site 'WinClickStartVulcan2.net'.

This is a Windows based site with MS SQL Server 2008 R2 available for use by my web apps.

At the moment it only has the later eVolumes of the eNotes, but I intend to place all notes and VS solutions on this site.

'ClickStartTSQL' has some useful information for you as well :-

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

Very recent publication - NEW !?

It is very useful to have quality reference books and this is one of those.

However, having searched through a lot of existing modern books I suggest that you come to terms with the

idea that you will find it very difficult, or almost impossible, to get reading material which gives an overall viewpoint.

Because it was difficult to source material to compare and contrast a lot of the WPF and XAML technology, I started

to write my own notes. So for specific details the reference books are often brilliant, but for overall stuff, as well as

Vulcan coding syntax, my ClickStart eNotes will come in very useful to you.

I did use, and still do at times, the 'orange Bible' below :-

This new book is very nicely printed, glossy and in full colour so it looks like the VS10 screen output.

This is a must to supporting anyone's drive to learn WPF and XAML.

Thoroughly recommended.

Author is Adam Nathan, published June 2010.

ISBN-13 [ 978-0-672-33119-0 ]

Alwyn Jennings-Bramly : the Book !

This is a 'MUST' for anyone coding Vulcan.NET and I refer to it on a regular basis, even after all

these years - yes, Alwyn started the work years ago now.

Also there is about three times the amount of material in the book than you get in regular paper copies from

the likes of Amazon.

I can't recommend it highly enough - you have to have a copy !!

Purchase a copy over the internet from :-

My copy looks like this when the PDF file is opened - each copy is customised - see my detail below :-

I have an icon on my desktop along with icons for my regular reference CHM material.

Checkout my 'fence' for reference material :-

Sams Teach Yourself WPF - in 24 hours :

Mark Long - Training DVDs :

PHH - 1st. November 2011


Installing SQL Server

Installing MS SQL Server can be a bit tricky, I have to say !

Over the last three years plus I have done it many times and on desktops as well as laptops.

It would seem to me that the best advice is try to download and install the latest version from Microsoft

which is on general release to the public. Do not try an early release version if you are new to SQL - it may

prove difficult to remove later, and any final public version generally will not install on your PC until you have

completely removed the pre-release version.

State of your PC / laptop :

Another thing which is likely to cause you grief when you try to install SQL Server is the state of your software

on your PC.

I found that a newish and pretty 'clean' laptop / PC would allow the download and install of the latest version of

SQL Server without too much hassle, or issues being thrown up.

The hardest case I had was with a development desktop system where I had accumulated older versions of Visual

Studio and other stuff - over years - I could not remove this because it was still needed to serve clients needs.

So if you have to do an install on an older and possibly 'messy' system - try out a download and install first on

an easier and cleaner PC.

The whole process is long and can takes days and days for you to get something suitable which works for you.

Also the 'state' of your operating system may also cause a problem or two to you while attempting an install.

Make sure you have a recent version of Windows and that it is right up to date with service patches and regular

updates. It is best to make sure this is done first, before you try an install of SQL Server.

Financial Cost, and the Express version :

These days the problem of a single developer getting hold of a genuine and fully functional version of MS

SQL Server is not a problem at all.

Microsoft provide very good downloads of the Express version of 2008 R2, with promises for 'Denali' to come.

The Express version now has quite a large database limit - far too large to worry any Vulcan guys wishing to

download and get started immediately.

Where to get your download :

I am afraid that this seems to vary with time, MS sites seem to come and go, and so you will need to be flexible

in your approach.

http://www.microsoft.com/sqlserver/en/us/get-sql-server/try-it.aspx

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

What else do we need ? :

Well, what I say now may be a little out of date - but you do need to be aware of it !

In previous downloads of MS SQL Server we the user had to select to include the 'Advanced Tools' or was the

phrase 'Advanced Services'.

Whatever it was it meant that we the user also got management Studio (a 'must have') and also Business Intelligence

Studio (BIDS), which allows Reporting Services to be used with your chosen version of SQL Server.

I have a feeling that these days it is all included in the one download - but try and check this as you certainly need

Management Studio (MS).

Oh! - I seem to have answered my own questions - see below :-

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

However, I have just had a bit of a play around with this and like all this stuff on SQL it seems to be not quite

as you may expect it to be. So from now on you are on your own - download the versions you think you need,

and hope you get ;-) and 'Save' them to your disk rather than 'Run' them. This way you can compare size of file etc.

before you commit yourself.

Pressing the Panic Button ! :

If you start an install then do not worry too much, or even 'panic' !

There is usually ways of leaving the install process once it is in progress, in fact the older install software actually

took you through screens and screens to gather data before it actually started to do anything 'serious'.

Using ClickStart for guidance :

I went through the painful process of writing detailed notes some time ago - but these are probably still very helpful

today. Most of my recent installs have seemed similar in nature to what I have previously written here :-

http://www.clickstartvulcan.net/WebNotes/SQL/ClickStartSQL.html

Try www.ClickStartVulcan2.net and find your way from there to "ClickStartSQL".

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

What to look out for in the Installation :

One thing for which you need to look out is the place in the install dialog screens where extra users and usernames

may be specified.

I say this because as most of us all know by now the modern Windows O/S is tied down by security, and security issues.

I have found that by including extra entries for users - and certainly add 'Administrator' as well as the logged in user - then

there are less problems with security.

I have to admit I also set Management Studio (MS) to open and run in Administrator mode. This too helps to stop the sorts

of issues which frustrated me in early days with working with SQL Server.

Okay then you guys, having warned about how difficult and tricky this can be, we will attempt to go through the full process,

from start to finish, in the following subsection.

Hold on to your hats and we will have one hell of a ride ;-0))

See you in the next subsection ...........................

PHH - 1st. November 2011.


Twelve points about install .........

- locate and download a suitable version of MS SQL Server - get the latest public release if possible

[ .. do not use CTP version as it will cause problems later .. ]

- make sure your PC has it Windows O/S, and stuff like Visual Studio, right up to date with latest patches and SPs

- run the EXE file and follow the dialog screens - use the guides provided here to help you respond correctly

- use Management Studio (MS) to browse for all instances of MS SQL Server, and find the one you just added

- in MS open the instance of the new SQL server

- in WE locate a suitable database (in a folder which you have R/W access) and attach it to the server instance in MS

- point to a suitable SQL table in the DB and use the context menu to list the TOP 1000 rows.

- ** just as a precaution, detach the DB and close MS. OPen MS again and re-attach the same DB

- when coding ADO.NET stuff from Vulcan the first time access after attachment can seem to be an issue

[ .. by trial and error I found the above may solve any problems ..]

- in VN2 code use ADO.NET to make a connection and try to open and close as a test - use Message Boxes to show results

- once a connection can be established, write a very simple method or two to query the server and get a returned data set.

- now write a simple method to allow you to write data into the DB.

[ .. once you can do all above you are ready for some serious code writing and project design .. ]

I can assure you that once you get through all these and you are successful, you will feel like a million dollars ;-0))

PHH - 1st. November 2011.


a Full Live Install ....

Okay then, having said how this can be somewhat of a problem - installing a new instance of SQL Server onto

a desktop or laptop, we will now attempt to go through the complete process on my other PC behind me.

I choose this PC because it is more modern and the O/S is cleaner I feel. If we can't do it there then we have

little chance on this old development machine of mine.

Although one or two of my captured images show CTP in the detail at places, in fact I eventually did a full install

of a Public release of Server 2008 R2, one which needed a serial number ( I am a member of MSDN).

I suggest you keep away from CTP (Community Technology preview) versions as this can cause problems when

the public release is made. Generally the two versions can't coexist on the same PC platform.

How to use these install images :

I suggest you have a look through the full set of images shown below, before you try your own install.

Depending on the type and version of your download file, you may (will?) get some dialog screens in a slightly

different order, so you need to be flexible on top of being well prepared.

So having clicked my install or setup .EXE file we see the following :-

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

Choose the 'New Installation' - you should then see the following :-

Notice that we get a list of all the other instances of SQL Server on the system. I show you how to do the same thing in

Vulcan code in Section I of these notes.

Depending on your version of install you may see this next screen at this stage of the installation :-

If you get issues other than the Firewall you may have to go fix then depending on what is suggested in the status 'blurb'.

The clean result above is one of the main reasons I used the PC that I did to try out the new install.

Now for Product Key you may have a free Express version - those with MSDN like me will have a key - mine is hidden beneath the

drop list shown below :-

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

The following dialog screen is simple, but necessary to get correct - see below :-

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

At a subsequent screen you may well have to select the features you require. For safety (meaning we want things to work for

this first install of yours) we can select 'All' - or everything.

After you select and click 'Next' you get an information screen - after a minute or two (few seconds?) :-

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

Then after this feed-back we are expected to do some real work to help our installation along.

Study the following image in great detail :-

I have manually typed the 'Named Instance' to be "SQL2008_VODCtest".

We need to remember this when we get a lot further through the installation, as well we be looking for it in list of

existing instances.

When we click the next button we see the following :-

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

And then once again we go to a screen which needs far more input by us.

Checkout the drop list input boxes in the following image :-

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

For this first trial install of your own, leave the 'network' option alone and just select 'system' from each of the drop lists.

And that includes the 'Agent one - don't do what I did - shown below :-

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

Note that the error message does not get removed until after you click 'Next' having selected a suitable input for 'Agent.

Once again we get a screen needing careful attention.

In fact the following screen is rather 'crucial' to the success of the install - check this out :-

I have to say that the input dialog screen are sadly lacking in terms of them communicating exactly what it is that

they require :(

What we are about here, is making an entry (or a few) into the white empty input text box.

The first thing to do is click the 'Current User' button - this is what I got - see below :-

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

Now we need a add a bunch more, and so we need to use the 'Add' button - see below :-

I have to admit then next part of this is NOT at all intuitive - BUT - what do you expect from these young guys at Microsoft ;-0))

For you PC or laptop just click 'Advanced' to bring up another dialog, just as strange, or 'odd' - see below :-

We are now needing to see the list of possibilities on the system, so click 'Find Now'.

You should see something like the image shown below :-

After selecting 'Administrator' we get the result shown beneath :-

And clicking 'OK' does this for us :-

So all we need do now is repeat the whole process to add many more items to the list as I have done.

See below to find out what I have added :-

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

Before we move off the "Database Engine Configuration" screen let us see what the tab called "Data Directories" has on show for us :-

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

The next screen for the "Analysis Services Configuration" is similar in nature to what we have just completed,

so we need say little about it except make sure you add a couple of meaningful users - see below for what I did :-

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

And now we get the choice screen for Reporting Services :-

You need to keep going as we are almost there - YES - it is a bit tedious I have to admit ;-0))

But if you want to "SQL it" and "ADO.NET from your code" then you have to suffer just a little bit.

You need to be pressing the appropriate buttons at each stage of this install, depending on what is

on offer at the bottom of the screens.

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

And, after all this we see a dialog screen which basically says we have not done anything yet - the install system has just

gathered input information from us.

So before we click to start the install itself we see this - see below :-

After clicking 'Install' we have a period when things whir away and hard disk click and clatter etc..

Then we see the following if we are lucky :-

It is now time for us to move on to the next section where we make our first connection and then see data and the likes ;-0)

However, before we do just let me say that it took me almost three weeks to get to this stage when I did it first, some three

years plus ago now. Needles to say I did NOT have some helpful notes to help me along.

See you in the next subsection .........................

PHH - 1st. November 2011.


Making our first connection

For this next subsection we are going to use management Studio - the software tool which comes as part of the

Advanced Services download.

Below is my desktop and how MS looks as an icon :-

When we launch MS by a double click on the icon on the desktop we see the following at startup :-

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

You will notice that I am actually using the version of MS which comes with "Denali" - or SQL Server 2012.

You may not be aware however that we have set the Privilege Level as shown below :-

Now for the BIG shock ! :

After struggling through an installation (or many more as I did in months gone by) I was expecting to see our new

instance in the drop list of the opening dialog screen!

But no this is what we get today - see below :-

It seems silly now, in the cold light of day, but in my haste and desperation to find my "SQL2008_VODCtest"

in the list on offer, I forgot that the last entry of '<Browse for more...> ' is in fact what we need to select.

When we do this it can take a while for the system to search through what is available.

Also we need to uncheck the collapse icon so we actually see the list on offer in the "Browse for Servers" dialog.

Hers is genuinely what I got this morning when at first I did not see it - check this out :-

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

Obviously we can now select what we want, now that we can see it - so choose as necessary :-

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

Click the 'Connect' button and this is what happens - hey presto !

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

The new MS software (which apparently was written in WPF) now shows we have an open server in the Object Explorer (OE)

pane at the left of the screen.

So all we have to do now is attach a database.

For this part of the exercise we will move on to the next subsection.

See you there for more success ...................

PHH - 1st. November 2011.


Attaching a Database

*** notice *** when using Management Studio, you only see databases in the Object Explorer that you have previously

chosen and attached yourself. The MS software shell does not go looking for your SQL databases !!!

The process of making a database name appear in the Object Explorer (OE) under the 'Databases' section

is called 'Attachment' or attaching.

We save we attach a new database to the server instance in focus.

Study the following image :-

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

Usually when we do this the default dialog file selection screen looks as follows :-

However, very often we are moving database files from another folder, or usually PC / laptop.

*** WARNING *** before you can copy an existing database (DB) from one place to another, the DB in question

must NOT be attached within a MS shell - even if the shell is closed. So you have to detach first - more on that later.

Here in the following image is a couple of files which make up a database :-

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

These files were copied from my other PC - the development one - and have come from a folder where me

as a user can have access to reading and writing.

It is important that you have DB files in folders which are NOT restricted for access. This can cause a LOT of grief to you

and make you think that there are all sorts of other problems - when in fact you do not have access.

I have placed my files under the username folder that I am logged on as - i.e.Philp (yes, I know, I left out an 'i' when I was

installing Windows - have just lived with it ever since I realised as by then it was written in other code and script etc..

Let us see what we have then - see below :-

So if we click on 'SmallTrader.mdf' we get the following :-

And simply by clicking the 'OK' button we get some positive results - see below :-

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

In the above image I have expanded some regions by clicking with the mouse.

I will now show some data from the Customer table. I right click in the highlighted region to get

the context menu as shown below :-

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

Now when we select the option to display 1000 rows we see the following appear :-

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

Note that a Query tab is automatically opened for us, and the query script and returned data set is displayed

in quite a nice and clear manner.

I will try and show you more about using the MS shell at Stuttgart.

For now we need to see that we can have a second instance of a SQL server open in the OE pane, and

in this case we have a 'Denali' version underneath the Server 2008 instance.

We can also have instances of versions for 2005 Servers too.

What we can't do is have these in an older version of MS software, or attach a 2005 DB to say a 2008 Server, or similar

mismatches.

MS is backwards compatible, but Databases need to be converted. It is not our worry at the moment, we need to focus

on ADO.NET and Data Binding.

Well, "that is that" ... we have come a long way today, BUT, I wish you to realise that everything I have shown you was

done live by me as I captured the screen images and carried out a 'live install'.

The DB files were genuinely moved and attached, and the connection made and ata found - all live stuff.

So if I can do this the so can you ;-0)) Good Luck !

Time to move on, see you in the next section ................

PHH - 1st. November 2011.


SQL Server in the future

Local and Distant :

Soon there is a new version to be released - a full and quite 'new' version of the MS SQL Server, the first for four

years. It is on 'Preview' release at the moment under the code name of "Denali", which is an Alaskan word for "the Great One".

Basically SQL Server 2012 will address quite a few problems apparently. certainly the write-up sounds good.

Just one word of warning - do not install this early version on your PC / laptop unless you really need to.

Quite often it is difficult to remove pre-release software cleanly, and one thing that public releases OFTEN demand is that all previous

pre-release versions are removed from your system - BEFORE - a new and clean install can be attempted.

So that is the local way forward. What of the 'distant' ?

Well, it is now quite easy to acquire a Windows based web provision. By this I mean that Internet Service Providers (ISP) are offering

a package to users based on Windows server 2008 and not Unix.

The Windows based package allows Silverlight to be run from the site, but also has MS SQL Server 2008 R2 available for backend

database provision, in place of the commonly used MySQL on Unix based sites.

The benefit of the MS SQL Server provision is that the installation and setup has already been done for us ;-0))

In addition to a new Windows based site being ready to use (as far as SQL is concerned) it also will have quality tools to aid us

manage our databases and tables. Usually these software shells are very easy to use, and quite powerful as well.

I have yet to explore the possibilities of this new way of doing Internet stuff, but will write some ClickStart eNotes when I do.

A new ClickStart eVolume may have to be entitled 'ClickStartWEB' as it will need to cover quite a few aspects of this new

'flavour' of web site.

ASP.NET and Umbraco are also possible from the Windows based sites. Below is an image of the the ISP site I used recently :-

Version types :

If at all possible use the new "Denali" version of MS SQL Server.

If you are new to SQL then install the CTP3 version (Community Technology Preview) and get into using that.

When the full version is release uninstall and completely remove the CTP3 version and download a fresh and full public

release 'Express' version of Server 2012.

*** Remember *** It is only the server software you are removing, any databases you have made using "Denali" will still

work OK with the full public release. Al you will need to do to see them is use the new version of Management Studio (MS) and

attach the DBs as per usual for MS.

To be perfectly honest I wish I could start completely afresh with Server 2012. Unfortunately I have clients 'stuck' on server 2005

and this unlikely to change I feel. So I need to have MS SQL Server 2005 and all its support software apps as well, on my PC

development platform.

However, since all my own stuff is based on Server 2008 R2, I will make an effort to move myself over to "Denali" completely. As

soon as the public release is made. I will look into the possible ways of converting DB from 2008 to 2012.

Below is the current web link to the site shown in the following image. I have downloaded and installed it onto a separate platform

here in my office - the Management Studio looks like a re-write on WPF technologies - it looks nice.

http://www.microsoft.com/sqlserver/en/us/get-sql-server/try-it.aspx

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

Find yourself a laptop or PC on which you can try out the CTP3 version shown above.

But do NOT put it on a PC platform where it will be difficult to remove at a later date.

Time to move on, see you in the next section ......................

PHH - 3rd. November 2011.


Section A : SQL access with ADO.NET

A slice of History :

Before we get to discussing today, and more importantly, the future, let us look at ADO from a historical

point of view.

ADO stands for "ActiveX Data Objects" and is :-

A programming interface from Microsoft that is designed as "the" Microsoft standard for data access.

First used with Internet Information Server (IIS), ADO is a set of COM objects that provides an interface to OLE DB.

The three primary objects are Connection, Command and Recordset.

The Connection object establishes a connection with a particular database management system (DBMS), or other data source.

It can also send a query to the database.

The Command object is an alternate way of sending a query to the database, and the Recordset object contains the resulting

query answer, which is a group of records. [ See OLE DB, RDO, DAO and ODBC. ]

ADO.NET :

ADO.NET is the .NET version of ADO, which is substantially different from ADO.

It supports XML documents and relies on .NET Data Providers as an interface layer between the application and the databases.

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

It is the ADO.net version which we will use extensively in our coding throughout these eNotes and the whole Small Trader application.

With this background information in mind, let us see what the modern SDK documentation has to say on the subject :-

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

Okay then, 'ADO' is dead as far as we are concerned, and "ADO.net" is the thing to use !!

Also be aware that we will be using only one type of data source, that from (or via) an instance of the MS SQL server. I currently us

version 2008 R2 (the Developer edition). Soon we will see from MS the 2012 offering from them, currently called 'Denali' or 'the Great One'.

Below is some extra information on a useful and related namespace :-

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

We will use 'heavily' some of these classes shown above - or their DbFactory equivalent.

SQL - Problem / Issue !? :

Like everything else in life and software development, you just think you are 'getting to grips' with something when

up comes another level of complexity :((

Because SQL 'stuff' is not totally standard, across various manufacturers brand or 'flavour' of SQL server, we can't

use the 'Sqlxxxxx' classes above in our code if we wish it to be usable with other manufacturers SQL servers without

large scale changes in code within the app files.

Overcoming the 'Flavour' of Server Problem :

There is a way around the problem / issue of hard coding a particular manufacturers code syntax (and related class types)

into our solutions, and that is to use effectively a 'super' class of provider classes.

First of all have a look at the following image from the 'SQL Connection Details' screen of the Small Trader app :-

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

As you can see in the above image we seem to have a choice of provider 'flavour' of server.

The one shown as selected - SqlClient - is the one for Microsoft Server and works for me with both

2008 and 2005 versions of the SQL Server.

You will recognise that this name corresponds to the one shown in the namespace images at the top

of this subsection, confirming the Microsoft 'link'.

The 'super' class we need to use to allow ourselves the freedom to write code which can later be switched

from one of these 'Providers' to another, is called the 'DbProviderFactory' class.

As it comes 'out of the box' in standard dotNet on my machine, the options are exactly as shown above

in the image.

Before we move on into the next subsection to see how to use the 'DbProviderFactory' class we can first

take a look at the following SDK documentation - study carefully :-

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

As you may agree, useful information indeed, and stuff we ought to take note of in our solution design and

code creation.

Even though I have never used anything BUT MS SQL Server, and probably never will either, I have coded my application

solutions using 'DbProviderFactory' after a good professional 'tip' or nudge, from colleague Colin Newell - thanks Colin ;-)

Now on to the next subsection where we will get to grips with using the 'super' class "DbProviderFactory".

See you there ..............

PHH - September 3rd. 2011


Ten Points about SQL and ADO.NET

Ten points about using MS SQL Server with Vulcan 2 :

- use ADO.NET and you will get success in your dotNet application coding.

- ADO.NET is a mature technology and works very well indeed.

- prepare and test any SQL query statements in Management Studio.

- 'Copy and Paste' tested queries into you VS dotNet editor - this approach generates success.

- use my "STATIC" class to code ADO.NET 'stuff' - it will be much easier [ .. this is called 'SQL_my_funcs' .. ]

- to create and update your own 'STATIC' class for each application, copy and adapt the one I supply with the 'Small Trader' applet.

[ .. this is called 'SQL_SmallTrader' .. ]

- base each 'group' of static methods around a custom 'Business Object' (BO) class.

- place 'All' of the code to deal with backend data access in your own 'STATIC' class - 'SQL_your_application_name'.

- whenever possible use the disconnected model, and returned DataSets in place of DataReaders, and then close the connection.

- in preparation for the 'Entity Framework' (EF4) approach, map your custom BO's to SQL tables at design time.

[ .. it seems a big difference to DBFs at first, but you will grow to LOVE it - I did, and have never looked back .. ]

PHH - 28th. October 2011.


Using the 'DbProviderFactory' class

As I have previously said, a few years back I took advice from a professional colleague who suggested that

in my coding of apps using an SQL server as a backend data source, I use the 'super' class named

"DbProviderFactory" instead of hard coding my solution to work only with the Microsoft 'flavour' of SQL server.

It does NOT give much of an overhead penalty at all, and at least makes my application much more flexible

as to how it could be used by others.

I see this issue as one which needs to be addressed early in one's experience of SQL coding, and from then on

it will be taken care of.

I seems stupid to me not to use DbProviderFactory!

Here is what the SDK has to say :-

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

This in fact is a simple class as classes in dotNet go, see below the members :-

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

And then seeing what the SDK has to say about the Constructor we see the following :-

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

How do we make our DbProviderFactory instance for SmallTrader ? What does the code syntax look like ?

Well, see this short code snippet from the working code :-

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

Obviously we need to know what the 'oConnDetails:cProvider' is and where it comes from.

We cab see that we have the code using a string property of our own class called 'ConnectionDetails', this class

is shown in detail below :-

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

The above class has been designed by me to work with the 'Connection' screen, so properties match up with

what is shown on the interactive entry form for connection information.

I have to be honest and say the basic design was done years ago (three?), and for a WinForms version, when dotNet

version 2.0 was all the rage ;-)

So there are a few old fashioned parts to the remaining 'underlying' code. But it still does work OK !!

Maybe later I can move it on towards a collection based approach, with binding to the form drop list.

For the moment see the following detail of the content to the 'Config' file where our app stores its SQL connection

and logon information :- [ the config file is named 'ConnectionDeatils.xml' ]

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

In the above image checkout the highlighted 'cProvider' tag line - note that it is "SqlClient".

All this extra information simply means we are effectively code a line such as :-

 oDFactory := DbProviderFactories.GetFactory("System.Data.SqlClient")

Where oDFactory is of type 'DbProviderFactory'.

Now what do we do ? :

Well, having got our instantiation sorted out, we can use our instance of the 'DbProviderFactory' to

supply us with the necessary 'bits' without need to use the 'SqlClient' namespace directly.

By this I mean we use the method 'CreateConnection' exposed by the 'super' class, and not the 'SqlClient'

class.

See below how our application code looks for creating a connection :-

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

As you can see in the above code, we need to see what is declared at the head of our class, as

class variables. There must be others as well as the 'connection' type variable show above.

Study this carefully :-

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

Since the ADO.net 'stuff' for connecting to the data source, and sending queries, is NOT an application

based class, we will use the same STATIC class for these ADO.net members for each separate app we

deign and create.

Notice in the above code we have suitable types for 'provider' and 'connection'.

Making a Connection String :

Now that we have 'oDConn', and instance of the DbConnection class, we can use this as follows to set a

connection string - see below :-

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

However, we first need to use the stored information in our deserialized 'config' file to make a suitable string.

Check this out below :-

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

Okay, I hear you say that there are connection string builder classes etc. etc..

But hey! this is simple enough, and it works well too - and has done for three years, so why change things ?

Moving Forward :

For those of you who wish it, and are interested, here is what the SDK has to say about the String Builder(s) :-

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

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

And for those who like to read, study and learn, here is a bit more information :-

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

I suggest that if you wish to do more than what I have included in the Small Trader app, then you do your own research and testing.

Basically, for now, I am happy enough with how I do things for connection operations.

Now lets move on and try and make a connection.

Opening up a Connection :

Once we have set up our DbConnection object correctly, we simply need to make one call from our code.

See below to find out how to attempt to open a connection :-

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

In the above image line 406 is the only one required to make the connection we require.

Below is the SDK documentation :-

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

In the images from the SDK we see we can close the connection with a simple one line of code as well.

Here is the Small Trader 'Force Disconnect' method :-

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

Line of code number 419 is that which is required.

Testing our Connection WPF Form :

Let us now move on to try and test the lines of code we have been studying.

I will first of all launch the Small Trader application and then navigate to the Connection screen.

Below I am testing the green button to try and make a connection with the string details existing in the

relevant object properties :-

And now what if we view the string which has been used - see below :-

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

It is interesting to look at the methods which respond to the button clicks of the respective push button

controls - see below for full details :-

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

Yes, it is obvious from lines 212 and 222 / 224 that we are simply calling on members from the STATIC class

making up layer 6, the 'SQL_my_funcs' class.

*** Tip *** when we refer to access of a STATIC field we must use a colon ( : ) before the name of the field itself.

Yes, the rest of the namespace / class test takes a dot ( . ) or point, but not for the 'access' symbol. For Vulcan

that must be a colon.

Let us now move on to the next subsection where we can see a few other interesting things about the Vulcan code

behind the Connection screen.

See you there ....................

PHH - September 3rd. 2011.


Connection screen - interesting details

The OLD and then the NEW ! :

Well then guys, this next part is going to be fun I feel.

First we are going to see the old style VO type coding I used some three years back when I first got a 'Connection' screen

to work for me, with WinForms and in Chris's VIDE.

Then we will attempt to modernize it by using collections and WPF data binding, set as much as we can from

the XAML script, and not done in dotNet code.

Since the older approach is working in Small Trader at the moment, it is a matter of style, and keeping up our

skills so that we can use the latest techniques and even fashions.

Let us start by having a VERY careful look at the older code, notice the For/Next loops :-

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

As well as checking out the above code, we need to also check that the XAML script has no binging.

Here is what is in existence at the moment :-

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

Yes, just what I thought, no markup extension script which specifies any Binding, at all.

Since line 332 actually gets a DataTable, we ought to be able to use this to do some sort of binding with

our drop list, or even a short list box control.

Oh! in case you are wondering, the 'FillDLTimeSecurity' method look as follows :-

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

As you can see in the above code, nothing too clever, and you should be able to appreciate what is being done.

Lets move on, and find out how to improve things .

Making a Few Exploratory Experiments :

The way to tackle this is to make a new WPF form and work from a blank 'canvas' as the 'man-in-the-street' would

say. That way we are keeping things as simple as possible.

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

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

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

PHH - 3rd. September 2011.


'Management Studio' - seeing your data

If you are going to do anything at all with MS SQL Server then you MUST install and start to use the

latest Management Studio (MS) software from Microsoft.

"Management Studio is your friend !"

I often refer to the MS 'shell' as it effectively sit around your attached databases.

It is VERY effective at communicating with the MS SQL Server, and I have logged up many hours use

with it over the last three years or more.

Using MS you can learn much indeed about SQL and the 'flavour' of script that is used to construct the query

statements. T-SQL (said Tee Sequel, and sometimes Transact Sequel) is like a programming language of old.

It would be described as a interpretive language as opposed to a compiled one, I would guess.

So the T-SQL script is parsed and run at the time it is required.

Getting the Latest Version :

Whatever you do, try to get the very latest version of MS, it tends to come with the download of the SQL server

itself, although in the past I think I have managed to find a separate download.

Microsoft seem to change the downloads available for SQL Server and its support software, on quite a regular

basis, so writing eNotes is quite tricky :((

The GOOD thing about the latest version of MS is that it is always back compatible, so I can connect to both

2005 and 2008 server instances with my current version, and deal with databases of the 2005 and 2008 variety

from the same Object Explorer (OE) pane.

*** WARNING *** MS is not forwards compatible however, and this can catch you out under certain circumstances.

In the following image we see one instance of Server '10' ( we call it 2008) and one instance of

Server '9' (we call it 2005) open at the same time in the OE pane.

Check this out :-

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

I am not sure whether the screen information in MS means that a connection is 'open' all the time

we see the OE pane as above. My guess would be that a possible connection is tried and confirmed

by the OE pane content, and that when we subsequently send query statements - with Execute - there

is then a connection opened and closed again afterwards.

It certainly feels to me as if the MS shell uses a disconnected model for its operation.

Once the MS 'shell' is open we make further connections like so - see the image below :-

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

And when the top option of Database Engine is selected we get the very familiar dialog screen - that which

is always shown when we launch the software initially.

Here is what I get on my development PC :-

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

Notice that there is a further option at the bottom of the drop list, to search for more instances of MS SQL server.

This can be helpful at times, particularly when a network is involved, wireless networks may come and go

as the 'shell' is in use.

One thing I do also notice on this old machine of mine, and that is I seem to have server instances which

are no longer functional - in 'street' parlance I would say it has some old 'crap' in it, and I don't easily seem

to know how to get rid of it. So take care with any installations you do - although you can reinstall it is not

always easy to clean up the drop option list.

Disconnecting - How and When :

At any time you wish (almost) we can choose to disconnect from a server present in the OE pane.

To disconnect just right click over the server item line in the OE pane to bring up the context menu

as seen below :-

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

Clicking any 'Disconnect' option is quick and the connection in focus is quickly removed from the OE pane.

Difference Between Connection and Attachment :

If a connection is a way of pointing the MS 'shell' at the instance of MS SQL Server either version 2005 or

2008 (and soon to be 2012), then what is the 'Attachment' ?

Well, once we have made a connection to the instance of a Server, we see the available databases listed

in the 'DataBases' folder in the OE pane - see below :-

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

There are not that many attached to this instance, so we had better look at a 2005 instance of mine to get

a fuller and more illustrative list - check this out below :-

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

I regularly work with both these instances, and with the databases attached to them.

The new work I do with my Vulcan and dotNet coding, is with server 2008, but I do have a lot of work

with a Cardiff client when I have to use 2005 'flavours' of everything, because that is what they use

on-site with their LAN.

Attaching Databases :

You need to think about this for a while, and make sure you get your head around it - just so it makes

the rest of these eNotes make sense, particularly when we come to VN coding.

When we choose to 'attach' a database in the MS 'shell' we seem to be doing no more than constructing a

suitable connection string which the shell tries out, and if OK it stores it, leaving an item in the OE pane

under the 'databases' folder.

It could be said we are registering a database with the 'shell' itself - making a link. Attaching is NOT a

thing we have to worry about when we write our dotNet code.

So how do we Attach and Detach ?

Right click over the 'Databases' folder name as seen below :-

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

After selection you get a couple of dialog screens, one after clicking the 'Add' button seen in the image below :-

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

Selecting a DB from those on offer should bring up something like show below :-

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

Then click the OK button and cross your fingers - well German friends can hold their thumbs - I do both ;-0))

This sort of stuff with SQL can be a bit of an 'art form' ! - as opposed to a science.

If all goes well you may see something like this in the OE pane :-

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

Yes, I know, the shell software seems to give the new database item a rather unwieldy name !

To put this to rights, try clicking for the context menu while pointing at the requi8red item, then select

to do a 'Rename' - having just done this I get the following :-

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

Depending on the 'depth' of the folder where the .MDF is stored, the name can either be large or even HUGE !

I remember getting an ear bashing by GS (Global Santa) for this when I was just starting out with SQL some three

years back - he did not seem to appreciate that it was done by MS itself. However, he was the only NG guy giving

me regular encouragement, apart from some gems fro Mike 'P' too I seem to recall.

Many guys seem to run a mile from the server download and installation itself.

Probably because few do it for themselves. Often at customers sites they already have a full and working installation.

This will be different for small companies like 'PC Planet', where for the Small Trader application I had to do the SQL

Server install, and get all bits talking together etc. etc..

Summary so far :

It would appear that making Server instance Connections, and Attaching databases, is really just registering connection

string data with the MS 'shell' itself.

Obviously there is no way we can have connections open to multiple servers with multiple DBs, all at the same time.

So in my mind I think of it as the disconnected model, where connections strings are registered wit the MS 'shell'.

This model has served me well enough for quite some time.

Detaching :

This is easy, and only really needs to be done to keep clear, or clean, the working space in the OE pane - no other

logical or technical reasons.

So to deregister the database part of the connection string we do as seen below :-

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

You do not need to cross your fingers an/or hold your thumbs for this operation ;-0))

So when is a connection made ? :

This is easy to answer, but takes more time and effort to demonstrate, as well as illustrate.

Any and every time we choose to send a query statement to the Server.

Here is a VERY common example of what I do - see the following image :-

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

And now shown below is the results displayed in the 'results' pane - plus other bits :-

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

Selecting to look at all the fields from the first 1000 rows of a table is VERY often the place you start when

trying to make up a new query statement. I must do this many times a day.

I suggest the 'shell' opens a connection when an 'action' is triggered, and then when all the data is returned, the

connection is closed by the 'shell' once again.

'Actions' - what are these ? :

One group of actions are those on offer on the context menu - there are a few more beside the Top 1000 rows,

and Edit the first 200. But we will see the scripting options later.

For now just realise that a menu option can cause an 'action', or query to be sent to the Server, or we can instigate

such an action ourselves.

Lets say we wish to start a new query, one of our own making. Checkout below :-

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

The result of this selection to to bring up a new tab as shown below - completely empty of content :-

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

Now we have a nice clean surface on which to type our own query scripts, or statements.

Simply we have effectively an editor in this tab. Once we enter a suitable query script we can 'Execute' the query.

To do a useful test I will slightly adapt the script we had for the Top 1000 rows - see below :-

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

Notice the red 'pling' and the 'Execute' icon/tag at the top of image.

Once we have some text in the tab clicking this cause the text to be sent to the server, whether it is a correct

statement or not. If not we get a error returned.

I suppose you had better see an error or two, just so you get familiar with what will happen quite frequently ;-))

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

Well, with my little test the 'shell' is smart and already seems to warn us that we have an error.

See above and below to find out what is returned as a message when we send incorrect scripts.

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

Obviously forcing an incorrect table name, as well as an incorrect field name does not fool the MS 'shell'.

PHH - September 5th. 2011.


ADO.NET - the Namespace

At first, coming to ADO.NET may seem a little confusing, as probably the first thing many of us might try

is to find the 'Namespace' for it.

Having failed in my search of the SDK documentation I use VERY regularly, I turned to good old Google.

Below is what I got, and I assume you will get something similar :-

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

So I tried the link shown in the very last entry in the image above and got the following :-

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

So having satisfied myself that I was not missing out on something I ought to know, I am using the 'System.Data' namespace

from now on in - see below for a start :-

*** REMEMBER *** It is not ADO you are going to explore using, but ADO.NET, and they are quite different apparently.

[ .. ADO is dead, long live ADO.NET !! .. ]

Why this Section ? :

I included this subsection mainly to clear up any confusion about the required namespace for 'standard' ADO.net, but

I realise that if we are to use the 'DbFactory' approach then we are going to use another namespace entirely, as you

already have seen.

But for our information, and our education, here is what the top of the SDK documentation looks like for 'System.Data' :-

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

Well you guys, reading the above image information, I hear a lot of what seems to be quite familiar stuff, no surprises, BUT,

I did not see 'DataReader' mentioned even once.

It would seem that although this is still supported, for the reason of moving towards web based processing (and the future?)

the disconnected model is being focused upon, and that means 'DataSets' returned and the connection dropped. Not getting

a DataReader which requires an open connection while application processing handles the data pulled down from the backend

data source via the reader.

I think and hope I am interpreting things correctly.

Anyway, you now know where to look for information, but remember to checkout the 'DbFactory' stuff too, as we will be using

this throughout the Small Trader application.

See you in the next subsection ...............

PHH - September 5th. 2011.


ADO.NET - various types of Query

So far we have seen that to use ADO.net in our application code we start with selecting a 'Factory', or

'flavour' for our brand of SQL server to be used.

After creating a 'DbCommand' object from this factory object, we then give it a suitable connection string.

This is effectively providing a key to the door of the server and the required database.

In the various types of query member we will examine in this section, we see the above repeated many times.

This is how it looks, study carefully :-

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

*** Remember *** that 'oDConn' is the connection object instantiated in the set connection method, and which

is used to Open and Close a connection to the server.

What else is required to send a Query ? :

If we are starting to get a 'feel' for this ADO.net 'thing' we will realise that in order to send a query to the server we

to set a couple more properties on the 'command' object before we call a 'command' method which then returns

us some data, in some form or another.

Before we get into the topic of 'Which method to call', depending on the expected returned data 'format', let us

take a look at the lines of code with ADO.net syntax, to send a query which returns a 'scalar' value.

Check this out thoroughly :-

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

In the above image there are three such 'new' lines of ADO.net stuff we need to explain.

Line 649 is easy, we just supply the query script as a text string.

Line 650 tells the server how to interpret this, and the possible options are shown below :-

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

As you can see, not a lot of choice, and usually for us guys doing standard SQL data access tasks, we need the 'Text'

option selected.

So finally, to line 651 in the image one above the last one shown, this calls the 'ExecuteScalar' method of the 'command'

class and if all goes well we get a result returned ready to use in our business logic code.

It is probably time to view the SDK documentation for the 'DbCommand' class - see below for details :-

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

Above are shown the properties of the class, some of which are the ones we have used so far.

Now we need to study the methods - see below for details :-

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

In the last of the images above, we see that the 'ExecuteScalar' member has been highlighted.

There are however, others we will use in our Small Trader application, and we shall move on to these in the notes

following this.

Types of Expected Return Objects :

It is now time to discuss the possible types of data object we may expect form our querying.

In the example above we are expecting a 'scalar' quantity, that is, a single 'thing'. So it may be a string type, or an

integer, double, Boolean or DateTime. Any of the number of the general data types.

However, there are others too, like nothing at all, as also a whole data set of results. On top of these extras, we

may even have a 'Data Reader' object returned, which will be used in our code as we wish.

So of the four method named 'Execute.......' we will use three, that for '....NonQuery', '....Reader' and '....Scalar'.

And, as usual things are not just this easy, there is another level of use too ;-) We can use a 'DataAdapter' to

allow us to get our whole data set returned all at once, as a 'chunk' of XML. This method, or approach, is different

to the 'Reader' one where the Reader is left open and data is read back from the server in a fast one way manner.

I have to say I prefer the 'all at once' approach, but then machines are faster and RAM much larger, and network

connections far faster and better, so maybe things have changed in our favour !!

[ .. Below we will discuss just the types of query and how to handle the data will be covered in the next section .. ]

No Values Returned :

Yep! we must remember that some queries we send to the server will not result in us receiving any data in return.

These types of operation are CRUD ones, for INSERT and UPDATE in particular. If we do not have an exception

thrown then the operation is a success.

[ .. we can always check with @@ERROR however, see later sections for getting error codes from the server .. ]

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

Returned Scalar Values :

Quite a common sort of result from our query is a single piece of data - a number or a string, etc..

We write the STATIC method so that we pass back to our calling code an object, which will be cast as we require

in our Business Logic code.

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

Returned DataSets :

Very often I find myself expecting a record set. That is a table of data, rows and columns. This comes back as a

DataSet, usually for me it is a single table within the set, which I then use or extract as a Table.

This method of approach is VERY flexible indeed, and can of course also handle a single row of data.

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

[ .. once again we see the use of the 'Factory' to provide the correct Data Adapter (DA), line 559 above .. ]

[ .. this time we pass the DbCommand instance into the DA as a property association .. ]

DataReaders Returned :

I am tempted to say that this is a 'dated', or old fashioned, way of getting back a lot of data items in a large data set.

The connection to the server needs to stay open as the returned DataReader object is used by the calling code to

process the data in a fast, one-way, manner. I have used this in the Small Trader app some place I recall, but

generally I favour using the 'disconnected model' where I open a connection, send a query, get the data back in

one go, and then close the connection.

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

Study the above code, and also realise that if you wish to follow a disconnected model approach, then it is

up to the developer to close the connection when the DataReader (oDr) is no longer required.

This makes the 'SendReaderQueryScript' STATIC method different to the others which get data, the others

are force a connection 'close' just before they return the data sent back from the server.

Now that we know how we can ask for different sorts of data, and in different and useful ways to us, we can

move on to the next section where we discuss how to deal with it so it is useful in our code.

See you in the next section ............

PHH - September 4th. 2011

Addendum :

In the above images of the STATIC members for sending queries, you will have noticed a line like the one numbered

619 in the very last image. the 'SaveAndDisplayErrorDetails' method is only called after an exception has been

thrown due to something going wrong with the query in the server.

We need to call this method before leaving the query method, and/or closing the connection. The reason being that

the error code is only available from the server if the connection is still open, AND, the request for the error code is the

very next one after the query itself. We need to be CAREFUL about this as any subsequent query (or any sort) will

change the error code set in the server for that particular connection.

For example if we request the error code twice, by sending the @@ERROR request query, the second time we will

get a zero ( 0 ) because the very previous query was a success - no error.

Here is the detail of the 'SaveAndDisplayErrorDetails' method - study carefully :-

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

And looking at the image shown above we realise we also need to be able to study 'GetErrorCodeFromServer' as well.

Here is the detail for retrieving the error code :-

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

Notice in the code shown that we are casting the returned object to an integer, simply because we know the error

code is an integer.

Also the 'flavour' of the query member used is one which does not open a new connection, and also one which

does not close the connection either.

In many ways this is the simplest and cleanest of our query methods - checkout below :-

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

Note that I have commented the line 448 since we could get into an endless loop if we had an error while

asking the server for an error code !!

I will need to think of a way to pass a parameter to allow the method to do the 'SaveAndDisplay.....' or not.

You need to beware of possible 'LOOPS' with this ADO.net sort of code and its members, it is easy to overlook

the possibilities of this happening - till it does ;-)) I know, I have been there !!

Maybe one way around this would be to write a special method just for querying when an error code is required.

But then I much prefer general methods, with suitable parameters.

"Watch this space ........ "

PHH - 4th. September 2011.


Returned data - how to handle it

Okay then guys, now we know that ADO.net code can be used to ask different sorts of query, then we

can move on to see how we may deal with the returned data - different types thereof - in our calling

Business Logic (BL) code.

General Comments :

It is in 'Layer 6' that we see the coded use of ADO.net members to do our tasks for accessing the SQL database

via the server. So this is where DbFactory 'stuff' is seen.

However, when it comes to using the returned data from our queries, it is the 'Layer 5' members which we will see

the code to deal with the data send back by the server. Now and then we may see some code in the Business

Logic (BL) layer too, but mostly the layer below the BL is where we will see it.

Scalar Values :

We have just seen an example of this in the last subsection when getting an error code returned from

the server.

See below for a cast to integer :-

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

[ .. above is an example from 'Layer 6' itself, but this is unusual, 'Layer 5' and the BL layer will be more common .. ]

So in line 665 above, the returned object is cast to an integer for the rest of the BL to process.

Obviously, it is relatively easy to cast to any data type we know the returned at to be, DateTime, Boolean,

or whatever.

Below is another example, but from 'Layer 5' this time, the STATIC class for Small Trader :-

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

As you can see in the above code, this query statement returns a customer reference number, so is cast to the

required integer type.

'DataSet' Records :

In the following image we see an example a method in 'Layer 5', where one or possibly more records are returned

for a customer reference number 'search'.

In doing my testing I have had occasion to get more than one record, simply because of previous errors etc..

Normally, in a correctly setup database the various constraints and the likes, on the DB itself would not allow the

Primary Key to be duplicated.

Checkout the following details :-

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

As you can see in line 228, we get a data set returned from our 'Layer 6' static method. This means that

when we get to executing code on line 229 the connection is closed and all data is in the object 'oDS'.

This is a contrast to what happens below when the

'DataReader' and Subsequent Use :

In the 'Layer 5' part of the Small Trader application, we see an old method of mine (not sure I would do things like

this nowadays!) which makes an 'ArrayList' object from the subsequent processing of a data reader object returned

by the server.

As you can see the 'DataReader' is used and processed after the line 169 is finished, or executed. If we close the

connection before this processing we get an error!

The open connection allows the data to be pulled down as required in the 'Do / While' loop and the processing code

may be written as seen below - or in a different way seen further below.

Checkout the details following, particularly the use of the oDr (DataReader) object :-

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

We can see in the above code that the data reader supplies a record at a time, and that record may have its fields

accessed by a fixed numerical form of supplying the 'ordinal' value.

Before we do more on the processing and access side of the data fields, let us view the last couple of lines of the

same method as that shown above :-

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

Notice that after the data reader is closed (line 196) we can the - on line 203 - close the connection.

Alternative Access to 'DataReader' Fields :

In some ways the fixed value 'ordinal' way od accessing the data fields is a bit old fashioned.

Luckily for us there is another, and more flexible, way of doing the same thing.

See in the following image how we can access the fields using a specified string value as opposed

to a numerical one :-

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

Doing things as shown in the image above, means that we do not need know the position of the field in the record,

and can even use a coding line order which more suits us and our processing.

However, we still need to know what type of data we wish to retrieve.

Please notice that we can even match the syntax of the code lines, it will not matter to the resulting casts and

property assignments.

Here is a useful image - see below :-

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

The 'GetProviderSpecificFieldType' looks interesting, as do the other couple of similarly named methods.

Well, I have had a bit of a play around with these and got nowhere, so will leave it up to you if you so wish.

Basically I was hoping to be able to get a value without having to say what it was be selecting 'GetString' and the likes.

However, maybe you are starting to see why I like the DataSet approach ;-))

See you in the next section ..............

PHH - 4th. September 2011.


ADO.NET - getting things better

When I sketched out the contents of this section, I intended to write about how the ADO.net code

required to do the database access was a bit longwinded, and how it may discourage a new user

coming from DBF related technologies.

However, since making my chapter and folder 'sketch' I have developed and tested the new STATIC

class called "SQL_my_funcs".

By providing myself (and you too) with this 'Layer 6' fixed class, which can be ported to every SQL

application that I write, and used in exactly the same way each time, I have in effect removed the

need for this subsection of the eNotes.

ADO.net is an impressive technology, it has just worked for me, right from the very start. I did not

have the benefit of using SQL or ADO (of old) prior to me coming to Vulcan.net, so I had to deal with

everything as 'completely new' ideas and ways of doing things.

The only thing that 'put me off' a little was the amount of code needed to leverage the power of ADO.net,

or so I thought. Now I have rearranged both my ideas and my code, I can use SQL from by VN2 code

as easily as using Management Studio (MS) to query the server and attached database.

This is just what I required, so now my Business Logic (BL) is not cluttered up with ADO.net code.

AND, since dotNet use has gotten me to think in terms of objects, Business Objects (BO) and even

with WPF forms objects at a GUI level, it is nice to be able to carry the idea of objects right down to

the database (DB) access itself.

Getting Things Even "More" Better :

If you excuse me the awful English ;-) the fact that EF (Entity Framework 4.0) is around the corner for us

VN2 guys we are in for an even simpler and better time ahead.

One thing that will not change when we are able to migrate to EF4 is the fact that we will still think in

'objective terms' - our BO as DB objects - as we have done with this demo and sample application of

the 'Small Trader'.

Our ideas of 'layers' is just as valid, and should be even simpler to put into practice - so we are not

wasting our time doing all this VODC 'stuff'.

So having said all that we can move on to the next section.

See you there .................

PHH - September 4th. 2011.


ADO.NET - improving record manipulation

Okay then, some of you may disagree with what I am about to say, BUT, that is your choice !

When it comes to handling returned data, whichever way we do it for a bunch of records, I prefer to

go straight into my application BOs, and make collections of these when it is a suitable thing to do.

If I design my BOs to be Dependency Objects and create collections which are particularly friendly to

WPF and its GUI controls, then life will be as easy in dotNet 4.0 as I can make it - as well as getting

all the extra benefits into the bargain ;-)

So this section could be called "Moving away from Record Manipulation."

Obviously we need to do some manipulation of records in DataTables and DataReaders, BUT, if we

go straight to BOs this will be kept to a minimum.

At times a DataTable object can be used directly, after wrapping it in a view, or similar. But generally,

it will be worth while writing the code to make the BO, and just go for it.

Certainly, when we come to EF4 we will be dealing exclusively in objects, so lets get mentally ready for it

before it hits us as a necessity.

Once again, the fact that we now have a 'Layer 6' STATIC class, along with our application based STATIC

class also, means we can keep this subsection very short indeed.

All that we need to say about processing records from returned data - sets and readers - can be dealt

with in the next subsection where we focus on making instances of BOs out or table and record based

data returned from the server.

And for any existing BOs in RAM we can use Layers 5 and 6 to send new and changed data directly to the

server, and hence database.

Let us now move on to see how we may choose to create our BOs from our returned data sets.

See you in the next subsection .................

PHH - September 4th. 2011.


ADO.NET - record to business object

Okay then you guys, this is an IMPORTANT section of these eNotes !

As most of our SQL applications will have more than one BO and as such we will need more than

one static method (in our Layer 5) to convert a record returned by the server, into an instance of the BO

in focus.

Let us take the Small Trader example, and also the 'Customer' class as a useful guide to what is required.

Using 'DataSets' as the returned source :

The STATIC member 'SendDataSetQueryScript' returns to us a DataSet object before it closes the connection.

Look closely at the following image details of how to access the field data values :-

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

The above code can be used to illustrate the techniques required, even though it appears to be an early bit

of my code in this particular application.

Basically, we are asking for records to be returned (one or more than one) for any Customer with a specified

reference number. Yes, I know that in an ideal world this should be always a single record - BUT !

So this less than 'perfect' method will return the first one (record) of any set returned for the specified Customer.

Since it is the process of coding that matters, we will not worry about other issues of rights and wrongs ;-0))

What is going on above :

Basically, we are expecting only one table to be returned in the data set, and this table ought to be a single row.

This will always be the case if we have setup our database and tables correctly - we will have only one customer.

So taking the first row from the first table in the set, is always going to give results if the Customer is found.

So yes! It would be a good idea to have another STATIC member which checks to see if a Customer is in the DB

in the first place, and then only if found will this member (method) above be called from the static class. I will work

on that extra checking method. [ .. see Addendum below .. ]

For now, notice that in the above code we extract a single data row as an object, and then for every property in the

newly instantiated BO, associate a data value with the appropriate object property.

Don't let the oDR variable name confuse you, it is a DataRow object and not a DataReader one. This means we

have to process the items a little differently.

There may be other ways to do this, but the 'convert' static system class seems to work for my data, just fine.

Alternative way - from 'DataReader' object :

WE have an alternative query method in our ADO.net STATIC class. It is called 'SendReaderQueryScript' and it

is in use below in the displayed image :-

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

You may think you have seen much of this previously, and indeed you would be correct, but now I have replaced

all the fixed value ordinals with a call to a method of the DataReader class.

Personally, I feel this change makes the whole code section more readable, and also easier to maintain and revisit

after a time away - AND - far less likely for mistakes to be made by the programmer / coder.

Note the last line, this is the correct and best way to close a DataReader object, nothing else is required. The SDK

documentation says so, and Don Caton said to old VO guys - not to use ' := NULL_OBJECT '.

Summary :

So there we have it, two different ways of making an instance of a BO from data returned from the server.

Let us move on to see what we need do to get ADO.net to work for us in the other direction. How we save and update

BOs in memory to the SQL database and specific 'object' tables.

See you in the next section .............

PHH - September 4th. 2011

Addendum :

In our discussion above we said we needed a static method to check on the existence of a Customer in

the database - are they there, or are they not ?

Well here is a short method which does as we require - it did not take very long now that I can do the

construction of the query string and then just call on the relevant static member from 'Layer 6'.

Checkout the following few images :-

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

Yes, this is correct, Customer 103 is already present.

I know from using Management Studio that Julie (reference 103) is in the table more than once - see below :-

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

However, the running customer reference number has not gotten any where near the thousand mark - see below :-

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

Now that we see that it appears to work, here is the coding of the new static method :-

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

This now allows us to rewrite the 'GetCustomerObjectFromRef' method as follows :-

[ .. I realise that some may prefer to trap the condition before entering this method - its up to you ! .. ]

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

In fact, looking at this new method, we may decide that we also need a static method to tell us how many times

a customer is present in the table - or one to supply any records for Customers who have 'Multiple Entries'.

I will leave these for another day - BUT - wish you to see how easy it is to add new 'Layer 5' methods which

allow us to write cleaner and clearer Business Logic (BL) code.

PHH - 5th. September 2011.


ADO.NET - business object to record

So far, in our process of using ADO.net, we have basically just asked reasonably straightforward

queries and gotten back a bunch of data from the server. The real work was processing this returned data.

Now with creating new records at the server (INSERT), and updating any that already exist (UPDATE), we

need to do all the code work before we send our query statement.

In fact there is a reasonable amount of coding required - luckily a goodly chunk of it can be placed in the

ADO.net 'Layer 6' so that for each application the 'Layer 5' members are reduced in size from what they

used to be before I made the STATIC classes.

For this section we start at 'Layer 4', the BO layer, and move downwards towards the server and database.

Once again, let us take our Customer so that we can illustrate what is going on.

If we imagine the scenario that we have a Customer business object in memory, all set up with data values

associated with each property, and we need to save this to the SQL database.

Well, the 'Customer' class is very straightforward and so we do not need to look at it directly, it is enough to

see it in use in the following code.

First lets look at the 'FillTypedList' method which has been written by me to go with the Customer class.

Check this out carefully :-

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

The above image shows one method of a pair which is required for each BO in our application.

Notice how each of the Customer properties is used to supply a value to instantiate a 'SQLfieldSpec' object

to specify and hold the necessary details for the field / column in question. There is a one-to-one correspondence

between the columns in the SQL table and the fields spec objects added to the strongly typed list.

Now then, what is done with this list once it is instantiated and filled ?

Making a Business Object :

Okay then guys, let us take the strongly typed list and do something useful with it - see below :-

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

If we look at the above code we see that line 153 takes the strongly typed list and supplies it as one of three parameters

to the STATIC member in the next layer down, 'Layer 6', where the 'MakeBusinessObjectDetail' method resides.

The other tow parameters are very simple, one the name of the data table in the SQL database, the other is the type

of operation - INSERT or UPDATE, and this is done via an Enumeration :-

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

Well then, what does the STATIC member 'MakeBusinessObjectDetail' (MBOD) do for us ?

It simply takes the information of the field data and its values and turn this into a format suitable to construct a

SQL query statement, so we can then send it to the server.

In the following image we see how this is achieved for us - remember this is all written and tested, and will not be

normally even seen by us - thank goodness.

Check this out :-

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

The above code is basically in two sections, one for INSERT and another for UPDATE.

The static members 'CreateFieldSymbolPairs' and 'CreateFieldsSymbols' are used depending on the mode of

'CRU' required. [ ** remember 'CRUD' stands for create, update and delete - here we ignore 'D' ... ** ]

There is a different syntax required for CR and U - hence the two different static members - 'FieldsSymbols' and

'FieldSymbolPairs'.

Okay then, we make a suitable text string to represent the long and complex query 'script', but before we can send

this script or query statement, we need to add each parameter to the 'command' object we have been building up.

In the STATIC class for 'Layer 6' I have included a few small supporting methods to assist in this task.

So if we study the static member called 'MakeBusinessObjectChange' we will see just what is required for these

parameters. Remember that the statement associated with the 'Command' does not have the actual values within

it, just the '@' type symbols.

Below we are getting towards the end of the longwinded sequence, keep patient! :-

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

Okay then, in the above image we see that the 'Command' object has now got a suitable query statement property,

as well as a connection, but we are passing the 'Command' object to a new static method called 'AllParamsSetAdd'

so that the parameters may be added.

Checkout below the details in this static method :-

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

[ *** note *** the '@@' is required simply because the word 'Symbol' is a reserved word, and this is VN syntax .. ]

In the above code, all we are doing is moving through our strongly typed list of field spec objects and using their

properties to add a suitably structure parameter to the 'Command' object.

The final static method required is only short, and called 'SetAddParam', here it is, shown below :-

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

Just to remind you, if we were doing things the old way in ADO.NET we would have to code a parameter group of lines,

like those above, for each of the fields in the table for our Business Object :((

As you can see the code makes a parameter, and adds it to the collection, from the 'input' method parameters.

Summary :

It may seem to have been a long winded way of achieving what seemed a straightforward enough task - sending the properties

of an in-memory BO to an SQL table, but at least most of this we do once in our code, and after thorough testing leave it to

be used, and only have to write the 'Layer 5' related stuff - that for the specific objects used with the business logic.

Having done this both ways, I know I am much happier with using the STATIC method approach.

I think a nice spin-off is that the ease of use encourages me to write probably more application related static methods than I

would have otherwise done - yes, this means better coding practice I suppose.

Time to move on, see you in the next subsection ....................

PHH - September 4th. 2011.


The Future - Entity Framework

PHH - July 17th. 2011


Objects all the way !

PHH - July 17th. 2011


Code First - another approach

PHH - July 17th. 2011


Section B : WPF Data Binding

Don't be in a Rush :

Some good advice to new guys to WPF and data binding - take it steady, do things step-by-step, and don't

be in a rush at all.

This is a big subject and one worth doing properly, and well.

It will take you some time to build up your skills and knowledge - BUT - at least you have these eNotes to help

you on your way. I did not !!

Also, I suggest you look at the later section of 'Binding Scenarios' and start with the simpler ones first. Do NOT

expect to do too much at once, give yourself a chance.

What is Data Binding in WPF ? :

Put quite simply, 'Data Binding' is a way of connecting controls on a form to properties on a business object.

If a BO has a property bound to a GUI control then it display the value automatically, and can even allow direct

editing of the value itself.

Dependency Properties are used for binding. You will get a feel for these as well see more code in action.

DPs are simply variable value properties - for example, instead of saying the colour of a control is 'Green', we say

that the colour of the control is the same as that of the 'Example Label', where this is a label with its own fixed colour.

So whatever the colour of the 'Example Label' then this is the colour of our bound control.

The dependency 'link' can be far more than just a chain of two - BUT - at some point the chain must stop and a fixed

value be used or defined.

So as the name implies, the property of the target object depends on the property of another object. For those guys

who ever did low level programming, this is similar to addressing modes - fixed, relative, etc., etc....

The type and extent of the binding depends on the nature of the control and the settings of some of its properties.

Put quite simply, if you bind a Read Only (RO) text block control to a VAT value of a BO, then it can't be edited.

On the other hand if we bind and editable text box control to a VAT value of a BO and set the binding mode to 'OneWay'

then we can't change the source value.

You will soon get the feel for the modes of binding, here is what is available :-

[ .. don't be scared of XAML, it will be your friend, it is easier than it looks! .. ]

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

You may wonder why in the above script we have 'TwoWay' and a RO control property setting. Well, with binding the

trick is to make it work for you, obviously this works, and there may be a reason for it - BUT - I guess it was just that

I decided to protect the control contents from user 'change'.

Below is shown part of a real invoice :-

The green amount 'cells' may be edited, however the grey total cells may not. These are those shown above in the XAML script.

It does not hurt to have a mode of 'TwoWay' if something else (another setting) precludes it - BUT - we must NOT have 'OneWay' if

we wish the underlying object property to be changed and consequently saved within the bound object.

After a short while this all gets to feel quite obvious and familiar.

*** Note *** One of the considerations we need to make when designing and creating our own DP objects is that we often need

code changes (like calculation of VAT value) to be reflected in the bound control - we MUST thoroughly test our forms to see that this

is done correctly. A lot of the time we simply have data entry values being stored in the object property, as in this case the value of

each of the amounts - but after each amount changes, the Cost, VAT and Total also change, this is a code calculation and it must

also cause the visible values to change.

Source and Target :

In the simplest of 'links' or 'chains', we have one source and one target.

T --- S (fixed value)

The source is the object with a defined or fixed value. The target is the object with the variable value.

However, in a complex 'chain', or set of 'links' we have the following :-

T --- S/T --- S/T --- S (fixed value)

Obviously, the objects between the fixed value source and the final target in the chain, are seen as both.

This is all very well for the 'OneWay' mode, in that case there is always only one direction in which the causality

'flows' - in our case above it is right to left.

However, if we have a 'TwoWay' binding mode, we need information (data) to flow in both directions, depending on

which end of the chain is changed or modified.

The best way for a new guy to get their head into this is to start out trying simple examples, and then work towards

getting more ambitious. I know, I have done it - just keep going, keep smiling, and eventually you get there.

It is certainly worth it.

Where do we use Data Binding within Small Trader ? :

The basic and simple answer to this question is 'Everywhere' !

All the forms, including the main one shown at launch time - below are some thumbnail type images to help us in

our following discussion :-

Okay the, enough of this colourful fun and imagery, we need to get into discussing binding ;-)

If we look at the main screen in the designer within VS, we see the following :-

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

Notice in the image above we have no information detail on the form itself, these 'empty' TextBlock controls are

bound to properties of a Configuration object.

Time to move on to the next section where we will see the dotNet code to carry out this binding on the main screen.

See you there .............

PHH - August 30th. 2011


Ten Points about 'Binding'

Ten points about WPF and Binding :

[ ... 'binding' is a general term to include 'Binding' and 'Data Binding' ... ]

- the GUI (form) and its controls is 'All' about binding - completely

- we should see Binding and 'Data Binding' (DB) as going 'hand in hand' - together

- Binding goes across GUI elements, DB is from 'Business Objects' to GUI controls

- if you are not using binding for everything in your WPF form design, you are doing it wrongly !

- XAML makes binding much easier as the syntax is 'short and sweet'.

- Design your custom 'Business Objects' correctly and your DB will work every time.

- Get to know and use the correct 'Collection' and your binding will work even better.

- it is a mistake to focus just on the item objects in a Collection when problems may be the Collection itself.

- if you wish to add and remove, and also edit items, use an editable collection of some sort.

- binding is about 'Dependency Properties' (DP) so create these in your custom objects.

- to be sure you always get correct DB behaviour in complex GUI situations, use an 'Event' in your custom class.

[ ... the event is an 'PropertyChanged' event, with an 'OnPropertyChanged' method ... ]

PHH - 28th. October 2011.

Addendum : [ or point eleven ? ]

Make sure any Child form is owned by the parent window. That means setting the 'Owner' property on the Child form to

be the parent or 'SELF'. [ .. see the last subsection of this Section 'B' .. ]


Binding with Code

Putting the Myths to Rest ! :

Whatever you may have heard, WPF and its supporting technologies are not XAML.

XAML is script for defining 'markup' for GUI and stuff supporting it. It defines and declares stuff, prior to runtime.

You can in fact do some powerful stuff without using any XAML, if that is your wish, and almost all (99.9%) of the

classes and properties of WPF exist in dotNet 4.0 namespaces, and can be placed in the PRG files as Vulcan code.

Having said that, XAML does make things easy for us once we get to grips with the syntax of the script. I now use

it for marking up my WPF forms, BUT, only when it makes my life easy, or I wish to explore things and learn ;-)

I will now show you the dotNet code way of setting up bindings - this Main screen 'form' was done way back by me,

before I had discovered the pleasures of XAML scripting.

Study the following images to see that we need a 'binding' object to be able to then specify what will be the Source

and what the Target :-

[ .. it is probably best to focus on just one control, say that for 'Add2' which is oBind2 and 'textBlock2' .. ]

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

Okay then, in the code shown in the above image, I have not renamed the GUI controls as I would these days.

It would make life easier if 'textBlock2' was 'tblkAdd2' in my opinion - but it still works well.

After we instantiate a binding and give it a suitable name, we have not specified a Source or Target.

So, 'self:oBind2 := Binding{"Add1"}'

simply means we have an empty binding with a tag name of 'Add1', this is the name of the source property

to be used, once we have provided a source object.

It is when we do the following that we provide 'source' details :-

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

Okay we have associated a configuration object with the 'source' of the binding, and set it to 'TwoWay', probably a bit

over the top for what you see, but it both works. I will leave it up to you to try 'OneWay', I may have been trying a little

too hard in these early days ;-)

So good so far, but now we need to define a Target and there is more than one way to do that in code. BUT, as I said

previously, this all gets easier when we more on to using XAML for our binding, it has much simpler syntax and is all in

one place as well.

For now see the dotNet code way - study the image below :-

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

The code lines have been re-ordered to make the reading of the above code easier.

Basically, the few lines of code mean that the binding (oBind2) which has a source already associated

with it, is to be bound to the target of the 'textBlock2' object and using the 'Text' property of this GUI object.

In order to bind the property on the GUI object has to be a DP or dependency property, and 'deep down' the system

knows each DP by its name with 'Property' added to the right hand end. We will see more of this when we come to

create our own custom objects, when we will need to define our own DPs within the custom class.

I have to say it is a little strange at first (I would say confusing because it isn't), but the strangeness will go with more

familiarity, and you won't even care once you discover the XAML way !!

Now, since I do have some XAML script for this WPF form / Window, we need to check that all and any XAML script

for binding is missing - not there.

The image below shows the whole of the XAML file for the Main screen form.

Look at lines 65 & 66 to to see that the 'textBlock2' control has a fixed value set at '... empty ...' this will remain as

such until our dotNet code binds the property from our configuration object - that called 'Add1' - to the Text property of

the TextBlock object.

Study the following details carefully :-

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

Basically, most of the XAML script was placed in the file by the interactive designer in Visual Studio 2010.

Before we leave this subsection we need to see how the 'Config' object is instantiated, as well as us studying

the 'Config' custom class itself.

Although at the moment the Config properties as fixed ones, it is my intention before VODC to link these up with

the Config object interactively changed by the user in the Configuration screen.

Check this out :-

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

And now we need to see the class itself.

Config Class of Custom Object :

Before we get into any real detail, let me say that this is a basic class, and because of how it is being used above

( and at the moment ) we don't need to define any Dependency Properties - as we are not doing editing via a 'TwoWay'

mode.

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

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

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

Some of the above details are not visible on the Main screen but get developed and used when we move to

a fuller version of 'Config' called 'Config2' when we make a DP version of this class type to go with the recently

included screen called Configuration - see below :-

Please realise that if we wish our object to take part in 'TwoWay' binding, so that we can interactively change

the values of properties, then the custom object MUST have those properties defined as Dependency Properties.

We will see this later when we become more familiar with DP and data binding generally.

Now lets move on to see how to do this in XAML .... see you in the next subsection.

PHH - August 30th. 2011


'Config' code listing

class Config

//----- as Custom class for Vulcan2, without DPs required -----

//----- see Config2, for DP use with WPF and XAML -----

private cTraderName as string

private cAdd1 as string

private cAdd2 as string

private cAdd3 as string

private cAdd4 as string

private cAdd5 as string

private cPostCode as string

private cPhone as string

private cMobile as string

private cEmail as string

private cVATregNum as string

private nVATrate as Double

private cConnection as string

private cDBfolder as string

private cDBfilename as string

private cDataFolder as string

CONSTRUCTOR()

SUPER()

self:Initialize()

return

method Initialize() as void

return

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

access TraderName() as string

return self:cTraderName

assign TraderName(cT as string) as void

self:cTraderName := cT:Trim()

return

access Add1() as string

return self:cAdd1

assign Add1(cA as string) as void

self:cAdd1 := cA:Trim()

return

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

access Add2() as string

return self:cAdd2

assign Add2(cA as string) as void

self:cAdd2 := cA:Trim()

return

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

access Add3() as string

return self:cAdd3

assign Add3(cA as string) as void

self:cAdd3 := cA:Trim()

return

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

access Add4() as string

return self:cAdd4

assign Add4(cA as string) as void

self:cAdd4 := cA:Trim()

return

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

access Add5() as string

return self:cAdd5

assign Add5(cA as string) as void

self:cAdd5 := cA:Trim()

return

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

access PostCode() as string

return self:cPostCode

assign PostCode(cP as string) as void

self:cPostCode := cP:Trim()

return

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

access Phone() as string

return self:cPhone

assign Phone(cP as string) as void

self:cPhone := cP:Trim()

return

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

access Mobile() as string

return self:cMobile

assign Mobile(cM as string) as void

self:cMobile := cM:Trim()

return

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

access Email() as string

return self:cEmail

assign Email(cE as string) as void

self:cEmail := cE:Trim()

return

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

access VATregNum() as string

return self:cVATregNum

assign VATregNum(cV as string) as void

self:cVATregNum := cV:Trim()

return

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

access VATrate() as Double

return self:nVATrate

assign VATrate(nR as Double) as void

self:nVATrate := nR

return

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

access Connection() as string

return self:cConnection

assign Connection(cC as string) as void

self:cConnection := cC:Trim()

return

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

access DBfolder() as string

return self:cDBfolder

assign DBfolder(cF as string) as void

self:cDBfolder := cF:Trim()

return

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

access DBfilename() as string

return self:cDBfilename

assign DBfilename(cF as string) as void

self:cDBfilename := cF:Trim()

return

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

access DataFolder() as string

return self:cDataFolder

assign DataFolder(cF as string) as void

self:cDataFolder := cF:Trim()

return

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

end class

PHH - 30th. August 2011.


Binding with XAML script

In this section we are going to compare the XAML script way of setting up our GUI data binding with

that which we have just see for the dotNet code way of doing 'all things binding'.

However, since we wish the Main screen to stay as it is, we need to use the 'Invoice' WPF form to see

how things are done with a much more modern, and 'up-to-date' approach.

First of all let us see the invoice form once again :-

There are many controls on this GUI screen which all have XAML script existing from the designer work I carried out.

As well as the script lines from the interactive use of drag and drop items from the Toolbox, I have hand scripted lines

similar to those in the small image below :-

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

Yes, it is as easy as that shown in the blue highlighted section - to set up a binding. All that remains to

be done is provide a 'Source' object.

This I still prefer to do in code, as it seems easy for me to do so. Study the image below where line 199

is the particular one to accompany our XAML script.

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

So from the above images, line 199 as well as the blue section, is all that is required to do what we did in a more

longwinded way in code.

Notice that to 'read' the blue section, it simply means "set up a binding with an object yet to be specified, and make the

binding of mode 'TwoWay' and use the property called 'InvRefNo' ".

The 'Down-Side' ? :

Okay I hear you shout, "now that we have made things easier in XAML, we are going to hear how they get more complex

again!", and unfortunately that is true ;-))

Well, it is only true if we want 'TwoWay' binding - AND - for interactive editing in dotNet code this will also be true, and a

requirement of our custom class. So its not all bad !

Instead of showing you the details of the Invoice custom class here, I will place it in the 'Designing Custom Objects' section

later in this section.

Look for it under C# .NET .

See you there, or in the next subsection ................

PHH - August 30th. 2011


Modes of binding - which to use

Let us first remind ourselves which 'modes' are available to us as developer / coders.

Checkout the intellisense list below :-

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

The dotNet intellisense shows the same as the XAML does - see below :-

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

[ .. we will look at the most used of these - for guys coming fresh to Data Binding ..]

It all Depends !

When you select a mode for your binding, you need to take into account two important things.

One is the type of Business Object (BO) to which you are going to bind your GUI controls (and its properties),

and the other is the particular functionality you expect from your GUI screen itself.

I suppose that is what 'mode' stands for, or means - ' how do you want it? '

Getting One thing Straight :

If you have a 'simple' BO (with 'simple' properties) then you CAN'T get it to take part in full both way binding

no matter what you do.

On the other hand if you have designed your BO to be a Dependency Object, such that each required

property is a DP or Dependency Property, then it will be able to function with any of the possible modes

of binding whether complex of simple.

I have to be honest, I used to try with simpler BOs just because I did not know how to make a full DP

custom object. After all, ignorance is bliss !!

Now that I do know, and that I can code it in Vulcan as well, then I tend to start out by making a suitable

BO with my new found skills and knowledge ;-) Some would describe this as 'Belt and Braces', just in case !

*** Remember *** When we engage in Data Binding it is the property of the BO which we bind to the property

of the GUI control. So for a full range of modes of binding to be available on a particular property, it must belong

to a custom class which is of type Dependency Object, AND, have that particular property declared correctly and

as a dependency property. Not all properties in a dependency object class need to be DPs, there can be standard

ones as well.

You can code and script for a 'simple' BO to have a binding mode of 'TwoWay' but if it is not designed to cope

with both way binding then the functionality in the WPF form will just NOT be there - it won't do it.

You will not get any sort of error - just get missing form capabilities.

I like to start with my BO and ask myself, ' which modes can this object support? '.

If in doubt, make your BO a full DP supporting class.

You problems may start if you have to use and existing class designed by others.

Binding Scenarios :

Let us look at Data Binding from another angle - turn it around if you like.

Let us think of a few scenarios of the sort of situations we expect to come across and wish to use.

Simple Data Display :

Example is displaying an existing Invoice - no data is to be changed.

In this scenario it would be sufficient to have a binding which took an initial look, or 'snap shot' of the

data in the BO being bound - since it is just a one-off display situation. The invoice is not going to

be changed anywhere else while being viewed, so the situation is a simple one.

So, we could do the above with a BO which is just the standard type of custom object.

[ *** warning *** in the text above it is the scenario I am discussing, I realise that in our Small Trader case ]

[ .. the 'InvoiceCS' class I use is fully setup do display data and property changes ..]

Simple Data Input :

Example is entering a new Customer - all relevant fields are to be supplied except the reference.

Suppose we wish to bind a BO to a WPF form such that what we enter into a text box for each bound property,

ends up in the property (and class field) of the BO - so that when we close the form, all previously entered data

may be saved to SQL table by processing the BO in memory at the time the form was closed.

This above scenario is probably the simplest of 'Input' situations we might come across. We are not expecting

changes in the data of the properties being edited to appear in real time in another form bound to the same

object, and the same properties of that object.

The object we use in the Small Trader application is called 'Customer' and is as straightforward as they come.

Look at the structure of it below - note some collapse of lines has been done of properties which are similar to those

still being displayed - jut to fit this page of the eNotes :-

*** Note *** We will look in more detail at coding custom BO classes a little later in these eNotes.

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

In the code of the above image we have not done anything out of the normal, for a standard custom object.

We have not declared any DPs or the class as a Dependency object.

Neither have we defined any events for such things as 'INotifyPropertyChanged' - which I have in the 'InvoiceCS' class

in C#.

More Complex Data Input :

Example is my invoicing software I run at the end of each month - see the following image :-

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

In the above image I am editing John Parker's company name in the 'Customer Details' form (light greeny colour), and

as I type the changes immediately show in both the 'Company' field (TextBlock) in the group box entitled 'Company / Contact'

and also the text showing on the combo box control above it.

This is probably the most complex of my binding scenarios to date, and I work on a 'needs driven' basis.

To make things more complicated, changing the Customer screen from 'ShowDialog' to 'Show' seems to remove

this multi form capability.

I did a test to open three forms at once, to see if I could force a more complex situation, but I have seemed to fail.

Study the image below :-

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

In the above image notice that as I type in real time there are no changes occurring in any of the other controls.

This is even the case when I have just the single form open in 'Show' as opposed to 'ShowDialog', so be warned !

*** Note *** We will look in more detail at coding custom BO classes a little later in these eNotes.

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

It would be interesting to see the results of such a test as above, if we had a custom object written in Vulcan.net

and using DP defined properties. I may try to set that up, but for now we have enough to think about ;-0))

Also, one very good reason to wait a short while is because in the next release version of Vulcan 2 we are told

we will be able to code our own events as in the image above of the C# class.

This will make life much easier as trial and testing can be done on one Vulcan class to which we add various

lines of code to do as we wish, DPs, standard properties, and events too.

So I suggest waiting a few weeks. [ I think this is now down to a couple days at the moment ! ]

PHH - September 6th. 2011


When do data changes show up?

Simple answer - "Not at all" - if we do not get our custom business objects correct, and define our

binding properties correctly as well !!

Rule One (for binding) - " If it does not work, blame yourself ! "

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

It is all very well making the correct Business Object (BO) with suitable properties of type DP, but

if you do not define correctly when you wish to see any data changes for your binding(s) then you

may be disappointed in the results ! Sometimes changes do not seem to happen elsewhere when

they should.

As well as 'modes' for our binding, we need to also consider (and then set) the property of the binding

called 'UpdateSourceTrigger' (UST).

If in doubt at all, set the mode to 'TwoWay' and the UST property to 'PropertyChanged'.

Study some working code in the following image :-

Trigger for Updating the Source :

[ .. a custom class needs to have supporting code for notifying the change of any property - see next subsections .. ]

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

And another bit of working code :-

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

If you have a complex form with MANY bound GUI elements, and / or you are concerned about performance, then

set the UST to 'LostFocus' and the changes will then only show when you move off the GUI control in focus, and go

to another.

I have to be honest with you and say that now I have some working knowledge and 'feel' for XAML script, it is usually

much easier to put any binding 'stuff' into the XAML editor for the form being designed.

However, if you need to have binding change at runtime for any reason, you will need to get familiar with the dotNet way

of setting up binding.

Time to move on, see you in the next subsection ..................

PHH - September 6th. 2011


Designing Custom Objects

What I am about to say is all my own 'thinking'. It comes from reading the SDK and spending many

hours trying out the various ways to do data Binding.

We have to be aware of 'history' and the passage of time. It would appear to me that when the Microsoft

guys were designing and creating WPF, and the fundamentals of the Dependency Property WPF system,

the performance of PCs generally was way, way, behind what it is to date. It was years back, has to be,

as I have been fiddling around with WPF for over three years myself.

This means that any coding approach, or style, that is carried out because of 'performance related' issues

to me probably can be totally discounted in today's world.

If we use the idea that making properties dependency ones, has an impact on performance, then lets accept

the idea and make sure we have the latest hardware and latest operating systems. And, then design our

coded BOs with the best of the WPF technologies going.

But, is this much of an impact, I doubt it. All things have an impact, and moving to using the Graphics

processor with the introduction of Vista, and the Windows 7, was a huge step forward with performance

generally - but did that make the 'XP heads' see the light ? No, then why should I worry about an impact

on performance from DPs in my custom objects when I have not seen any issues myself.

AND, on top of that, this PC I use heavily for development is OLD itself, well over five years since I built it.

Only if we seem to hit a BIG performance issue should we consider 'knocking back' our code capability.

So onwards and upwards my friends !!

Consequences :

To me the consequences of this thinking is for me to find out the most reliable (and comprehensive) way to code

a custom class required for WPF form binding, and then to use this style of coding for any and all the custom

classes I require for binding with my WPF GUI controls.

Note that this approach only need apply to those objects which are bound to the GUI. Any other custom classes

used for general Business Logic can be designed and created in the normal way - standard properties, and no

special events either.

PHH - August 30th. 2011


Vulcan 2 .NET

If we wish to have a custom designed class of our own making, and for use with our newly made apps,

and also to be capable of 'TwoWay' data binding, then we need to learn how to define our own DPs.

Yes, 'DP' is dependency property.

To a high level coder like myself - meaning I don't dig deeper into the 'system' than I have to - it would

appear as if a DP as we define it means that in place of a property storing its data value in a field attached to

or within the class, it is passed into the WPF system for handling DPs and data binding.

The general structure of the code shown below would strongly suggest what I say is correct - that along

with what I remember of trying to read and understand the SDK documentation :((

To start our exploration of a Vulcan DP custom class let us use the newer Configuration class called 'Confg2'.

Let us first take a look at the partially collapsed beginning section - sorry it is very wide, and best seen that way.

Study the detail below carefully :-

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

In the code shown in the image above, if I were to shorten the lines then it would not visually scan as well as it does

when wide.

So to me it would appear that 'Add1Property' is a bit like a static field, a place to hold a publicly available value - only

it is kept outside of the class in the system 'area' for managing DPs. Well, that is how I see it.

So looking more closely at the line for 'Add1' we see that we have declared it as a string type called 'Add1' and that it

belongs to the class 'Config2' in the namespace of 'SmallTrader'. If we look at another but different entry we may get a

clue as to what the Metadata parameter does.

Checkout below :-

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

See that instead of an empty string ( " " ) we have a value of -1 to give us an idea of what the Metadata is.

From this and some other classes I have made and used, it appears to be the default value of the DP itself.

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

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

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

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

Instead of inserting the value into the suitable class field, line 235 places it into the DP defined at the head of

the class definition, in this case it is the 'Add1Property' dependency property.

Likewise line 233 retrieves a value from the DP.

So it would appear that 'GetValue' and 'SetValue' replace the basic field assignment. All you need do is copy

this syntax a time or two and you soon get the hang of things.

PHH - August 30th. 2011


Microsoft C# .NET

I am including this subsection even though it is not Vulcan based, simply because up till now, and for at least a

couple of months or so (before VODC 20011 ?) we will need to use C~ so that we can define our own Event.

The event relevant to us with two way binding on our plate, is that defined for 'INotifyPropertyChanged'.

Invoice Custom Class :

In order to use the custom class in the Small Trader application we need to add it to the References section in

the SE pane - see below :-

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

And in order to include it like this it has to have been created and saved as a 'ClassLibrary'.

See below for more details :-

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

Obviously, I made the 'ClassLibrary' as part of an earlier project - much earlier as it was a WinForms one.

A Full Listing of the Class - called 'InvoiceCS':

A full and detailed listing of the C# class is included in the child section called 'Full code listing'.

However, we will extract small sections of it here and discuss some parts in more detail.

Overall, it is NOT a complex business to use a C# class library, only the event and its interface is

really any different to what we already know etc..

Below is the full list of fields with their properties - sorry it is so long !

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

Okay, this shown above is pretty straightforward, simple almost.

Yes, but we need to see how each of the simple properties (when accessed or used) triggers the event

which we below define as 'PropertyChanged'.

So after each updating of any property, our class code uses the 'OnPropertyChanged' method of our class

to trigger the related and necessary event. [ See the bottom of the following image. ]

For an example of this call of the class method, see lines 75 & 85 below :-

[ .. also see that we have two overloads on the Constructor, one an 'empty' form .. ]

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

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

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

In the code shown in the above image - just towards the bottom of the image - we see that

Notice that the name of the property is passed, and so the WPF DP system will be able to use this to make

our 'TwoWay' binding work for us.

Let us now move on to the next subsection, see you there ......................

PHH - August 30th. 2011


Full code listing

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

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

using System.ComponentModel;

namespace ClassLibraryInvoice

{

  public class InvoiceCS : INotifyPropertyChanged

   {

      private int invrefno;

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

      private int custref;

      private int jobref;

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

      private DateTime datemade;

      private DateTime datemodify;

      private DateTime invdate;

      private Boolean printed;

      private string customer;

      private string company;

      private string person;

      private string add1;

      private string add2;

      private string add3;

      private string postcode;

      private string phone;

      private string email;

      private string date1;

      private string date2;

      private string date3;

      private string date4;

      private string parts1;

      private string parts2;

      private string parts3;

      private string parts4;

      private string parts5;

      private string parts6;

      private string parts7;

      private string parts8;

      private string parts9;

      private Double amount1;

      private Double amount2;

      private Double amount3;

      private Double amount4;

      private Double amount5;

      private Double amount6;

      private Double amount7;

      private Double amount8;

      private Double amount9;

      private Double cost;

      private Double vat;

      private Double total;

      // Declare the event

      public event PropertyChangedEventHandler PropertyChanged;

      public InvoiceCS()

       {

       }

      public InvoiceCS(int nRef)

       {

          this.invrefno = nRef;

       }

      public int InvRefNo

       {

          get { return invrefno; }

          set

           {

               invrefno = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("InvRefNo");

           }

       }

      public int CustRef

       {

          get { return custref; }

          set

           {

               custref = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("CustRef");

           }

       }

      public int JobRef

       {

          get { return jobref; }

          set

           {

               jobref = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("JobRef");

           }

       }

      public DateTime DateMade

       {

          get { return datemade; }

          set

           {

               datemade = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("DateMade");

           }

       }

      public DateTime DateModify

       {

          get { return datemodify; }

          set

           {

               datemodify = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("DateModify");

           }

       }

      public DateTime InvDate

       {

          get { return invdate; }

          set

           {

               invdate = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("InvDate");

           }

       }

      public Boolean Printed

       {

          get { return printed; }

          set

           {

               printed = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Printed");

           }

       }

      public string Contact

       {

          get { return person; }

          set

           {

               person = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Contact");

           }

       }

      public string Company

       {

          get { return company; }

          set

           {

               company = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Company");

           }

       }

      public string Customer

       {

          get { return customer; }

          set

           {

               customer = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Customer");

           }

       }

      public string Add1

       {

          get { return add1; }

          set

           {

               add1 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Add1");

           }

       }

      public string Add2

       {

          get { return add2; }

          set

           {

               add2 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Add2");

           }

       }

      public string Add3

       {

          get { return add3; }

          set

           {

               add3 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Add3");

           }

       }

      public string PostCode

       {

          get { return postcode; }

          set

           {

               postcode = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("PostCode");

           }

       }

      public string Phone

       {

          get { return phone; }

          set

           {

               phone = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Phone");

           }

       }

      public string Email

       {

          get { return email; }

          set

           {

               email = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Email");

           }

       }

      public string Date1

       {

          get { return date1; }

          set

           {

               date1 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Date1");

           }

       }

      public string Date2

       {

          get { return date2; }

          set

           {

               date2 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Date2");

           }

       }

      public string Date3

       {

          get { return date3; }

          set

           {

               date3 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Date3");

           }

       }

      public string Date4

       {

          get { return date4; }

          set

           {

               date4 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Date4");

           }

       }

      public string Parts1

       {

          get { return parts1; }

          set

           {

               parts1 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts1");

           }

       }

      public string Parts2

       {

          get { return parts2; }

          set

           {

               parts2 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts2");

           }

       }

      public string Parts3

       {

          get { return parts3; }

          set

           {

               parts3 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts3");

           }

       }

      public string Parts4

       {

          get { return parts4; }

          set

           {

               parts4 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts4");

           }

       }

      public string Parts5

       {

          get { return parts5; }

          set

           {

               parts5 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts5");

           }

       }

      public string Parts6

       {

          get { return parts6; }

          set

           {

               parts6 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts6");

           }

       }

      public string Parts7

       {

          get { return parts7; }

          set

           {

               parts7 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts7");

           }

       }

      public string Parts8

       {

          get { return parts8; }

          set

           {

               parts8 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts8");

           }

       }

      public string Parts9

       {

          get { return parts9; }

          set

           {

               parts9 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Parts9");

           }

       }

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

      public Double Amount1

       {

          get { return amount1; }

          set

           {

               amount1 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount1");

           }

       }

      public Double Amount2

       {

          get { return amount2; }

          set

           {

               amount2 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount2");

           }

       }

      public Double Amount3

       {

          get { return amount3; }

          set

           {

               amount3 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount3");

           }

       }

      public Double Amount4

       {

          get { return amount4; }

          set

           {

               amount4 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount4");

           }

       }

      public Double Amount5

       {

          get { return amount5; }

          set

           {

               amount5 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount5");

           }

       }

      public Double Amount6

       {

          get { return amount6; }

          set

           {

               amount6 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount6");

           }

       }

      public Double Amount7

       {

          get { return amount7; }

          set

           {

               amount7 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount7");

           }

       }

      public Double Amount8

       {

          get { return amount8; }

          set

           {

               amount8 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount8");

           }

       }

      public Double Amount9

       {

          get { return amount9; }

          set

           {

               amount9 = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Amount9");

           }

       }

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

      public Double Cost

       {

          get { return cost; }

          set

           {

               cost = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Cost");

           }

       }

      public Double VAT

       {

          get { return vat; }

          set

           {

               vat = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("VAT");

           }

       }

      public Double Total

       {

          get { return total; }

          set

           {

               total = value;

              // Call OnPropertyChanged whenever the property is updated

               OnPropertyChanged("Total");

           }

       }

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

      // Create the OnPropertyChanged method to raise the event

      protected void OnPropertyChanged(string name)

       {

          PropertyChangedEventHandler handler = PropertyChanged;

          if (handler != null)

           {

               handler(this, new PropertyChangedEventArgs(name));

           }

       }

   }

}

PHH - 30th. August 2011.


When to use which !

If the news from Grafxsoft is as correct as usual, then we will be able to define our own events

in Vulcan code within a couple of months from now.

This may mean that we can replace the need for C# custom classes just before VODC in Stuttgart 2011.

"Update - NEWS !!! " :

Version 208 of Vulcan was released a few weeks back and supports the use of Events in user define classes.

I now use Vulcan 2 for all my own Business Objects and custom classes.

The way to code Dependency Properties and use the events for 'OnPropertyChanged' (OPC) is illustrated fully in the

child subsection below this section.

For discussion purposes I will show here what is necessary.

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

..................... { many more declarations of DPs } ......................

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

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

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

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

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

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

In the above images notice that it is lines like 110 which call on the event method with the name of the property

which is being changed.

If you code you custom classes without using the Event approach, then they will certainly NOT function correctly under certain

circumstances. However, you will get enough 'performance' under simpler situations to think that all is well - but it isn't !

However, if there are properties within your class which are for internal use only, and / or will never need to be bound in

the GUI, then declare then as 'standard' properties, and don't call the 'OPC' method.

It is my intension to always code my custom classes in this way - as standard. Then I will not get caught out later in

my coding and design process. After all, we know that we often expect more once we get into the process of developing

then at initial design time.

Once you get to use binding, and get results, you will try in use it in more and more places. So prepare your custom classes

in a suitable fashion.

Time to move on, see you in the next subsection ............

PHH - 3rd. November 2011


Full working code ...

//----- Seat.prg -----

#using System.Collections

#using System.Collections.Generic

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

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

BEGIN NAMESPACE VenueSeatBooking

[Serializable]CLASS Seat INHERIT DependencyObject IMPLEMENTS System.ComponentModel.INotifyPropertyChanged

STATIC PUBLIC PartyProperty := DependencyProperty.Register("Party", typeof(INT), typeof(VenueSeatBooking.Seat), PropertyMetadata{-1}) AS DependencyProperty

STATIC PUBLIC StatusProperty := DependencyProperty.Register("Status", typeof(SeatStatus), typeof(VenueSeatBooking.Seat), PropertyMetadata{SeatStatus.Free}) AS DependencyProperty

STATIC PUBLIC BookDateProperty := DependencyProperty.Register("BookDate", typeof(DateTime), typeof(VenueSeatBooking.Seat), PropertyMetadata{DateTime{1900,01,01}}) AS DependencyProperty

STATIC PUBLIC FreeDateProperty := DependencyProperty.Register("FreeDate", typeof(DateTime), typeof(VenueSeatBooking.Seat), PropertyMetadata{DateTime{1900,01,01}}) AS DependencyProperty

STATIC PUBLIC ReserveDateProperty := DependencyProperty.Register("ReserveDate", typeof(DateTime), typeof(VenueSeatBooking.Seat), PropertyMetadata{DateTime{1900,01,01}}) AS DependencyProperty

export cName as string

export cBlock AS STRING

export nRow AS INT

export nNumber AS INT

export nCost as double

EXPORT cUser AS STRING //----- person doing the booking -----

//----- Events -----

EVENT PropertyChanged AS System.ComponentModel.PropertyChangedEventHandler

CONSTRUCTOR()

Super()

self:cBlock := "X"

SELF:nRow := -1

SELF:nNumber := -1

SELF:nCost := 0.0

self:Initialize()

RETURN

CONSTRUCTOR(cB AS STRING, nR AS INT, nN AS INT, nC AS double)

Super()

self:cBlock := cB:Trim()

SELF:nRow := nR

SELF:nNumber := nN

SELF:nCost := nC

self:Initialize()

RETURN

METHOD Initialize() AS VOID

SELF:cName := SELF:nRow:ToString()+SELF:cBlock+ SELF:nNumber:ToString() //---- e.g. 5B11 -----

SELF:cUser := ""

RETURN

METHOD ToString() AS STRING

LOCAL cResult AS String

cResult := self:Name+" .. £"+self:Cost:ToString()

return cResult

//----- Methods -----

protected method OnPropertyChanged(name as string) as void

local pChanged as System.ComponentModel.PropertyChangedEventHandler

pChanged := self:PropertyChanged

if (pChanged != null)

pChanged:Invoke(self, System.ComponentModel.PropertyChangedEventArgs{name})

endif

RETURN

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

access Name() as string

return self:cName

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

access Block() as string

return self:cBlock

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

access Row() as int

return self:nRow

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

access Number() as int

return self:nNumber

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

access Cost() as double

return self:nCost

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

access BookDate() as DateTime

return (DateTime)self:GetValue(BookDateProperty)

assign BookDate(dtB as DateTime) as void

SELF:SetValue(BookDateProperty,dtB)

self:OnPropertyChanged("BookDate")

return

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

access Party as int

return (int)self:GetValue(PartyProperty)

assign Party(nP as int) as void

SELF:SetValue(PartyProperty, nP)

self:OnPropertyChanged("Party")

return

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

ACCESS Status() AS SeatStatus

return (SeatStatus)self:GetValue(StatusProperty)

assign Status(eS as SeatStatus) as void

SELF:SetValue(StatusProperty, eS)

self:OnPropertyChanged("Status")

return

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

access FreeDate() as DateTime

return (DateTime)self:GetValue(FreeDateProperty)

assign FreeDate(dtF as DateTime) as void

SELF:SetValue(FreeDateProperty, dtF)

self:OnPropertyChanged("FreeDate")

return

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

access ReserveDate() as DateTime

RETURN (DateTime)SELF:GetValue(ReserveDateProperty)

assign ReserveDate(dtR as DateTime) as void

SELF:SetValue(ReserveDateProperty, dtR)

self:OnPropertyChanged("ReserveDate")

return

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

END CLASS

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

PHH - 3rd. November 2011


Binding to Collections, Views

A common approach to binding with WPF forms and GUI controls upon them, is to use a 'collection' as

the data source and bind this to the GUI elements required.

If the GUI control is an 'ItemsControl' them this is quite straightforward, but if the control is a 'content control'

them we need to somehow point at a particular item within the collection itself. There are a few useful ways of

doing this and one is to use the selection of an 'items control', for example a ListBox, to take part in the selection

and then to point something like a TextBlock to the 'selected item' within the ListBox itself.

A similar thing cab be done with a DataGrid, and obviously a ComboBox. I have little or no experience of TreView

controls and so I will have to miss them out as far as this discussion is concerned.

Alternative approach :

There is another way to 'point' at a single item within the collection being bound, and that is to use the notion of a

'current item' - this is all part of more modern and complex 'views'.

By binding to the collection's current item we bypass using any other GUI element as part of our reference. So we

do not use 'ListBox' or 'ComboBox', or similar.

Useful Example :

Let us look at an example which is short but very useful for us to see what can be done - see below :-

In the above image we have four buttons within a larger push button. Each button takes part in navigation.

As we click the buttons the 'current' pointer within the collection 'view' is changed, and the content of the 'TextBlock'

elements, within the red border element, are changed accordingly.

If the current item is on show within the displayed item range then a blue highlight is also shown.

Since a 'Collection' is a group of items, we ought to look at the class of the items used in the example above.

Displayed below is the Person class - a simple example indeed :-

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

Now we can use this class to make different types of 'Collection' and the following image shows

three types I have successfully made and tested :-

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

In the above image we see three collections, each based upon a different type of collection, and ones which

also tend to be increasingly more complex, and possibly also more recent in their inclusion with dotNet itself.

Certainly, the latter (number 3) is to be found in the dotNet 4.0 SDK and not the earlier stuff - see below :-

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

But the 'ArrayList' and 'List<T>' are to be found in the 3.5 version of my dotNet SDK documentation - see below :-

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

It looks as if the ArrayList and List<T> have been around for a lot longer than the 'ObservableCollection', and in fact the

next image would suggest that List<T> was added new into dotNet 2.0 and that the ArrayList class was around in

dotNet 'one' of either 1.0 or 1.1 variety - that is before my time so we will stop the history there !!

Checkout the red type below [List Generic is the same as List<T> ] :-

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

Whereas the 'ArrayList' seems to have been there from the start, as it is NOT new to dotNet 2.0 - see below :-

[ .. as Peter Pan would say, its been there 'for ever and ever' ..]

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

So we have three types of collection created in our Vulcan code above, ranging from very early in dotNet to the

latest version of it - version 4.0.

You may think this would cause a problem when we come to use the collections - BUT - Microsoft have provided

us with a nice (and modern) wrap around, in the shape of the 'ICollectionView' class.

In the following image we provide the data source (in this case ObservableCollection) from which to instantiate a 'view'.

This same process works well for all three types of the data source we have discussed above.

Checkout the most up-to-date way of doing things in the image shown below :-

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

So the secret in the success of the on-screen performance of the binding, pointer movement, and selection etc.,

is the use of the 'view' and how it is bound to the GUI elements.

Before we see alternative ways to provide the data source, and 'bind' as such, let us see what it is that the 'view'

provides for us - what functionality are we after ?

Notice in the following image that 'ICollectionView' is modern, and part of WPF as it is in the dotNet 4.0 SDK docs. :-

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

By using a 'view' based on the ICollectionView class, we gain access to navigation features via the 'Move' methods shown below :-

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

So if we use in our code, the 'view' as a data source for both the DataGrid and the TextBlock elements, we can provide

full navigation control. This way we can bind our TextBlocks directly to the current item of the 'view' instead of having to

go through the selected item of the grid control.

In the following image you will see that extra to the navigation, we get the ability to sort, group and filter - checkout

the properties below related to these functions :-

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

What does the XAML look like ? :

First of all let us see how the five TextBlocks are bound - the image below just reminds you which these are - notice the

grey rectangles within the red rectangular Border :-

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

Each of these 'TextBlock' elements is bound as shown below :-

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

If we take one of these - the first one - and isolate the script as seen below :-

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

We notice that we have said that the Text property is to be bound to an object, and the 'Path' is to be the 'Firstname'

property of the current item. This 'slash' ( ' / ' ) syntax is special for the current item of a collection.

So which object is bound ? Well, we have done that in our Vulcan code, see below :-

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

Line 60 in the above image binds the 'view' to the first TextBlock via the 'DataContext' property.

The commented line was a test to see what would happen if the data source was bound directly, and we actually

lost the ability to display the current item. This is nicely shown below when I uncomment and do a test.

Checkout this following image :-

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

Yep! all the other TextBlocks are displaying current item data, except the first one! We do not have a

Sally Logan in our collections. We get Sally because that is the first item, and therefore this first TextBlock

is not displaying current pointer data.

Binding the Collection to the DataGrid :

From tests so far it does not seem to matter whether we use the 'ItemsSource' or 'DataContext' property for the

data grid element - both seem to work in the simple way I have tested above.

However, it would feel 'more modern' to do the following :-

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

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

The commented lines were there for testing purposes, and left as such so I can see what was tried by me earlier.

It may well be that we see fuller functionality with later grid facilities, possibly when we need to add and edit rows and cells.

Here is what I have added, and then used the auto sort of the 'Surname' column - check this out :-

Each of the cells was entered interactively for Jim Anderson, including the date of birth.

Summary so far :

Make the most modern type of collection you are able to.

Always wrap a modern 'view' around the collection.

Always bind the 'view' to the 'DataContext' property of the GUI control.

For 'ContentControl' elements use the special syntax for current item display.

Don't forget to set IsSynchronizedWithCurrentItem="True" on the GUI 'ItemsControls' being bound.

You can bind a collection to any number of GUI controls, multiple 'ItemsControls' and multiple 'ContentControls'.

When doing this think of the 'history' and how Microsoft have effectively updated older data classes by providing

more functional and up-to-date 'wrappers' for us to put around our data sources. Views are examples of such

wrappers.

In essence, MS have prolonged the life of older classes by using the interface 'system' to provide wrappers which

then give us suitable interface facilities to classes without such facilities, when left on their own.

However :

There is at least one catch to all this good news, and that is when we need all of the possible functionality for binding,

in the most complex and demanding of circumstances, we do need to construct our custom classes accordingly.

We have to move on from simple classes and add DP functionality to our properties, as well as providing

'INotifyPropertyChanged' events within them as well.

In the old adage (saying), "you get out what you put in" !!

PHH - 20th. September 2011.


working code and script

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

<Window x:Class="DieterCrispienWPFstuff.TableGrid"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Title="Dieter : Table 'n' Grid :" Height="440" Width="500">

  <Grid>

      <DataGrid AutoGenerateColumns="False" Name="dataGrid1" Background="Beige" Margin="0,0,0,82"

                IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}"/>

      <Button Margin="172,0,178,9"

              Name="pbAllInOne" Height="27"

              VerticalAlignment="Bottom">

          <StackPanel Orientation="Horizontal">

              <Button Content="|&lt;" Width="30" Click="pbFirst_Click" />

              <Button Content="Prv" Width="30" Click="pbPrevious_Click" />

              <Button Content="Nxt" Width="30" Click="pbNext_Click" />

               <!--<Button Content="&gt;|" Width="30" Click="pbLast_Click" />-->

              <Button Width="30" Click="pbLast_Click" >

                  <Button.Content>

                       &gt;|

                  </Button.Content>

              </Button>

          </StackPanel>

      </Button>

      <Border Margin="0,0,101,39" Height="37" VerticalAlignment="Bottom" BorderBrush="Red" BorderThickness="2" >

          <StackPanel Orientation="Horizontal" Height="28"

                      VerticalAlignment="Bottom" >

              <TextBlock Height="23" HorizontalAlignment="Left" Name="textBlock1" Background="Bisque"

                        VerticalAlignment="Top" Width="86" Text="{Binding Path=/Firstname}"/>

              <TextBlock Height="23" HorizontalAlignment="Right" Name="textBlock2" Background="Bisque"

                        VerticalAlignment="Top" Width="74" Text="{Binding Path=/Surname}"/>

              <TextBlock Height="23" HorizontalAlignment="Left" Name="textBlock3" Background="Bisque"

                        VerticalAlignment="Top" Width="37" Text="{Binding Path=/Age}"/>

              <TextBlock Height="23" HorizontalAlignment="Right" Name="textBlock4" Background="Bisque"

                        VerticalAlignment="Top" Width="51" Text="{Binding Path=/Owes}"/>

              <TextBlock Height="23" Name="textBlock5" HorizontalAlignment="Right" Width="124"

                        Background="Bisque" VerticalAlignment="Top" Text="{Binding Path=/DoB}"/>

          </StackPanel>

      </Border>

  </Grid>

</Window>

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

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

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

#using System.Collections

#using System.ComponentModel

#using System.Data

BEGIN NAMESPACE DieterCrispienWPFstuff

/// <summary>

/// Interaction logic for TableGrid.xaml

/// </summary>

PARTIAL CLASS TableGrid INHERIT Window

  private oDT as DataTable

  private alTablePeople as ArrayList

CONSTRUCTOR()

  SUPER()

   InitializeComponent()

  self:Initialize()

RETURN

method DoMoveNext() as void

  self:oView1:MoveCurrentToNext()

  if self:oView1:IsCurrentAfterLast

      self:oView1:MoveCurrentToFirst()

  endif

return

method DoMovePrevious() as void

  self:oView1:MoveCurrentToPrevious()

  if self:oView1:IsCurrentBeforeFirst

      self:oView1:MoveCurrentToLast()

  endif

return

method DoMoveFirst() as void

  self:oView1:MoveCurrentToFirst()

return

method DoMoveLast() as void

  self:oView1:MoveCurrentToLast()

return

PRIVATE METHOD pbNext_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

  self:DoMoveNext()

RETURN

PRIVATE METHOD pbPrevious_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

  self:DoMovePrevious()

RETURN

PRIVATE METHOD pbFirst_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

  self:DoMoveFirst()

RETURN

PRIVATE METHOD pbLast_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

  self:DoMoveLast()

RETURN

END CLASS

END NAMESPACE

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

// PartTableGrid.prg

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

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

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

BEGIN NAMESPACE DieterCrispienWPFstuff

/// <summary>

/// Interaction logic for TableGrid.xaml

/// </summary>

PARTIAL CLASS TableGrid INHERIT Window

  PRIVATE oView1 AS ICollectionView

  private oMyOC as ObservableCollection<Person>

method Initialize() as void

#region {..  commented alternative coding ..}

  /* self:alTablePeople := PersonsCollections.GetPersonsAL()

   local lPersons as List<Person>

   lPersons := PersonsCollections.GetPersonsLT()

   self:oMyOC := ObservableCollection<Person>{lPersons}

*/

  //self:oMyOC := ObservableCollection<Person>{}

  //oView1 := CollectionViewSource.GetDefaultView(self:alTablePeople)

  //self:dataGrid1:ItemsSource := self:alTablePeople

#endregion

  self:oMyOC := PersonsCollections.GetPersonsOC()

   oView1 := CollectionViewSource.GetDefaultView(self:oMyOC)

  //oView1 := CollectionViewSource.GetDefaultView(PersonsCollections.GetPersonsAL())

  //self:dataGrid1:ItemsSource := oView1

  //self:dataGrid1:ItemsSource := self:oMyOC

  self:dataGrid1:DataContext := oView1

  //self:dataGrid1:ItemsSource := PersonsCollections.GetPersonsAL()

  self:dataGrid1:AutoGenerateColumns := true

self:dataGrid1:AlternationCount := 2

self:dataGrid1:AlternatingRowBackground := Brushes.LightGreen

self:dataGrid1:SelectedItem := 0

  self:textBlock1:DataContext := oView1

  //self:textBlock1:DataContext := PersonsCollections.GetPersonsAL()

  self:textBlock2:DataContext := oView1

  self:textBlock3:DataContext := oView1

  self:textBlock4:DataContext := oView1

  self:textBlock5:DataContext := oView1

return

END CLASS

END NAMESPACE // DieterCrispienWPFstuff

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

<Application x:Class="DieterCrispienWPFstuff.App"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  StartupUri="TableGrid.xaml">

  <Application.Resources>

  </Application.Resources>

</Application>

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

#using System

#using System.Collections.Generic

#using System.Configuration

#using System.Data

#using System.Linq

#using System.Windows

BEGIN NAMESPACE DieterCrispienWPFstuff

  /// <summary>

  /// Interaction logic for App.xaml

  /// </summary>

  PARTIAL CLASS App INHERIT Application

  END CLASS

END NAMESPACE

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

//----- Person.prg -----

//----- 13th. September 2011. -----

//----- for Dieter Crispien -----

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

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

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

//#using System.Data

BEGIN NAMESPACE DieterCrispienWPFstuff

class PersonsCollections

static method GetPersonsAL() as ArrayList

  local alResults as ArrayList

   alResults := ArrayList{}

   alResults:Add(Person{"Smith","Sally",13, 4.56, DateTime{1997, 03, 17}})

  #region {.. for ease of display ..}

   alResults:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

   alResults:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

   alResults:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

   alResults:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

   alResults:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

   alResults:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

   alResults:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

   alResults:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

   alResults:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

   alResults:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

   alResults:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

   alResults:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

  #endregion

   alResults:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

return alResults

static method GetPersonsLT() as List<Person>

  local lResults as List<Person>

   lResults := List<Person>{}

   lResults:Add(Person{"Smith","Sally",13, 4.56, DateTime{1997, 03, 17}})

  #region {.. for ease of display ..}

   lResults:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

   lResults:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

   lResults:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

   lResults:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

   lResults:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

   lResults:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

   lResults:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

   lResults:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

   lResults:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

   lResults:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

   lResults:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

   lResults:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

  #endregion

   lResults:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

return lResults

static method GetPersonsOC() as ObservableCollection<Person>

  local ocResults as ObservableCollection<Person>

   ocResults := ObservableCollection<Person>{}

   ocResults:Add(Person{"Smith","Sally",13, 4.56, DateTime{1997, 03, 17}})

  #region {.. for ease of display ..}

   ocResults:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

   ocResults:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

   ocResults:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

   ocResults:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

   ocResults:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

   ocResults:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

   ocResults:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

   ocResults:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

   ocResults:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

   ocResults:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

   ocResults:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

   ocResults:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

  #endregion

   ocResults:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

return ocResults

end class

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

class Person

  private cSurname as string

  private cFirstname as string

  private nAge as int

  private nOwes as double

  private dtDoB as DateTime

constructor()

  SUPER()

  self:Initialize()

return

constructor(cS as string, cF as string, nA as int, nO as double, dtD as DateTime)

  SUPER()

  self:Initialize()

  self:Age := nA

  self:Owes := nO

  //----- check that DT is valid !? -----

  self:DoB := dtD

  self:Surname := cS

  self:Firstname := cF

return

method Initialize() as void

  self:Age := -1

  self:Owes := 0.0

  self:DoB := DateTime{1900,01,01}

  self:Surname := ""

  self:Firstname := ""

return

access Age() as int

return self:nAge

assign Age(nN as int) as void

  self:nAge := nN

return

access Owes() as double

return self:nOwes

assign Owes(nN as double) as void

  self:nOwes := nN

return

access DoB() as DateTime

return self:dtDoB

assign DoB(dtD as DateTime) as void

  self:dtDob := dtD

return

access Surname() as string

return self:cSurname

assign Surname(cS as string) as void

  self:cSurname := cS

return

access Firstname() as string

return self:cFirstname

assign Firstname(cF as string) as void

  self:cFirstname := cF

return

end class

END NAMESPACE

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

PHH - 20th. September 2011.


Validating data while binding

This is an interesting and more 'fun' part of data binding with WPF.

Let us first of all look at how it appears on the screen when in action - see below :-

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

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

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

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

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

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

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

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

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

PHH - September 6th. 2011.


Formatting - TextBox & TextBlock

Just prior to dotNet 4.0 being released we were given the WPF feature of using 'String Formatting' with the bindings

available to us on a number of the GUI controls.

Lets take a look at the results of just a few changes to the invoice screen in Small Trader :-

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

In the above image note the 'Binding' tag, with the properties of Path, Mode, and 'StringFormat' - these are on

the Text property of TextBoxes.

However, we see in the following image an apparent change in the syntax - this will be explained at the

end of this subsection - just look and notice for now :-

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

[ .. if the tag is 'Binding' we have no commas, and double quotes are use around the values .. ]

[ .. if Text is given a { } value then commas are used as separators, and no double quotes .. ]

Further to our own examples within 'SmallTrader' we can study the following examples from the MSDN blog

which has a link given after the useful and colourful images :-

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

The above image and the following one deal with the 'TextBox' control only, but do show 'MultiBinding' in

operation.

Study the detail carefully :-

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

Finally, we are shown how to pickup text properties on other controls which do not have a 'Text' property

as do TextBox and TextBlock.

Check this out VERY carefully :-

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

Images thanks to >> http://blogs.msdn.com/b/llobo/archive/2008/05/19/wpf-3-5-sp1-feature-stringformat.aspx

TextBlocks :

TextBlock controls work like TextBox ones with regard to formatting strings this way - as shown above in the top images.

Labels : (Warning) :

We need to find a string property to use a StringFormat approach so do NOT use 'Content' as 'Text' in TextBox,

but the 'ContentStringFormat' as shown in the image above.

Other Controls :

It would appear that a few other controls can have 'ContentStringFormat' properties used in this way - above are samples

of ListBox, GridView, and GroupBox. You will need to go check on others if you wish to find more.

Possible Problems !? :

In the following image we see that the string formatting does NOT restrict what we enter in any way - it is not seen as

part of our validation, and it is not a 'mask' of any sort - check this out :-

As you can see the formatting actually 'gets in the way' of our current validation to a certain degree.

Unless we edit (and remove) the currency symbol we get a validation error 'signal' as seen with the red indicators.

Highlighting and over-typing works OK however.

In the image shown below I have done some suitable editing / data entry, and the totals display correctly as I

go through the rows of 'amounts' - check it out :-

What is interesting to notice, is that the zero currency formatting is not indicated if left alone, BUT, if I edit the

cell and then replace it with the same '$0.00' we do get a validation error being indicated.

This will be down to events being fired - once they have been, then no going back ;-)

Notes on 'Variable' Syntax :

[ This little section is courtesy of Adam Nathan, and his excellent book which is discussed in the first section

of these eNotes - check out those details. ]

Take as our example :- StringFormat={ }{0:N0 } << all (both) zeros >>

[ .. this gives output like '54 item(s)' and '1,001 item(s)' .. ]

"The funky { } at the beginning of the value is there to escape the { at the beginning of the string.

Without this the string would be incorrectly interpreted as a markup extension."

The { } is not necessary if you use the element form of Binging as shown below :-

<Binding.StringFormat> { 0 } items(s) </Binding.StringFormat>

Finally - a warning !?

At the moment .........."{ Binding StringFormat={ } {0:C} }" is not at all places parsed & interpreted correctly,

whereas this is :-

"{ Binding StringFormat=\{ 0:C\} }" ... so it looks as though the escaped way ' \x ' makes the extra curly braces OK !!

See you in the next section ............

PHH - 1st. September 2011.

Addendum :

There appears to be a well known issue with the 'StringFormatting' discussed above, not displaying the correct local or current

culture - it is not automatically picked up by the WPF system.

Having found a few articles which appear to agree with each other, I seem not able to get the line or two of code to work for me.

In the following image note the language setting MUST be made BEFORE the 'InitializeComponent' call :-

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

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

Above is the French Canadian setting, see the dollar is now at the R.H.S.

And finally, the UK British way to do things ;-0))

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

Well, after searching the net for :-

RFC 3066 Language code assignments

and

ISO 639-1 language prefix

I got the settings I require for the UK.

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


Various types of GUI control

As you are probably all too aware by now, there are quite a number of different, and varied, GUI controls

and elements within the WPF Toolbox in dotNet 4.0.

However, the good news is that they generally fall into two, possibly three, different categories. It all

depends on how you classify them.

One way to classify the GUI elements is to say some are designed to take 'Data' as we usually think of

it, and others are designed to take other GUI elements.

StackPanels and Borders take other GUI elements, including themselves. Whereas TextBox takes data, as

does a ListBox.

Single and Many :

One way to look at the GUI elements is by numbers of what they 'take'. So some GUI elements can take only

a single other GUI element, and some can take many of them.

Likewise on the 'data' item issue, some can take a single item - these are know as 'Content Controls', and

some can take many, these are known as 'Items Controls'.

Let us now give some examples :-

A push 'button' can only take one GUI element, but a StackPanel can take many. So by giving a button a

StackPanel first, this can then hold many buttons, so one button 'appears' to hold many buttons.

Panels are one form of 'container' element, and are designed to hold a group of many and different GUI elements.

In the following image we see two good examples of this written in XAML script :-

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

This looks like the following when the application is run :-

I will leave it to you to work out which script lines generate which GUI features on the form ;-)

Date Items :

The 'single to many' aspect of classifying GUI elements is also useful when we wish to look at data.

A TextBox can take one piece of data and a ListBox can take many - a collection of items, in fact.

Rather than say too much here about binding data to Content controls, as well as ItemsSource controls,

we would be better to deal with the separate common controls in the following, and appropriate subsections.

I will put real and working examples in the subsections later in this section.

It is important for you all to get very familiar with script and code to bind data to the common WPF GUI controls.

See you in the next subsection where we deal with Content controls, those that take single 'things'.

See you there ...............

PHH - 20th. September 2011.


What is Binding ?

Let us ask a simple enough question, and then try to answer it, also in a simple BUT correct fashion.

"What is binding ?"

Well from all I have done with it in WPF so far I would say this :-

It is a way of displaying on the GUI user interface certain aspects of data. The data can come from

properties of a custom business object, properties of a system object, or indeed other properties of

other GUI objects.

Notice that it is all about displaying properties.

Sometimes the binding required is relatively simple, and is possibly a one-off data display, other times

we require a fast response where changes even within a property are shown immediately in other GUI

controls, and even on other WPF forms.

To get the full interactive responses when such requirements are for objects within collections, and for the

collection changes to be reflected elsewhere immediately, we need to design our data objects as well as

using the right collection types, and do so in the correct manner.

Quite often we can get away with use of simple collects, like ArrayLists, but I always these days design

my Business Objects so that they should work in all data binding scenarios.

I have found that as I develop an application I often end up using my BO's in binding situations that I did

not think of at the time I designed and created the business object classes.

For this reason I make my BOs quite responsive to all data binding scenarios. Checkout the earlier subsection

which deals with "Designing Custom Objects" in this same section.

To Summarize :

Binding is about displaying properties on chosen GUI controls.

Data Binding is about displaying our custom object properties on chosen GUI controls.

These two simple ideas will help us go a long way into the realm of 'Binding' generally.

See you in the next subsection ...............

PHH - 26th. October 2011.


Binding from Code and Script

sorry ! - work in progress ;-0))

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

PHH - 26th. October 2011.


Content and 'single' Controls

In the following handful of subsections I will give working examples of binding to some common

GUI controls and elements.

Where possible I will include binding of properties from both data objects and also other GUI objects.

To get nice visual WPF forms we need to know how to bind across GUI properties, and even at times

to bind across properties on the same GUI control ;-) yes, it can be termed 'self binding'.

The first group of GUI controls we are going to examine are those which do not take a handful, or

collection, of items. They take just one - a single item, or 'thing'.

Remember, I have explained that XAML and the WPF form structure is all about "things within things",

well this group are elements is about 'a thing within a thing'.

Many controls which do not directly take a collection of items are called 'Content Controls' as they have

a 'Content' property, and this takes one object.

However, we also have a couple of rather 'special' controls where the 'Content' property has been replaced

by a 'Text' property. These are the 'TextBox' and the 'TextBlock' control.

PHH - 24th. October 2011.


Button

Let us ask a simple enough question.

What sort of things can we bind to a Button ?

Or better still, what kind of properties from other objects can we bind to the Button.

What we need to do is create a demo sample, where we can bind properties from other GUI elements

to selected properties of one 'sample' Button.

Making a simple start :

Let us start this subsection of binding by doing some real simple stuff.

Let us make two button controls and display text data on them in two different ways.

Checkout the code and script it the following images :-

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

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

In the above images we can see that the code on line 29 simply assigns a text string of characters to the property

of the Button called 'content'.However, the other button has XAML script to say that the 'content' property is going to

be bound to an object which is specified at run time.

The dotNet code on line 31 then associates the binding with the string object type I have called 'cMyString'.

Going Further :

Let us now do some simple binding with the colours of the buttons.

See the first was to do this below :-

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

And below is the XAML script to go with the above dotNet code :-

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

Now some of you may be familiar with just how flexible the XAML script makes things for supplying values. Are you

aware that there is a set of in-built 'value converters' ?

In the dotNet code this is not the case and so instead of "Red" we need to supply the correct object which is a Brush,

and a 'SolidColorBrush' in particular.

Binding GUI to GUI :

We can now go another step and use the text value on the button to colour the other button in a pair working

together in the binding.

Study the following images carefully :-

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

This time all the work is done in the XAML script, and the value converters of XAML change 'SkyBlue' into a suitable

brush, as well as doing the same for 'LimeGreen' for the other Button.

Okay then, we are starting to get a hang of this binding thing ;-0)

What about if we wish to bind to the colour word on the same control - we call this 'self binding'.

Well look at the XAML script shown below - it looks a bit funny at first but I will help you read it in a meaningful way after

studying the image following :-

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

Think of the binding as the property 'RelativeSource' being bound to an object which is instantiated from a class of

type 'RelativeSource' with parameter 'self'. This is very similar to some of the stuff we code in dotNet and don't give it a second thought.

So maybe its not that odd after all !?

We could go on to do nice things with Fonts, and their size weight and family - BUT - it is all pretty much the same sort of binding stuff.

Also I can show you the font 'stuff' with Labels later in this section. Let us move on to the TextBox control for now.

Lets move on to TextBox in the next subsection, see you there .........................

PHH - 20th. September 2011.


Text Box

TextBox control :

In the material shown below we are going to bind the 'Text' property of a TextBox control to properties on

other GUI elements.

We will start by having the 'background' property of a Button control as the 'target' of the binding, obviously the

'source' of the binding is the TextBox 'text' property.

Study carefully the image shown below, and notice that it 'works' even in design mode :-

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

The XAML script is easy enough to understand, the control which is to be the 'target' is the one with the binding

script details - in this case line 32 for the Button control.

All I have done with the 'source' control is specify a meaningful 'Text' content.

Although this works immediately in the designer, as shows on the design surface, when we run the app we can

replace the character string of the 'text' property as follows :-

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

If we input something which is a colour in the 'system color' list we see as above, when we input 'DarkBlue'.

However, if we do not supply a suitable string we get something like the following - see below :-

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

Okay then, so far so good. But we may wish to also bind the Foreground property, so we get a colour for the

Button text itself. lets do this now.

Going Further :

If we make a second TextBox and bind the 'text' property to the Foreground property of the same Button

we have the following script in the designer :-

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

And when we run this test app we can change the 'TextBox' inputs to produce the following results :-

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

Notice that the value converter for the Background and Foreground properties in XAML cause there to be

no issues over the casing of the characters - even though the intellisense tries to force correct casing as

seen in the following image :-

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

Let us now move on - we have had enough fun for the moment, binding between GUI elements. It is about

time we looked at some data objects.

Binding to Business Objects :

For this part of the eNotes we are best to find a real working example of where we have a 'TextBox' control

bound to a property in one of my own custom business objects.

In the image shown below we have the Status textbox in operation - study the details :-

In fact there are a number of 'TextBox' controls bound to the 'Seat' object which is in focus when the

above seat booking screen is displayed.

We can examine some of the XAML script, and how the designer looks, in the following image :-

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

Notice that line 48 specifies that the object which is bound has its 'Status' property bound to the 'Text' property

of the control. The object which is being bound is specified in the dotNet code which is run at app launch time.

Study carefully the following .NET 4 code, and line 44 in particular :-

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

Okay then, a bit longwinded, but we can see that ten (10) GUI controls and elements are bound to the

supplied 'Seat' business object.

However, the following also works, where the one line of code on line 40 specifies that controls belonging

to the Window object are to be bound to the 'Seat' object.

Study the code below VERY carefully :-

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

Notice that in order to get the ComboBox binding correct we have to specify this separately on line 54.

If we did not do this then at run time the code cause the system to try and bind to a suitable property

within the 'Seat' object - when it can't find one it does not throw an error, it simply fails to make a binding.

The result is an empty drop list as can be seen below :-

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

However, with the line 54 restored to active code and associating the binding with the parent 'ArrayList' object

(note this is now line 52 after some tidying) we get he following at run time :-

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

Note that all the other bindings seem to work just fine. All those which need to be bound to the 'Seat' object

apparently are, simply because we bound the 'Seat' object to the form itself, or the Window GUI object.

Looking at the 'Seat' custom class :

Now that we have seen the 'Seat' class in action - that is some working application functionality - we need to

look in detail at how I have coded the class.

Although it looks complex at first, the following Dependency Object code style is straight forward enough, and can

be easily copied and adapted for other data types as well.

In the code shown below I have only made selected properties of the type 'DependencyProperty'.

All those properties of the Seat object which are set at instantiation time, and are not to change, and left as standard

class properties.

Also, in addition, I have not provided Assign methods for those properties I do not wish to change - like Row, Block and seat Number.

Checkout the following image details :-

[ .. note that the 'Metadata' specifies the default value of the dependency property .. ]

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

Notice that in addition to the standard fields an DPs we also have an Event. This can be done since version 208 of the

Vulcan compiler was released a couple of weeks back.

We will see how the Event 'PropertyChanged' is used later in this section. For now just look at the following code and

see what it does for us :-

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

In the above code see how the syntax has changed from standard Access and Assign methods, to a format

which deals with Dependency Properties.

The 'BookDate' property is a standard DateTime type, but the one shown below, called 'Status' is a property

based around my own 'Enum' called 'SeatStatus'.

Check this out :-

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

And the 'SeatStatus' Enum is as follows :-

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

Okay then guys, we have seen and learned a lot in this subsection - BUT - none of it is rocket science and

therefore you should be able to copy and adapt, to make stuff work for you and your developing apps.

Let us move on to some colourful images. See you in the next subsection ...............

PHH - 26th. October 2011.


Image

Let us now delve into the world of images and pictures, even photographs.

Just because we have an image, as opposed to a string or a number, does not mean we have

to think of binding in any lesser way or fashion.

We can have images as properties in custom classes, and as such we can therefore bind them

to suitable GUI controls in a similar way to strings and numbers, and collection items.

Let us take a real and working example - below is the 'WorldTime4' applet.

As we look at the image below we can see nine (9) images, on nine different 'Image' controls.

Two of these are bound to data object and 7 are not. The 7 have one of the Image properties associated

with an image file - much in the same way as we associate an numeric property with an actual number.

Study the following image carefully and try and guess (predict) which two images are bound :-

Okay then, yes, the answer is the two images showing Paul are the ones which are bound.

The seven icons showing the day, from morning until night, are 'fixed' and not bound.

The reason the other two are bound is because the app allows the user to move through the collection of 'GlobalPlaceTime4' objects.

The top image of Paul is one within the 'DataTemplate' associated with the Item template of the 'ComboBox' control.

The second, and larger, image of Paul is simply a Image control on the form itself.

As we select from the drop list (ComboBox) we see the two images change to reflect the person selected

from the list in the combo box control.

Let us now look at the section of the 'DataTemplate' which binds the Image to the custom object dependency property.

Study the following details carefully :-

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

In the image shown above (highlighted area) we have setup a binding to the property called 'FaceImage'. This

property is one on the 'GlobalPlaceTime4' item objects.

So we now need to look at the Vulcan code to see where we provide the binding object - check this out below :-

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

Notice above that in good practise I am binding to a suitable 'View' and not just the 'oGL' object. There are benefits to

making a specific 'View' an just allowing the default one to be used, without us doing any coding as such.

Yes, that is right, if we don't make any 'View' the system uses a default.

Fixed Images :

Before we go any further with our binding, let us see how we display a file image on an 'Image' control.

Study the follow XAML script :-

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

In the above script we are associating the 'Source' property with a fixed 'value' - we are using a Resource object.

Checkout the Images folder in the next image, and see that Chris has the 'Build Action' set to 'Resource', which is

the same as all the others.

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

Because we have this as a Resource we can use the syntax ... "Images/startsNight1.jpg" .. it is a relative approach,

or method - we do not need to specify a hard disk folder and path etc. etc..

Bound Images :

This part gets more interesting, because the two images of Paul are on 'Image' controls which are bound in different ways

to the collection items.

We have seen at the top of this subsection how the image in the combo box item is set using a dataTemplate, with suitable

script for binding to a collection item.

However, for the larger Image control on the form itself, we have the SOurce property of the Image control associated with

the selected item in the drop list.

Checkout the following script syntax and notice in particular the 'Path' property value, or association.

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

Yes, line 79 shows we need to use the general syntax form of 'Object.Property' and this for us here is 'SelectedItem.FaceImage'.

So the syntax is a relative one I suppose, since 'SelectedItem' is not a specific item, and only defined at runtime after any selection

has taken place.

Be aware that this is a VERY common thing to do and it is well worth trying out for yourself as soon as possible.

Okay then guys, lets move on - see you in the next subsection ..................

PHH - 20th. September 2011.

Addendum :

Sorry guys, we really need to see the structure of the 'GlobalPlaceTime4' class.

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

Note we could not use Events when I made this class, and somehow we made do without it. Probably because

we were not doing anything too much with on-screen editing and making changes etc. etc..

Here is the rest of the class after some collapsing to allow us to see things more clearly :-

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

Hope this makes things a little clearer.

PHH - 26th. October 2011.


Label

Well, although the Label object seems to be quite a simple GUI element, it does have a 'Content' property

and it also can be bound in a few useful ways.

So let us start by seeing the XAML script for a standard Label - see below :-

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

So with little editing required in the XAML script we can add a TextBox and bind the Content property of the Label (as target)

to the Text property of a newly added TextBox control. This property will be the source of the binding.

Checkout the following image details :-

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

So if we run this test applet then we can interactively change the label 'string' as seen below :-

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

Okay then guys, it is now not difficult to add a further control, that of a ComboBox and then fill this with

some names of colours.

We can bind the 'Content' property of the selected item (itself a ComboBoxItem) to the Foreground property

of the Label element.

Study the following image closely to see what has been done :-

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

The above image shows the applet being run and after text string editing and input, the Blue item has been

selected from the drop list.

Making things pretty :

If we wish to enjoy a bit of fun we can make things more colourful and do some 'self binding'.

In the following image see what has changed to make the self binding work. And yes, I do know that

there is a neater and cooler way to set the Style on the ComboBoxItems. We will come to that later.

Checkout the Resources set for the Grid, and how these are used :-

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

So how do we improve our XAML scripting to achieve the same results ?

Well it is easy really. If we do not give the Styel in the Resources section a Key, then it will be applied to each

and every 'ComboBoxItem' in the Grid which does not have a Style property specified.

Now look carefully at the following image where these changes have been done, and got to work :-

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

After all that fun with Labels we need to move on I feel.

There is plenty more interesting stuff to explore, so lets get to it !

See you in the next subsection ......................

PHH - 27th. October 2011.


GroupBox with Slider

Although at first sight the GroupBox may seem a pretty tame, or uninteresting control to look at for

GUI binding, it does behave like all the other GUI controls and we can do a few nice things with it too.

Standard - 'out of the box' :

Let us first see what the XAML script looks like when we just slide a new instance of a GroupBox from

the Toolbox and onto the designer surface.

In the image below we see the 'standard' script entry :-

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

In the above image we see that the text string at top RHS (right hand side) is the 'Header' property. Also that

the body of the GroupBox is actually a Grid as it comes from the Toolbox. A bit like a mini Window, or Form.

If we change the property string for 'Header' we expect to see the RHS portion change on the form, lets do some

changes to the standard script below.

Applying some of our skills :

What you will see in the following images is simply me applying some of the skills and ideas we have developed

in the earlier subsections of this chapter.

Study carefully the details displayed below :-

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

Notice that although the 'Header' property is not actually called 'Content', it does behave in a similar manner. So we

can nest within it a StackPanel containing a TextBlock. And as you can see above, the usual changes to colour and

font can be made.

Note that this also shows how to expand upon property details if there is too much to be placed in the usual simplified

way - like <GroupBox Header="groupBox2" Height="77"

Notice above that the Header has to be removed and then the fuller section inserted like :-

<GroupBox.Header>

  <StackPanel Orientation="Horizontal" >

        <TextBlock Text="my other header!" FontWeight="Bold" FontSize="13"/>

  </StackPanel>

</GroupBox.Header>

Okay, I realise that in this case we could have left out the StackPanel but this approach is needed if we are to make a 'composite'

header from a string and a bound property, we will see this demonstrated and illustrated below.

Adding a Slider control :

First of all lets add a slider control to our GroupBox by dragging from the Toolbox.

We must also check that the slider tags are within the Grid tags - see below for what we are trying to achieve :-

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

In the above image we have a Slider within the Grid but we now need to specify the values for the start and end

of the Slider as well as the increments along its length.

Setting up the Slider :

In the following image we can see that we have added a few more values to certain properties.

Checkout the details of what has been done :-

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

As we can see above, the 'thumb' of the slider is one third of the way along from left to right - because 13 is one third of 39.

However, for our little demonstration on binding, we need to connect the two objects in some way.

We need to have information from the slider be shown on a property of the GroupBox.

Making a connection :

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

We can see in the image above that the designer shows '13' in the design surface, but at runtime after I have dragged

the slider 'thumb' around with my mouse we see a different result.

Yes, I know, the format of the value is awful, and we need to change it to something shorter and more pleasing to the eye.

But, we have made a binding, where the source is the 'Value' property of the Slider control and the target is the Text

property of a 'TextBlock' control within a 'StackPanel' within the Header property of the GroupBox itself.

All very nice stuff, and similar to what we can do elsewhere - WPF is very consistent ;-0)

Looking at a real example :

In the 'WorldTime4' application we see the following at runtime :-

There are three GUI element getting updated every five seconds. There are two string time values, one in

the combo box item, one labelled as 'Local Time', and the other is the Slider.

Yes, we have the slider 'responding' to the value of a data object property change.

This means that we have our 'Slider' binding the other way around to my test sample above. This time the

source of the data is a custom data object, and now the target of the binding is the 'Value' property of the 'Slider'.

Let us look at some XAML script - checkout below :-

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

In fact, when we study the above details we see that the Slider Value property is actually bound to the selected item

from the drop list control. So there are actually three objects in the binding 'chain', since we have a collection bound to

the ComboBox control itself.

Doing an Experiment :

There is a nice experiment we can do to show this slider binding in effect. It is difficult to show in static images and words

however, so you will need to do it yourself.

Simply get hold of the slider thumb with your mouse and slide it way off where it is at the moment.

Now as the 5 second intervals pass the slider is updated because of the binding, and what you see is the thumb 'jump' back

to just about where it was previously.

I have tried to show some of this behaviour below, although for my final image I waited a full nine hours ;-0))

Okay you guys, time to move on ............ see you in the next subsection ..........................

PHH - 28th. October 2011.


*Text Block*

Okay you guys, are your ready for this ?

In many ways I used to think that the TextBox control and the TextBlock control were VERY similar.

Possibly I even thought of one as being the Read Only version of the other.

Well they are most definitely NOT !!!

Let us explore the 'TextBlock' element and try to see just what it can do, and how it may differ from the TextBox.

Checkout the following image and see that the TextBlock object has a 'Text' property but not a 'Content' property :-

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

The next image shows we can't find 'Content' in the intellisense list :-

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

So it would appear from the above image that there is no Content property to be found.

However, if we place something between the <TextBlock> start and end tags we get some interesting results - some would

say quite weird even.

Let us start out by just adding some text string information - see below :-

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

This green coloured string data seems to take precedent over the information in the Text property. can this really be correct.

Well, in the image shown above we can see the Text property, but anything and between the start and end tags shows

up in the control - both immediately in the designer surface, BUT, what happens at run time ?

Checkout this for size ! ;-0)

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

Yes, very strange and interesting. But to see which text string is place first we need to change one or the other.

Here is a second result at run time :-

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

So from this result it would appear that the Text property string data is placed first, then the 'stuff' which is between the tags.

I say the 'stuff' between the tags, as the next bit of exploration is going to knock your socks off ;-0)

Take a VERY careful look at the following image, and in particular the GUI object in XAML script between the Tags.

Check this out :-

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

As I said, most interesting!

But also when we run the app we still get the extra information inserted from the Text property - see below :-

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

If I had had a suitable image file at hand I would have displayed it by setting the Source property on the Image control.

As it is the existence of the group of varied tags shows that we can have a multiple set of objects.

I suspect this is the case for the TextBlock control so that it can take 'Document' objects of some type or another. These

usually contain other objects related to the structure of the document. We obviously need to investigate this further, but probably

when we are not focusing on Data Binding and the likes.

Let us now conclude our exploratory trials by seeing it the TextBox control is as complex and powerful as the TextBlock control.

Carefully check this out below :-

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

And no surprise, it appears as if the TextBox GUI control is simpler than the TextBlock one.

The only thing I changed in the test was the names in the start and end tags, and made TextBlock into TextBox - as you

can see it did not work - the compiler did NOT like it at all.

Time to move on - although we have not cover binding, or Data Binding as such, that part of the behaviour of the TextBlock is

very similar to the TextBox control we looked at earlier. By now you should be able to explore this for yourself.

If this is not the case, then there are loads of example of binding with TextBlocks in my samples elsewhere in the other subsections.

Lets move on, see you in the next subsection ...............

PHH - 27th. October 2011.

Addendum :

And here is our answer to the strangeness above :-

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

And some more information in the Remarks section :-

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

This is why a control which one would think is a 'content' type, i.e. taking a single object, is in fact a multi content

control which can take a number of mixed and varied objects - as well as simple text strings.

Please do note the 'lite' specification - light weight that is !!

PHH.


Items Controls - multi

In WPF we use the term 'Items Controls' to describe those GUI controls which are designed to take

not one but a whole bunch of objects - we usually mean data objects when we say this. And by data

objects I mean custom class objects, or as we also often term them Business Objects.

The most common, and probably obvious GUI controls like this are the list controls, ListBox and ComboBox,

as well as the DataGrid control - all of these take a collection of data objects and display their properties in

a flexible way which is very much in the hands of the developer and coder guy.

Yes, we as developers need to learn a few new tricks, but when we do we have far more freedom to do as we

wish when it comes to how thing look on the WPF Form.

In the following three or four subsections we will explore some of the many possibilities.

I hope you enjoy the ride, look at is as a voyage of discovery, and not a chore, or difficult task to perform.

Now let us roll our sleeves up and get to work.

See you in the next subsection ...............

PHH - 20th. September 2011.


List Boxes

Before we get into examining all the details, let us first take a look at what a suitable ListBox is like when used

in a typical fashion - see below to start us thinking about the possible issues to be addressed :-

The little ListBox control labelled 'Party Seats', where the listed items are in red, is what we are about to study.

The purple screen is from the Hall Venue seat booking applet, and is to display the details of any registered

Party in the system.

A Party can be simply a single person, or a person acting on behalf of a group of people. So the list of seats

are those seats for the whole group - booked in effect by the one Party reference number.

The XAML script :

Let us start at the XAML script end of things - what do we need to put into the script we write ?

Well checkout below to answer this :-

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

In the above image we see that the ListBox has a DataTemplate associated with the 'ItemTemplate' property of the Items

within the collection. This approach allows us to display one or many data fields (or properties) from the item object.

What happens if we don't use a 'DataTemplate' ? :

Let us do a small experiment - let us comment out the DataTemplate in the XAML script and see what we get.

Checkout the following details carefully :-

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

Oh! dear - we have something rather useless as far as informing the user which Seats have been booked for our selected Party.

Luckily there is an easy way around this problem.

Since the 'ListBox' contains item which are 'Seat' objects, we simply need to code a 'ToString()' method as follows :-

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

In the image above, we see that by adding the four lines at 62 through 65 we at least allow the Seat names to be

shown in black - no red or bold font weight.

Also notice that it is the Seat class which had to be coded differently, not the Party class. Although we are

looking at Parties, we are actually displaying in this ListBox a set of Seat objects.

The Vulcan2 dotNet code :

Now that we have seen the on-screen results, we need to look at the code sections which find and then associate the

set (or collection) of Seat objects with the actual 'ListBox' control.

Let us work backwards - from when a Party is selected from the Party drop down list - see below :-

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

It would seem that in response to a selection change in the Parties drop list control, we then execute

to method called 'DoChangePartySelection' - which is shown beneath :-

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

It is line 72 in the above image which then finds all the Seats relating to the newly selected Party.

Checkout the code below for the 'FindPartySeats()' method :-

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

As you can see in the above image, it is line 60 which finally makes the association of an ArrayList

collection of Seat objects, with the 'DataContext' property of the GUI 'ListBox' control - called 'lbxPartySeats'.

Alternative 'ToString()' methods :

There are different ways of creating, and using, a 'ToString()' method, but we will only look at a simple

alternative here in this subsection.

We have seen how using DataTemplates we can ver flexibly (and easily) combine data from many properties

into a single item display.

Well, without using a DataTemplate we can make our 'ToString()' method more complex - see below :-

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

PLEASE don't use this approach instead of the 'DataTemplate' one - we are using WPF improved GUI technology

so we MUST learn to use it well and to the full.

Anyway, how would you display an image, or photograph, of the item using a simple 'ToString()' method ?

Another Alternative - DisplayMemberPath :

Just to make things even more interesting, there is yet another way to specify what is shown in either a ComboBox

control, and/or a ListBox element.

Here is the dotNet code for using the 'DisplayMemberPath' property of the control itself - see below :-

self:ocbxCustomers:DisplayMemberPath := "Fullname"

However, we can also do this in XAML script too. The following image has all the details for us to see clearly :-

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

So the intellisense drop list shows for the control itself that there is a property of this name.

The same is true of the ListBox control, and below is shown how the properties of the ListBox itself can

be used to force bold red text for the Cost property on display.

Checkout the following details carefully :-

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

The problem with using DisplayMemberPath is that we can only use one property, and yet the DataTemplate allows many, and

we can also change the font settings for each separate TextBlock as well. The 'DT' approach is much more flexible.

Please note the following, which I got just there now while making these tests and checks for the eNotes.

We apparently can't have both specified - that is DisplayMemberPath and also a DataTemplate specified and associated

with the Items within the collection.

Study the following image carefully :-

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

So we can have the font weight and foreground colour set for the control and then over-written by the DT specification.

See below how we specify 'blue' and 'normal' but we see from the application of the DT at runtime that we actually

see red text and a bold weight.

Check this out :-

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

Okay then guys, we seem to have more ways of doing this than we may possibly need ;-0))

Still, it all makes sense and it also feels right to me.

I think DataTemplating is the thing that needs looking at closely, and I need to get better with it too.

There are other benefits with WPF and using DT, as you can set DataTriggers as well, which allow colour and

font details to change as we get mouse-overs and other stuff too.

Lets now move on .......... see you in the next subsection ...........................

PHH - 27th. October 2011.


Drop List / Combo Box

In this subsection we will look at a couple of different ways to create a collection of items and

then have this collection bound to the 'ComboBox' control.

For many of us, most of the time, we will create our collections from within our dotNet code at

runtime - so this is what we will checkout below.

First of all let us see how we associate a 'GlobalList' object with the 'DataContext' property of the

combo box on the main form of the 'GlobalPlaceTime4' application.

Study the following code carefully :-

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

In the image above it is line 54 which makes the association.

So what does the 'GlobalList' class look like ? Look at the following code :-

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

Basically, we make and add six 'GlobalPlaceTime4' objects to the collection.

As the class inherits from 'ObservableCollection' and the GPT4 objects are added to the class itself, then

the oGL object of line 47 is indeed itself such a collection.

Launching the app and dropping down the combo box to reveal its contents show the following results :-

Obviously we have done something right, so let us take a look at the XAML script for this WPF form.

Study the following details carefully :-

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

In the above image we can see all the necessary XAML script to both bind the necessary properties to the GUI elements,

as well as define a nice 'DataTemplate' to apply to the items within the combo box.

Note that the 'DataTemplate' object is associated with the 'ItemTemplate' property of the ComboBox control.

Lines 13 through 18 and also 22 set the binding as required.

Notice that in these lines the object bound is not specified - this is done in dotNet code on line 55 if you recall.

If you can't here it is again ;-0))

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

Before we move on I would like to draw your attention to the fact that we make extensive use of StackPanels in order to

create the DataTemplate - this is common, very easy, and it looks good. So I suggest you learn quickly how to adapt

the approach for your own applications.

Another Collection - simple ArrayList :

Okay then you guys. For those of you who are still just that bit wary of new and possibly 'strange' collection types,

here is a way of using an 'ArrayList' to do exactly the same sort of job for our GlobalPalceTime4 application.

Study the details carefully :-

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

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

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

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

In the code above you will notice that I have only added four people as items to our collection list.

We should therefore see a difference when we run the code - see below :-

And indeed we do seem to be a few guys short of the previous list ;-0))

Sorry to Robert, Brian and Dan Logan !!

Although we can often get away with simpler collections, we should not run away from using (or manage without?) more

complex ones, as there will be many times we do need the extra functionality of the more modern, and recent, collection

class types.

I have been informed that 'ObservableCollection' is quite a good and reliable type to try out when you first try to code a

solution to a new project.

Using 'ComboBox' selection to bind to other GUI elements :

Okay then, we now need to focus on another way of binding with 'ComboBox' controls.

Very often we wish to select an item from a list and then have the properties of the selected item show up

in other GUI elements and controls on the WPF form.

Below is a working example of this from the Hall Venue seat booking example app. - check it out thoroughly :-

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

Notice that in order to get one of the many GUI controls to bind to the object selected as an item from the

collection bound to the 'ComboBox' control, we simply use the syntax shown below :-

Text="{Binding ElementName=cbxParties, Path=SelectedItem.Firstname}"

This syntax would be appropriate for both 'TextBlock' and 'TextBox' GUI controls.

Notice that we specify the name of the combo box object - as there could be more than one, and often is so -

as well as using syntax for the 'Path' in the form 'SelectedItem.xxx' where the x's represent the name of the

property required, taking special note of the casing of the letters.

Child Forms :

Although we are not going to cover using Child forms here in this section, we need to be aware that often, once

we have selected an item from the collection - in a combo or list box - we pass this through to a 'Child' form in

which we may often allow full editing of all the appropriate fields which may need changing or updating.

On this Child form we script and code the GUI elements and controls just as if the data object were a standard

business object. Yes, this does mean that source object properties are then bound to a number of target controls,

but in a 'Two Way' mode so that changes are reflected in the 'Source' properties.

It is when we are doing such binding 'activities' that we need our custom object class to be written in as full a

dependency property fashion, and also with the INotifyProperty changed event being used appropriately too.

Further Reading :

Look at the previous subsection on the 'ListBox' control for other examples that apply to ComboBoxes as well.

Study the next section to see how we code correctly the use of Child forms.

See you in the next subsection ..................

PHH - 26th. October 2011.


Data Grids

Let me say from the very outset that DataGrids are the developers friend.

They are easy to use and allow us to supply DataTable objects directly from SQL queries.

I was 'over the moon' when I first discovered then and what they could do for my programming / solution design.

However, since the DataGrid is an items control it will take collections just like those we have seen used for

both ListBox and ComboBox controls.

let us first study some work code and XAML script from the Small Trader application.

In the following image we see the Find Customers screen in which the DataGrid (DG) is to display a DataTable returned

from a query to the Customer table.

Study the details carefully :-

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

As you can see in the above image certain properties of the DG have been set, or have had associations made.

However, we will see that others are set in our dotNet code.

For the moment lets see what we get displayed on screen when we use a DataTable (DT) as the collection :-

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

In order to get the DT collection we have used the following Vulcan.net code.

Notice that we single out a DT from within the DataSet, even though in this case we only have one table in the set.

Study the following details thoroughly :-

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

In the above image line 613 queries the database and a returned DataSet (DS) is set to the object 'oDS'.

Using the single table from the DS we create a suitable 'view' to go around it, and then associate this 'view'

with the ItemsSource property of the DataGrid control.

As you may have noticed, there is really not much code or script required to get the nice on-screen results

we see.

Let us now move on to see how we may use another type of collection.

Using 'ArrayList' collection instead of a DataSet / DataTable :

I am doing this test as an experiment to see if we can supply the ArrayList we already have in the solution - it is

an ArrayList of Customer objects.

In the image shown below I have simply changed the code lines so that we now make a 'view' from the Customers

ArrayList.

After you have studied the following code we will see what it produces on the screen at runtime.

We will then see just what the SQL_my_funcs.SendDataSetQueryScript(cStatement, "Customers") code does

for us when it is used.

First study the method to return a DataSet (DS) from the SQL database :-

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

This code produces the following screen output, and it is very obvious that the order of the fields / columns is quite

different to that which we got from the DS.

Look carefully at the columns, their names and the data held :-

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

So what is the code like which produces the ArrayList collection of Customer objects, and how different is it

to that for the DataSet ?

Well, shown in the following image is the code to acquire the Customer data and to make the ArrayList - check it out :-

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

To Summarise :

We can use a variety of collections with the DataGrid control, even though we have just seen a DataTable and ArrayList in use.

It is best to use a 'view' at all times.

Set the property called IsSynchronizedWithCurrentItem to true so that any binding with other GUI controls has the item

displayed from the collection in sync with all other controls doing a similar display task.

It seems odd to me that this property default is set to 'False'. Very weird !

If you wish to be able to edit change the collection, like adding and removing items in the collection, you will need to select

a suitable collection type. Not all collections provide this facility.

Checkout the section on Collections to find out more about this aspect of screen interaction.

Now, lets move on to something else ......... see you in the next subsection .......................

PHH - 27th. October 2011.


Child Forms - one object, many forms

While on the subject of binding we need to discuss Child forms.

I have to confess that I used child forms a lot, and seemed to have some success, without really

understanding what I was doing.

Now I have been recently enlightened after a discussion at my local User Group meeting - we cal it VOUG.

Although the discussion initially revolved around MDI and parent windows 'owning' the children, it made me realise

that certain strange behaviour with binding I had witnessed during my development, may be down to my previous

poor use of Child windows.

SDK documentation :

It is probably a good time to look at some helpful SDK blurb - documentation if you like ;-0)

Study this carefully - see below :-

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

What I had noticed, while doing postings for Pearls, was that there was sometimes a difference in behaviour of the DataBinding (DB)

I had setup on a Form when it was opened with 'Show' as opposed to 'ShowDialog'.

In my examples I studied, the behaviour was OK when using the modal 'ShowDialog' but not complete, or fully correct, when I use 'Show'.

For this reason I have done some research and testing and am now coding my Child forms as I will show you below.

Take a look at some code snippets from the Hall Venue seating booking application - see below :-

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

Notice line 60 where the child form Owner is associated with 'SELF'.

And here is another example, involving the Party display details screen :-

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

What are the other Benefits ? :

Well, in addition to possibly improving all Data Binding performance and behaviour, we also get a correct MDI behaviour

too. By this I mean that any owner form (not modal) which is open when the parent form is closed, also is closed in the

same one operation.

So in my test I opened a copy of a booking form (screen) for each of the seat blocks A - E using a 'Show' and placed these

around the main form. On then clicking the close window icon on the main window, all five children were neatly closed, as well

as the main window itself.

Now time to move on .......... see you in the next section ...............

PHH - 28th. October 2011.


Section C : WPF Extras + 'bit and bobs'

I have included this section simply so that I may add anything I come across in my development work

which seems useful to tell you.

That is anything to do with .NET 4.0 and Vulcan generally. Anything to do with Visual Studio and its use in

developing solutions, any also anything useful regarding XAML and WPF.

Let just hope I can find some nice 'snippets' to help you (and me) out in the future.

PHH - July 17th. 2011


Resources at App Level

It is all too easy for developers to forget the Application level for placing resource items.

putting the TextBox style at the APP Resources level, so all validating XAML bindings can use the same.

show how to 'subclass' this by basing a more local style on the App one, and then changing say the background colour.

even show at a Grid and GroupBox level, not just a Window level.


General integration issues

Now that Vulcan version 208 is out and being used, it would appear from News group feedback that

many things have improved with the Visual Studio integration.

Before 208 I could have made a long list of things here in this subsection, but now I don't need to ;-)

It would appear to me after using VN 208 that much has improved, and intellisense generally is far better

than it was.

However, there are still a few issues which could be tackled to improve coding stuff for WPF. Up till

now I did not bother the Dev Team too much on things WPF, simply because they had other major issues

to sort out.

After VODC in November 2011 I will get on to them to see if I can feedback some points that they now wish

to focus upon.

So to summarize, this section is largely taken care of by release 208, but a few things need improving.

Do take into account that there is nothing currently which stops you using VN2 to create and develop some

VERY nice WPF applications.

PHH - July 17th. 2011


Gotchas

for this app - getting the connection details right in a file in Release (folder) and forgetting to place a copy in Debug folder as

well - so either it uses old strings for connection or finds no file - either way it is easy to get an uninformative lock-up

#using and not 'using' - something I came across while doing the STATIC stuff.

single and double quotes - or non at all !?

WPF/XAML full Designer screens - hide behind each other - PANIC ?!

error in XAML - manual entry - stop reload of graphics - comment out script and gradually uncomment parts at a time.

no space between end of double quote in value and the name of the next property ! get error

different with commas in the { } - extended markup syntax.

having or leaving references to older versions in the Reference folder. look out for yellow ! (said pling) and remove and

re-add (but first make a written note of what these entries are).

Check all in the References section as shown below - look out for ' v4.0 ' in the Full Path box

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

PHH - July 17th. 2011

Spurious Error Box :

Currently I am running VN 208 and this is still an issue to me.

In VS when we try to add a new WPF (or Window) we currently get this error message - see below :-

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

In fact if we just ignore it we get no grief, it just goes away and all else seems fine.

So the message is "do not panic".

PHH - 28th. October 2001.

Connecting to a Recently Copied SQL database :

After testing for an 'attachment' in Management Studio, make sure you 'detach' before you try to use code from

your dotNet application - using ADO.NET.

The O/S seems to leave a flag set on the DB files and an error is thrown.

Detaching the DB then resets the flag and the code will work OK.

I can't be totally 'prescriptive' about this BUT it has hit me, and I have managed to get around the problem by

doing as suggested above.

PHH - 5th. November 2011.

Windows 7 - Security, and SQL Backup Failure :

In transporting my VS solutions and other material to my laptop for use at VODC in a week's time I have discovered

an issue which can certainly catch out anyone - it did me ;-0))

The following code section is from the STATIC method in my layer 'six' which is called 'SQL_my_funcs'. Study this

now and in particular look at line numbered 73 :-

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

Since I failed with this code on another machine (my faster laptop) and it is not available at the moment, I am having to

interpret ('guess at' if you like) what was going on.

What I do know was that when I manually made the new folder required - called 'C:/BackupSQL' then the application run

the following time, did not 'stick' at the final backup, and actually made a BU file for me.

It is my guess that on the new laptop I did not have suitable access rights and that the attempt in code to make the

directory first (we now use the word folder instead), actually failed.

Maybe you need to check this out on your PC my existing (development platform) has this set and does not appear to

have the same problem as seen on my laptop - check this out below :-

So for a similar STATIC method called 'DoSequentialBackup' here is the amended code :-

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

It is up to you then - th either use an area of the disk surface that you know is available to any user, or change the

security access rights - or - just do it manually as I did to get my VODC stuff to work :((

PHH - 7th. November 2011.

CARE !!

Error when metadata for type double is specified as ' 0 ' or '0.0' - seems to only allow blank and yet INT takes ' 0 '.

STATIC PUBLIC OwesProperty := DependencyProperty.Register("Owes", typeof(double), typeof(CollectionItemDisplay.Person), PropertyMetadata{}) AS DependencyProperty


Enumerators

Something guys new to Vulcan, possibly from the world of VO, have to get to know is the use of the Enumerator.

Using an 'enumerator' object facilitates moving through a 'collection' of items (any type of object) and being able

to 'grab hold', as it were, of each object. This one-by-one process is a Read Only process, we can't change the

items we examine.

I think I am right in saying that any sort of expected 'order' of the items can be forgotten, just be glad you can

get a hold of the items, one by one !

Before we look at the SDK documentation on this, let us see an example from my working code.

We will be taking an example of 'System.Array' in the image seen below, and you will also see in the class

listing in the SDK the following snippet :-

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

This tells us that when we have an object of type 'System.Array' there is a method of this class which will provide

us with a suitable Enumerator - see below how we use 'IEnumerator' :-

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

Well, I find it is easy enough to get it to work for what I require, and we we see it used a lot more as we need

to use 'Collections' at lot more with the WPF technologies.

Let us see what the SDK documentation has to say about 'IEnumerator' - see below :-

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

*** Note *** At the present moment we in the Vulcan 2.0 community have not the facility to use 'foreach' but that

is not an issue at all - will just be handy when it comes.

Here is an example of C# code using 'foreach' :-

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

Now the 'History' lesson - this enumerator above is a little 'old fashioned' in that it seems to have VERY few members

as can be seen below - we will soon be looking at more modern classes (like ICollectionView) in which we can do

a lot more in the way of moving around the collection of items.

First, let us see the SDK stuff :-

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

As you can clearly see above, nothing but a forward R/O pass through the collection, which may be reset .

Now contrast this with what can be done if and when we use 'ICollectionView' - study the following :-

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

It would appear that when we use ICollectionView - placing a 'view' around our data source - we have available to

us an improved set of members to help us move around the items in our collection.

These methods, although rather awkwardly named, allow us to move or change the 'current' pointer in a far more

flexible manner. We can go back as well as forwards and even to a specified position.

But before we get carried away, let us return to some earlier dotNet technology.

Let us see what can be done wit the ArrayList class, as far as enumeration is concerned.

As you can see in the following image from the SDK we can get an enumerator :-

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

Let us now take some working code and try some trial tests - see below :-

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

The intellisense shows us that we get the same 'old' enumeration with ArrayList as System.Array, which to

many of us will not be a surprise at all.

DataTables and Enumerators :

Okay then guys, we are used to the idea that a SQL query statement often returns a DataSet object, and that

this is made up of one or more DataTables. All good ADO.NET stuff !

Well then, look at the following code snippet from the working Small Trader application - see below :-

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

In the above code notice how easy it is to make a 'view' from a table of data. The DataView class has also a

method entitled "GetEnumerator", as can be seen in the following image from the SDK :-

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

Let us use the working code to carry out a few trials - see below :-

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

Once again we will see that the enumerator is of the same type as above in System.Array and ArrayList.

Check this out :-

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

So it may be fair comment to say that the older dotNet 2.0 classes have an older style 'Enumerator' which has

rather reduced functionality to methods we may see available to us through members of the likes of 'ICollectionView'

Take my word for it, there are no members in 'DataView' which allow us item access in the way that those in the

'ICollectionView' class do.

We now had better see the SDK 'blurb' for DataView(s) - overview as well as class :-

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

And when you read the following useful information, take note that at the end of it we see that we MUST find out

about LINQ technology when we get a spare moment ;-)

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

Before we move on to another subsection, let us see some of the methods from a 'ICollectionView' in use in

some of my working code.

First shown below we are setting up the view from a 'CollectionViewSource' - check this out :-

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

Once we have the 'views' we can initialize the enumeration - the choice of wording by myself, in the

image shown below may have been better if I had used 'Enumeration' instead - since we have not

got an object which is an 'Enumerator'.

Remember, the methods to navigate the source collection are within the 'view' class itself.

Study the code carefully :-

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

All I can say is that it works very well and the bound data in the text box elements has a special XAML

syntax to bind them to the item being 'pointed' at in the collection.

See the 'slash' syntax in the image below :-

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

In the above image the " /ServerName " as a Path 'value' means display the property called 'ServerName'

from the data item in focus (being pointed at) within the collection.

This is opposed to the following syntax in my commented script, which binds to the selected item of the list

box element - check this out :-

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

Obviously we also have to provide the name of the GUI element from which the 'selected item' is to be used

in the binding process - the second green line above is the crucial one.

What have we Achieved ? :

Well, more than you may think !

It is easy with all this dotNet stuff to get too close to the details of what we are doing, and NOT see the bigger picture.

In fact we have managed to be able to take an older class of object, like DataTable, and provide it with a more modern 'view',

and one which gives us some better control over the items navigation in the items collection or data set.

Let us now take what we have learned and do the same for the old ArrayList class - study the following details carefully :-

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

I have commented out the old way to get the limited functionality of an IEnumerator, and replaced

it with something from dotNet 4.0 - well it is to be found in the SDK and that usually means it is new to dotNet.

Notice that as soon as we have the 'view' object we have available to us the fuller set of navigation methods.

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

Now, let us cross our fingers and see if this will work for us ! ;-)

Let us see if we can use the navigation methods to place our pointer on the last but one item in the collection.

Checkout below :-

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

Yep! it seems to work for us, and I suggest we could also do the same for our old System.Array object too.

If you wish to have a do yourself, the try this new approach on the code at the start of the section above.

See you in the next section ......................

PHH - September 14th. 2011

Addendum :

We can use a static member of 'CollectionViewSource' to do the job as shown below :-

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

So we can do away with the need to instantiate a 'CollectionViewSource' object.

And tidying the code to see just where we are at, we get the following :-

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

It still works with the improved navigation members.

See you in the next section ................


Listing Enumerations - on the GUI

This is a very common task to wish to do.

VERY often we need to include a list, or drop list, of items which are all the options from a system enumeration.

In fact, once we get into using enumerations generally, we will then wish to present our own custom ones in

GUI lists as well.

Let us start out by working on the System enumerations first of all. Later we can come to our own!

Namespaces - Getting them Right !? :

One issue which always seems difficult to a new guy coming to XAML and WPF forms, is the way to 'get at' the correct

namespaces, those where dotNet related stuff seems to reside.

In the following image look at lines 5,6 and 7 - to see how to create suitable 'prefixes' for use in the XAML scrpit.

Study the details carefully in the image below :-

[ .. we will make 'local', 'sys', 'SIO', and 'MS_Win32' .. ]

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

The following image just completes the code for the last entry in the above image :-

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

If we can point at, and use, the correct namespaces then we can access any of the enumerations to be found within them.

I have commented in green in the above image to show examples of enumerations from different namespaces.

For example the 'FileModes' comes from the System.IO whereas 'DayOfWeek' is in System.

To do any enumerations we MUST have access to 'System' as the class containing the static method called 'Enum.GetValues'

is held within the System namespace - and so we need the 'sys' prefix to use it.

For those of you who like to see dotNet code which does the same job as some XAML script, then look below at lines

44 - 46 where we make a System.Array of enumerations and provide it as a Source to a drop list control. This is a little

different to what we have done so far in XAML.

Check this out carefully :-

[ .. you may like to notice that 'FormType' is my own custom enumeration .. ]

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

There are many examples in the demo app shown above, and these will be included in the full working

code / script section included as a sub-folder of this.

More in-line with what we have included in the XAML script is that test code shown below - where we use

the same combo box for testing purposes.

Checkout lines 34 through 43 :-

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

Probable Issue, and Possible Resolution !? :

When I started trialling the code and script for the VODC 11 eNotes there was a problem with the intellisense not

displaying for custom enumerations. However today via the auto notification system I got this below :-

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

So there is another thing which will be sorted out by the time we meet at VODC in November.

By then we will have another version of Vulcan2 to delight and amuse us ;-)

Lets move on now - see you in the next subsection .................

PHH - September 11th. 2011


Full Working Code & Script

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

<Application x:Class="WPFcustomEnums.App"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

StartupUri="Window1.xaml">

<Application.Resources>

</Application.Resources>

</Application>

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

#using System

#using System.Collections.Generic

#using System.Configuration

#using System.Data

#using System.Linq

#using System.Windows

BEGIN NAMESPACE WPFcustomEnums

/// <summary>

/// Interaction logic for App.xaml

/// </summary>

PARTIAL CLASS App INHERIT Application

END CLASS

END NAMESPACE

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

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

BEGIN NAMESPACE WPFcustomEnums

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

END CLASS

END NAMESPACE

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

<Window x:Class="WPFcustomEnums.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:sys="clr-namespace:System;assembly=mscorlib"

xmlns:SIO="clr-namespace:System.IO;assembly=mscorlib"

xmlns:MS_Win32="clr-namespace:Microsoft.Win32;assembly=mscorlib"

xmlns:local="clr-namespace:WPFcustomEnums"

xmlns:XYZ="clr-namespace:System.Reflection;assembly=mscorlib"

Title="Custom Enums :" Height="400" Width="500">

<Window.Resources>

<!--================== System.Window / System.Windows.X Enums ========================-->

<!-- X = /Automation/Controls/Controls.Primitives/Data/Documents/Forms.Integration/-->

<!-- X = /Ink/Input/Media/Media.Animation/Media.Effects/Media.Imaging/Media.Media3D-->

<!-- X = /Media.TextFormatting/Navigation/Shapes/Shell/-->

<ObjectDataProvider x:Key="VertAlignments"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="VerticalAlignment" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="ResizingModes"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="ResizeMode" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="BitmapScalingModes"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="BitmapScalingMode" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="FontCapitals"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="FontCapitals" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="SystemGestures"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="SystemGesture" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="myMediaStates"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="MediaState" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<ObjectDataProvider x:Key="myPenLineCaps"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="PenLineCap" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<!-- ===== System IO : Enums ===== -->

<ObjectDataProvider x:Key="myFileModes"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="SIO:FileMode" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<!-- ===== Microsoft.Win32 : Enums ===== -->

<ObjectDataProvider x:Key="myRegValKinds"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="MS_Win32:RegistryValueKind" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<!-- ===== 'System' : Enums ===== -->

<ObjectDataProvider x:Key="myDaysOfWeek"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="sys:DayOfWeek" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

<!--================== Custom Enums ========================-->

<ObjectDataProvider x:Key="myFormTypes"

ObjectType="{x:Type sys:Enum}"

MethodName="GetValues">

<ObjectDataProvider.MethodParameters>

<x:Type TypeName="local:FormType" />

</ObjectDataProvider.MethodParameters>

</ObjectDataProvider>

</Window.Resources>

<Grid>

<!-- ===== Custom Enums ===== -->

<ComboBox Name="cbxFormType" Height="23" HorizontalAlignment="Left"

VerticalAlignment="Top" Width="126"

ItemsSource="{Binding Source={StaticResource myFormTypes}}"

SelectedIndex="0" Margin="75,25,0,0" />

<Label Content="Forms :" Height="28" HorizontalAlignment="Left" Margin="12,20,0,0" Name="label1"

VerticalAlignment="Top" Width="57" />

<!-- ===== Custom Enums ===== -->

<ListBox Height="79" HorizontalAlignment="Left" Margin="348,20,0,0" Name="lbxAlign" VerticalAlignment="Top"

Width="108" ItemsSource="{Binding Source={StaticResource VertAlignments}}" />

<Label Content="Vertical Alignment :" Height="28" HorizontalAlignment="Left" Margin="223,20,0,0" Name="label2"

VerticalAlignment="Top" Width="114" />

<ListBox Height="79" HorizontalAlignment="Left" Margin="348,127,0,0" Name="lbxResize" VerticalAlignment="Top"

Width="118" ItemsSource="{Binding Source={StaticResource ResizingModes}}"/>

<Label Content="Resising Modes :" Height="28" HorizontalAlignment="Left" Margin="223,124,0,0" Name="label3"

VerticalAlignment="Top" Width="114" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="348,223,0,0" Name="cbxBitmapScaling" VerticalAlignment="Top"

Width="118" ItemsSource="{Binding Source={StaticResource BitmapScalingModes}}"

SelectedIndex="0" />

<Label Content="Bitmap Scaling :" Height="28" HorizontalAlignment="Left" Margin="223,218,0,0" Name="label4"

VerticalAlignment="Top" Width="114" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="348,264,0,0" Name="cbxFontCaps" VerticalAlignment="Top"

Width="118" ItemsSource="{Binding Source={StaticResource FontCapitals}}"

SelectedIndex="0"/>

<Label Content="Font Capitals :" Height="28" HorizontalAlignment="Left" Margin="223,259,0,0" Name="label5"

VerticalAlignment="Top" Width="114" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="348,310,0,0" Name="cbxSystemGesture" VerticalAlignment="Top"

Width="118" ItemsSource="{Binding Source={StaticResource SystemGestures}}"

SelectedIndex="0"/>

<Label Content="System Guestures :" Height="28" HorizontalAlignment="Left" Margin="223,305,0,0" Name="label6"

VerticalAlignment="Top" Width="114" />

<ComboBox Height="23" HorizontalAlignment="Left" Name="cbxSysWinControls" VerticalAlignment="Top"

Width="142" ItemsSource="{Binding Source={StaticResource myMediaStates}}"

SelectedIndex="0" Margin="18,220,0,0" />

<Label Content="Media States :" Height="28" HorizontalAlignment="Left" Margin="12,197,0,0" Name="label7"

VerticalAlignment="Top" Width="84" />

<ComboBox Height="23" HorizontalAlignment="Left" Name="cbxSysWinMedia" VerticalAlignment="Top"

Width="142" ItemsSource="{Binding Source={StaticResource myPenLineCaps}}"

SelectedIndex="0" Margin="18,275,0,0" />

<Label Content="Pen Line Caps :" Height="28" HorizontalAlignment="Left" Margin="12,252,0,0" Name="label8"

VerticalAlignment="Top" Width="101" />

<ComboBox Height="23" HorizontalAlignment="Left" Name="cbxMSwin32Enums" VerticalAlignment="Top"

Width="298"

Margin="18,168,0,0" />

<Label Content="Enums for Microsoft.Win32 namespace :" Height="28" HorizontalAlignment="Left" Margin="12,140,0,0" Name="label9"

VerticalAlignment="Top" Width="240" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="18,332,0,0" Name="cbxIO_FileModes" VerticalAlignment="Top"

Width="142" ItemsSource="{Binding Source={StaticResource myFileModes}}"

SelectedIndex="0"/>

<Label Content="System IO : File Modes :" Height="28" HorizontalAlignment="Left" Margin="12,303,0,0" Name="label10"

VerticalAlignment="Top" Width="142" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="18,96,0,0" Name="cbxMS_Win32RegValKinds" VerticalAlignment="Top"

Width="142" ItemsSource="{Binding Source={StaticResource myRegValKinds}}"

SelectedIndex="0"/>

<Label Content="Win32 : Reg Val Kinds :" Height="28" HorizontalAlignment="Left" Margin="19,69,0,0" Name="label11"

VerticalAlignment="Top" Width="141" />

<ComboBox Height="23" HorizontalAlignment="Left" Margin="223,96,0,0" Name="cbxDaysOfWeek" VerticalAlignment="Top"

Width="93" ItemsSource="{Binding Source={StaticResource myDaysOfWeek}}" SelectedIndex="0" />

<Label Content="Days of Week :" Height="28" HorizontalAlignment="Left" Margin="223,69,0,0" Name="label12"

VerticalAlignment="Top" Width="93" />

</Grid>

</Window>

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

//----- PartWin1.prg

//----- created June 11th. 2001 for Pearls & -----

//----- ClickStartXAML at ClickStartVulcan2.net -----

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

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

#using Microsoft.Win32

#using System.Collections.ObjectModel

BEGIN NAMESPACE WPFcustomEnums

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

method Initialize() as void

//self:cbxSysWinMedia:ItemsSource := Enum.GetValues(typeof(PowerModes))

//self:cbxSysWinMedia:ItemsSource := Enum.GetValues(typeof(SessionEndReasons))

/* local oT as Type

oT := typeof(SessionEndReasons)

self:cbxSysWinMedia:ItemsSource := Enum.GetValues(oT)

oT := typeof(PowerModes)

self:cbxSysWinMedia:ItemsSource := Enum.GetValues(oT)

oT := typeof(RegistryView)

self:cbxSysWinMedia:ItemsSource := Enum.GetValues(oT)*/

local oGet as GetEnums4MicrosoftWin32

oGet := GetEnums4MicrosoftWin32{}

self:cbxMSwin32Enums:ItemsSource := oGet

return

END CLASS

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

enum FormType

WP10SS := 1

WP10MDASS := 2

WP10PN := 3

WP10CN := 4

WP10SPSS := 5

WP10IPSS := 6

end enum

///class GetEnums4MicrosoftWin32 inherit ObservableCollection<object>

class GetEnums4MicrosoftWin32 inherit List<object>

constructor()

super()

self:Add(typeof(PowerModes))

self:Add(typeof(RegistryHive))

self:Add(typeof(RegistryKeyPermissionCheck))

self:Add(typeof(RegistryOptions))

self:Add(typeof(RegistryValueKind))

self:Add(typeof(RegistryValueOptions))

self:Add(typeof(RegistryView))

self:Add(typeof(SessionEndReasons))

self:Add(typeof(SessionSwitchReason))

self:Add(typeof(UserPreferenceCategory))

return

method AddnewItem(oE as Enum) as void

self:Add(oE)

return

method DeleteOldItem(nS as int) as void

self:RemoveAt(nS)

return

end class

END NAMESPACE

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

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

PHH - 12th. September 2011.


Visual Studio - 'Productivity Power Pack'

is this Productivity Power Pack ?

PHH - July 17th. 2011


Self binding!

A powerful and interesting feature of binding - but a bit less common than most other stuff !

As with the 'self join' in SQL data tables, where one table is joined with itself, there are few good examples

to be shown and discussed - BUT - we still need to know.

Below is a good example of self binding, where the string content of one property is bound to the background

property of of the same element - in the following image checkout 'Yellow' and the colour of the list box item :-

In the image above lots is going on, and most of it VERY interesting too.

However, we need to focus in particular on how the background colour is showing what it is.

Let us start our investigation by seeing the script for the XAML - checkout below :-

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

The above image shows a simple enough definition for the ListBox. But since there seems to be

no mention of the Background property, it has to be dealt with somewhere else.

Let us look at the Resources section to see what we can find :-

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

Yes, it would appear that in the Grid Resources - that is the grid for the complete little form - that we

have a style defined for any 'ListBoxItem' element which part of any 'ListBox' control, which is placed within the Grid.

There are a number of properties of the 'ListBoxItem' which are setup in the style section, but it is that on line 8 which

is the 'self binding' example we are looking for.

Isolated it looks like the image of the line above.

Let us now try to explain, or read, this line in some sort of English.

It goes something like this .... to the Background property of the 'ListBoxItem', bind the value of the 'Content' property of the

data source which is on the relative source - the relative source is then defined as of type 'self' - meaning it is bound to the

same ListBoxItem, Background from Context, target from source.

Yes, I know the syntax seems a little difficult at first, but it is due to having two levels of 'Extension Markup' - where '{ }'

stands for 'extension markup syntax is enclosed'.

I will leave you to think on this and come up with how you may do a similar 'trick' when the words in the items are Font Families.

Checkout lines 12 through 16 to see that there appears to be an easier syntax for binding to the selected items

in the ComboBoxes on the form.

And here is the script working - see below :-

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

Time to move on to the next subsection, see you there ...........

PHH - July 17th. 2011


Changing the target framework

At first sight this sounds like a good idea ! And indeed one we would all wish to follow up on.

What I mean is, that one has an existing application, developed some time back with dotNet 3.5 (or earlier)

and it now seems like good sense to make it work on the dotNet 4.0 platform, to get the benefits of the new

CLR and all sorts of things.

Well, just be cautious - I tried this for my 'Small Trader' app because this is exactly where I stood with its

development - it was 3.5 from many moons ago.

It seemed easy at first, and after a fiddle with pointing my WPF data grids at the new namespace in

place of the 'Tookit' for WPF, and changing some application settings, I ended up with a non-runner :((

As yet I can't see what the problem was - I still intend to convert Small Trader (ST) app, but till I find out

more, just take CARE !!

Luckily, since my first failure, I have since done a HUGE amount of 'changing around' with my Small Trader

app - doing the layering, and adding the STATIC classes too.

So now I can retry at a move from dotNet 3.5 to 4.0 - fingers crossed !

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

[ .. IMPORTANT - make one, or even two, backups before you do any chopping about in the editor and panes .. ]

Good News : Less than an hour later - more like half !? :

Well, this time I seem to have a runner ;-) Yes! I mean success !!

I copied the existing 'Layered' ST app to a new folder on drive 'D:\' and worked from there in my VS10 shell.

Here is my current Project Properties pane :-

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

I suppose I ought to change the output file to include a 'DN4', standing for dotNet 4.0 - may do this later,

once I find out what exactly I need to do. It may be more 'awkward' than it first appears.

Moving on - below I will try to tell you all that you need to do and what you have to look out for.

Firstly, as seen in the image above, open the Properties pane for the project (by right clicking in the SE pane)

and change the target framework to version 4.0 - this will start the process off.

My new References folder in the S.E. pane looks like this :-

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

As you can see above, no yellow warning " ! " signs (said pling). This will tend to happen after you change the target

framework, as some of your references will bound to be too old.

Also, as you can see in the following image, each of the entries has been checked in the same way for the

correct version - i.e. ' v4.0 '.

Here is what it looks like for the newer System.Windows.Forms entry (which I had to change) :-

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

Yes guys, do NOT make the mistake of thinking that older 'stuff' will be okay and it is just the newer

entries that we have to get right.

Let the yellow warnings help you, the make a written note of all being flagged as warnings, and then remove them.

[ .. I had to change System.Windows.Forms, Microsoft.VisualBasic, and System.Drawing .. ]

Use the improved facility in VS10 'PPP' (Productivity Power Pack) to add back the latest versions of each reference required.

Going from 3.5 to 4.0 all mine now seem to be correct, and up to date.

Take care with any DLLs you have made yourself, and change these to the newer version of dotNet (as a target) as well.

Problem - WPF Toolkit(s) :

This issue is a little more awkward to correct and deal with. However, it is possible, as I have just shown by my efforts.

In the earlier version (3.5 target) of my ST app (started some 18 months ago) I used some GUI controls for my WPF forms

which were not currently within the full .NET release. They were in a Toolkit which I had to download and install separately.

Notably the 'DateTimePicker' and the 'DataGrid' controls.

Let me explain what I had in the 3.5 version of my ST application.

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

[ .. remember three of the above are older version, but do not get a '!' until the target is changed to 4.0 .. ]

You can see in the image shown below that line 4 is the one pointing at the namespace for the Toolkit.

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

With the namespace mapped to a 'prefix' of tag value of 'tkt' the following script shows where this needs to be changed :-

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

By removing the reference to the Toolkit from the References folder, and by deleting the line 4 for the namespace mapping,

the compiler will then tell you which script lines need adjusting.

Note that the controls are now included in the dotNet 4.0 namespace as standard, and so no special reference needs to

be made once the prefix is removed.

After making these changes the yellow 'pling' ( ' ! ' ) on 'System.Xaml' disappeared too - see below :-

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

As you can see in the above image, the ST app now runs as a dotNet 4.0 application.

Just to warn and possibly help you too, I had to remove the dotNet code I had which like the following

image referred to a namespace that was no longer acceptable to the compiler.

For some reason 'Microsoft.Windows.Controls' can be done without - see below :-

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

Consequently, as a result of deleting #USING lines, the following type of code also

needed to be cleaned up by removing the full namespace reference.

Check out the details below :-

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

As you can see in the above image, the PPP facility for 'Finding' is very helpful, with a much better 'look-feel' to it.

For the moment I can't think of anything else which needs to be said.

Yes, it is a bit fiddly, and yes, there is no guarantee that you will get success, but that should not stop you trying !

Good Luck with any upgrading of your target framework.

Now time to move on to the next section, see you there ...................

PHH - September 14th. 2011


Section D : Printing invoices

I have included a section of these eNotes on printing for a couple of reasons.

Firstly, it is a VERY necessary part of this real life application - we MUST be able to print out invoices and

job sheets for the customer and for the accountant too.

Secondly, I think it is time to remind ourselves that we can do a whole pile of our own printing, using our own

code - all Vulcan.net stuff.

Thirdly, I wished to show how we could use an existing (and older application) to print invoices which we made

in our new app code as objects, and then serialised to disk and used in the old EXE file when launched.

I actually do this for my own in-house invoice printing at the end of each month - it just saved a bunch of

work and my effort, and it works fine !!

Printing as always is a complex business, but with a suitable 'document' or printing class library we can make

life for ourselves a bit easier. My older printing EXE module uses this class to print the invoices.

Shown below is the folder and PRG where I have included the 'BestDocument' class in the Small Trader solution :-

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

The class itself is not rocket science or particularly complicated - BUT - it is complex.

It is made up of many small members each doing a specific job. At a quick current estimate there appears to be

about one hundred and ten methods making up the class.

For anyone wishing to do a lot of their own printing I did start to write a ClickStart volume called 'RnP' I believe. Although

it is not yet completed there is a whole heap of useful stuff to get you going with doing your own printing - using the

'BestDocument' class.

In the following image we see a typical invoice created in SmallTrader and printed in the older module :-

Please appreciate that all text, lines and images have been placed on the sheet by the 'BestDocument' class

members. However, only a few of the 110 were used I would guess.

I think we should leave the introduction to printing as we have it, and move on to the more detailed subsections

where we see how to do each part necessary in detail

See you there ..............

PHH - 31st. August 2011.


General information - printing

Well guys, we need to see what support material is out there right now, to helps us with our printing.

The link below will take you to a useful - but incomplete - set of my ClickStart eNotes which was to be

done for DevFest 2009 - the event which never happened - sadly :((

http://www.clickstartvulcan.net/WebNotes/ReportsAndPrinting/ClickStartRnP.html

However, there is enough documented stuff there to really get you going with the 'BestDocument' class

which is included in the Small Trader application.

Below are included a couple of images of the sort of stuff to be found in the eNotes :-

And I even went on to make some methods for nicely printing tables of data too.

I will revisit this printing project when I am done with the WPF and SQL stuff for VODC.

I think a few guys, including myself, ought to know how to help ourselves when it comes to printing.

Remember all this class is written in dotNet code within Vulcan, all the earlier converted stuff from VO

has been changed and upgraded in many ways - just one of those ways was to use dotNet data types and

their related methods and properties.

Check this out for Vulcan code which is years old :-

So why is the ClickStart eVolume not finished ? :

Well, after putting a huge effort into making these eNotes what you see today - the date was late 2008 - I found out

that due to the economic crash DevFest was to be put on ice, so this hit me and my enthusiasm 'hard'.

One way to prepare session material, notes, and even learn new stuff, is to promise to deliver for some specific event

or happening. Once the motivation is removed or lost, the project usually takes a dive - that is my excuse.

And anyhow, the class itself exists, and can do a lot of stuff, it was just the HUGE effort to explain all in detail,

with suitable examples too, that was the BIG strain.

Maybe some time soon I will return to the printing ClickStart eVolume and do some WPF related stuff with it.

See you in the next subsection .................

PHH - 31st. August 2011.


Invoice printing module

PHH - 31st. August 2011.


Linking Job module to Printing

PHH - July 17th. 2011


Changing the layout of the invoice

PHH - 31st. August 2011.


Making a new document or letter

PHH - 31st. August 2011.


Section E : Tips and Tricks

The purpose of this section is to try and include all those important things which I have come across

in my learning of WPF, which seem to be either a BIG stumbling block or are not clearly documented

elsewhere.

It seemed to me as a struggled to get WPF and XAML working for me that there were a heap of annoying

little things which seemed to just "get in my way", and I thought they did not need to.

Some of the topic areas in this section are specifically focused on WPF and XAML, abut others are more

general and about SQL solutions using dotNet 4.0 in its wider sense.

Simply put, I have tried to include the things that took me a while to get my head around, so that I may

save you a lot of time by NOT having to duplicate my errors or struggle to find the details.

PHH - 13th. September 2011.


Making a Project Structure in S.E.

[ .. please note I only use VS2010 and so these comments may not apply to VS2008 ..]

This is one easy way to save yourself grief and how not to loose yourself time and effort.

If you can first think about any new project and application.

Design and build a sensible structure in the SE pane right from the start.

By all means copy and paste smaller 'parts' from another instance of VS open with possibly a test app.

However, like most of us at some time, you will need to do some re-structuring I would guess so do as suggested

in the details below.

First some words of WARNING :

Although we expect the behaviour of the 'Solution Explorer' (SE) pane to be perfect, and allow

drag and drop, copy and paste, of files, folders and their contents, to a selected target place in the

pane, this is likely to cause some problems for a little while.

So what we need is a strategy to cope with the fact that we will need to cope with things as they are.

And after all they are not that bad as they stand - we just need to exercise a little care ;-0)

So my 'warning' would be "Do not take a reasonably well developed app and hack around in the SE pane,

dragging files and folders here and there."

I caused myself grief by doing just that to an app I had spent quite a while developing. Believe me it is

easy to get to a stage where the app does not work and it seems impossible to put things back the way

they were before you 'broke' it.

I can suggest a couple of ways you can avoid breaking your apps, while tidying them and giving them structure

in the SE pane.

First and Foremost - use a Copy :

After having my first disaster I realised a simple 'MUST' was to always work on a 'copy' of your application and not

the master itself.

Yes! before you start to move files and folders around in the SE pane, make a copy of you VS solution and use that

for the restructuring. That way if you get to a point when it stops working you can always start all over again with a

fresh and 'clean' copy - and nothing is lost but some of your time.

You will have gained some knowledge and experience by doing so, and therefore it is not as bad as it seems to break

an app. !!

Below is an image of the app I needed to completely restructure, note that like a lot of what I suspect you all do, it is

based around many and different Forms. In hind sight probably not a good idea - but it works.

In the following image the yellow folders are directly related to WPF forms and therefore a class of similar name :-

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

Remember, that the above application was a working one and one that I simply wished to give a SE pane

structure to, so that we the users could think of it in a different way.

My initial efforts were NOT to do with changing PRG code or XAML script.

Study the new SE structure which resulted after some efforts on my behalf :-

So if we go a bit further, or deeper in the structuring, we see something like the following :-

And yes, if we expanded the GUI layer at the top we would still expect to see a folder for each of the Forms.

The image below illustrates this :-

So you have a Copy - what Next ? :

How de we go about structuring our app, once we do not need worry about breaking it ?

Well, I would still issue words of 'caution' as we can still put much effort into doing the work of

re-structuring, and we don't wish to lose that either.

So 'steady as you go' seems a sensible "mission statement" - or approach to take.

I actually did a full restructuring of the Small Trader app above, this is not just for publication here, I did

it to find out if it could be done, and what problems would come out of the changes I was making.

One method I liked was using two instances of VS - see below for details.

Two Separate Instances of VS :

The benefits of opening the 'copy' of the app to be restructured in one VS instance and using this to copy from,

is that we can then make the new and second instance with freshly created folders and sub-folders etc..

So one way is to make a new solution and new project in a VS second instance, and then copy and paste files

from the older - but working - app solution.

I would also use just 'Copy and Paste', and leave 'Drag and Drop' alone - either across instances or within the

same instance when we look at an alternative approach. If copy and paste does not work then nothing will.

Benefit of this Two Instance approach :

It would seem to me after doing a restructure and making it work, that the benefit of this clean and fresh approach

is that fact that the SE pane never gets confused by complex Drag and Drop operations within its own structure. It

appeared to me that we lacked a reliable way to undo what we do by DnD operations within the SE so we need to

be careful, or cautious, about what we try.

Try for yourself this two instance approach, and see if you can get it to work for you. Otherwise try the alternative

approach discussed below.

Alternative - One Instance approach :

Now believe it or not there are a couple reasons why a 'One Instance' approach may suit you more.

Firstly, if you have two monitors then the Two Instance approach is easy to do and does not become tedious. So if

you have one screen only this may become a real awkward task or chore.

Secondly, if we 'bit by bit' gradually 'improve' our solution/project structure, we can compile and test run at many

stages along the way - to see if the whole thing still works. I suggest testing more often than less !!

Also I suggest making new folders with the appropriate name and then one file at a time copy and paste. This way

you can try again if something small goes wrong with the paste operation. Once the file appears where it should, go

back and delete the original one.

The more you try to do quickly during this process the more likely you are to have a 'breakage'. I suggest taking files

one by one, or at best being very careful and doing plenty of checks.

Once you have empty folders from the original structure, remove then and do another build and run test, just to be sure.

I think I did this to good effect while working on the new structure of the Small Trader app.

IMPORTANT ! - there is one place to be cautious about when it comes to making new folders, and moving files etc.

and that is discussed below.

Leaving the WPF 'Window1' at a project level :

I am reasonably sure that in the past I got problems when trying to move the main form WPF/XAML 'Window1' into

a sub-folder. Be aware of this and only do anything after thorough testing on a small test applet. I must admit that

after doing so much restructuring work on the Small Trader app I did not have the courage, or nerve, to try out any

change with 'Window1'. yes, my strategy is based on pain and experience !!

Good Luck with any SE changes you make, and remember 'go steadily' do NOT be too ambitious ;-0)

Time to move on, see you in the next section ................

PHH - 9th. November 2011.


Navigating in the VS 'shell'

Here are a few things I picked up from Paul, Willie, and others in the News group postings.

I now use them regularly, and frequently, to help life while coding in the VS shell.

Seeing the Bigger Picture ! :

Ctrl +M, +L .. toggle outlining (do it twice, if necessary)

This will close down (or collapse) each and every region until you get something like this :-

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

Then expand just the parts relevant to your editing and code creation - see below for an example :-

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

Moving Across and Between PRG files (and XAML too) :

There are many times that we need to move from one pane in the editor to another, and even more,

just to see the syntax of code or script there, or to copy and paste selected items.

These days of WPF we even need to move across .NET code and XAML script.

Well, the VS shell allows this to happen quite easily.

Ctrl + '-' step back through cursor positions >> and Shift +Ctrl + '-' to go the other way

[ .. said 'control minus' and 'shift control minus' .. ]

This is difficult to show in static images, but here is some colour and imagery :-

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

I show a WPF pane for good reason - simply because the movement is NOT restricted to just PRG files with

Vulcan code - it is all text panes in the editor generally.

Digging Deeper into our Code :

Frequently we are studying code and we need to see the method or function, or even field or property.

[ .. this also works for dependency properties (DPs) as well ..]

Well we place the cursor in the text relating to any of these required 'text' elements then press F12 or

use the context menu option to 'Goto Definition', as seen below :-

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

To bring up the context menu I placed the cursor in line 489 within the 'AgeProperty' text and then menu

selected as seen by the colour of the option line above.

The result was as follows :-

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

The only place this does not work as expected, or as I would like, is when I click in the text of an event handler

method in XAML script. Maybe Fabric and the Dev Team can help out here ;-0))

Here is what I would like :-

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

To be able to click in the blue text of the 'click' event for example, then move to the following with F12 or a menu selection :-

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

Once you get used to using F12 in other places it does become noticeable when it does not work in the XAML editor.

IMPORTANT ! - after using F12 then use 'Ctrl'+ ' - ' (control minus) to return to the 'cursor calling position'.

Folding Up our Code :

Quite often we do not wish to hide, or close up all of a section of code we are working on, or indeed just looking at / studying.

To solve your problem use #region & #endregion to make things visually more clear for navigating and the like.

Personally, I use it a lot for display purposes, both for Pearls postings and for sections of the ClickStart eVolume notes.

Below is shown a good example :-

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

If I did not use #Region with #EndRegion we would have the following to look at :(

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

It is surprising just how much improvement to the task of code creation and editing, that these few simple 'features'

bring. I do suggest you give them all a go.

And if you have any ideas to add to these then please post a message in the News group to help us all.

Time to move on, see you in the next subsection ...............

PHH - 9th. November 2011.


Getting into 'DateTime'

Government Health Warning ! :

*** Be well aware that you can get very confused by this topic, so tread lightly ! ***

These comments are made regarding 'DateTime' so as to try and make you life a bit easier.

Completely give up the idea of thinking and using 'Date' and 'Time' separately. Always carry,

as a variable, the 'DateTime' value.

'Date' and 'Time' came from days when things seemed simpler, but in fact we did things in a

way which actually made things more awkward - once you get your head into 'DateTime' you

will agree with me.

DateTime is a dotNet 'structure'.

The Universe we Inhabit :

There is one dimension in our Universe which deals with TIME, and this is a 'Continuum'.

The continuum concept of the 'Time' dimension is important - it effectively means that it spreads

in both directions into infinity.

We have chosen to give one point on the continuum an arbitrary value, so that we can use it as a

reference point for measuring in either direction from it.

When I was at school we used to 'term' dates as B.C. and A.D. - meaning before Christ and 'anno domini'

meaning the year of 'Our Lord'.

Now things are more modern, as we will in a global world with many religions, so the terms BCE and CE

have been universally adopted. 'CE' meaning the 'Common Era' and 'BCE' the obvious, 'Before the Common Era'.

Yes, the arbitrary 'DateTime' zero is still the supposed date of Jesus's birth, even though scholars know it

was different.

The result of all this is that we have a 'DateTime 'zero from which the date-time continuum can be measured

in both directions, stretching in both ways to infinity.

Values and no Nulls :

If we are to have a point in 'DateTime', it is a specific point on the continuum 'line', or axis. Therefore it MUST

have a precise value.

The meaning of this is that a DateTime can't be 'NULL'. It does not make any sense at all.

So the dotNet data type of 'DateTime' is true to this and is not 'nullable'.

The difference between two DateTime values is a duration of time, and is called a 'TimeSpan'. We are far more used

to the idea of a TimeSpan than a DateTime.

The Problem with Storage :

Most of the time we can manage with the above arrangement - forgetting Date and Time as something separate. I

seem to have managed in my coding with not too much of a problem.

However, there does come a problem when we start to use SQL data tables, and we have fields (or columns) in

our tables which are for the purpose of holding 'DateTime' values. Remember a 'DateTime' can't be NULL by simple

definition.

Yet we all know we see plenty of NULLs when we look at SQL table data. This is easier to explain than you may

think, and it makes sense, and it does not contradict what we have said above either - no confusion in my mind.

The Cell and the Value :

Think of the field in a table data row as a 'cell', then if the field is defined as of type 'DateTime' then it will hold values

of 'DateTime' - so what does the NULL mean to us ?

Well, think of it this way, the cell being NULL, as with any cell, of any data type, it just means that the cell has not

been given a meaningful value - or that any meaningful value has been removed.

It does NOT mean that the value of the data contained is actually NULL.

Because of this situation it is useful to have certain data types - like SqlDateTime ? - which are nullable. This means

we can use then in memory in the same way as we would a table 'cell'. Which is convenient when we need to transfer

values from memory variables to stored SQL data tables.

Just don't think of it as the 'value' being NULL - null means indeterminate - think of it as without a value.

So the 'nullable' property belongs to the cell or defined data type, and NOT the 'DateTime' itself. This has caused

much confusion in the past with coding and development guys, and still will I imagine.

If you need to remember anything, remember that a 'DateTime' object always has a value - and it is somewhere on

the continuum.

Read the later subsection of this section on NULLs and their use.

Granularity of DateTime :

If the DateTime axis is a continuum then there should be no separate points on it, just a long line - if you will, an infinity

of points joined together.

BUT, we can't have this in our computer simulation, we need to have a 'granularity', meaning some small space between

one 'DateTime' value and the next one. This is a 'tick', from the concept of a ticking clock I should think.

For most of us ticks are just too fine a detail, and we are happy with Seconds, Minutes, Hours, and Days.

So 'TimeSpan' values can be quoted in any of these 'granularity' values, it still makes sense, it just depends on how big

or small a number it is you wish to carry in your calculations or display format.

Values of 'DateTime' in General Use :

Because we as human beings tend to live is a very short range of the DateTime continuum we do not need to be able

to instantiate DateTime values with a value outside of the present date range 'scope'.

It is time to see a small amount of what the SDK documentation has to say - see below :-

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

As you can see from the above image we have quite a range of dates to our disposal (for us to use) in just the

standard instantiation range - I leave you to worry if you need to go outside this scope ;-)

However, this is just the dotNet side of things. Most of us at some time will need to come to terms with the

SQL side of this problem / issue, so take a look at the following SDK documentation - see below :-

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

Viewing the above details we see that the scope of the DateTime values in SQL tables has been reduced, or narrowed.

If you clients and customers are not happy with years after 1753 then I suggest you find different customers !

As a simple rule of thumb to myself, I never use values before '1800-01-01' anywhere I am using DateTime, that way I

manage to keep with both scopes (or ranges) always in my apps.

SqlTypes - how is 'DateTime' affected ? :

If you study the SDK documentation shown below you will see that the 'SqlDateTime' structure mirrors exactly the

situation with the SQL data tables - as do all such SqlType classes and structures.

Meaning that value limits (scopes) and precision etc. are the same as for the SQL data tables.

Using these in your dotNet code reduces the problems of incompatibility at the point of transferring data values from

data tables via returned data sets, to application in-memory variables.

Checkout what is said below :-

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

I suggest that it is the following fields and properties of this structure which will be of interest to most guys, simply

because this now allows us to handle the 'NULL' situation easily in our code. This will be become obvious below.

Note that we can use the static field to associate NULL to an instance of the SqlDateTime structure :-

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

So now, using some of these fields and properties we can do the following :-

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

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

In the above code we have passed a 'DateTime' value into an SqlDateTime local variable.

We have also set an instance of 'SqlDateTime' to be NULL. And no errors !

In the following image of dotNet Vulcan code we see further useful ideas. Check this out :-

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

Summary :

Try to use the SqlTypes when at all possible, while you are coding for SQL Server access. yes, they are designed for

Microsoft SQL Server but hey! that's what most of us are using anyhow. And, I think with care you can use then with

other DbFactory 'flavours'.

Time to more on - SORRY! should have said 'DateTime' to move on ;-)

See you in the next subsection ................

PHH - 13th. September 2011.


WPF elements - 'things within things'

What I have to say in this section concerns GUI elements, and nothing but GUI elements. The sort of

objects which make up a WPF form.

As a central 'theme' or problem for this section, let us ask ourselves a simple question, here it is.

"Would we ever wish to place a button on another button ?" And ... "who on earth would want to place

multiple buttons on a single push button ?"

Well you guys, in the course of this section unfolding we may be able to answer these question, and learn

something VERY useful to help our understanding of WPF and XAML at the same time.

"Things within Things!" :

This is a phrase I have used many times in recent months, and it came about after I suddenly realised how simply

the WPF form GUI structure was.

And just how all the possible WPF controls and elements had properties and functionality which fitted the idea

of 'things being contained within other things'.

It will help a lot if we focus now on the types of element we see on any typical WPF form.

Two Types of GUI Control / Element :

Let us start by saying simply that there are two different types of GUI element. By element we simply mean

any object which can be placed on (or within) a Window object.

Even the Window object, or element, is also of one of these types. So there are no exceptions.

The two types of WPF element are classed as 'Content' and 'Items'. We talk of 'content' controls, and 'Items' controls.

This is just another way of saying that any WPF GUI element either accepts (takes) a single object as it's 'thing' within it,

or that it accepts multiple (or many) objects as it 'things' within it.

The first, a 'Content' type of element takes ONE thing, and the latter, an 'Items' type takes MANY things. It is acceptable

in my eyes to say they contain one or many sub elements.

At first you may be forgiven for thinking that this causes limitations on the possibilities of what can be done, except that

we have container elements which can be placed inside a 'single content' element, which then in turn can contain many.

In order to put many in a 'Content' control, first add a single object, which is designed to take a bunch of things itself.

A good example of this is a Push Button, into which we add a StackPanel, and then within the StackPanel we can add in

as many things (other elements) as we like to.

Special elements like 'panels' are such one to many elements, and they are VERY versatile.

Time foe an example!

Example One - navigation controls :

In this first example we look at what the old style of GUI may have been like - see below for the group of

push buttons to navigate through a collection :- [ OK not that clever ;-) ]

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

The above code works and navigation is successful - BUT - there is a more modern style.

Above was the 'old style' - now we can see the new style, built on - "Things within Things!"

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

The old style buttons have been deprecated to the lower part of the screen / form and then shown below they

have been removed altogether, once the new ones were tried and tested.

Checkout the following XAML script and the schematic in the Document Outline pane.

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

Okay this may appear to be moving in the right direction to look better, but we still need to add the appropriate events and

handler associations - this we will do here in the XAML script as it is very convenient.

After tidying, improving and simply correcting errors we get this :-

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

Summary to Example One :

Quite simply we have seen that a nested approach to form layout may have a lot to offer us, we will need to school

ourselves as to how to apply this well in our GUI designs.

Example Two - binding to make a label :

A label can have a horizontal stack panel with a number of 'TextBlock' elements within it. (Things within things)

and if the 'Text' property of these text blocks is bound to different properties of as business object, we can construct

a linear element which reads OK but which is the sum of parts. Even text 'padding' can be added this way too.

Let us try to make this happen !

Study the image above, particularly the single label at the bottom of screen.

The live app actually allows a user to change selection and the content of the label changes with the collection

item 'pointer'.

So parts of the label 'information' is bound to the item selected in the list box from the bound array list object. Well,

it is a 'view' object actually, made from the ArrayList object. More of that later.

Let us see the XAML script and the DO pane to show us the 'Things within Things' approach to this - see below :-

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

Yep! in the above image we see an awful lot held in one little label ;-))

Since the WPF form Designer has few rules to it, the 'Text' properties of the 'TextBlock' elements can be treated

as in TextBlocks anywhere else, this means we can bind data to them.

So for now we have bound the three TextBlocks to three separate and different properties of the Person object in

focus by selection within the list box.

Let us make it more obvious by setting a background colour for each - see below :-

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

Don't forget, we could have made up the label 'information' from different bound data sources - the possibilities

are quite endless. For a 'silly' ? example we could have two list boxes, and bind some parts of the label (certain

text blocks) to the properties of one selected item of list box one, and some others bound to those properties

from the selected item in list box two.

And so forth and some on - great 'stuff', very flexible!

Okay then, so far so good. Probably we need to concentrate on the syntax of what we have above,

just to make things more clear.

Syntax of the XAML script - 'Content' - implicit and explicit :

Okay then, this next little topic used to catch me out a lot (more confusion really) before I worked it out for myself.

DotNet programmers are used to code being very 'Explicit' and by this I mean "If you want it you write it." We don't

leave command word and property names 'out' and have the meaning of what we wish to code conveyed by where we place

some other text entry.

Let me take a simple, but good, example of what I mean about XAML script being Implicit / Explicit.

Study the lower part of the following image :-

In the above image the first section is actually a 'Label' element and this is coded in either of a couple of ways.

Checkout the script shown below :-

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

In fact all three of the script lines works, and does the same thing for us in our application.

Let us see line 44 when it is active - see below :-

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

Yet in line 44 (that immediately above), we do not see mention of the property 'Text'.

Lines 45 and 46 are 'Explicit' because the have the property 'named'. However, in the syntax of line 44 it is implied,

or is said to be 'Implicit' syntax - because the text between the "> <" chevrons is translated by the compiler / parser as

the 'Content' property.

*** Remember *** The 'Text' property of TextBox and TextBlocks are special cases of 'Content'. They have a 'Text' property,

unlike other elements, and do NOT have a 'Content' property like all other elements which are not 'Items' elements.

You will see a big difference in scripting styles when referring to text books and learning from the web blogs, so you need

to be up to reading both types of syntax.

Often we get an explicit form when more characters need typing. See below for an example :-

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

In the above image we have used explicit syntax because we have a lot to associate with the 'Text' property.

We can even do this - see below :-

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

Obviously it is possible to put a lot more text, or string data, between these new tags - whether I would want to is another matter ;-)

Here is another example but with 'Content' in place of 'Text' - we are using a Button and not a TextBlock :-

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

It would appear from my tests that all characters placed between the tags, in this fully explicit syntax, are taken as character

strings and not command instructions or property names.

My suggestion to you is to just get used to reading this stuff, and don't get phased by it - it is not difficult, just rather 'human' ;-))

Lets move on a pace ............

Text a special form of 'Content' :

I have to say something about this, but I am well aware that I may confuse you, and probably myself too.

Remember, we are trying to simply say "things within things", and each thing can contain one or many other 'things'.

Unfortunately, when it comes to the Text property of a TextBlock control things are more complex it would seem.

This complexity was pointed out to me by colleague Alwyn (Jennings-Bramly), also known locally as "The Book".

I do not intend to go into ant real depth but just to point out the differences.

Before I try to clarify the 'rules' for this, just study the following image carefully :-

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

Well, as you can see above the 'TextBlock' control element seems to take a 'Text' and another 'thing' - in this

case the extra 'thing' is a 'StackPanel' - BUT - it is worse than this, it takes many more 'things' as well.

First of all lets illustrate how the string characters in the double quotes for a property value, seem true to what we

type in than when we use the space between the chevrons.

Look at the following small image and compare the 'gap' between the end of the text and the start of the red button - both

in this image and the one above :-

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

Okay then, here is another point of interest, if we comment the StackPanel, we are then placing three Button objects

(or things), in the TextBlock.

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

It was suggested to me that 'TextBlock' breaks all the rules because it needs to mix text and images, and possibly

more too.

List Controls : (ListBox, ComboBox, DataGrid & ListView)

A few VERY useful GUI controls are designed to take collections of 'things'.

The ones I use a lot are shown above, in the heading - although these days 'ListView' seems a little neglected :((

Below is a working example of a ListBox :-

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

In the above image the items source is an array list of Person objects - only the 'Firstname' property is being

displayed in the example.

In the images below I am showing that the intellisense shows a whole bunch of menu options beginning with 'Item'.

Check them out below :-

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

Here is the full list, and there are no others out of view :-

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

The 'ListView' control can have a 'GridView' display, but the DataGird is very versatile to use.

To Summarize :

Only TextBlock seems to be the odd man out as far as "one or many / content or items" is concerned. TextBox is OK.

TextBox and TextBlock both have no 'Content' property, and their 'Text' property seems to be a special case of the

'Content' property seen in other controls.

Panels are designed to take many 'things' and so do not have a 'Content' or any 'Items' related properties.

List controls are designed to take items, or a data source which is a collection of items.

Don't Panic :

All I can add to me trying to clarify and point out a few things, is to not worry or panic at all. You will soon start

to get a feel for all this if you start to use it for real. I did, and I can't remember too much pain !!

Now time to move into the next subsection, see you there .....................

PHH - 15th. September 2011.


Adding 'namespaces' in XAML script

Get this right and your application development 'life' will be worth living.

Obviously this subsection if all about WPF projects and GUI script written in XAML.

The parts we are going to fully examine are just those few lines of script early in any XAML file

generated by the GUI designer in VS2010.

Let us look at a typical 'stub' generated for us - see below :-

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

In the above image we see lots of stuff that we get for any new WPF form we start to create.

The only bit that is different each time is the name in line one - this is dependent upon the project

name and can bee seen in the Solution Explorer (SE) pane for this project, in the image below :-

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

Note that 'Window1' is always generated as the main window by the VS system - we can force a change,

but I found it is best to just 'live with it'.

Okay then what is the problem or issue ?

This can be answered quite easily. Before we can refer, in any XAML script we manually generate, to any

dotNet related 'stuff', classes and the likes, we need to have a way to 'point' at where it can be found.

If you recall, in our dotNet coding we have #USING statement lines at the head of our PRG files - see below :-

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

Yep! we often have plenty of these namespace lines, and above is quite a short list really.

Well, in a similar way we can add manually our own lines of script to the top of the XAML file.

Checkout the following :-

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

Lines shown above numbered 5, 7 and 8 were all added by my own hand.

The first one points to the namespace for the 'Extended Toolkit' - which we will discuss in another section - and

the other two point to two dotNet namespaces and also specify the assembly required as well.

So to read the above, we are pointing in line 7 to the 'System' namespace to be found in the 'mscorlib' assembly.

Remember this is shorthand for 'Microsoft Core Library'.

The second line (numbered 8) is pointing at the 'System.Data' namespace which is to be found in assembly 'SystemData'.

What else is Important ? :

In the two lines we have so far discussed, we have not mentioned the prefixes which are to be found after the 'xmlns:' and

these are quite crucial to what we are about.

You can see that I have called the prefixes 'sys' and 'data' but in fact this is quite arbitrary and is my decision only. I can

chose what I wish to call the namespace, as long as it is unique. There are a few accepted prefix names which we see

when we look at script form web examples and SDK stuff too.

I try to keep with convention when I can as well as trying to keep them short. We need short prefix notations because we

are to use these in the body of our script.

Study the script lines following, and see if you can spot all the uses of the prefix notation :-

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

Yep! line 12 has 'sys:Enum' and line 14 has 'data:DataView........'

Take care with the upper and lower case letters - assume everything is case sensitive, as most times it is!

If the XAML script is not familiar enough to you as yet, then to see what is being done, look at the following

image in which we use dotNet code to do exactly the same task :-

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

And as if you need more proof, study this next image to see a comparison between code results and

the XAML script binding ones - a nice illustration indeed ! :-

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

Remember we are discuss and looking at namespaces - so checkout in the above image the places the dotNet

code refers to the .NET namespaces - and of course these have to be present as #USING statements too.

Just to finish our little demo above - see how easy it is, once the static resource is defined, to bind the Array

to the drop list control - see below :-

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

In this script we both bind the array to the control as well as set the selected index to zero.

More Examples of Namespaces :

Let us have a little explore of the editor surface, which is part of the WPF Designer itself.

If we return to the top of the XAML script file and type 'xml' we see the following happen :-

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

As soon as we select the option 'xmlns' (stands for XML namespace) we get a further option list - a long one.

Checkout the following detail below :-

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

The only thing 'odd' that occurs to me is that there is not an obvious time to insert your own prefix.

The final result - without attempting to add a prefix - is shown below :-

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

However, it does not seem to give the Designer surface any grief by adding something suitable afterwards.

In the following image I have added the prefix ':IO' - check it out :-

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

Finally, we can see how the tidied top of my XAML script file looks with a bunch of namespaces included :-

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

Okay lets move on a pace or two.

So Good So Far - and Now Further !? :

While on the topic of namespaces, and making sure we can access dotNet classes from within our XAML script, there

is one consideration which needs to be taken into account.

Have a look at the following image from an excellent book by Adam Nathan - I totally recommend you buy this book and

I discuss more detail in the first section - under the subsection entitled "Useful Support Material".

Read the following carefully and thoroughly :-

So to summarise this part, all of the above dotNet namespaces are mapped by one line of XAML script.

The line is numbered '2' in our own image one above this and the line seem on its own is shown below :-

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

Using XAML 'stuff' in our dotNet PRGs :

So how do we use any XAML stuff in our Vulcan code files ?

Well, the problem does not seem to really exist for me at the moment. I have been happy and successful in simply

using the names of any (and all) of my GUI controls in my dotNet code, and all works OK.

This could be because I have not really explored the depths of XAML and code working together.

So for now I just try to provide a meaningful name to my elements on the WPF surface, and then use them as shown

in the images below :-

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

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

Good Luck with your namespaces, and mixing script and code in your apps. keep trying as the results are good ;-)

Time to move on to the next section, see you there ............

PHH - 13th. September 2011.


Set & Unset - Event Handlers

This topic is possibly a controversial one!

By this I mean that some of my colleagues in the 'IT world' of application development do not seem to

see while I am so keen on what I do with associating my event handlers for the GUI.

Some have even said - "What do you want to do that for ?"

However, I am also aware that I have probably dome more with GUI design and development over the years

than most of them - that includes WinForms too. So I could say "what do they know?" , but I don't because

that is simply not my style.

What I am about to explain to you is something I do because I have found it removes runtime errors and problems,

under certain circumstances, when I am creating complex GUI forms.

Taking a Peek at What I do :

Okay then you guys, let us see what it is I do with my event handler associations.

Typically, I have a method called 'Initialize' which I call just after initializing a GUI form.

Here is a partly collapsed typical example :-

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

In the above image you see that I 'Unset' right at the start of the method and then 'Set' again just before

completing code line execution of the method.

What I am trying to do is have no event handler associations 'active' while I do the necessary setup of the

rest of my form stuff. This I did in WinForms and now I do the same in WPF forms.

I make the effort to gather together all of the event handling associations for the form in focus. And that may

mean I have to remove script from my XAML file due to me adding events and related 'stuff'.

We can come back to adding events in XAML for ease of development later.

What are 'Unset' and 'Set' ? :

Before we get to discuss 'Handler Association' let us look at the code and all of its details - see below :-

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

The first thing to notice in the image above is that both groups of lines are virtually identical - same numbers, same syntax,

except for the crucial ' + and - ' symbols.

The minus symbol ' - ' means to remove any event handler association, and it will not cause an error if there isn't one.

Conversely the positive symbol is translated as meaning to make a new handler association. This will add to any similar

one already present, so we need to take great care with lines of code such as these !!

If done correctly, and together, and in the correct order, we should end up with there being NO associations at the time

when the rest of the setting up of the form is done.

By this I mean that filling drop lists with collections and groups of items will not rigger events, which have in the past

brought about strange and unpredictable results.

WPF forms - an Extra Issue !? :

I have only experience to call on when I state what I am about to state ;-)

It would appear that even with the Unset / Set methods as shown above, with XAML script in place which is making such

associations - but in XAML - if we do not remove these we can get the undesired effects I mentioned earlier. Not always,

but definitely under more complex circumstances.

Let us see what an event handler association looks like in XAML script - see below :-

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

The above image shows four on line 35 a 'Click' event being associated with the 'pbFirst_Click' method which we have to

make in our code section / Vulcan file. There are four similar script line entries.

If this was not just a simple test / demo applet I would remove these line sections and replace them by suitable code - shown

in just a moment. However, for simple apps there is no real point.

Why do I add them in the first place you may well ask ? - well simply because the Designer automatically places the

following code in my 'stub' area of the PRG file - see below :-

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

All but the blue highlight is added by the designer and so the correct 'event args' are used and such.

There code I have to manually add when I remove the XAML script looks like this for a push button :-

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

Obviously, there are a few name changes required to suit our current situation, but you get my drift.

In the ClickStart eNotes - both XAML and WPF - there are plenty of examples of many types of common associations. I suggest

you consult these eNotes and copy and adapt etc..

Extra benefits of Set & Unset :

As well at the benefits described above, those at application launch time - or even Form opening time for child windows - there

are times when the same approach can help us get around runtime issues and problems.

A few times over the years, when I have had complex forms, and usually involving refreshing or filling drop lists, it can be most useful

to enclose the code section doing the List control updating with "Unset and Set", this way there is nothing setup to cause any triggering

of subsequent actions.

Quite often such situations as above are caused half way through such an update, when the list is empty and/or the selected item

does not exist - it is a null object etc. etc..

Summary :

So now you know, quite a few little tricks with regards to event handling for WPF, XAML, and complex GUI design.

And just a final word of warning - remember that '+=' done more than once, will cause multiple triggering of the event handler method.

Not ideal while printing, and probably many other scenarios too ;-)

Time to move on, see you in the next subsection ..........

PHH - 14th. September 2011.

Addendum :

A few useful examples - you adjust sign/symbol, as well as names :-

Radio Button :

self:rbMissing:Checked -= RoutedEventHandler{self, @rbMissing_Checked()}

Text Box :

self:textBox1:TextChanged -= TextChangedEventHandler{self, @textBox1_TextChanged()}

Combo Box: //----- care --- only works the first time, not for successive selections - note the 'change' -----

self:ocbxCustomers:SelectionChanged -= SelectionChangedEventHandler{self, @ocbxCustomers_SelectionChanged()}

Text Box :

self:otbxProblem:TextChanged -= TextChangedEventHandler{self, @otbxProblem_TextChanged()}

Menu Item :

self:omiCustomerCreate:Click += RoutedEventHandler{self, @DoCreateCustomer()}

Window :

self:winSmallTrader:Closing        += CancelEventHandler{self, @winSmallTrader_Closing()}

List Box :

self:olbxTables:SelectionChanged += SelectionChangedEventHandler{self, @olbxTables_SelectionChanged()}

Data Grid :

self:odgCustomers:SelectionChanged -= SelectionChangedEventHandler{self, @odgCustomers_SelectionChanged()}


Dealing with NULL - Some Aspects

This is a 'thorny' subject and one which can be confusing to the new guys - or more correctly I should

say that 'it is confusing' to the new guys - all of us !

I like to think of things like this - NULL is not a value, in fact 'null' is the absence of any value.

However, a table 'cell' or a specifically designed data type can be NULL simply because it has no meaningful

value.

If we are using SQL data tables then we MUST focus on NULLs at some stage, and we need to find a way of

dealing with them in a suitable fashion - one which does not cut across what others do.

We must NOT develop our own 'cock-eyed' way of doing things 'Null'.

let us start by looking at some SDK stuff to help us :-

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

And then there is a little more helpful stuff from the SDK blurb - see below :-

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

In many ways we can thing of NULLs as being fine while they are in the data table cells. While they are there

they cause us little harm, but our SQL query statements must take then into account in a correct and acceptable

manner.

However, it is often when we need to handle the returned data sets that we get into NULL difficulties. We will

discuss more on this later, but for now we need to study the following SDK information, which describes a

namespace which can really make our coding lives easier.

Check this out :-

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

I gather from reading some of the earlier SDK blurb above, that we need to be careful in the semantics of our

statements and words, so I will make it clear to you that when I say 'nullable' I simply mean that a variable can

be made to take 'null' - as opposed to one which can't.

So for both writing to and reading from SQL tables the SqlTypes can be most helpful to the coder.

Although I have only used MS flavoured SQL server, I would think that these could still be employed with

other 'flavours' of the DbFactory approach.

If you wish to have a data grid in GUI and have a DateTime column which can take NULLs, then it may be best

to give this a type of SqlDataTime, depending on the grid type you are using.

Just remember, it is NOT the fundamental data type which is NULL, but the 'container' which can accept 'null'. Just

in the way a cell in a table or grid 'contains' something, think of the SqlDateTime variable instance as a container.

Now let us stop theorising, and look at some real coding situations - see below :-

Dealing with Returned DataSets - converting rows to custom objects :

Vey often in our applications we need to turn returned data rows from the SQL Server back into business

objects, before continuing with our business logic.

In the following image we see a typical example of this - study the lines starting at 896 and 828 in particular :-

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

For instances of my own custom objects I usually prefer not to have NULLs and more often than not I

design my code so that all of my BO properties have values, even if these are default ones set in the

constructor.

There may be better, or more ideal ways to do this, but at least I feel I have gotten around the potential

error situations, and am being reasonably true to both 'DateTime' and NULLs.

In the code shown above I am only likely to get NULLs because of errors due to development, and runtime

crashes during the development phase itself.

Once my code is finished and thoroughly tested I should have values for my DateTime properties.

HOWEVER - alternative style of coding :

You do need to be aware that there will be many other scenarios in which NULLs are required in memory

as well as in the SQL database. After all a NULL shows the absence of a meaningful value, and that in

itself can be useful.

So to anyone need to use this style of coding I suggest you look closely at using SqlTypes to handle the

NULL situation correctly, and without errors being thrown. This will probably mean you including them in

your custom class definitions.

At least now you are more aware of the possibilities and can at least try and solve your own problems from

a more knowledgeable standpoint.

Time to move on, see you in the next section ................

PHH - 13th. September 2011.


Giving up ArrayList

After coming to Vulcan from VO some years ago now, I started to use the ArrayList (AL) class for many things in my code.

I suppose I started to use it in place of the System Array, simply because I did not need to specify a length for it when

I first instantiated an object of the AL class, and could just keep adding to it as programming "needs be".

All round, the AL seemed very flexible and just worked for me.

However, now that .NET 4.0 is with us, and WPF and the new GUI controls, all being able to bind to 'Collections', things

have changed just a little bit.

*** Note *** below are some of the namespaces required for this 'stuff' :-

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

Give up AL for good :

From much research into using data binding with WPF, and the necessary 'collections' which go along with the new GUI

approach, it would seem that using 'List<T>' (the generic list) is MUCH better than using the AL.

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

Use the generic list - List<T> instead :

This does seem a bit 'odd' or 'strange', since in many ways they appear to be similar at a user level.

But at a deep level, for use with data binding and WPF forms collections, we are best to forget about using AL and move

over totally to using the generic list, List<T> (my shorthand is LT).

This class (LT) still allows ordinal access as an array would, but all items in the list are strongly typed.

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

Study line 404 in the above image to see 'usual / standard' array syntax with 'ordinal access'.

Making a Collection from List<T> :

When doing data binding in WPF we often need a collection of our own making.

The 'ObservableCollection' (OC) class is our friend, as it is easy to use and very flexible, as well as having all the functionality that

we seem to require. Basically, it just works for us !

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

Now how does this concern AL and List<T> ? you may say.

There is a Constructor overload which allows us developers to create a new OC at time of instantiation, directly from a generic list.

Instead of making an OC directly as in the above image, we can do as below and use the generic list :-

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

However, there is no similar overload for AL.

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

I also have a feeling that object Serialisation will be easier with LT than AL, but I have yet to fully test this. If you

remember, in the section on serialization we had to write extra code to take care of any AL in the class to be

serialized.

PHH - 8th. November 2011.


Collections for WPF data binding

Forget all other collection types, when you need to do 'Data Binding' in WPF, other than that called

'ObservableCollection<T>' (OC). This is a dynamic data collection.

This class is easy to use and seems to provide the developer with all the functionality we require for

GUI data binding. We can add and remove items within a GUI data grid, and all changes seem to be

handled such that other GUI elements are notified of the changes.

An instance of the OC class can be made directly from a generic list - List<T> - or by making an empty

collection (strongly typed) and adding items to it.

PHH - 8th. November 2011.


Section F : STATIC classes

I used to think that STATIC methods and properties were something complex and confusing. As a consequence

I used those in the system when I had to, and left things at that for quite a while.

However, after using a few common and standard examples of both of these in my general coding, over time,

I started to change my mind. I could see how easy they were to use, and how useful some of them were to me.

Very recently I decided it was time for me to explore the depths of the STATIC class in general, and now

I consider the whole STATIC 'thing' a great aid to the developer's toolkit, or armoury. We can quite often use

a STATIC approach to improve or code, and general solution style.

In this section on STATIC 'everything', I will demonstrate what is going on when we use the individual parts of

a static class.

First of all however, let us look at some System static methods and properties we may well have met by now.

A Good Start :

Basically, it is a good idea to think of STATIC methods as Functions. The functions I used frequently in VO

can be made into STATIC methods. These can be used directly in our coding.

In fact some STATIC properties may also be thought of as functions in the old world, but also we could think

of them as GLOBAL variables. It all depends where you declare your class containing the STATIC methods.

When we use the System static methods and properties, these are Global, it is our own that we need to

carefully scope, depending on where we wish them to be used.

Of course when we say that system static classes are global - that is provided that we code the namespace

correctly at the head of the appropriate PRG files, and that the system assembly is included in the References

section of the Solution Explorer pane.

A Big Difference :

Quite simply, another major difference when using Static methods is that we do not need to instantiate.

Well, actually it goes further than that - we must not instantiate, as this will cause an error at compile time.

[ *** Note *** so the consequence is we can't use SELF within the code syntax for STATIC stuff. ]

Study the following image :-

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

And just to confirm the point, here is another good example using the System.Math class.

Study the image below :-

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

Returning to the File class.

We will see in the next subsection many details about the File class in the 'System.IO' namespace.

For now it is enough to realise that it is a static class and so can't be used in instantiation. So to use

the static method 'File.Exists()' we must just use it 'as is' - no instantiation, allowed or required.

Similarly, when we have a class which may be used with instantiation, but within it there exists a method

which is static, we can't instantiate either, see below for an example :-

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

We can't use the 'Now()' method with an instance of the structure 'DateTime'. The above image

shows what happens.

We can improve this as shown below :-

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

As you can see from the output messages, the above syntax is acceptable to the compiler.

Both the instantiated oDT1 variable, and the oDT2 assigned variable obey the syntax rules.

Notice that when we use code for static classes, methods and properties we use a point ' . ' and not a colon

as in the rest of dotNet Vulcan syntax. Take care with this until you become familiar. It does get quite easy

after a short while.

My Tip - Points or Colons :

Let us now discuss another 'thorny' issue which hits any guy coming to dotNet and Vulcan - not just those

moving from Visual Objects.

When do we use a colon between our Namespace/Class/Method, and when do we use a point ?

My own rule of thumb is that if I am using something already existing in a namespace / assembly and I am

NOT instantiating, then I use a point - just as I would in C# for example.

However, if I am making a instance in my VN2 code, and then using this object, the colon is the thing to use.

Following this rule of thumb of mine, if I use either a STATIC class from the system or one of my own making,

then it is a point which must be used - below are two examples of this :-

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

The class 'SQL_my_funcs' is one of my own making - we will see a lot of this later in this chapter.

In the code above I am using both a static method and a static property - lines 143 and 150.

In both cases I use the 'point' even though these are my 'stuff'. The colon is NOT used because I

have not instantiated. I like to think that once instantiation has taken place then the 'thing' belongs

to Vulcan.net and as such needs it colon syntax.

The above explanation may not be technically based, but it does serve me well while coding in VN2.

Let us move on to the next section and we can deal with some of the above issues in more detail as

things unfurl, and as we start to understand more.

In the next subsection we will look at some system STATICs in action. In the subsection after that we

will start to make our own.

See you in the next section .......

PHH - 20th August 2011.


STATIC methods, properties and fields

In this subsection we will start to look at STATIC classes, methods, and properties which are available

to us in the system namespaces.

Let us start with a simple, but commonly used method, that called 'File.Exists'. This is a method

called 'Exists()' in the class called 'File' which is in the 'System.IO' namespace.

Study below the relevant line (411) and the syntax used :-

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

Notice that we use the 'point' and not the colon, and that no instantiation takes place. The method is just

used directly.

If we look at the next image we see that the intellisense provides quite a lot of information for us :-

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

The context message says that the static method will return a Boolean, or logic.

Notice also that we can see a lot of other useful stuff for file management, like Copy and Delete.

Let us stand back a few paces, and see what the SDK docs have to say about the Namespace and the Class itself :-

[ note the File entry is the last one, right at the bottom ... ]

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

Having clicked on the File hot link we get more detailed information on the methods - and other things too :-

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

*** NOTE *** The red 'S' symbol / icon indicates that the method is a STATIC one.

This is commonly used across all the SDK docs.

Here are the rest of these :-

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

*** Note *** All the methods in the File class are STATIC methods. There is nothing else to this class.

[ Just 37 static methods to aid our development and coding - no properties, no constructor, or anything else. ]

Moving on to another commonly used coding area, that of DateTime.

'DateTime' is a structure, but it has a Constructor. Since we as coders can use the Class itself it is not a STATIC class.

However, do realise that static classes can have Constructors which are used internally by the static class itself.

Although the 'DateTime' structure can be instantiated, it does have some methods and properties which are STATIC.

Below is shown one of 11 overloads of the Constructor - we are instantiating with Year, Month and Day :-

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

Here is a VERY commonly used STATIC property :-

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

*** Note *** I think this is an error on part of Microsoft, It is not a Class but a Structure !?

So the above is what we do if we want a 'DateTime' (DT) of this instance in time, both the date and the time parts

of the DT.

In the image shown below there is also shown a 'Today' static property which returns zeros for hour, minute, second etc.

after the date values of Year, Month, Day :-

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

You may have noticed that there are two STATIC fields which return maximum and minimum values.

I think the difference in Fields and Properties is that the value of fields is fixed and does not change during

the lifetime of the app, but Now and Today are variable and do change.

If this rule of thumb of mine is true then we would expect the values of Pi and 'e' to be fields also.

Have a look at what we see in the Math class to confirm our ideas :-

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

Pi and 'e' are fixed values, constants of mathematics.

Back to the 'DateTime' structure :

Notice in the image shown below that we have some STATIC methods too for the 'DateTime' structure :-

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

If we look at the above image, we can see that there are a couple of methods which may well be useful to most guys, that of 'Compare',

and that of 'IsLeapYear', as well as 'Parse' too.

As well as STATIC methods and properties, it is possible to have static Fields and Operators as shown below :-

[ Remember that this DT structure is not a STATIC one, and can be instantiated. ]

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

We have already made some basic observations about Fields, so let us now concentrate on the Operators.

Quite simply, the eight defined operators above mean that we can use symbols like +, -, >, <, >=, <=, <>, and = as

long at the types on either side of the operators are correct.

So for example, for + and - we must involve a DateTime and a 'TimeSpan', and the result has to be another DateTime.

So if we look at the SDK sample C# code we see :-

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

And in Vulcan syntax we have the following :-

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

WARNING - Take Care !!! :

On line 168, if we neglect to add the 'as TimeSpan' then the Vulcan compiler seems to make it a USUAL.

This does not fulfill the Operator requirements and so we see the following at runtime, as the syntax is NOT syntactically

incorrect :-

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

In my tests I also noticed that the Try/Catch did not trap this, presumably because it is happening outside of the dotNet

'sphere' of the underlying system.

Message to me is clear, keep away from USUALs and strongly type everything.

Further Care :

When we use the operators + and - we normally expect the order of the values to NOT matter. However this

is not true for how they are defined for 'DateTime'.

Checkout the following image, of what happens when we reverse our above order :-

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

It would appear that for the operators to apply they need to follow a DT value, for which they are defined.

The other values involved depend on the type of operator.

The other seven operators are pretty straightforward, as long as you get the correct data types in your statements,

and also in the correct logical order.

Summary :

As developer / coders, we use STATIC methods and properties as we would functions in other languages, and as

most of us did in VO.

We can use static methods, properties and fields from both System defined classes and structures, as well as those

of our own making.

A non-static class can have static methods and properties, and may be instantiated too.

Static classes and structures may have Constructors - which are used internally.

Where Now ? :

It would seem that we are in a position to try and make our own first examples of STATIC 'stuff', and to get a

feel for what can be done to make our coding life more easy, and possibly better structured too.

Eventually, we will see how to make class libraries of our STATIC classes, so that they may be used in many

of our different solutions - 'off the peg' so to speak.

See you in the next subsection where we get our hands dirty .................

PHH - 21st. August 2011.


Defining our own STATIC classes

In this subsection we will create our own STATIC class, Methods, Properties, and a few other bits

as well.

We will get a good 'feel' for what is going on.

Possible Problem ;-))

Just a slight warning ! Like all things we try to demonstrate in a simple fashion, the quality and relevance of

the example is often 'suspect' and a bit meaningless.

Our first demo example here is probably such a case. Unfortunately, if we make it more relevant it becomes too

complex to early.

So if we wish to have a clear sample to study, we need to be aware that it is a bit 'Micky Mouse' as they say ;-0))

Where to Start ? :

Instead of introducing to a standard class, parts which are STATIC, and building up to a complete STATIC class,

I think it makes more sense to start with a full STATIC class, and work the other way.

To my mind we see quicker and easier what causes what, when we do a full class first.

Let us call our class 'LogDetails'.

In order to do our design, creation and testing we need a simple solution and project - as seen below :-

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

To our newly made WPF app I have added a new folder and beneath that a file (PRG) to take the code for

our new STATIC class.

Study the following image :-

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

Well, obviously we need to make a few changes to this 'stub' code from the VS integration.

So after some thought and typing I have made a start to our little example, and here is how it runs - see below :-

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

In the above image take special note of line 30, as this is the instruction which accesses the newly made class.

I will now show you the code of what we have, and which produced the above result.

Study the following code very thoroughly, as a few new things will be found in the syntax :-

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

The above class was placed in the "VODCeNotesDemos" namespace.

Before we move on, let us explore the possibilities of removing the class name from before the method name

of any static method used throughout the other static methods and properties, and the constructor itself.

Take a look at the following image :-

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

Although we have to use the class name in code outside the STATIC class, if we have the class defined as STATIC we

can drop the need to do so in code within the class methods and properties.

This topic will have to be revisited by us when we come to having static methods in an 'open' or non-static class. One

that may be instantiated. For now lets move on to examine the role of the Constructor.

What Part does the Constructor Play ? :

Let us look again at the calling code in the Initialize method of our new WPF form applet.

Study this image below :-

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

As we said previously, line 30 is the only place from which the static class is referred to.

If we have not done any instantiation, then how does the Constructor of the STATIC class get involved ?

It would appear that the answer is quite simple - for a STATIC class the first time any element of it is accessed by

code, the constructor is called, and any values setup within it are therefore 'set' for future use.

Yes, that means all static fields keep their properties 'as is' for the lifetime of the application, or until they are changed

by code accessing related properties and/or methods.

*** Note *** I think it 'taken as read' that the constructor has only one overload, and that takes no parameters !!

Thus it may be called by the system whenever it needs to do so.

Persistence of Values within the Class :

Let us explore this persistence by doing another simple test - see details in the image below :-

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

And now exploring using the VAT increasing method from outside of the STATIC class itself we see that the

value of the 'nVAT' field must have persisted from one use of the class to another. See the following image :-

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

So at this point I hear a number of you shouting - "Global Variables" - and this is true, we do have a tidy

way to afford ourselves the possible luxury of variables which are available across the application, or at

least across a number of WPF form classes.

We can see a good example of this later in the subsection on 'SQL_my_funcs' where I wish to have methods

and properties (and fields) available for ADO.NET use with my calls to the SQL database. Using a static

approach to this makes my life as a developer a LOT easier ;-) And I like that.

Existing Issues :

At the moment I have an issue which may be put to rights when I consult colleagues. At the moment even Alwyn's

book does not give me the answers :((

I see in the DateTime structure that there are two fields which are both read-only, and I think there are a couple more

in the Math class.

I do not know how to make my Fields publicly available (outside of the class) and yet not allow them to be changed

from outside.

I have tried the modifier term 'PROTECTED' and this is not what we require - see below :-

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

And the code used for this :-

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

"Watch this space !"

I will now chew Alwyn's ear ;-0))

Answers and Reasons :

It was there in 'the Book' after all, and Alwyn kindly pointed me to it.

In the following image we have public access to a fixed value field, and one that can't be overwritten

from outside of the class by any other - user or developer.

Study what we get at runtime :-

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

In the image above the code is using a field called 'RootTwo'.

Let us now view the code to see how I set up the class and the value for the 'Read Only' field :-

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

Okay then, two steps forward and one step back ;-)

Firstly, we seem to have a fixed value field (here defined as CONST) which returns the correct value from outside of

the class itself.

If we do a test to see if we can change this value we get something like the following :-

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

This above output would suggest that we have done the right thing, and that we have a Read Only field.

However, the backwards step is that we have to have a value which exists at compile time. See the image

shown below for an example of the problem / issue.

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

And at compile time the STATIC dotNet class cant' be used to provide the fixed value.

Unless we can find some way around this then we will be limited in at least the flexibility of such a way of providing

a Read Only fields - STATIC or otherwise.

Once again I will take advice. I don't think "the Book" can help ou this time as I have read all the related pages.

Keep watching this space.

STATIC Locals :

This actually may have more relevance to the next subsection, where we study a class which is not static but

which has some static methods and/or properties.

Make sure you visit the next section to see how field data may be persisted from one instance of a non-static class to

another, since we can already do this through STATIC fields in our STATIC classes, the relevance seems to be more

with non-static classes, and non-static methods.

A more Complex Example of a STATIC class :

Before we leave this section let me say that soon in this section we will see a full and 'proper' example of a STATIC class.

The class will be completely our own making, and will do a really useful job. It will allow us to use ADO.NET to access

our data in SQL tables, in an easy to code manner. We can then make sure this class is available for use anywhere in

our application.

It took me some years to realise that a Static Class was the way to go, but now I am there, it does feel right ;-0))

Probably time to move on to the next subsection, so see you there .........

PHH - 21st. August 2011.


Classes with mixed static/non-static members

Remember from discussions in earlier subsections, there are a few System classes which are of

a 'mixed' type. By a 'mixed' type I mean that the class itself is non-static, and that it has a few static

methods as well as non-static ones, and the same probably goes for the mix of properties, and possibly

fields too.

It is really the methods and properties which we will usually refer to when we talk of 'mixed' type.

Once again the example we will create and test is not a very clever, or useful one, but we can keep it

simple and short in lines of code, so that we can see clearly what is going on.

Before we look at the class and all of the supporting code, let us look at the result at runtime :-

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

So we see above we can use the un-instantiated form of the syntax, and all works OK.

After seeing the code, we can try to instantiate as well.

First study the code as see below :-

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

OK then guys, time for the big test - instantiation - see below for the image of the results :-

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

Well, the intellisense looks promising, so what do we get when we complete the code and run the test app ?

Study the full details carefully in the image below :-

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

Yes, we seem to be able to use both modes - instantiation and direct STATIC use.

You may notice when studying the class code VERY carefully, that I have tried to mix all the various ways of

defining methods and properties, as well as fields.

It is impossible to get a meaningful example, so you will just have to live with the best 'silly' idea I could come up with ;-)

You will see some use of the 'self' qualifier in the code, as well as the static syntax of 'Class.Member' too.

I have to say that the mixed form class is by far the most difficult to follow and even create. We are mixing two separate

approaches and although they work when coded correctly, they can be hard to understand for new guys coming to

STATIC 'stuff' for the first time.

STATIC locals :

Reading AJB's book I see that there is yet another type of variable which persists for the lifetime of the

application, and this is a local qualified / modified with a 'STATIC'.

The following tests will show that if within any method we use such a 'static local' the value of this will remain for the lifetime

of the app itself. This is even for non-static classes with non-static methods.

In the sample of code shown in the image below, we instantiate three MixedDetails objects, one after another. However,

the value of the count seems to be remembered from previous instantiations as the value keeps on increasing at each

and every new instantiation.

Study the code and other details very carefully :-

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

[ The above image was generated with a bit of screen capture 'overlay' but is quite genuine otherwise! ]

Now then, what does the code look like which causes this behaviour ?

Notice in the following few lines of code, that if no 'record' of a previous value was stored and remembered, then

each time we instantiate a new object of the class, we ought to get a ' 1 ' as the output :-

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

Access to Members (Properties, Methods, etc..) :

As I have stated previously, the mixing of STATIC and NON-STATIC features in one class may be very useful,

BUT, it is more difficult to understand initially, certainly for new guys.

Let us get straight one aspect first, and that is how to code our solutions so that we use both types from one of

these 'mixed' classes - either system or our own.

Any static member is accessed without instantiation and by using the "Namespace.Class.Member" notation as

shown below :-

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

In the lines numbered 58, 61, 64, 68/69 we are using the syntax of Class.Member and are dropping the namespace

as we are within that namespace, so it is superfluous.

The above example shows all three types of access to static members, Method, Property and Field.

However, we do have a slight complication which is shown in lines 68 & 69. Here we have two acceptable ways of

finding the character value of the data item - new guys may finf that on line 69 hardest to get to grips with.

I like the braces around the field data item, as I know this is an object and also one made in our Vulcan code - therefore

it may be used by us and the colon used for any further stuff we require.

Note that trying to use the 'point' notation for the 'ToString()' does not work, as shown below :-

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

I suggest you don't worry about this, but make a few small samples and test them for yourself. It will

all start to get a bit easier then, and you will soon get the hang of it - this is what happened to me !

Non-Static Access :

This is simpler, as it is really just the same as all over classes which can be instantiated.

Any confusion may arise when we mix the 'modes' of use - however, don't let the fact that instantiation has occurred

affect your judgement. Even if you have an instantiated object, to get to the STATIC members of that class you

keep to the 'Namespace.Class.Member' notation as seen in the working example in the image below :-

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

In the above code we have an instantiated object 'oMD', of class 'MixedDetails', but this does not change the way

we have to code line 42. This would work even if no 'oMD' existed at all.

A system example of this type of mixed mode class/structure is 'DateTime'. We should be familiar by now with

the very useful static property of 'DateTime.Now', and yet we instantiate other DateTime objects quite happily elsewhere

in our own code.

Just remember that we are effectively dealing with Functions (of old) and as such a function related to the class in focus

is simply placed there for our convenience. Obviously a 'function' to give us the date of 'Today' needs to be with other

'DateTime' stuff.

On the other hand, the System.Math class is completely just a bunch of single and separate 'functions', and nothing else.

Actually, after a short while, all of this 'stuff' on STATIC 'things' becomes quite straightforward and also VERY useful to us

developers and coder guys.

*** Note *** I still need to find out if we can make a STATIC filed which is read only - like 'PI' in Math, and 'MinValue' in DateTime.

>>> correction to above, see the relevant part of the previous subsection - now filled out with an example <<<

PHH - 21st. August 2011.


Globally available data objects

my personal comments - lazy way to handle objects and scoping

when don't know how to pass one form to a child - whole object etc.

old programming style - declare at start all variables to be used - or not - as the case maybe.

however, we do have need for some widely available data - stuff to do with user, company and logons

as well as maybe printing, and certainly data source access.

Global use of anything needs to be well thought out and carefully used - simple rule ? Keep to a minimum

Often in the past programmers grab for a global when they were in trouble, and to make a quick fix to scoping problems.

This is NOT the way to use STATIC elements / members.

selecting a printer ?


Example 1, the 'SQL_my_funcs' class

Background to making the Class :

What I am about to say is all to do with my personal experience of coming to SQL for the first time, and at that

same moment starting to use ADO.NET in my Vulcan dotNet code.

I had not used any other way to access data in an SQL database (DB). So my experience of querying the DB was

solely based on my experiences with the Management Studio 'shell'.

The early advice I was given by colleagues was to conform to the 'disconnected model' or whatever the phrase is,

meaning that when existing data was required, or storage of new data needed, a connection to the server be made, and when

the transaction was complete the connection be removed, broken, or we say we disconnect from the server.

So I started to write methods in my business solutions which looked like the following.

Study the code carefully, and try and find the 'parts' to the code 'chunk' :-

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

Yes, the code in the above method is a bit longwinded, even verbose, but it does do the job quite nicely.

Basically we have part one which tries to connect to the server with the 'set' connection string, and also create

a command object.

The second 'part' or section sets the query script, and the third sends this to the SQL Server with the method of

the command class entitled 'ExecuteReader'.

Obviously the return dataset is associated with the waiting DataReader object.

The collapsed section simply takes the first row in the returned set and builds a Customer object from it. I will show

this section expanded in the image below :-

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

Finally, after the above code, we then close the connection to the SQL server.

This may be a bit of an eyeful to take in if you are not used to SQL stuff. And I agree with the sentiment

whole heartedly.

This is one of the reasons I was motivated to try and come up with a better solution, and one which was not complicated,

or difficult to use in my new apps.

Let us now look at possibly better way to code a method in which I am trying to query the server and get an returned

data set.

Study the following code thoroughly :-

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

This time, as illustrated in the above image, we form a query and then give it to a STATIC method to be sent to the

server. From this method is returned a DataSet, either filled with data or 'empty'.

So the process of forming a query and then processing the results from that, are covered in just a few lines, and the

number of the lines will depend on the complexity of the query.

This seems to be an improvement to me, and so we need to study the STATIC method which was used to do the work.

In the 'chunk' of code below we see that the opening of a connection (and its close) are handled along with the attempted

querying of the database.

I tend to think of this stuff as the 'messy' but necessary side of using SQL. Study the code used below :-

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

By removing all the 'dirty' and necessary bits from the more business logic type of code, it is easier to see what it is

the we the developer is trying to do.

Hopefully, you will see a lot more of this sort of improvement in the later part of this subsection.

Where are we headed ? :

Well, ideally, I would like to make a ClassLibrary assembly ( a DLL to you and me).

This library I would like to be able to add to my Reference section in the SE (solution explorer).

Then with the suitable '#USING' at the head of my PRG's I will be able to use my STATIC methods, Properties, and

any Fields too.

With a fixed Class in my library, I can then get very familiar with using the members in the same way each time I code a

new solution.

Thinking Further ahead, I would like to make a separate STATIC class which is specific to the business objects I use with

the SQL tables - so for example 'Customer' will have a method or two to access a row, or fuller dataset, as well as writing

a new row, and updating a targeted row.

The plan would be to have a short set of members (methods and properties) for each table to business object relationship

or association.

So we may expect to see something like the following :-

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

Unfortunately, at the moment I have this mixed in with the 'SQL_my_funcs' class, so I need to change that ASAP.

More on the new Class later. Since it is related to a whole business solution, the Class ought to be named after the app

itself. So the above methods ought to appear in a Class called 'SmallTrader', since this is what has the Invoice, Customer,

Configuration, and Service Job objects. These are custom made business objects.

At the moment the 'Job' pair (or small set) of methods has not been made. You will see more of all this later, for now, lets

move on to our next sub-topic.

Size of the Class :

At the moment, the 'SQL_my_funcs' class has 22 methods, 7 properties, and 16 fields. However since this is very much

a 'work in progress' class, I expect these numbers to increase.

The size of the class, although not huge by any stretch of the imagination, does make it difficult to present it in these eNotes

and for readers to easily focus on what is being discussed. This is just due to the complexity of dotNet and we will have to both

live with it as well as do our best ;-)

Before we move on, let us see a collapsed section of the class code - that from middle to the end. check this out :-

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

Let us now move on to see more on what the class is and how to use it.

About the Class and its Use :

Lets do a quick summary of where we have got to - we need a STATIC class which can be used from anywhere in our

applications, and which is easy to use, as well as being VERY like using SQL scripting with management Studio.

The ADO.NET stuff will be hidden from view after being thoroughly tested.

The resulting class members will then allow us to right the business logic methods in a more succinct and easy to

read manner.

The consequence of this set of requirements is that we must ensure we cover all the possible ADO.NET 'stuff'.

We must not create a class that just solves those issues and problems for the 'Small Trader' app.

Okay then, let us first tackle the issue of a 'config' file on disk, which when read will provide us with the fixed

details for the MS SQL server and the connection, as well as the name of the database too.

My usual style for this situation is to have a 'config' file residing in the same directory (or folder) that the app's

EXE file is in.

Below is a real example of this :-

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

And we can open the file in Notepad to see the contents - the data which makes connection possible.

Look out for the necessary details - see below :-

In the image shown above the XML tags help to indicate just what is what.

So the connection string itself is :-

Data Source=BEST-V64QUAD\SQL_DEV_08_R2;AttachDbFilename=C:\Users\BEST\SmallTrader.mdf;;Integrated Security=True;Connect Timeout=0;

The server is :-

BEST-V64QUAD\SQL_DEV_08_R2

and the database is named :-

C:\Users\BEST\SmallTrader.mdf

We now need to see how the relevant method in our STATIC class reads this file and sets the class static fields and properties.

Here is the code for reading the config file - study carefully :-

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

What is happening in the above code ?

It is quite simply really, even if you are unfamiliar with serializing objects to disk / file, you can at least appreciate what

is being done.

The folder (or directory) is found which holds the application file, and since I always place the config file there, we try to

see if it is available.

All being well (usual behaviour) the file is found and the contents read in by a 'reader' then deserialized and cast into a

'ConnectionDetails' object. This is an object of a class I made way back to go along with an interactive 'Connection' screen

which I first made with WinForms.

To get your head into this, lets look together at the coding of the class - study carefully the following image :-

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

For those that are not yet aware, serializing an existing object in memory, to a file on disk, is very easy and takes few lines

of code. Paul Piko first pushed me in this direction years back - thanks again Paul, yet another good tip from down under.

Since I like working with objects, all the time in my modern way of coding (I was not always a convert to "objects everywhere"),

doing things this way has a lot to offer. Not least being able to bind the object to a WPF 'connection' form if I so choose.

When designing and making a class which is serializable we need to make sure that it as a Constructor which takes no

parameters - and 'empty' constructor in simple mans terms.

I will put more on Serializable Classes in the relevant section of these eNotes. See "Section H", Where you also see the

In order to help new guys to Serialization, and using 'config' files on disk, it seems sensible to show the basis of a new STATIC

method I will develop and test more thoroughly, but as it is it serves well to demonstrate the process involved.

See below the image of the code to serialize a 'ConnectioDetails' object to disk file :-

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

Now that we have a deeper interest in static classes, we may like to notice that in the above code we have

used two 'system' static methods - these are in line 442 and 445.

Also notice that my old code, which is being re-used from the "BEST company" app, has some lines commented

(457 & 458) because we have been advised to let the garbage collector for the CLR have its head, and NOT to mess

with things for tidying up memory. So lines like this need to be a 'thing of the past'.

Setting up Our STATIC Class :

In passing, you may notice that the value of the application directory could be made a class variable, and its value

determined once, in the Constructor so that the first time any member of the class is accessed it will be set for

future use.

Remember that my new class 'SQL_my_funcs' is still in the making, and I am improving it as I write these notes, so

why don't we take this opportunity to improve things ? Let us change the Constructor to be as shown below :-

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

The above code changes mean that as soon as one member of the STATIC class is accessed, the Constructor will

be run that time only (as it usually does) and then the fields with directories and file names will be populated, ready

for use by the other members.

Let us now see how the two methods have been cleaned up and tidied by these changes - see images below :-

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

In the newly cleaned code above you may also notice that I have changed the old code from VIDE days (years ago)

so that the upper case in command words is now all lower case. Quite often I leave stuff just as it is so I know

which lines of code are old and been reused, and which are new - a history trail if you like ;-0))

And now the Object to Disk Method :-

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

I think you will agree that it is cleaner and more tidy !

The only other thing to notice and discuss is the 'MyMessage' STATIC member. This is shown

below and allows the messages shown in this fashion to be toggled on or off. This applies to the whole lot

in the application done like this. It is VERY helpful when developing and testing - here it is :-

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

Taking this Class Further :

Now that we have seen the basic idea of our real and larger STATIC class, I think we need to split the rest of

the eNotes and explanation into subsections, so things are easier to manage for both reader and writer ;-)

I will try and use section heading which reflect the function of the STATIC members.

See you in the first of these subsections, I will call it "Connecting" ...........

PHH - 22nd. August 2011.


Full working code

//----- SQL_my_funcs.prg -----

//----- created August 2nd. 2011 for VODC in November -----

//----- for "Small Trader" app. - by PHH -----

#using System

#USING System.IO

#using System.Data

#using System.Xml.Serialization

#using System.Data.Common

#using System.Collections.Generic

#using System.Collections.ObjectModel

#using System.Collections

#using System.Windows

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

BEGIN NAMESPACE SmallTrader

STATIC CLASS SQL_my_funcs

#region {.. class variables ..}

static public lConnectionTestedOK as Boolean

static private cFileXML as string

static private cAppDir as string

static private cWork as string

#endregion

#region {.. other variables of the class ..}

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

static public oConnDetails AS ConnectionDetails

static private oDFactory AS DbProviderFactory

static private oDConn AS DbConnection

static private cStrConn as string

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

#endregion

#region {.. more class variables ..}

static private cSource as string

static private cAttach as string

static private cSecurity as string

static private cTime as string

static private cInst as string

static private cDBfilePath as string

static private lConnected as Boolean

static private lShowMessages as Boolean

#endregion

#region {.. and more ..}

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

static private nErrorCode as int

static private cErrorMessage as string

static private cErrorLanguage as string

#endregion

STATIC CONSTRUCTOR()

nErrorCode := 0

cErrorMessage := "No Error"

cErrorLanguage := "1033"

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

lConnected := false

lShowMessages := false

//lShowMessages := true

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

cAppDir := System.IO.Directory.GetCurrentDirectory() +"\"

cWork := System.IO.Directory.GetCurrentDirectory()

cFileXML := cAppDir +"ConnectionDetails.xml"

SQL_my_funcs.ReadSerialXmlFile()

RETURN

//========================= [ Backup Database ] ===========================

static method DoBasicBackup() as Boolean

local lResult := true as Boolean

local oF as DirectoryInfo

oF := Directory.CreateDirectory("C:\BackupSQL")

local cName2Use as string

cName2Use := oF:FullName+"\StaticBasic.bak"

MessageBox.Show(cName2Use,"Will be Used :")

local cScript as string

cScript := "BACKUP DATABASE [" + SQL_my_funcs.DBfilePath + "] "

cScript += "TO DISK = '"+cName2Use+"' "

cScript += "WITH FORMAT, "

cScript += "NAME = 'Full Backup of SmallTrader' "

MessageBox.Show(cScript, "Back-Up :")

try

SQL_my_funcs.KeepOpenSendNonQueryScript(cScript)

MessageBox.Show("Query has been sent .... ","After BU :")

catch oEx as SystemException

MessageBox.Show(oEx:Message, "ERROR :")

end try

local nError as int

nError := SQL_my_funcs.GetErrorCodeFromServer()

if nError = 0

MessageBox.Show("No apparent error","Error ?")

else

MessageBox.Show("Error number :- "+nError:ToString(),"Error ?")

endif

//MessageBox.Show("Just before force disconnect ....","TEST :")

SQL_my_funcs.DoForceDisconnect()

RETURN lResult

//========================= [ End Backup] ===========================

//========================= [ Error Object ] ==============================

static method SaveErrorToSQLtable(oE as MyError) as void

SQL_my_funcs.ChangeErrorRecord(oE, RowChangeModeSQL.Insert)

return

static method Error_FillTypedList(oE as MyError) as List<SQLfieldSpec>

local aSFS as List<SQLfieldSpec>

aSFS := List<SQLfieldSpec>{}

aSFS:Add(SQLfieldSpec{"[UnRef]", "@ur", DbType.Int32, oE:UnRef})

aSFS:Add(SQLfieldSpec{"[Occurred]", "@occ", DbType.DateTime, oE:Occurred})

aSFS:Add(SQLfieldSpec{"[Method_Name]", "@mn", DbType.String, oE:MethodName})

aSFS:Add(SQLfieldSpec{"[Object_Class]", "@obj", DbType.String, oE:ObjectClass})

aSFS:Add(SQLfieldSpec{"[Namespace]", "@nsp", DbType.String, oE:Namespace})

aSFS:Add(SQLfieldSpec{"[Operation]", "@op", DbType.String, oE:Operation})

aSFS:Add(SQLfieldSpec{"[Main_Message]", "@mm", DbType.String, oE:MainMessage})

aSFS:Add(SQLfieldSpec{"[Inner_Message]", "@im", DbType.String, oE:InnerMessage})

aSFS:Add(SQLfieldSpec{"[SQL_Error_Text]", "@set", DbType.String, oE:SQLerrorText})

aSFS:Add(SQLfieldSpec{"[SQL_Error_Code]", "@sec", DbType.Int32, oE:SQLerrorCode})

return aSFS

static method ChangeErrorRecord(oE as MyError, eMode as RowChangeModeSQL) as Boolean

local lResult := false as Boolean

if eMode = RowChangeModeSQL.Delete

MessageBox.Show("Wrong mode requested >> DELETE !?","Change MODE :")

return lResult

endif

local cTable := "[MyErrors]" as string //----- change for other Tables -----

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

local aSFS as List<SQLfieldSpec>

aSFS := SQL_my_funcs.Error_FillTypedList(oE)

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

//MessageBox.Show("Just about to try the STATIC func!","Testing :")

lResult := SQL_my_funcs.MakeBusinessObjectDetail(aSFS, cTable, eMode)

//MessageBox.Show("Just after MBO - try the STATIC func!"+lResult:ToString(),"Testing :")

return lResult

//========================= [ End Error Object ] ===========================================

//========================= [ Message for Exceptions ] =============================

static method MyInnerMessage(oEX as Exception) as string

local cText := "" as String

if oEX:InnerException <> NULL

cText := oEX:InnerException:Message+CRLF

cText += "Exception Type : "+oEX:InnerException:GetType():Fullname+CRLF

cText += "Source : "+oEX:InnerException:Source+CRLF

cText += oEX:InnerException:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oEX:InnerException:TargetSite:ToString()+CRLF

endif

//MessageBox.Show(cText)

return cText

static method MyMainMessage(oEX as Exception) as string

local cText as String

cText := oEX:Message+CRLF

cText += "Exception Type : "+oEX:GetType():Fullname+CRLF

cText += "Source : "+oEX:Source+CRLF

cText += oEX:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oEX:TargetSite:ToString()+CRLF

//MessageBox.Show(cText)

return cText

//------------------- And Another Overload --------------------

static method MyInnerMessage(oDBX as DbException) as string

local cText := "" as String

if oDBX:InnerException <> NULL

cText := oDBX:InnerException:Message+CRLF

cText += "Exception Type : "+oDBX:InnerException:GetType():Fullname+CRLF

cText += "Source : "+oDBX:InnerException:Source+CRLF

cText += oDBX:InnerException:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oDBX:InnerException:TargetSite:ToString()+CRLF

endif

//MessageBox.Show(cText)

return cText

static method MyMainMessage(oDBX as DbException) as string

local cText as String

cText := oDBX:Message+CRLF

cText += "Exception Type : "+oDBX:GetType():Fullname+CRLF

cText += "Source : "+oDBX:Source+CRLF

cText += oDBX:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oDBX:TargetSite:ToString()+CRLF

//MessageBox.Show(cText)

return cText

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

//========================= [ End Exception Messages ] =============================

static method MakeBusinessObjectDetail(aSFS as List<SQLfieldSpec>, cTable as string, eMode as RowChangeModeSQL) as Boolean

local lResult := false as Boolean

local cText, cHeading as string

if eMode = RowChangeModeSQL.Update

local cFieldSymbolPairs := "" as string

SQL_my_funcs.CreateFieldSymbolPairs(cFieldSymbolPairs,aSFS)

cText := "UPDATE "+cTable+" SET "+cFieldSymbolPairs+" WHERE [UnRef] = @ur ;"

cHeading := "Updating :"

endif

if eMode = RowChangeModeSQL.Insert

local cFields := "" as string

local cSymbols := "" as string

SQL_my_funcs.CreateFieldsSymbols(cFields,cSymbols,aSFS)

cText := "INSERT INTO "+cTable+" ("+cFields+") VALUES ("+cSymbols+");"

cHeading := "Inserting :"

endif

//MessageBox.Show(cText,"Script :")

lResult := SQL_my_funcs.MakeBusinessObjectChange(aSFS, cText, cHeading)

return lResult

static method MakeBusinessObjectChange(aSFS as List<SQLfieldSpec>, cText as string, cHeading as string) as Boolean

local lResult := false as Boolean

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

SQL_my_funcs.DoSetConnectionString()

IF !SQL_my_funcs.DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SQL_my_funcs.DFactory:CreateCommand()

oCommand:CommandText := cText

oCommand:CommandType := CommandType.Text

oCommand:Connection := SQL_my_funcs.DConn

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

SQL_my_funcs.AllParamsSetAdd(oCommand, aSFS)

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

oCommand:ExecuteNonQuery()

lResult := true

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message, cHeading)

END TRY

SQL_my_funcs.DoForceDisconnect()

return lResult

static method CreateFieldSymbolPairs(cFieldSymbolPairs ref string, aSFS as List<SQLfieldSpec>) as void

local oLTE as IEnumerator

oLTE := aSFS:GetEnumerator()

local oSFS as SQLfieldSpec

local i := 0 as int

do while oLTE:MoveNext()

i +=1

if i > 1

cFieldSymbolPairs += ","

endif

oSFS := (SQLfieldSpec)oLTE:Current

cFieldSymbolPairs += oSFS:FieldName+"="+oSFS:@@Symbol

enddo

return

static method CreateFieldsSymbols(cFields ref string, cSymbols ref string, aSFS as List<SQLfieldSpec>) as void

local i := 0 as int

local oLTE as IEnumerator

oLTE := aSFS:GetEnumerator()

local oSFS as SQLfieldSpec

do while oLTE:MoveNext()

i +=1

if i > 1

cFields += ","

cSymbols += ","

endif

oSFS := (SQLfieldSpec)oLTE:Current

cFields += oSFS:FieldName

cSymbols += oSFS:@@Symbol

enddo

return

static method SetAddParam(oCommand AS DbCommand , oDbT as DbType, cSym as string, oV as object) as void

LOCAL oParam AS DbParameter

oParam := SQL_my_funcs.DFactory:CreateParameter()

oParam:ParameterName := cSym

oParam:DbType := oDbT

oParam:Value := oV

oCommand:Parameters:Add(oParam)

return

static method AllParamsSetAdd(oCommand AS DbCommand, aSFS as List<SQLfieldSpec>) as void

local oSFS := SQLfieldSpec{} as SQLfieldSpec

local oLTE := aSFS:GetEnumerator() as IEnumerator

oLTE:Reset()

do while oLTE:MoveNext()

oSFS := (SQLfieldSpec)oLTE:Current

SQL_my_funcs.SetAddParam(oCommand, oSFS:DbT, oSFS:@@Symbol, oSFS:Val)

enddo

return

static method ReadSerialXmlFile() as logic

local serial as XmlSerializer

local oSR as StreamReader

local cText, cHead as string

local lResult as Boolean

lResult := false

lConnectionTestedOK := false

if File.Exists(cFileXML)

cText := "Existing file will be read!"

cHead := "XML file :"

//----- use my STATIC toggled message system -----

MyMessages(cText,cHead)

//----- read object from XML file -----

serial := XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFileXML}

oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

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

lResult := true

lConnectionTestedOK := true

//----- use my STATIC toggled message system -----

MyMessages(oConnDetails:ToString(),cHead)

else

cHead := "XML file :"

cText := "File can't be found !?"+CRLF

cText += "Go to Connection and create a string."+CRLF

MessageBox.Show(cText,cHead)

endif

return lResult

static method SerializationToFile() as void

local cText, cHead as string

if oConnDetails = NULL_object

cText := "NULL object ! - ConnectionDetails ..."

cHead := "My Error :"

//----- use my STATIC toggled message system -----

MyMessages(cText,cHead)

return

endif

local serial as XmlSerializer

local oSW as StreamWriter

if File.Exists(cFileXML)

cText := "File exists already!"

cHead := "XML file :"

MessageBox.Show(cText,cHead)

endif

//----- save object to XML file -----

serial := XmlSerializer{typeof(ConnectionDetails)}

oSW := StreamWriter{cFileXML}

serial:Serialize(oSW, oConnDetails)

oSW:Close()

return

static method SerializeOthersToDisk() as void

/*local cFile as string

cFile := "C:\Temp\customers.xml"

local oSW as StreamWriter

oSW := StreamWriter{cFile}

//----- Save object to XML file -----

local serial as XmlSerializer

local aTypes as Type[]

aTypes := Type[]{1}

aTypes[1] := typeof(Customer)

serial := XmlSerializer{typeof(ArrayList),aTypes}

serial:Serialize(oSW,self:alCustomers)

oSW:Close()*/

return

static method MyMessages(cB as string, cH as string) as void

if ShowMessages

MessageBox.Show(cB,cH)

endif

return

//======================== [ basic ADO.net & settings ] ==============================================

static METHOD DoSetConnectionString() AS VOID

IF String.IsNullOrEmpty(oConnDetails:cProvider)

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

RETURN

ENDIF

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

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

oConnDetails:cUserName := ""

ENDIF

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

oConnDetails:cPassWord := ""

ENDIF

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

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

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

oDConn := oDFactory:CreateConnection()

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

cStrConn := ""

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

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

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

cDBfilePath := oConnDetails:cFilePath //----- for backup ! -----

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

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

cTime := "Connect Timeout="+oConnDetails:nTimeOut:ToString()+";"

//----- Willie said this would leave it always open if ZERO is supplied! -----

cInst := ""

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

oDConn:ConnectionString := cStrConn

oConnDetails:cConnectionString := cStrConn

MyMessages(cStrConn,"My Connecting String :")

RETURN

static METHOD DoTryToConnect() AS LOGIC

LOCAL lResult AS Boolean

lResult := FALSE

lConnected := false

local cText := "" as string

TRY

oDConn:Open()

cText := "Vulcan.NET : Small Trader : << Connected! >>"

MyMessages(cText,"Try to Connect :")

lResult := TRUE

lConnected := true

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

RETURN lResult

static METHOD DoForceDisconnect() AS VOID

LOCAL cText AS STRING

IF ! oDConn = NULL_OBJECT

oDConn:Close()

cText := oDConn:ConnectionString+" - State = "+ oDConn:State:ToString()

MyMessages(cText,"Disconnecting? :")

lConnected := false

else

cText := "Vulcan.NET : Small Trader : << Disconnected! >>"

MyMessages(cText,"Disconnecting? :")

ENDIF

RETURN

//========================= [ query ADO.net members ] ================================================

static method KeepOpenSendScalarQueryScript(cScript as string) as Object

local oResult as object

oResult := object{}

try

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch oEx as SystemException

MessageBox.Show(oEx:message,"Start of Scalar Query !")

end try

//----- getting a scalar quanity returned -----

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oResult := oDComm:ExecuteScalar()

//----- need to cast in calling code section! -----

CATCH DbEx AS DbException

//SaveAndDisplayErrorDetails(DbEx,"Keep, Returned Data Object :")

MessageBox.Show(DbEx:Message,"Keep, Returned Data Object :")

END TRY

return oResult

static method KeepOpenSendNonQueryScript(cScript as string) as Boolean

local lResult as Boolean

lResult := false

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of Non Query !")

end try

//----- getting no data returned -----

oDComm:CommandType := CommandType.Text

TRY

LOCAL cStatement AS STRING

//----- use a terminator ; as recommended by Paul Nielsen -----

cStatement := cScript

oDComm:CommandText := cStatement

oDComm:CommandType := CommandType.Text

//----- execute the supplied statement -----

oDComm:ExecuteNonQuery()

lResult := true

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Keep-Non Query :")

/* local cText := "" as string

cText += "Error Code :- "+DbEx:ErrorCode:ToString()+CRLF

if !DbEx:InnerException = NULL_object

cText += "Inner Exception :- "+DbEx:InnerException:ToString()+CRLF

endif

cText += "Source :- "+DbEx:Source:ToString()+CRLF

cText += "Message :- "+DbEx:Message+CRLF

//cText += "Stack Trace :- "+DbEx:StackTrace+CRLF

MessageBox.Show(cText,"DbEx Details :")*/

//MessageBox.Show(DbEx:Message,"Non Query :")

END TRY

return lResult

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

static method SendNonQueryScript(cScript as string) as Boolean

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

local lResult as Boolean

lResult := false

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of Non Query !")

end try

//----- getting no data returned -----

//oDComm:CommandType := CommandType.Text

TRY

LOCAL cStatement := cScript AS STRING

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cStatement

oDComm:CommandType := CommandType.Text

//----- execute the supplied statement -----

oDComm:ExecuteNonQuery()

nError := 0

lResult := true

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Non Query :")

/* nError := GetErrorCodeFromServer()

nErrorCode := nError

//FindErrorMessage()

cErrorMessage := DbEx:Message

MessageBox.Show(nError:ToString(),"Code ?! :")

MessageBox.Show(DbEx:Message,"Non Query :") */

END TRY

SQL_my_funcs.DoForceDisconnect()

return lResult

static method SendDataSetQueryScript(cScript as string, cTableTitle as string) as DataSet

nErrorCode:= -999

cErrorMessage := ".. reset for use .."

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

local oDS_Result as DataSet

oDS_Result := DataSet{}

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

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

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

CATCH DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of DataSet Query !")

end try

//----- getting a data set returned! -----

oDComm:CommandType := CommandType.Text

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

LOCAL oDA AS DbDataAdapter

oDA := oDFactory:CreateDataAdapter()

oDA:SelectCommand := oDComm

oDS_Result := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS_Result, cTableTitle)

nErrorCode := 0

cErrorMessage := "No error thrown"

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Error DataSet :")

END TRY

SQL_my_funcs.DoForceDisconnect()

return oDS_Result

static method SaveAndDisplayErrorDetails(DbEx AS DbException, cHead as string) as void

cErrorMessage := DbEx:Message //----- CARE with order, must be before next line -----

nErrorCode := GetErrorCodeFromServer()

if !lShowMessages

local cText := "" as string

cText += ErrorMessage+CRLF

cText += ErrorCode:ToString()+CRLF

MessageBox.Show(cText, cHead)

endif

#region {.. test code ..}

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

//----- make oE from properties and fields -----

//as MyError, eMode as RowChangeModeSQL

/*local eMode := RowChangeModeSQL.Insert as RowChangeModeSQL

local oE := MyError{} as MyError*/

//ChangeErrorRecord(oE, eMode)

#endregion

return

static method SendReaderQueryScript(cScript as string) as DbDataReader

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

//----- using the DataReader approach as an alternative -----

//----- to the 'DataTable' one I like particularly -----

local oDr as DbDataReader

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

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

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

CATCH DbEx AS DbException

MessageBox.Show(DbEx:message,"Error DataReader :")

return NULL_OBJECT

end try

//----- getting a data set returned -----

oDComm:CommandType := CommandType.Text

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oDr := oDComm:ExecuteReader()

//----- execute the select statement -----

nError := 0

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Error Datareader :")

/*MessageBox.Show(DbEx:Message,"Error DataReader :")

nError := GetErrorCodeFromServer()

MessageBox.Show(nError:ToString(),"Code ?! :")

nErrorCode := nError*/

//FindErrorMessage()

END TRY

//----- do NOT close the connection - needed Open by DataReader ! -----

return oDr

static method SendScalarQueryScript(cScript as string) as Object

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

local oResult as object

oResult := object{}

try //----- to Open a connection & make a command object -----

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch oEx as SystemException

MessageBox.Show(oEx:message,"Start of Scalar Query !")

end try

//----- now try getting a scalar quanity returned -----

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oResult := oDComm:ExecuteScalar()

//----- need to cast in calling code section! -----

nError := 0

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Returned Data Object :")

END TRY

SQL_my_funcs.DoForceDisconnect()

return oResult

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

static method GetErrorCodeFromServer() as int

local nResult := -999 as int

local cScript as string

cScript := "SELECT @@Error;"

nResult := (int)SQL_my_funcs.KeepOpenSendScalarQueryScript(cScript)

if lShowMessages

if nResult = 0

MessageBox.Show("No apparent error ! ","Error Code :")

else

MessageBox.Show("Returned code is :- "+nResult:ToString(),"Error Code :")

endif

endif

return nResult

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

/*static method FindErrorMessage() as void

local cScript as string

cScript := "SELECT text FROM [master].[sys].[messages] where [message_id] = '"

cScript += ErrorCode:ToString() + "' and [language_id] = '"+cErrorLanguage+"' "

cErrorMessage := (string)SQL_my_funcs.KeepOpenSendScalarQueryScript(cScript)

return*/

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

static access Source() as string

return cSource

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

static access Attach() as string

return cAttach

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

static access StrConn() as string

return cStrConn

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

static access DBfilePath() as string

return cDBfilePath

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

static access DFactory() AS DbProviderFactory

return oDFactory

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

static access DConn AS DbConnection

return oDConn

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

static access ShowMessages() as Boolean

return lShowMessages

static assign ShowMessages(lM as Boolean) as void

lShowMessages := lM

return

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

static access ErrorCode() as int

return nErrorCode

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

static access ErrorMessage() as string

return cErrorMessage

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

END CLASS

END NAMESPACE // SmallTrader

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

PHH - 17th. October 2011.


Connecting

This is probably the most important part of any SQL related work.

If you can't connect to your data server, and the correct database too, then the rest of the 'stuff' is no use at all.

Obviously, if we connect, we also need to disconnect satisfactorily as well.

The Model :

I think I mentioned someplace earlier in these eNotes that the 'model' I have been using to date, in all my SQL solutions,

is the one where a connection is made with the server ONLY when there is a requirement. By this I mean the application

requires some existing data, or indeed it may wish to change existing data, or add new data.

Once the 'transaction' is completed the connection to the server is closed.

*** TIP *** It is always a good idea, at application launch time, to do a test connection, just to see that things are as

they should be ;0)) So I usually try to connect, show a message, and then force a close - i.e. drop the connection, or

disconnect.

Problem with the Disconnected Approach :

There is one possible 'problem' or issue we may face when applying the 'disconnected model', and that is when it comes to

errors taking place within the server itself.

If for any reason the server throws and error, the error number is available to us (from the server) as long as we send the

correct query immediately after getting an error, and ALSO while the current connection is still intact.

We will deal in greater detail with error codes from the server in the relevant subsection, two or three from here. For now we

will continue assuming that between connecting and disconnecting, we send one query to the server, and get either no reply or

a data object which is a dataset or a single value.

Details of the String Required for Connection :

Before we can open a connection to the server, we need to put together a 'string' value which contains all the necessary

details about which server and which database file.

Usually, this information is know only to the PC user, administrator, and developer. It could be hard coded into the application

code but this is not recommended and is a little naive in my opinion.

Since moving an application to a different PC platform will undoubtedly mean that the connection string details will change, it is

more usual to have a 'config' type file of some description or another, and this is likely to exist outside of the application itself.

In the example shown below we have already read the XML configuration file mentioned, and shown, earlier in the previous subsection.

If you recall the static method 'ReadSerialXmlFile()' is used to populate the class object named 'oConnDetails'. At the point when

we call the 'DoSetConnectionString()' method all the connection details are help in the properties of the 'ConnectionDetails' object.

In the following image, study what is being done, and how the details are accessed from the relevant object :-

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

In the above code, please notice that we are just simply getting string data together in the correct order and making it

available for subsequent static method to try to use it for achieving a connection to the server.

Let us now look at the static method to try and gain a connection.

Study the details in the following image, and see how we check to see if we already have an open connection before

we attempt to make a new one :-

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

Notice that the above code does work, and the message box was generated live - from this very code. The static method

is not very long or complex. And the one to close the connection is even simpler.

In the next image we see the code in the method to force a disconnection - or drop the connection.

Unfortunately, in both of these short methods I have not as yet replaced the three lines to produce the MessageBox

with the one line to use the 'MyMessages' member. I will do this now in my source code so as not to forget ;-)

Study the code below :-

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

Once again you are seeing live output from the very code we are viewing.

Note that the name of property of the connection object, which is relevant to these operations, is entitled 'State'.

Okay then guys, before we close this subsection, and move on to the next one, we need to see the original form

written to work interactively with the ConnectioDetails class of object - note this was a WinForms creation and done

with VIDE.

Viewing the above image, it should be obvious which controls serialize to file and which deserialize from file. There is

another which just reads the XML file as text for display in the scroll box.

It is not necessary in all apps to have a way to change and test all the connection details - BUT - it is a great help

when setting up apps initially. Although most users should never go near the 'connection' part of SQL the facility

this form / screen offers is excellent for administrators and developers.

I would recommend having such a form - these days a WPF one - and password the access to this form to keep

away "those that know not what they do".

*** TIP *** The easiest way to find the server name is to use Management Studio (MS), open this as shown in the following

image, and all the possible server instances are shown in a drop list. You can also search for more as well.

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

I usually like to open MS and open the tables list in the Object Explorer pane - see below for what I did after

first selecting the server from the above drop list :-

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

*** TIP *** For reasons I do not understand, I have found in the past that sometimes to get my Vulcan.NET and ADO.NET to

work for the first time (connect etc.) I have to detach the database in focus, close the MS shell, and try again. Don't worry,

it is perfectly possible to have the database attached in MS and also have your app running. I work like this all the time, it is

sometimes the first time that causes a problem.

If you first fail, or have problems, just keep on trying - that is the way to success with SQL 'stuff'.

And finally, before moving on, lets see the WPF version of the Connection form - the one which will be bound to our object.

Don't be fooled by the improved colours ;-0) it does the same basic job of interactively setting the connection details.

As yet this form does not have data binding applied in the usual WPF dotNet 4.0 manner. This copy shown above was

created about 20 months ago and was one of my first few attempts at using WPF for real apps.

I will attempt to improve things by doing them the 'right' way, although this particular screen has not that much to benefit

from data binding - not like many of the other screens in the Small Trader application.

Time to move on, see you in the next section ..........

PHH - 23rd. August 2011.


Returning data

Okay you guys, are you up for this subsection on getting back data from our SQL database ?

In many ways it is quite straightforward, some would even say easy.

One Thing or Many :

When we send a script to the SQL server with the intension of getting back data, we will have in mind

whether we expect to get a single piece of data, or a bunch of stuff.

An example of a 'scalar' quantity (single thing) is for example a date - say the date of the last order of a

specified customer. We can obviously have single or 'scalar' value returns for any data type which is

supported by the SQL table field types. So we can expect Boolean, Strings, Integer and Double, as well

as a few possibly less well used types as well.

If we expect as a result, of our query statement being sent to the server, that we will get a row or a number

of rows from our data table(s), then we are talking about having a DataReader or a DataSet returned.

What form of data we expect returned determines which STATIC method we use to send our query statement.

Let us start with a scalar example.

Getting One (Scalar) :

In the image shown below we are trying to find the unique reference number of the next customer. Checkout

the full details carefully :-

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

*** Note *** I realise that we can have the settings on the Customer table such that the action of supplying a new

customer automatically increments the primary key etc. - but we are using this application as a learning tool and

so we will do some simple things for ourselves for the moment.

Looking at the above code we see that it is quite short, as well as easy to read.

basically, we are forming a string which is the query and then in one line sending this to the server, getting a result,

which we then cast into the type of value we expect. What is returned from the static method is just an object.

So if that was quite easy, then what does the static method itself look like ? Study the following image :-

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

The above static method may be used to get and return any scalar value, of any type. It is very flexible in its use.

Now lets move on to getting a bunch of data.

Getting Many ('DataReader' and 'DataSet') :

Okay then, lets start with using a 'DataReader' to access the data returned by a query statement.

The first thing we need to realise about a data reader is that we need to keep the connection to the server open until we

have done all our reading of the data items. This is not the case with a DataSet, this seems to be returned 'intact', or as a whole.

So we need to remember to close the connection from our calling code - method.

Let us look at the Small Trader application and see the WPF form which finds and displays the customer objects.

In this form I have made the source for the customer drop list an 'ArrayList' - it works OK, but is not the way I would do it today,

more than 20 months down the line.

Study the image following :-

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

As you can see, the customers are certainly fetched back from the SQL table okay.

So now to see what the form method looks like which achieves this result - see below :-

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

In the code in the above image line 736 is the one accessing the STATIC member which uses the statement to return

the DataReader object. remember the data is still at the server until reading is completed, and then, and only then, can

we close the connection.

On line 771 we force a disconnect, as the 'DataReader' is no longer required.

So now that we see how to call our static member, what does the code look like which gets us the reader ?

Study carefully the image shown below :-

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

As you can see from the above code, it is not that different to the Scalar version, and we will find that it is

also not that different to the 'DataSet' version to come.

The one important thing which is different is line 753, where we MUST leave the connection open.

'DataSet' - my Preferred Approach :

[ in order to get a DataSet we use the DataAdaptoer in ADO.NET ]

Let us now look at the third of the alternatives for getting data returned from the SQL server after sending a

query statement, or script.

This time we will do things in such a way that the whole bunch of data will come back to our app as a 'DataSet'.

Once the data is returned we will have our static method close the connection.

Let us start out with a problem to solve - from our 'Small Trader' application. Let us ask for a whole bunch

of job sheets. Study the image shown below :-

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

As you can see in the above code, the task of getting our whole pile of data is quite easy - simple almost.

Okay, we also need to look at our method for creating the query string, which we will do a little later on, but first

we need to do an experiment to prove that the second parameter provided on line 169 is actually the name of the first

table and not that of the DataSet itself - study the following image :-

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

The point at which our Small Trader application applies this code, is when in the following WPF form, and when

the DataGrid needs filling.

At the moment the 'Refresh' button makes sure that the grid is up to date, and is there simply because I can't

get the appropriate event to fire / work on the 'DatePicker' control. I will work on this, it is something non-standard.

Note that it is the controls which complicate the logic of creating the statement string, or script.

We need to take into account which set of records we require, those with invoices or those without.

Study the following code carefully, particularly lines 149 and 158, note the Left Outer Join makes sure we

get all of the Job Sheets, even those with no match in the invoices :-

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

So the above code simply makes a long statement string for either of the selected modes (or groups) and also applies

the restrictions from the start and end date controls too.

Okay then guys, it is time to look into the final part of all this, the STATIC method which actually communicates with

the server.

Look closely at the detail of the following image :-

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

Yes, it does look familiar, as it is not that different from the others we have seen and studied.

The difference here is that we close the connection before returning the 'DataSet' object to the calling method.

Please also be aware that there are other overloads for the Fill method, and so other and slightly different ways of

using Fill to solve our problems.

For the straightforward SQL coding I have done so far, and for standard types of problem I have tried to solve, these

techniques shown above have been perfectly adequate, so I stick with them for now.

Okay then, time to move on and see how we edit data and store new stuff we collect at our WPF form inputs.

See you in the next subsection ......

PHH - 23rd. August 2011.


Making and Changing Table Rows

This subsection is about storing new table row data and also changing existing rows as well.

For those of you who are familiar with SQL scripting, the command words related to these operations

are INSERT and UPDATE.

If we look at the five existing members of the STATIC class we are building - related to sending statements to the server - we

see that we have two which deal with data 'changes'. Note I have yet to deal with DELETE as my usual style of database use

does not involve me deleting records, I usually mark then as inactive so as not to lose the information.

These operations are generally know by the acronym 'CRUD' - for Create, Update and Delete. (These are general terms.)

For much of what I do below in this section I wish to use 'CRU' as I will be just referring to INSERT and UPDATE commands

in the T-SQL script language.

Have a look at the following image to see what we currently have in our STATIC class :-

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

In the above code lines please notice the ones marked 'NonQuery', these are on lines 615 and 651.

The 'Non-Query' refers to the fact that the SQL statement sent to the server is not querying for data to match an enquiry,

but more of a way of sending a command statement to the server, and one which does not expect any return at all.

We will more on this non-query aspect a little later on.

Going Further :

I am afraid to have to tell you that these (CRU) are the operations which are generally VERY verbose or longwinded. if

you as a developer / coder don't have a good and well thought out system it is VERY easy to make errors, and ones which are hard

to find later.

It is for these operations that we need most to construct a good 'SQL_my_Funcs' class.

What is the Problem :

Well, funnily enough, there is not a problem as such. If the table structure is small, meaning it has only a few fields, then there

is not too much of a problem of coding our Vulcan in a similar way to what we may write in the Management Studio editor.

Let us look at the following image, one which shows two simple tables and their structure :-

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

In the above image the tables named 'Goods' and 'Services' are shown fully.

It would be quite easy to do 'CRU' on these without any special classes or members.

In fact looking through my code created early in this project I find the following, where the task has been split into

two parts, even though the tables for Goods and Services are simple - study the following carefully :-

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

By studying the above code you will see that the task seems to be in two parts, the script or statement itself, and

the list of parameters which must be provided - these are 'tag and value' pairs.

Now then, the above code is complex enough for only four (yes 4) fields, but imagine what it gets like when there are many fields.

And I have to admit that most SQL tables can have VERY many fields.

This 'Small Trader' app is just a small demo one really, even though it does a useful task for my friends at the computer repair

store in Newport. And yet if you look at the image shown below you can see just how complex the Invoice table has become :-

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

Yes, plenty of fields, 41 in total I make it, and I am not going to count those in other tables ;-0)

Don't think of tables with many fields as being a result of poor design, it is just quite necessary sometimes (or even quite often)

to have a whole bunch of fields.

So now you see the problem, one of longwinded code on a limited size screen, and parameter definition a long way from the script

line for checking purposes. All a bit of a pain I think.

And so I came up with my alternative approach

PHH - 23rd. August 2011.


How to do this in MS

In this short section we will see how to manually create an INSERT statement which when sent to the SQL server

will add a new row to the table in focus.

Obviously we must observe the mandatory fields, and provide values for these, otherwise we would get an error from

the server. However, all other fields can be supplied or not. This is NOT the way I approach things with my business

object approach, and I suspect that s true for the Entity Framework approach where MS too deals with business objects.

It makes much more sense to update a row, and create a new one, where all the fields of the row are provided, and these

match on a one-to-one basis with the business object properties. In this case the Customer table has exact matching

fields with the Customer class properties.

Let us see how to make a new row in MS - study the image detail carefully :-

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

Notice that I have missed out 3 or 4 of the non-mandatory fields - like Add5, Mobile, and Email.

If we write another query we can see the results of our first statement to do the INSERT.

Checkout the following image :-

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

As you can see we have at least a couple of NULL values, simply because we did not specify a value (or the field) in our

previous INSERT statement.

In the simple way I now do my 'Business Object' (BO) approach this would not be the case, I would set default values to all my

properties of a new BO before using it in the business logic. Changes and edit would simply change the values.

So for the moment in my new way of doing INSERT and UPDATE I deal with all fields and no NULL properties.

Changing rather than Creating :

While we are at it, and I have the software tools in front of me, we ought to try at least one direct query in the MS shell to do

an UPDATE to an existing table row.

Let us try and change the very row we have just made, and maybe provide some values for those fields previously let without

any specified value.

Check this out carefully :-

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

And now we can run the same query as before to see if the row (or record) of customer 131313 has changed - see below :-

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

In the above image you can see that I have collapsed the row width of unchanged fields / columns, so the results are easier to see.

Notice too that with an UPDATE the mandatory fields do NOT play a part - unless for some strange reason you were trying to make

them NULLs.

If the row has been made previously, all mandatory 'rules' or conditions must have been met, so that the server did not throw an

exception. So we simply update, or change, those fields we wish.

Lets move on to the next subsection ..........

PHH - 24th. August 2011.


My Old Way !

I think it is worth showing an example of how I started out saving a business object of my own making

into an SQL table related to this object. Below we will see how I treated an object of the 'Customer' class

in my first real SQL applet called 'BESTcompany'.

Because I was copying the way this was done in the Management Studio 'shell' I have ended up with everything

in one method - not nice ! Below we will see an 'UPDATE' version of my old style coding.

So let us see if I can make this readable in the images shown below :-

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

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

As you can see in the images above we have quite a bunch of code lines, and this is just for EIGHT (yes 8) fields !

To my mind the biggest problem is have parameter code with important parameter names, separated by many lines

from the text string which is to be the statement, or query. The chosen parameter name has to appear in both parts,

be exactly correct, and not be a duplicate. This is therefore difficult to achieve with the code arranged as it is above.

Make sure you see the previous subsection which shows the origin of this 'Old' approach, making a direct query from

the Management Studio (MS) shell.

The extra problem to the MS shell is that we have to 'extract' from, or associate with, the properties of our business object,

in this case a 'Customer' object. In MS we supply a fixed value at query time.

See you in the next subsection ...................

PHH - 24th. August 2011.


Possible Problems - New Way

Possible Issues or Problems :

errors in the text entries for the SQL statement - see below

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

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

This is one side of the coin for using new tools for SQL like the EF (Entity Framework) and others like LINQ, checks are all done at compile time.

It could be possible for a compiled and tested app having such an error like this somewhere, waiting to blow things

out of the water, and everyone thinks things are OK :((

and here is another example when I have made a first attempt at saving Goods (parts) in this new way :-

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

PHH - 23rd. August 2011.


Errors at the Server

Having errors take place within the SQL server after we have sent a query statement from our ADO.NET based

methods in our Vulcan code, is an issue we MUST address, and one which can be handled quite nicely - if we

go about it in the right way.

If we use dotNet code 'as is', and not LINQ and Entity Framework technologies, then the strings we send to the

server for it to process, can have errors within them, which only show up when the server tries to parse and then

execute the statement.

The Vulcan compiler can't possibly detect any error in a string value making up the statement script. This means

we can have code which compiles and runs, which may at some later stage throw an error because of a hitherto

untried string which has been constructed at runtime by the dotNet method code.

For this simple reason we MUST use a system which handles errors in the server, and respond to them in an

appropriate manor.

How to Detect Errors :

Well, this little subsection is just Good News !

If we send a short query statement to the server immediately after any previous statement, we can detect if the

previous statement failed, and if it did what the reason was.

The script requesting this MUST be the one immediately after the statement in focus, AND, has to be for the same

connection. So in layman's terms we must keep the connection open, and make sure that no other query statement

is sent after the one in question, and before the script to enquire of the error situation.

First of all let us see what can be got back from the server after an error - below is what we get in the next subsection

if we neglect to include a database name in the script to request a database backup :-

As you can see it is not as yet VERY informative, except that we know we definitely had an error, as zero ( ' 0 ' ) is

what comes back to us when no error at all has taken place previously.

So from this simple output we know we had an error, and that its code is 1038.

Interpreting the Error Code :

Well, this bit is also easy, as Microsoft have provided a 'View' in the master database for us to use.

Study the following image of the Object Explorer in Management Studio :-

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

In the above image see that the 'System Views' comes within the 'master' under 'System Databases'.

If we look at the following screen shot of what I found on Googling the topic, it suggests that there is a table

which belongs to the system - this maybe true BUT it is the 'view' which is accessible to us, and which we can

use to find the explanation, or error message.

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

So from the above image we know we are looking for 'sysmessages', but instead of a table to use, we find

a 'view' - but with a name of 'sys.messages' instead.

In effect the 'view' is a Read Only form of the table!

The image shown below clearly illustrates that we can't find a table which is suitable :-

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

However, using the correct 'view' - called 'sys.messages' - we can access the messages associated with

the error codes which are returned to us from the server.

Study the following image carefully :-

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

From the above information we can now form a more specific query statement and send it to the server.

Study the short script below :-

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

As you can see in the above image, the filter condition in our script means that we now only get eleven rows (11) returned

to us - AND - that ten of these are not in my local language.

So if we wish to have all the field data for the error code 1038, and just in English, we can do the following :-

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

Equally, Dieter and our friends in Germany code use '1031' as the filter and get the message in German.

Very nice to have it this simple, and I think using 1049 would do Chris OK, it looks like Greek to me ;-0)

Getting the Error Code :

This in itself is easy enough, study the following code :-

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

But do remember that this MUST be done immediately after the query your are concerned about.

For an example to help you understand, if you sent this script immediately after the Backup error we saw above, we

would get returned to us '1038', and if we then ran the 'GetErrorCodeFromServer()' again we would get a different

result - you guessed it, ZERO, since there was no error when we sent the first statement of "SELECT @@Error;".

After all, this is a query statement like any other, so if it ran without error then we get zero back when we do it

a second time.

Problem - Issue ? :

Okay then, "what is the catch ?" I hear you say.

Up until now we have designed and used our static methods to query the server in a fully 'disconnected' fashion. So

before and after each query - within the static method - we Open and Close a connection.

The consequence of this is that we can't run the get error method after using any query methods, we do not have the

same connection. If we did we would simply get zero each time around.

Top Down Design of what is Required :

Before we get into all the necessary details, let us look at how we may wish to use the facility to automatically pick up

any error code (and message) which may happen during the course of a standard query.

Study the following code carefully :-

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

What we have done above is make an incorrect statement string (on line 214) and then send it to the newly changed

static method (entitled 'SendScalarQueryScript') - then print out some stored details.

If we then run the above handful of code lines - from the end of the 'Initialize()' method of the main form - we see the results

of our efforts displayed in the message boxes. We can see some of the changed code in the background, but we will

study the full details soon.

Check this out :-

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

And now for the next message box, that which shows us the error code returned from the server.

Note the error code is stored in a static field called 'nError' within the SQL_my_funcs static class.

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

And finally, the last of the three message boxes, that which displays the stored error message itself.

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

Okay then guys, we can see the idea - after any query, the error code and message are always

available if required by the developer / coder.

What Needs to be Done in the Static Query Methods ? :

So what do we have to do to change our existing static query methods to bring this about ?

We will use the 'SendScalarQueryScript' method to explore the possibilities, as well as doing

some testing too.

The New Fields :

Before we can do any processing we need a few more data fields - see below :-

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

This simple method of storing an error code and related message, is not perfect, but will certainly do us for now.

Let us go on to study in detail how the 'SendScalarQueryScript' method has been changed to make all this work

for us :-

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

In the image above the first thing to notice is that lines 635 and 660 Open and Close a connection to the server.

This means that on other static member call must involve any other connection changing - making and breaking!

Quite simply, this means the 'GetErrorCodeFromServer' static member, as well as the 'FindErrorMessage' static

member must written so as to use an open connection and then leave it open when done - see below :-

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

And the new error message method :-

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

From the two above images of the code being successfully used we see that one special (and new) static method

is required - this is entitled 'KeepOpenSendScalarQueryScript' and it does not Open or Close a connection.

Checkout the code below :-

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

Yes, this is a simple or 'primitive' form of the other similar query of the name 'SendScalarQueryScript', and obviously it

must not have any error checking or anything fancy - other than the query itself, and passing back the result.

So Where Now ? :

Well, it would seem that now we have a working 'Auto Error Catcher' form of the Scalar query method, we now need to

make similar changes to the methods for 'Reader', 'DataSet' and 'Non Query'.

This I have attempted, and after making a slight improvement in the code to that I first tried in the 'Scalar' version, I have

also tested out each with an incorrect script to see if things generally worked OK.

See below for my first attempts at testing :-

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

Having had some success with all the first test I changed the script and the type of error I 'forced' into it.

See below for more testing - AND - the results by 'magic' screen capture too :-

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

[ *** note *** Once again the testing was done just before the end of the Initialize() method in the main Window1 form.]

From the above runtime output it would seem that the SQL server uses the same table of error messages when

it provides us with an exception message in the form shown below :-

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

In that case we may be better to say the exception message itself, rather than do a lookup of the message table.

Anyway, enough for the moment, now let us see the improved code I used in the new query methods - below is

the one for DataSets - check it out thoroughly :-

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

Looking at the above image I realise that the green L.H.S. markings more or less indicate where the changes

have been done.

Summary :

To summarise, we can now automatically check for errors after each type of statement query to the server, and the results

of the error check are stored in fields in the static class.

Since we are not making a special effort to check for an error, we need to use the field values from other code when we feel

we may have a problem.

In fact, now that we auto capture any errors, we could decide to save them in a table too. We could do this on an

automatic basis as well.

I will have a think about this and add more here if anything comes of the idea.

Now it is time to move on to the next section ...............

PHH - 26th. August 2011.


Alternative Error Handling

So far in the handling of errors taking place within the SQL server, we have tried to take control of

things ourselves by sending the SELECT statement with '@@ERROR' included.

Well, that works okay, and can be done from Management Studio and other places too. After all

it is just standard T-SQL stuff and regular server behaviour.

HOWEVER, we are using dotNet code, and have more sophisticated tools at our disposal. Previously,

you may have seen me use code similar to that shown below :-

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

In the above code you will see that in the TRY / CATCH we use the exception class 'DbException'.

Usually I use 'SystemException' for my TRY / CATCH structures, but there are certain benefits to us

using this other class - which is specially designed for SQL server use.

If we take a look at the SDK documentation for the class - shown below - you see we ought to have

all the details of the server error already at our finger tips, we don't need enquire of the server ourselves.

Study these details carefully :-

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

In the image shown above it is the handful of properties which prove to be the most benefit to us trying to

acquire and store the details of any error occurring.

Let us now do a test to see if we can use some of these properties, ones like ErrorCode, InnerException, and Source -

as well as the Message we already use.

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

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

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

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

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

Problems - numeric code returned from this method should be 102, 208, and 207 - but is always -2146232060

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

PHH - 27th. August 2011.


Backing up of the Database

To do basic backups of the full database being used in an application is actually quite easy.

In fact sometimes more than one database may be being used, and in that case 'they' all can be

backed up in a similar fashion - all quite straightforward.

First Attempt - Basic backup :

To our existing STATIC class 'SQL_my_funcs' let us add a further static method, and call it 'DoBasicBackup'.

I have coded this new method and run a test too - the image below shows a lot, study it carefully :-

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

Although the backing up members of the STATIC class are very much a 'work in progress' project area, the above

code does work nicely and can form a good basis to build upon.

Following is a few small images to show the calling code and the results in a 'WE' pane output :-

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

And just to prove that if we get something wrong, and still try to send an incomplete statement to the server, we

have the following images to study - check these out :-

*** notice **** the sting for the database is obviously 'empty' !

The following is what we get back from the server after using the STATIC method called 'GetErrorCodeFromServer()'.

We will need a way to lookup the code (here 1038) to see what the message is.

"Watch this space" ;-)

And finally a couple of error boxes to show that my Try/Catches are working at various levels :-

*** Note *** 'GAC' is not the usual interpretation, it is my acronym and stands for 'Get All Customers'.

So I have an issue somewhere in the following code :-

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

And yes, it is because I am still converting parts of the original Small Trader app to the new way of using

the 'SQL_my_funcs' static class, in place of the older 'built-in' SQL methods.

And finally the highest of my level catches :-

PHH - 25th. August 2011.


Example 2, the 'SQL_SmallTrader' class

Background to making the Class :

Earlier I explained that it was a good idea to keep any methods related to SQL tables, and hence business

objects, from the 'SQL_my_funcs' static class. We ideally need to keep this class a general one, and one which

can support any application needing SQL database access.

To be in-line with this 'aim' or design idea, I have created a new STATIC class called 'SQL_SmallTrader' which

now has pairs of members to deal specifically with tables and objects which are in the Small Trader solution.

Let us have a look at what we now have - study the following image carefully :-

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

Since previously mentioning this 'pairs' of methods (members) structure, I have added three further pairs for the Service Job sheet,

the Goods (or parts) required for a repair, and the Services carried out during a repair. These can be seen at the end of the above

image of my coding.

If we now look at my cleaned up MS image of the database tables - in the object explorer - we see the details shown

in the image below :-

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

In the above image we can see that we have a table called 'MyErrors' and this does match have a matching pair of

members in the new static class. Why is this?

Well, it is quite simple, the table called 'MyErrors' will be a general table, and one present in all my solutions to SQL

application problems. The idea is that it will hold errors generated in the server itself, as opposed to application

dotNet exceptions.

The consequence of this is that the similar pair of members required for INSERT and UPDATE need to be in the

generally available 'SQL_my_funcs' class - see below :-

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

Although I have decided to place the error members within the class 'SQL_my_funcs' the handling of the server errors

is still a 'work in progress' task, so thing will change and improve before I document anything in these eNotes.

In the code above you may also notice a few general members related to displaying screen messages from exception

objects - I have included an overload to provide the inner message, an idea first put to me in a news group posting

from Johan in SA. (Thanks.)

Going Further :

Now that we have firmly in our mind that a complex set of members is required to carry out any INSERT and UPDATE

procedure from our business objects in RAM, we can look at what is different about the DELETE side of the general

phrase 'CRUD' - create, update and delete.

Foolow the ideas in the next subsection to see how DELETE differs, and is much easier to deal with if we choose to

use in in our projects and solutions.

Putting the 'D' into 'CRU' :

With the acronym 'CRU' we are covering commands in the T-SQL scripting language of 'INSERT' and 'UPDATE' and both

of these feature and involve field data - that is data values for each column in the table.

However, when we come to the DELETE command in T-SQL we are simply identifying the rows or records which need to

be removed. So the statement we have to construct is very similar to a query statement, or script.

Since we are running a selection query in principle, we can use the existing static method(s) to achieve our goals.

To make this more clear let us see this in action in the MS shell.

Study carefully the following sequence of scripting actions :-

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

By using the above query script we see that for some reason we have two entries (rows) for Lee Purkiss.

So we would like to remove one of these rows with 'UnRef' of value 113. Let us try to select the one without

'himself' as the 'KnowBy' value.

Let us start by trying to select both of the rows - see below :-

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

Okay, this is fine, but now we need to identify the empty field under 'KnownBy' - see below :-

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

Having got the SELECT query to find the very row we are after, we can now simply change the content

of the query pane in MS to carry out the removal. Note that I have followed what Paul Nielsen recommends

and test the script with SELECT first, then, and ONLY then, change it to 'DELETE' and re-run it.

Check out the following :-

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

Well, the messages tab content would suggest that all went well and that now we should have only one

Lee Purkiss in our table.

Let us try re-running the earlier general query to see what we have - check this out :-

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

Yes, checking the above image of the results after removal / deleting, we see the result we were after.

So Where does this Leave Us ? :

If you remember, we started this DELETE section with the purpose of finding out how we could use the existing

member(s) of our STATIC class to achieve the results we are after.

Since we ended up using a relatively short and simple statement, and that we required no data returned from the

server itself, we could in fact do what we did in the MS shell with a :-

SendNonQueryScript(cScript)        //----- where 'cScript' is the tested query from above

so we could code something like :-

cScript := "DELETE FROM [C:\USERS\BEST\SMALLTRADER.MDF].[dbo].[Customer] where UnRef = 113 and [KnownBy] = ''"

and then calling the method as seen above.

If we once again look directly at our data we see we have another duplicate on which we could try out our Vulcan code.

See below for the double entry of '101' :-

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

This time we can use the 'Salutation' to distinguish one row from another.

Here is my test in the MS shell :-

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

And when I transfer this to the Initialize() method of my Small Trader application we get this :-

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

Well, the above image seems to suggest to me that we have had success with our simple test.

Let us now return to the MS shell and re-run the general query as previously :-

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

Yep! things look good, and we now have only one Stephen Smith, the 'Mr' row has been removed by the server.

Summary :

Although DELETE puts the 'D' into 'CRUD' we do not need any special member in our STATIC class to deal with the

removal of unwanted rows.

If we construct a query statement and then use the member called 'SendNonQueryScript' we get the job done.

Oh! one word of caution, if you have referential integrity set on the database tables then you may NOT be able to do

some deletions, as their are rows in other tables which depend on some of those you are trying to remove.

At the moment with this development / test app I have left it off and even Primary key values may be duplicated. We

can see how to change things to switch on referential checking etc. later in the eNotes.

Finally - The 'ServiceJob' Members :

Since I first started taliking about the 'pairs' of members related to each table and business object, I have gone and

created those for ServiceJob and the related Goods and Services.

This refreshed in my mind how easy / difficult the task was, to first create and then test a new pair of methods.

Let me not run away from reporting back to you that the task is a little tedious, it has to be said, BUT, it is a one-off

task for each major table - and business object. So well worth doing carefully, and testing thoroughly too.

It may be best to see the results of my labour before I discuss any more details of my efforts and testing.

Study carefully the new code for the 'ServiceJob' pair of members shown below :-

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

Since there are few changes to be made to the second of these methods - I took and existing one and copies, pasted, and

adapted it - then it is how I went about creating the first method which is of particular interest.

My approach was to have the table open in the Object Explorer (OE) in MS, showing the column / field specifications - see below

for an illustration of this :-

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

To make and code each line of my Vulcan static method, I first clicked on the relevant

column in the OE, simply to help keep track of where I was up to, and so as not to miss out any

columns from the set required.

Then each dotNet coded line was created with the appropriate column name, parameter 'symbol' or tag,

the data type, and then the property of the business object which is to provide the data value at runtime.

In the above image I am pointing at the first column, that named 'UnRef' and so the dotNet code line being

worked on is the following one :-

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

Although I have stated what the four parameters represent in the above code line, we need to look in more detail

what actually the 'SQLfieldSpec' class is and does for us. So we understand what happens when we instantiate

with these parameters.

Before we move on to see the 'SQLfieldSpec' class we need to say that the overall process, or procedure, of adding

the code lines for each column, is a matter of taking care, and checking spelling, as well as making sure that the data

type is correct - AND - that each chosen 'tag' for the parameter is unique.

** Reminder ** The approach I am taking with the 'SQL_myFuncs_ and 'SQL_SmallTrader' static classes is that each

business object relates one-to-one with an SQL table. Also that each field is given a value by the appropriate property

of the business object having an acceptable value. The constructor in the business object class makes sure that we

have no NULLs.

Not because I have a 'thing' about NULLs, but rather that for the tasks I do with the static class, it suits me best to have

chosen default values, and not NULLs. Personally, I think that every guy should get into using NULLs correctly, and do

some thorough homework on the topic. If you run away from NULL use it will come back to bite you sometime ;-0)

>> I suggest you look at Section E in these notes, at the relevant Tips & Tricks on NULL. <<

Below is the way I initialize the values of the fields in my 'ServiceJob' class at instantiation, study carefully :-

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

There are 27 fields in the MS OE pane for the table, and also 27 class fields up to line 110 where we have

other field data related to the Goods and Services tables.

Lets move on to the 'SQLfieldSpec' class, and its use to us in the application.

The 'SQLfieldSpec' Class :

To support the STATIC class of 'SQL_my_funcs' we need to have a class to hold the four parameter values we

saw earlier in this subsection - while making the 'FillTypeList' for the ServiceJob table and business object.

Below is the collapsed outline of the new class :-

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

Basically, the point of this class is simply to hold in one place all of the necessary four parameter values

which we will need to create the SQL statement for INSERT and UPDATE.

Having such a class makes it easier for the developer to code correctly and as fast as possible without using

EF (Entity Framework).

Let us expand the parts we have used so far in the images above, firstly the Constructor and Initialize method :-

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

We have used the overload of the constructor which takes all of the four parameters, and so we also need to

look into the relevant overload of the Initialize() method.

As I said earlier, the class is really a 'container' for the four different parameters.

Here are the properties of the class :-

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

Think about it now, I may have been better advised to have called the 'Symbol' property 'Tag'. But the

purpose would not have changed in doing this, just the semantics and the syntax a little ;-0))

What is done with the set of 'SQLfieldSpec' objects ? :

For us to see and understand the use of this class we need to start the code trail within the 'ServiceJob_FillTypedList()'

member on the STATIC class 'SQL_SmallTrader'.

Let us view the code below :-

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

Looking at the above image we can clearly see that a strongly typed list is being made.

The 'List' is of 'SQLfieldSpec' objects, and in our current example we should get a list of length 27, one for

each field of the SQL table in focus.

So one 'Job' object (class 'ServiceJob') is made into a single typed list in line 211, with 27 'SQLfieldSpec' items.

All that is left to do is done in line 213, where an SQL statement is both constructed and also sent to the server.

Once again, hindsight is a wonderful thing, and maybe another, and better, name ought to have been chosen. I think

what I was thinking was that a "business object" was being processed to use its detail to make a statement, which could

then be sent to the server.

You need to realise that this method handles any type of business object structured in the fashion described above, and so

we could not use a specific name - just plain old 'Business Object'.

The subsequent method called 'MakeBusinessObjectChange' was actually going to carry out the querying of the server, and

hence bring about 'change'.

Let us first study the static method which creates the SQL string to use in the query. See below :-

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

When we examine the above code we see that in lines 146 and 153 there are a couple of string variables which are being

used to construct the statement - these are generated in the static members called 'CreateFieldSymbolPairs' and

'CreateFieldsSymbols'.

We will look at this aspect later, but for now we need to see that the 'eMode' parameter switches between INSERT and UPDATE

as the style of SQL statement being constructed. We will look at the related enumeration a little later on.

Also in the above code, before a Boolean result is returned, the code of line 157 sends the string statement to the server

for execution within the server itself.

By way of a good test, if I uncomment the line at 156 and run the application we will see just what statements are being

made for us by the method.

In the sample illustrations below I am saving a 'ServiceJob' in the usual way for the Small Trader app.

Study these images carefully, they tell us a lot :-

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

The above statement is for the Service Job business object itself, and as usual shows something a bit of a nightmare

to read, check, code, or script manually. Works nicely in a code method however ;-) I know what I will be using

from now on.

Now, because I chose a Service Job with some parts and service too, we get as many Goods and Services statement

message boxes as there are items in the Goods and Services arrays - see one illustration form each set :-

First for a single 'Goods' object :-

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

And now for a single 'Services' object :-

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

Okay then, we can see that the static method creating the statement seems to be working fine, but how

does the method which sends the SQL query statement look ?

And don't forget, as well as a script statement like those in the message boxes above, we have to create ad provide

a whole bunch of parameter objects for the Command object, so we will need 27 of these added to the array or

collection needed by the Command object.

Firstly, let's check this out below :-

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

Above we see the Vulcan code associates the 'cText' input parameter with the SQL query statement - line 170.

And the static member used in line 175 turns our set of 27 'SQLfieldSpec' object items into a collection of parameter objects

for the Command object.

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

Parameter Objects - Making and Adding to Collection :

Let us concentrate now on how a single parameter object is created, and then how these are arranged into a collection

for use with the Command object of the ADO.NET querying process.

Let us start with the static method of the 'SQL_my_funcs' class which makes the collection- see below :-

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

This static method needs to use the static member (method) 'SetAddParam()' for each of the 27 strongly typed list

items for our example using the 'ServiceJob' business object.

Look carefully at the following code :-

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

So in the above image line 225 is the point at which each parameter item is added to the growing collection.

Field Symbols and Field Symbol Pairs :

The differing styles of the INSERT and UPDATE statements mean we need some string 'snippets' which are either

field and symbol 'pairs' like those below :-

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

or a whole set of fields followed by a whole and matching set of symbols, as seen below :-

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

First let us see the static method which makes the UPDATE style of fields/symbols :-

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

And then the member which does the whole set grouping of fields and symbols for the INSERT style of statement.

Study the following carefully :-

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

This is not complicated, or rocket science - BUT - it is complex, and because it has many parts and can be

just a touch longwinded, it can be hard to know what you are doing - on a bad day !!

However, lets put the finishing touches to a difficult and long - but VERY necessary - section.

The 'Row Change' Enumeration :

You may have noticed in the code we have seen previously, that instead of me using fixed string values as "INSERT" and "UPDATE",

I have used enumerations in their place.

Enumerations offer a lot of benefits including making code better to read, easier to maintain, and checkable by the compiler.

Also of course we have intellisense in the editor as well.

The possible variations are illustrated in the image shown below :-

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

At the moment with VN2 version 207 we have to place the 'Enum' definition outside of any class definition, in the same way at the

moment that we can't have nested classes defined.

This now makes a lot of sense of code like the following (which you saw earlier) :-

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

Well, that is all for now I think - it has been a long journey but I can tell you the rewards are GREAT.

I wish I had had this solution to using SQL in such a business object way some years back, still, better late

than never they say ;-)

See you in the next section ................

PHH - 25th. August 2011.


Full working code

//----- SQL_my_funcs.prg -----

//----- created August 2nd. 2011 for VODC in November -----

//----- for "Small Trader" app. - by PHH -----

#using System

#USING System.IO

#using System.Data

#using System.Xml.Serialization

#using System.Data.Common

#using System.Collections.Generic

#using System.Collections.ObjectModel

#using System.Collections

#using System.Windows

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

BEGIN NAMESPACE SmallTrader

STATIC CLASS SQL_my_funcs

#region {.. class variables ..}

static public lConnectionTestedOK as Boolean

static private cFileXML as string

static private cAppDir as string

static private cWork as string

#endregion

#region {.. other variables of the class ..}

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

static public oConnDetails AS ConnectionDetails

static private oDFactory AS DbProviderFactory

static private oDConn AS DbConnection

static private cStrConn as string

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

#endregion

#region {.. more class variables ..}

static private cSource as string

static private cAttach as string

static private cSecurity as string

static private cTime as string

static private cInst as string

static private cDBfilePath as string

static private lConnected as Boolean

static private lShowMessages as Boolean

#endregion

#region {.. and more ..}

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

static private nErrorCode as int

static private cErrorMessage as string

static private cErrorLanguage as string

#endregion

STATIC CONSTRUCTOR()

nErrorCode := 0

cErrorMessage := "No Error"

cErrorLanguage := "1033"

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

lConnected := false

lShowMessages := false

//lShowMessages := true

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

cAppDir := System.IO.Directory.GetCurrentDirectory() +"\"

cWork := System.IO.Directory.GetCurrentDirectory()

cFileXML := cAppDir +"ConnectionDetails.xml"

SQL_my_funcs.ReadSerialXmlFile()

RETURN

//========================= [ Backup Database ] ===========================

static method DoBasicBackup() as Boolean

local lResult := true as Boolean

local oF as DirectoryInfo

oF := Directory.CreateDirectory("C:\BackupSQL")

local cName2Use as string

cName2Use := oF:FullName+"\StaticBasic.bak"

MessageBox.Show(cName2Use,"Will be Used :")

local cScript as string

cScript := "BACKUP DATABASE [" + SQL_my_funcs.DBfilePath + "] "

cScript += "TO DISK = '"+cName2Use+"' "

cScript += "WITH FORMAT, "

cScript += "NAME = 'Full Backup of SmallTrader' "

MessageBox.Show(cScript, "Back-Up :")

try

SQL_my_funcs.KeepOpenSendNonQueryScript(cScript)

MessageBox.Show("Query has been sent .... ","After BU :")

catch oEx as SystemException

MessageBox.Show(oEx:Message, "ERROR :")

end try

local nError as int

nError := SQL_my_funcs.GetErrorCodeFromServer()

if nError = 0

MessageBox.Show("No apparent error","Error ?")

else

MessageBox.Show("Error number :- "+nError:ToString(),"Error ?")

endif

//MessageBox.Show("Just before force disconnect ....","TEST :")

SQL_my_funcs.DoForceDisconnect()

RETURN lResult

//========================= [ End Backup] ===========================

//========================= [ Error Object ] ==============================

static method SaveErrorToSQLtable(oE as MyError) as void

SQL_my_funcs.ChangeErrorRecord(oE, RowChangeModeSQL.Insert)

return

static method Error_FillTypedList(oE as MyError) as List<SQLfieldSpec>

local aSFS as List<SQLfieldSpec>

aSFS := List<SQLfieldSpec>{}

aSFS:Add(SQLfieldSpec{"[UnRef]", "@ur", DbType.Int32, oE:UnRef})

aSFS:Add(SQLfieldSpec{"[Occurred]", "@occ", DbType.DateTime, oE:Occurred})

aSFS:Add(SQLfieldSpec{"[Method_Name]", "@mn", DbType.String, oE:MethodName})

aSFS:Add(SQLfieldSpec{"[Object_Class]", "@obj", DbType.String, oE:ObjectClass})

aSFS:Add(SQLfieldSpec{"[Namespace]", "@nsp", DbType.String, oE:Namespace})

aSFS:Add(SQLfieldSpec{"[Operation]", "@op", DbType.String, oE:Operation})

aSFS:Add(SQLfieldSpec{"[Main_Message]", "@mm", DbType.String, oE:MainMessage})

aSFS:Add(SQLfieldSpec{"[Inner_Message]", "@im", DbType.String, oE:InnerMessage})

aSFS:Add(SQLfieldSpec{"[SQL_Error_Text]", "@set", DbType.String, oE:SQLerrorText})

aSFS:Add(SQLfieldSpec{"[SQL_Error_Code]", "@sec", DbType.Int32, oE:SQLerrorCode})

return aSFS

static method ChangeErrorRecord(oE as MyError, eMode as RowChangeModeSQL) as Boolean

local lResult := false as Boolean

if eMode = RowChangeModeSQL.Delete

MessageBox.Show("Wrong mode requested >> DELETE !?","Change MODE :")

return lResult

endif

local cTable := "[MyErrors]" as string //----- change for other Tables -----

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

local aSFS as List<SQLfieldSpec>

aSFS := SQL_my_funcs.Error_FillTypedList(oE)

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

//MessageBox.Show("Just about to try the STATIC func!","Testing :")

lResult := SQL_my_funcs.MakeBusinessObjectDetail(aSFS, cTable, eMode)

//MessageBox.Show("Just after MBO - try the STATIC func!"+lResult:ToString(),"Testing :")

return lResult

//========================= [ End Error Object ] ===========================================

//========================= [ Message for Exceptions ] =============================

static method MyInnerMessage(oEX as Exception) as string

local cText := "" as String

if oEX:InnerException <> NULL

cText := oEX:InnerException:Message+CRLF

cText += "Exception Type : "+oEX:InnerException:GetType():Fullname+CRLF

cText += "Source : "+oEX:InnerException:Source+CRLF

cText += oEX:InnerException:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oEX:InnerException:TargetSite:ToString()+CRLF

endif

//MessageBox.Show(cText)

return cText

static method MyMainMessage(oEX as Exception) as string

local cText as String

cText := oEX:Message+CRLF

cText += "Exception Type : "+oEX:GetType():Fullname+CRLF

cText += "Source : "+oEX:Source+CRLF

cText += oEX:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oEX:TargetSite:ToString()+CRLF

//MessageBox.Show(cText)

return cText

//------------------- And Another Overload --------------------

static method MyInnerMessage(oDBX as DbException) as string

local cText := "" as String

if oDBX:InnerException <> NULL

cText := oDBX:InnerException:Message+CRLF

cText += "Exception Type : "+oDBX:InnerException:GetType():Fullname+CRLF

cText += "Source : "+oDBX:InnerException:Source+CRLF

cText += oDBX:InnerException:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oDBX:InnerException:TargetSite:ToString()+CRLF

endif

//MessageBox.Show(cText)

return cText

static method MyMainMessage(oDBX as DbException) as string

local cText as String

cText := oDBX:Message+CRLF

cText += "Exception Type : "+oDBX:GetType():Fullname+CRLF

cText += "Source : "+oDBX:Source+CRLF

cText += oDBX:StackTrace:ToString()+CRLF

cText += "Targert Site : "+oDBX:TargetSite:ToString()+CRLF

//MessageBox.Show(cText)

return cText

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

//========================= [ End Exception Messages ] =============================

static method MakeBusinessObjectDetail(aSFS as List<SQLfieldSpec>, cTable as string, eMode as RowChangeModeSQL) as Boolean

local lResult := false as Boolean

local cText, cHeading as string

if eMode = RowChangeModeSQL.Update

local cFieldSymbolPairs := "" as string

SQL_my_funcs.CreateFieldSymbolPairs(cFieldSymbolPairs,aSFS)

cText := "UPDATE "+cTable+" SET "+cFieldSymbolPairs+" WHERE [UnRef] = @ur ;"

cHeading := "Updating :"

endif

if eMode = RowChangeModeSQL.Insert

local cFields := "" as string

local cSymbols := "" as string

SQL_my_funcs.CreateFieldsSymbols(cFields,cSymbols,aSFS)

cText := "INSERT INTO "+cTable+" ("+cFields+") VALUES ("+cSymbols+");"

cHeading := "Inserting :"

endif

//MessageBox.Show(cText,"Script :")

lResult := SQL_my_funcs.MakeBusinessObjectChange(aSFS, cText, cHeading)

return lResult

static method MakeBusinessObjectChange(aSFS as List<SQLfieldSpec>, cText as string, cHeading as string) as Boolean

local lResult := false as Boolean

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

SQL_my_funcs.DoSetConnectionString()

IF !SQL_my_funcs.DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SQL_my_funcs.DFactory:CreateCommand()

oCommand:CommandText := cText

oCommand:CommandType := CommandType.Text

oCommand:Connection := SQL_my_funcs.DConn

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

SQL_my_funcs.AllParamsSetAdd(oCommand, aSFS)

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

oCommand:ExecuteNonQuery()

lResult := true

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message, cHeading)

END TRY

SQL_my_funcs.DoForceDisconnect()

return lResult

static method CreateFieldSymbolPairs(cFieldSymbolPairs ref string, aSFS as List<SQLfieldSpec>) as void

local oLTE as IEnumerator

oLTE := aSFS:GetEnumerator()

local oSFS as SQLfieldSpec

local i := 0 as int

do while oLTE:MoveNext()

i +=1

if i > 1

cFieldSymbolPairs += ","

endif

oSFS := (SQLfieldSpec)oLTE:Current

cFieldSymbolPairs += oSFS:FieldName+"="+oSFS:@@Symbol

enddo

return

static method CreateFieldsSymbols(cFields ref string, cSymbols ref string, aSFS as List<SQLfieldSpec>) as void

local i := 0 as int

local oLTE as IEnumerator

oLTE := aSFS:GetEnumerator()

local oSFS as SQLfieldSpec

do while oLTE:MoveNext()

i +=1

if i > 1

cFields += ","

cSymbols += ","

endif

oSFS := (SQLfieldSpec)oLTE:Current

cFields += oSFS:FieldName

cSymbols += oSFS:@@Symbol

enddo

return

static method SetAddParam(oCommand AS DbCommand , oDbT as DbType, cSym as string, oV as object) as void

LOCAL oParam AS DbParameter

oParam := SQL_my_funcs.DFactory:CreateParameter()

oParam:ParameterName := cSym

oParam:DbType := oDbT

oParam:Value := oV

oCommand:Parameters:Add(oParam)

return

static method AllParamsSetAdd(oCommand AS DbCommand, aSFS as List<SQLfieldSpec>) as void

local oSFS := SQLfieldSpec{} as SQLfieldSpec

local oLTE := aSFS:GetEnumerator() as IEnumerator

oLTE:Reset()

do while oLTE:MoveNext()

oSFS := (SQLfieldSpec)oLTE:Current

SQL_my_funcs.SetAddParam(oCommand, oSFS:DbT, oSFS:@@Symbol, oSFS:Val)

enddo

return

static method ReadSerialXmlFile() as logic

local serial as XmlSerializer

local oSR as StreamReader

local cText, cHead as string

local lResult as Boolean

lResult := false

lConnectionTestedOK := false

if File.Exists(cFileXML)

cText := "Existing file will be read!"

cHead := "XML file :"

//----- use my STATIC toggled message system -----

MyMessages(cText,cHead)

//----- read object from XML file -----

serial := XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFileXML}

oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

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

lResult := true

lConnectionTestedOK := true

//----- use my STATIC toggled message system -----

MyMessages(oConnDetails:ToString(),cHead)

else

cHead := "XML file :"

cText := "File can't be found !?"+CRLF

cText += "Go to Connection and create a string."+CRLF

MessageBox.Show(cText,cHead)

endif

return lResult

static method SerializationToFile() as void

local cText, cHead as string

if oConnDetails = NULL_object

cText := "NULL object ! - ConnectionDetails ..."

cHead := "My Error :"

//----- use my STATIC toggled message system -----

MyMessages(cText,cHead)

return

endif

local serial as XmlSerializer

local oSW as StreamWriter

if File.Exists(cFileXML)

cText := "File exists already!"

cHead := "XML file :"

MessageBox.Show(cText,cHead)

endif

//----- save object to XML file -----

serial := XmlSerializer{typeof(ConnectionDetails)}

oSW := StreamWriter{cFileXML}

serial:Serialize(oSW, oConnDetails)

oSW:Close()

return

static method SerializeOthersToDisk() as void

/*local cFile as string

cFile := "C:\Temp\customers.xml"

local oSW as StreamWriter

oSW := StreamWriter{cFile}

//----- Save object to XML file -----

local serial as XmlSerializer

local aTypes as Type[]

aTypes := Type[]{1}

aTypes[1] := typeof(Customer)

serial := XmlSerializer{typeof(ArrayList),aTypes}

serial:Serialize(oSW,self:alCustomers)

oSW:Close()*/

return

static method MyMessages(cB as string, cH as string) as void

if ShowMessages

MessageBox.Show(cB,cH)

endif

return

//======================== [ basic ADO.net & settings ] ==============================================

static METHOD DoSetConnectionString() AS VOID

IF String.IsNullOrEmpty(oConnDetails:cProvider)

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

RETURN

ENDIF

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

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

oConnDetails:cUserName := ""

ENDIF

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

oConnDetails:cPassWord := ""

ENDIF

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

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

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

oDConn := oDFactory:CreateConnection()

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

cStrConn := ""

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

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

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

cDBfilePath := oConnDetails:cFilePath //----- for backup ! -----

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

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

cTime := "Connect Timeout="+oConnDetails:nTimeOut:ToString()+";"

//----- Willie said this would leave it always open if ZERO is supplied! -----

cInst := ""

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

oDConn:ConnectionString := cStrConn

oConnDetails:cConnectionString := cStrConn

MyMessages(cStrConn,"My Connecting String :")

RETURN

static METHOD DoTryToConnect() AS LOGIC

LOCAL lResult AS Boolean

lResult := FALSE

lConnected := false

local cText := "" as string

TRY

oDConn:Open()

cText := "Vulcan.NET : Small Trader : << Connected! >>"

MyMessages(cText,"Try to Connect :")

lResult := TRUE

lConnected := true

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

RETURN lResult

static METHOD DoForceDisconnect() AS VOID

LOCAL cText AS STRING

IF ! oDConn = NULL_OBJECT

oDConn:Close()

cText := oDConn:ConnectionString+" - State = "+ oDConn:State:ToString()

MyMessages(cText,"Disconnecting? :")

lConnected := false

else

cText := "Vulcan.NET : Small Trader : << Disconnected! >>"

MyMessages(cText,"Disconnecting? :")

ENDIF

RETURN

//========================= [ query ADO.net members ] ================================================

static method KeepOpenSendScalarQueryScript(cScript as string) as Object

local oResult as object

oResult := object{}

try

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch oEx as SystemException

MessageBox.Show(oEx:message,"Start of Scalar Query !")

end try

//----- getting a scalar quanity returned -----

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oResult := oDComm:ExecuteScalar()

//----- need to cast in calling code section! -----

CATCH DbEx AS DbException

//SaveAndDisplayErrorDetails(DbEx,"Keep, Returned Data Object :")

MessageBox.Show(DbEx:Message,"Keep, Returned Data Object :")

END TRY

return oResult

static method KeepOpenSendNonQueryScript(cScript as string) as Boolean

local lResult as Boolean

lResult := false

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of Non Query !")

end try

//----- getting no data returned -----

oDComm:CommandType := CommandType.Text

TRY

LOCAL cStatement AS STRING

//----- use a terminator ; as recommended by Paul Nielsen -----

cStatement := cScript

oDComm:CommandText := cStatement

oDComm:CommandType := CommandType.Text

//----- execute the supplied statement -----

oDComm:ExecuteNonQuery()

lResult := true

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Keep-Non Query :")

/* local cText := "" as string

cText += "Error Code :- "+DbEx:ErrorCode:ToString()+CRLF

if !DbEx:InnerException = NULL_object

cText += "Inner Exception :- "+DbEx:InnerException:ToString()+CRLF

endif

cText += "Source :- "+DbEx:Source:ToString()+CRLF

cText += "Message :- "+DbEx:Message+CRLF

//cText += "Stack Trace :- "+DbEx:StackTrace+CRLF

MessageBox.Show(cText,"DbEx Details :")*/

//MessageBox.Show(DbEx:Message,"Non Query :")

END TRY

return lResult

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

static method SendNonQueryScript(cScript as string) as Boolean

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

local lResult as Boolean

lResult := false

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of Non Query !")

end try

//----- getting no data returned -----

//oDComm:CommandType := CommandType.Text

TRY

LOCAL cStatement := cScript AS STRING

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cStatement

oDComm:CommandType := CommandType.Text

//----- execute the supplied statement -----

oDComm:ExecuteNonQuery()

nError := 0

lResult := true

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Non Query :")

/* nError := GetErrorCodeFromServer()

nErrorCode := nError

//FindErrorMessage()

cErrorMessage := DbEx:Message

MessageBox.Show(nError:ToString(),"Code ?! :")

MessageBox.Show(DbEx:Message,"Non Query :") */

END TRY

SQL_my_funcs.DoForceDisconnect()

return lResult

static method SendDataSetQueryScript(cScript as string, cTableTitle as string) as DataSet

nErrorCode:= -999

cErrorMessage := ".. reset for use .."

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

local oDS_Result as DataSet

oDS_Result := DataSet{}

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

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

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

CATCH DbEx AS DbException

MessageBox.Show(DbEx:message,"Start of DataSet Query !")

end try

//----- getting a data set returned! -----

oDComm:CommandType := CommandType.Text

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

LOCAL oDA AS DbDataAdapter

oDA := oDFactory:CreateDataAdapter()

oDA:SelectCommand := oDComm

oDS_Result := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS_Result, cTableTitle)

nErrorCode := 0

cErrorMessage := "No error thrown"

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Error DataSet :")

END TRY

SQL_my_funcs.DoForceDisconnect()

return oDS_Result

static method SaveAndDisplayErrorDetails(DbEx AS DbException, cHead as string) as void

cErrorMessage := DbEx:Message //----- CARE with order, must be before next line -----

nErrorCode := GetErrorCodeFromServer()

if !lShowMessages

local cText := "" as string

cText += ErrorMessage+CRLF

cText += ErrorCode:ToString()+CRLF

MessageBox.Show(cText, cHead)

endif

#region {.. test code ..}

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

//----- make oE from properties and fields -----

//as MyError, eMode as RowChangeModeSQL

/*local eMode := RowChangeModeSQL.Insert as RowChangeModeSQL

local oE := MyError{} as MyError*/

//ChangeErrorRecord(oE, eMode)

#endregion

return

static method SendReaderQueryScript(cScript as string) as DbDataReader

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

//----- using the DataReader approach as an alternative -----

//----- to the 'DataTable' one I like particularly -----

local oDr as DbDataReader

try

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

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

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

CATCH DbEx AS DbException

MessageBox.Show(DbEx:message,"Error DataReader :")

return NULL_OBJECT

end try

//----- getting a data set returned -----

oDComm:CommandType := CommandType.Text

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oDr := oDComm:ExecuteReader()

//----- execute the select statement -----

nError := 0

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Error Datareader :")

/*MessageBox.Show(DbEx:Message,"Error DataReader :")

nError := GetErrorCodeFromServer()

MessageBox.Show(nError:ToString(),"Code ?! :")

nErrorCode := nError*/

//FindErrorMessage()

END TRY

//----- do NOT close the connection - needed Open by DataReader ! -----

return oDr

static method SendScalarQueryScript(cScript as string) as Object

local nError := -999 as int

cErrorMessage := ".. reset for use .."

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

local oResult as object

oResult := object{}

try //----- to Open a connection & make a command object -----

SQL_my_funcs.DoSetConnectionString()

SQL_my_funcs.DoTryToConnect()

LOCAL oDComm AS DbCommand

oDComm := oDFactory:CreateCommand()

oDComm:Connection := oDConn

catch oEx as SystemException

MessageBox.Show(oEx:message,"Start of Scalar Query !")

end try

//----- now try getting a scalar quanity returned -----

TRY

//----- use a terminator ; as recommended by Paul Nielsen -----

oDComm:CommandText := cScript

oDComm:CommandType := CommandType.Text

oResult := oDComm:ExecuteScalar()

//----- need to cast in calling code section! -----

nError := 0

CATCH DbEx AS DbException

SaveAndDisplayErrorDetails(DbEx,"Returned Data Object :")

END TRY

SQL_my_funcs.DoForceDisconnect()

return oResult

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

static method GetErrorCodeFromServer() as int

local nResult := -999 as int

local cScript as string

cScript := "SELECT @@Error;"

nResult := (int)SQL_my_funcs.KeepOpenSendScalarQueryScript(cScript)

if lShowMessages

if nResult = 0

MessageBox.Show("No apparent error ! ","Error Code :")

else

MessageBox.Show("Returned code is :- "+nResult:ToString(),"Error Code :")

endif

endif

return nResult

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

/*static method FindErrorMessage() as void

local cScript as string

cScript := "SELECT text FROM [master].[sys].[messages] where [message_id] = '"

cScript += ErrorCode:ToString() + "' and [language_id] = '"+cErrorLanguage+"' "

cErrorMessage := (string)SQL_my_funcs.KeepOpenSendScalarQueryScript(cScript)

return*/

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

static access Source() as string

return cSource

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

static access Attach() as string

return cAttach

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

static access StrConn() as string

return cStrConn

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

static access DBfilePath() as string

return cDBfilePath

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

static access DFactory() AS DbProviderFactory

return oDFactory

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

static access DConn AS DbConnection

return oDConn

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

static access ShowMessages() as Boolean

return lShowMessages

static assign ShowMessages(lM as Boolean) as void

lShowMessages := lM

return

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

static access ErrorCode() as int

return nErrorCode

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

static access ErrorMessage() as string

return cErrorMessage

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

END CLASS

END NAMESPACE // SmallTrader

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

PHH - 17th. October 2011.


Layer 5 - creating within a new app

It has already become apparent that it is 'Layer 5' of our overall scheme of things which will need

most of the work and effort.

Every time we start a new application we will need to construct a fresh 'Layer 5'.

The 'STATIC class' which makes up this layer reflects the business objects in use. Remember, the BOs

also have a close relationship to the data tables in the SQL database.

Structure with the 'STATIC class' :

The whole of Layer5 for an app with contain a group of methods which are specific to each and every Business

Object (BO) used in the application.

So for our Small Trader app we have a class called 'SQL_SmallTrader'. This class will be appropriately named in

any new application.

Within the static class we will see a set of groups of methods - I think it is best to group them such that all the

methods for say 'Customer' are together - see below for an example of the collapsed groupings :-

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

If I now expand the 'Customer' group so we see the next 'level' it looks as follows :-

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

Within the 'Customer' group of methods I have placed the two important ones to add data to the SQL 'Customer' table,

together at the top, and then bunched up the others in another group.

All of these methods have SQL query statements constructed within them and relating to the 'Customer' table. Also

each of these methods will call on a suitable method from Layer 6 - a general ADO.net related method.

For example, let us see the detail within the 'FindNextCustomerUnRef' method - check this out :-

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

Line of code 163 constructs the statement string (in this case it is quite short and simple) and line

numbered 168 calls a method from the next layer, 'Layer 6'.

We will see this relationship between layers 5 and 6 in each of the methods in our BO grouping, except

one - that is the 'FillTypedList' for the BO, as this is actually used within the 'Change_XXXXX_Record' method.

It may look like quite a bit of work, and indeed that will most likely be the case, BUT, it does do the majority

of the work for our application, and hence makes our Business Logic (BL) layer much simpler. All we need do

in the BL layer is call on the 'Layer 5' static members.

Let us move on to see this in action - within our Small Trader application.

Mini Project / Task - Finding Duplicates :

In the topic section we will add some extra functionality to the main menu by creating options to find and display

any duplicate entries in the Customer, ServiceJob, and Invoice SQL tables.

Let us see a good start - the menu and XAML script for it - as yet we have no functionality behind the menu items.

This is what it looks like now :-

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

And the XAML script to do this was quite easy to insert into the existing stuff - see below :-

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

And then after interactively adding the events handler 'stuff' in the XAML designer surface we see this below :-

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

And the dotNet 'stub' code too has been added by the Designer - see below for this :-

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

So before we 'hook' anything up, we now need to go and make some dotNet methods to carry out the necessary

tasks querying the server.

Exploring the Query Script :

Let us now turn to our trusty tool, the Management Studio 'shell'.

Look at a couple of query statements I have developed and got to work for us :-

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

As you can see in the above image, we have one query, the second one, which seems to do just as

we require. It finds only the duplicate Customer entries, and returns the reference number of any who

have more that one entry, along with the number of occurrences within the table.

*** Note *** the result of the probable required script is right at the bottom of the output pane.

And correct to what we have previously seen, Customer 103 is duplicated, we have two of Julie.

Now that we have something suitable, and which does the 'set algebra' just as we require, we can try

to massage the script to be more suitable to how we need to construct it when we write our dotNet code

and static method.

Checkout the new script syntax, seen below :-

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

The SELECT statement is now in a form we can use with our ADO.net static members in 'Layer 6'.

So let us look at an image of success before we examine the working dotNet code in detail :-

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

As you can see, a simple test from within the Initialize() method of the main window gives us a data table

with only one row.

And taking our test and print-out a little further we see the following at runtime :-

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

This looks very promising, we seem to be finding just what we need.

Let us now see the static method we have used to create our output :-

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

I simply copied and pasted the script from MS and then tidied the code by adding double quotes etc..

Don't forget, Management Studio is your friend when dealing in all matters SQL server ;-0))

Before we move on to use the same 'set based' algebra to find other duplicates, in the ServiceJob and Invoice table,

we first need to link-up the menu option click to at least a simple MessageBox output display.

Well below is shown a very basic way, but one which needs improving as far as display of information is concerned.

Check this out :-

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

I will leave it to you to add the loop code to cycle through all records, when there are more than just one !

Finally we see the event handler being called - see below :-

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

Lets move on to the duplicate ServiceJobs.

ServiceJobs :

If we once again start in the MS 'shell' and view the table data, we see we have in my test data one duplicate.

ServiceJob '102' has a double entry. Don't get confused with the 'CustomerRef' - that is just unfortunate coincidence ;-0))

Find the duplicate in the image following :-

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

So all we have to do it take the script for Customer, and adjust it ever so slightly to make it work for 'ServiceJob'.

Checkout the details below :-

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

And because we have done all the basic work previously, it has been VERY easy for me to copy, paste, and adapt

the dotNet code and script to do as we need for the ServiceJob duplicates.

I have even added the loop structure to handle displays of more than one dunplicate found.

Checkout the following images, with all their detail :-

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

Here is the new static method - only a few small changes from the Customer 'flavour' :-

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

And finally the method to include in the Windows1 class, note the extra loop structure :-

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

>>Whoops, forgot to edit line 239 - should read 'ServiceJobs' ! <<

And for the sake of being complete, lets move on to the duplicate invoices.

Invoices :

If we are going to do this - since it is VERY similar to the other two - we need to do something extra.

What if I make the effort to add a few extra duplicates, just so we can test the code more thoroughly ?

Okay then, just spent a few minutes in the MS shell, and the "Edit Top 200 Rows" context menu option.

I think we have some useful test data - see below :-

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

These MS results would suggest that we have three duplicate invoice references, and with 2, 3 and 4 occurrences

of each it would seem.

These results seem to be totally in-line with the recollections I have of actually doing the editing in MS.

Now for some positive results :-

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

Yep! that seems to square up with my thoughts on what should be found, and displayed.

Now here is the new static method :-

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

And also the event handler method for the Window1 class :-

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

This time I think I have managed to edit all the places 'Invoices' should appear ;-0))

Now let us move on to the next subsection ................

PHH - 5th. September 2011.


Layer 6 - a couple of exceptions!

In layer 6 the ADO.net layer, we said we would not generally see query statements being constructed in

the static methods. That is generally true but there are a couple of exceptions.

In layer 5 we see MANY static methods with query statements relating to the BO class, and the BO related

tables in the database - these belong in layer five as they are BO related tasks.

However, there are a couple of statements we can construct which do NOT relate to the BOs at all.

As way of an example lets see the 'DoBasicBackup' static method - check this out :-

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

Like a lot of things in SQL Server, we send a query string to get something to happen.

In this example above the task is NOT related to the BOs at all, but the database itself, we are requesting a back-Up.

Another obvious one we have used in these eNotes is the 'GetErrorCodeFromServer' static method.

If we do more and more database related task, the more likely we are to need to add methods to our layer 6 which

have query statements.

However, it is still true that all BO related statements belong in layer 5.

Let us move on, see you in the next subsection ............

PHH - 31st. August 2011.


Section G : using 'View's with a data source

It is VERY convenient to work with collections of objects, and here I really mean collections of business

objects - those of our own making.

If we make the extra little effort to wrap a view around our collection we can do some nice things with it,

and quite easily too.

Here is some of the SDK documentation for the 'ICollectionView' interface :-

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

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

Following on from the detail shown above, here is an image which shows just how quick and easy it is

to create and use a view around a data table.

Study the following carefully :-

As you can see in the image above, once we have a suitable DataTable (made in lines 182 through 185),

we can easily use this to get its default 'View' and build our new view around it.

Three lines do the trick - 187 through 189 - and the others just set up the available 'DataGrid' on our WPF form.

Since we can create and use a view so easily, we need to find out just what 'magic' stuff a 'view' can do for us.

Let us see some more SDK doc. detail first - checkout below :-

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

By looking at the methods section in the above image we see that we can have an enumerator and also navigate

our way through the collection.

Whereas the properties list above indicate that we can Group, Sort, and Filter, quite easily too.

*** Remember *** The underlying data is not changed or altered at all by these three 'actions', it is the 'view' which

is sorted and NOT the underlying data. This applies also to the Group and Filter operations.

And finally, before we do some tests and experimentation, we need to realise that we can create multiple 'views' of the

same underlaying data, and use these with different GUI controls on our WPF form.

Since it appears as though a data source like a DataTable is used in a DataGrid and other controls using a default 'view',

we may as well take charge of the situation and access the view as well as set it up in a better fashion - for our own use

with sorts, filters and grouping etc..

Let us now move onto the next subsection where we can find out more details regarding 'Views' and do some good

practical experiments.

Let us give ourselves a task - to sort, group and filter the SQL server records in the earlier image.

See you in the next part ...........

PHH - 27th. August 2011.


What is a 'View'

This question is probably best answered by us doing some practical coding with a simple collection.

To this collection we will apply sort conditions, and then grouping ones, and finally filter conditions.

Before we can start to really enjoy ourselves we need to make a new solution / project with which we

can then do all our experimentation.

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

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

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

Sorting via the column header functionality :-

Clicking in the header of the 'Version' column we see the above sort order - shown in the L.H.S. image.

The right hand image has columns reordered, and the Instance column sorted A-Z.

But what of our our sort order ?

See below :-

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

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

Notice it has found another instance on the other PC of mine on the wireless network - just turned it on.

The PC is called "W7-BIG-T".

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

Because we are struggling to show the results with such a limited data set I have commented out the sort descriptions -

or conditions to you and me ;-)

At least we appear to have grouped by version OK.

Notice below if we sort the second column by clicking the header we still have a grouping, and then the sort order :-

PHH - 27th. August 2011.


When to use a 'View'

This small section is going to be more obvious to those guys already using SQL technologies, than

those still firmly in the camp of using DBF and file based data solutions.

PHH - 27th. August 2011.


Many 'Views', one data source

This small section is first of all a bit of a WARNING to us all !

If we think we can easily make more than one view for use with different controls on our GUI surface

then be careful. The way we have made the 'view' we have used so far, is not the way to make others.

Look at the following image, of three ListBox controls - each with a different display member :-

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

The above image may look good at first glance, and there is nothing in the XAML to suggest any potential problem.

However, when we look at the supporting VN2 code we see the following :-

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

In the code above we are making a serious error in thinking that the lines numbered 45 - 47 give us different 'new'

view objects. They don't.

What they do do is get the pointer to the default view which exists, and so the three views are all they same one!

If we were to get different view objects we would need to do the equivalent of a 'clone', so that we do end up with three

different objects.

Once we have different objects we can then apply different sort and grouping criteria, and they would not interfere

with each other in any way.

A Quick and Simple Remedy :

Well, there is a quick way around this, but it does not really answer any questions ;-)

Look at the image below to see the results of some new coding on my behalf :-

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

Studying the image show above we can see that we now have three different orders of the list items.

The blank entries actually match up with the 'W7-BIG-T' item in list three, so something has moved around.

Also if you study the second column you see we have an alphabetic ore A-Z of the items, this is different to

the case in my previous tests.

Also the order of the first column items is exactly the same as the previous one. So something has changed

and improved, even if just form a functional point of view.

The problem is that if order to make things look as I wished, I have had to make three data sources, which

was NOT me original intention at all :((

Look at the code below to see what has been done :-

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

It is obvious that I need to do some more research to see it is it possible to have many 'different' views applied to the

same data source in the WPF technology.

Obviously a question for our friend Paul Piko ;-)

"Watch this space" !

What the SDK documentation has to say :

Well, it looks as though my think may have been right all along - thank goodness!

Here is what I have just read in the help notes for the SDK :-

"You can think of a 'collection view' as the layer on top of the binding source collection that allows you to navigate and

display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself.

Because views do not change the underlying source collections, each source collection can have multiple views associated with it.

For example, you may have a collection of Task objects. With the use of views, you can display that same data in different ways.

For example, on the left side of your page you may want to show tasks sorted by priority, and on the right side, grouped by area."

A Possible Solution :

Okay you guys, we seem to have hit it lucky, it is simple enough, you just need to know how ;-0))

Take a look at the following image and study the the code there carefully :-

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

Yes, that does look and feel better, I think we now have three genuinely separate controls, each displaying

different 'views', and all based on the same underlying data source.

I am not as yet sure what the highlighted items mean, BUT, if I were to hazard a guess I would say that this

is the 'current' item - the first in the underlying data source.

Soon, when we get to navigating through the data set we will be able to see these changing - BUT - the books

and SDK docs tell us we MUST remember to set synchronicity to 'ON' in our code.

No Default View !? :

I have it on good authority that when we make our own 'views' and apply them (use them) we then do NOT have

a default view of the underlying data source. The data source is used to make the view, as a 'CollectioViewSource'

object, and this is then used to bind to the GUI controls.

So, because we don't ever bind to the actual data source, the default view never gets created. Performance is

improved by doing things this way as well.

PHH - 27th. August 2011.


Many GUI controls, many 'Views'

Okay you guys, let us have some fun in this subsection on 'Views'.

By the end of this part of the eNotes we will be able to bind to many views which have been generated

from just the one data source, then set different Sorts, Filters and Groupings on each view, and as an

extra also have a button for each of three ListBoxes which allows a user to step through the list items

while the individual items properties are displayed on TextBoxes which are bound to the properties

we on the item objects.

Lets get started !

When I did this earlier today I gradually built up the applet a part at a time.

The first thing I tried to put in place was three list box controls, each with a separate and different 'View'.

here is what I experimented with, and actually got going Okay :-

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

In the above code we are using the 'CollectionViewSource' (CVS) class to allow us to make three new instances of

the class, each with it's source property associated with the same underlying DataTable.

Also in the above code you can see that each of the three CVS objects is associated with a different list box.

However, before we know if this is going to do as we require, we need to be able to step trough the collection items

one at a time.

Putting the 'Step-Throughs' on One Button :

The next part of moving our app on towards a working solution, is to get one button to do all the three steps

at the same time - if this works we can make the extra necessary code changes to have separate buttons and

deal with each separately.

First the three in one approach - study the following carefully :-

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

The above code shows a method to setup the enumerators to be at the top of the list.

It also shows a simple method to move-on each enumerator by one - each time it is used or called.

*** Note *** before we progress to see more detail of the working solution, we may like to study the SDK doc. stuff

on the CollectionViewSource class itself.

Check this out :-

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

In the above image of the SDK documentation on the 'CollectionViewSource' (CVS) class, it is the properties of 'Source'

and 'View', as well as the existence of a constructor, which suggest it may be the class to solve our problems !

We will have already had a quick preview above, of how to use Source and View to make us new and separate 'Views' of the

same underlying data source - we will see more as we take our applet further.

While we are in the SDK documentation, see below the C# code we can try to apply in Vulcan, to make filter conditions for

ourselves, and our SQL server instances little example.

Study the form of this filter approach :-

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

It looks simple enough as laid out in the above image, so fingers crossed we can make this work for us later.

Half Way There ! :

I have moved on the applet, and made each of the three 'Next' buttons work independently on each of the

three views - so enumeration takes place for each.

Study the following image of 'Success' :-

*** Notice *** The three highlighted 'bars' are in different positions. They have been placed as they are

by clicking the three buttons a various and suitable number of times.

We will see a little later how we have improved the few lines of code to handle a button click for

the 'next' item in the collection. It now cycles through the collection, top to bottom, then re-starts,

so we do not get stuck at the bottom, or have any error thrown up either.

First of all, let us look at the extra controls I have added to the applet WPF form.

See in the following image that we now have four text box controls below our list boxes, and

each group is associated with the first, second or third list box and view. Study the details :-

Although in the above image I have used TextBox controls, we could equally have used read only TextBlock ones.

We are not going to edit our underlying data, it is just the basic functionality of binding that we are exploring here.

Now checkout the XAML script which is part of the final working app. See that one set of text boxes are shown, the

other two being VERY similar.

Study in particular lines 20, 23 and 29 :-

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

In the image shown above, line 20 of the script illustrates the approach I used, the Text property of the control is bound to the 'Current Item'

of the bound collection. Note the special syntax in the XAML script - the slash ' / ' refers to the current item of the 'view'.

Obviously, this only works if the 'DataContext' property of each control is associated (in my case in Vulcan code) with the

'view' object itself - see further below for this.

However, I can get the form to function just as well if I bind the text boxes to the selected item of the list boxes themselves.

Study the following image to see the difference in the syntax required -

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

If we choose to use the binding to the list boxes themselves (from each of the group of TextBoxes), then

we do not need to do a 'DataContext' association in VN code.

Now study below, in a small group of images, how the Vulcan code makes this all happen :-

Let us start with the button click :-

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

This when clicked cause the following method to be called and run :-

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

However, the enumerator must be setup correct at the start of the app - I do this from the 'Initialize' method.

First study the 'setup' code :-

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

And now the code which creates the CVS objects and sets up the enumerators, Sorts, Groups, and Filters when we

get around to coding them :-

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

Notice in the code above, we seem to be able to equally use 'View' and 'ViewSource' in the

'DataContext' associations.

I think that basically it works like this - if we use the VS association and no view has been instantiated, then

the default is used, other wise the existing instantiated view is used. Either way then our coding works fine.

Here are a few other necessary bits of code to get things working for yourself :-

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

And that's about it. All now works as we required at the top of the section ;-)

However, for more on Sorting, Filtering and Grouping, we need to go to another section altogether.

But for the immediate, lets move on to the next subsection ..............

PHH - 27th. August 2011.


Ordering - Sort, Filter & Group

Believe it or not, these THREE can be done quite easily once you know how.

Also I have to say I have been disappointed in how little useful information I found in published texts, even

in the well renowned volumes. They seem to mention as little as possible, show one or two simple examples,

and leave it at that.

So to keep you guys happy I will try to approach these topics in a more mature way, and one which is driven by

the demands placed on us by the applications we have to design and create, rather than be 'topic sections' in

a textbook.

After doing much research and testing it has become clear to me that any good success depends on starting

with the correct 'object'.

By this I mean we need to always wrap our collection with a 'view' and there are better ways to do that too.

It is then on the 'suitable' view object that we do our Sorting, Filtering, and Grouping.

Let us now explore the best way to create and use a 'view' object in our Vulcan coding.

Starting with the Right Object :

We are going to use and explore the 'ICollectionView' class - below is some SDK documentation to study :-

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

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

Study the above image detail, and see that the class is all about Navigating items in the collection, as we as

Sorting, Filtering and Grouping.

The Underlying Collection - item source :

At all times we are dealing with a 'view' we need to realise that the collection of items which is beneath the 'view'

is not changed by how we manipulate the 'view'. The 'view' does what it sounds like - it gives us a view of the underlying

data objects - which happen to be custom items, from a collection.

It is also a little more complicated than this - note that we have 'CanSort', 'CanFilter' and 'CanGroup' properties. This would

strongly suggest that the object we eventually have as an 'ICollectionView' (ICV) object behaves differently depending on how the

instantiation takes place.

To my mind I see it simply - we can make an ICV in different ways, and some will allow us to Sort etc. and some will not.

What we need to establish, as soon as possible, is a way in which we can get an ICV which will allow us to do all the

'Order' we wish to do.

Checkout the following image to see the code in lines 147 through 151 particularly :-

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

I will rewrite the important lines for you to make it a bit more clear as to what is going on.

Study carefully the code below :-

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

And when this is applied we see the collection items in their default order.

The default order is indicated from the following Vulcan code :-

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

In the above image checkout the order of the items displayed in the list box and those in the code section.

Final Check :

And so we now need to make a final check that we are in fact able to Sort, Filter, and Group on the

object we have created - see image shown below :-

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

Well, according to the properties of the 'ICollectionView' class itself, we seem to be OK to continue.

This is reassuring to know - so any problems will be our own doing ;-0))

Summary so far :

OK then guys, let see where we stand at the moment.

The approach I have chosen because it seems to me to promise most possible success, is to make a 'view'

object from a 'CollectionViewSource' (CVS) object, which has had its 'Source' property associated with an Observable

Collection object.

The 'view' we will then use is created from the View property of the CVS itself. Note that to make quite separate

'view' objects we have to make separate CVS ones using the same data source - the items collection.

WARNING ! :

If we just make multiple 'view' objects from the same CVS object, then these all have the same internal 'pointers', meaning

they are really the same 'view' object.

So be careful when you wish to have many list boxes showing the same data items, but in different ways. You need quite

separate views in that case. We will see more on this later.

Time to move on, lets go to the next subsection and see how to do some useful Sorting.

See you there ...............................

PHH - 7th. November 2011.


Sorting

Ok then guys, we seem to have a suitable object on which to do our sorting, and as you saw in the last

subsection this is the 'ICollectionView' (ICV) class which offers us much success it would seem !

Here is what the SDK documentation has to say about the 'SortDescription' property :-

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

So it appears that the collection of 'SortDescription' (SD) objects are actually just for one type of sort.

Yes, this was confusing to me when I came to it, for quite some time.

The collection of SD objects it to allow us to apply a 'Multi-Key' sort. The ideas of multi key sorting go way

back to days of Tandy PCs and 8 bit computing.

For the moment we will do our research and testing with a single 'key' or sort description.

It also ought to be recognised by both you and me, that before we start to set a new sort we really should

code things such that any previous sort collection is 'emptied' and existing 'SortDescription' items removed.

Checkout the following details regarding this approach :-

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

So if we follow through this idea and 'clear' the collection, when we add our new and single 'key' sort on

the 'Surname' property we se the following results :-

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

We seem to be doing just fine, we can set single key sorts on various item properties. I suppose it is just as

well to remind ourselves that we can only use properties that below to the items in the collection being sorted.

If you wish to do more complex sorts than this, but wish to keep with this approach, then you could write / create

a specific extra property in your custom object class, which you could then use.

In fact you could make this property quite flexible, where its value is determined by another property of the class

which you set before you use the 'special sort' property itself.

I hope this last bit does not confuse you, I will try to demonstrate it a little later on.

Let us move on to see if we can improve the functionality of our GUI screens.

Two 'views' are better than one !? :

It is quite likely that many of us will need to be able to show the same underlying data collection in separate GUI controls

and have the collection items displayed in different ways.

For the moment we will just be looking at displaying the items by different Sort orders. Later we can move into including

filtering and grouping as well, at the same time.

Study the following image in which we see two separate list boxes, display two different 'views'. Each 'view' is set to a

different sort order - see below :-

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

In the above image I have added a second list box element and simply applied the same, or existing,

'view' to the second 'ListBox' control.

The resulting identical display is therefore NOT surprising.

Now let us move on to making another and separate 'view', so that we can have as shown below - and

that is two different sort orders being displayed at the same time on our WPF form.

Check this out below :-

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

In the above image the code shows that we have made another 'view' - I have had to call it three or '3rd.' as

my existing application code already has a second one ;-0))

By instantiating a new and separate CVS object we then get a different 'view'. This will have a different system

internal pointer to the first view. So 'oView3' and 'SELF:oView' will not have the same pointer - their pointers

will be different, and hence point to two separate objects.

If we had made two 'view' objects based on the same CVS then this would NOT have been the case. The two 'views'

would have effectively been the same (as their pointers would match) even though they would have had different

variable names in our code.

You need to think carefully on these ideas above, and maybe make some experiments of your own.

Multi-Key sort condition :

Okay you guys, we said we would look at "multi-key" sorting so here we go !

Nothing difficult at all - in fact we could say its simple, once we understand what a collection of 'SortDescription' (SD)

object items are doing for us.

Remember we first need to remove any previous entries from within the collection, so in the image below, line 184

does the business for us.

Study the details carefully in the following image :-

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

In the above image you can see we are sorting on two "keys". These are two of our item's properties.

The first SD we add is the major order of the final sort. So we expect to see Surnames 'bunched' together

and then within each 'set' of similar Surnames we get the Firstnames in ascending order.

Look at the 'Smiths' in the image above, then see how Alan, Elizabeth and Jane come in alphabetic

order A-Z.

Looking at the lower part of the same list box we see the following information :-

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

This shows all the Jones folk together and then within the 'set' of Joneses we see < 'F', 'Ja', 'Je', and 'M' >

as the order of the individuals.

Obviously we can have more keys, BUT, the big problem for me is to show that such a MK sort is actually

working. By the nature of my research and demo application creation, I need to keep some things as simple as

possible.

So I leave it up to you do do some more complex sorting ;-0))

Time to move on, see you in the next subsection where we get to grips with 'Filtering'.

See you there ...................

PHH - 7th. November 2011.


Filtering

Before we get into any depp details regarding 'Filtering', here is some success - shown below - and in the

displayed image you can see how simple it is to set a couple filters, just as long as the filter methods have

been written - study the following :-

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

We will not study the code for the two methods just yet, that will come later.

Remember the two 'views' used in the above code are associated with the two different ListBox controls.

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

[ Now something I wrote yesterday, and don't wish to lose. ]

Okay you guys, I have a lot of useful things to say about presenting collection items on GUI elements in

different orders, in various groups, and with certain filters applied.

We just have to be patient and work through the Vulcan code steadily - 'step by step'.

The good news is that after quite some time in the trying I have now gotten all such Sorts, Groups and Filters

to work for me in my demo apps, so I can pass on the knowledge and experience - such that it is !

See the following image for details :-

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

So it would appear that if we have an object on which we wish to set a filter condition then we can first do a test to see if

this ought to be possible.

In the image shown below we do this and get a positive response - check this out :-

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

Without dwelling on problems and failure too much, just be aware that if you do not do this right, and starting with the

most suitable objects, you can get message screens like the following appear - see below :-

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

It would now appear that after Chris's help we have some suitable syntax, but the wrong basic object?

And finally, once again below we see what can happen if we do not get our coding and basic design

correct. I must admit it threw me badly, up till this morning when I came and got it all to work for me,

including Groups too.

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

Lets put the past and the failures behind us, and see how to get success from now on.

Looking on the Bright side of Success :

Right now guys, it is time to focus on the code to write in Vulcan to make this stuff work for us ;-0)

Below we will see some of my code from the demo app I have developed to write part of the subsections

in Section K, on 'Collections in WPF'.

In the successful filtering shown below, we have taken a generic list ( List<T> ) and used this to create an Observable

Collection (OC) - see line 101. This OC was then used as the 'Source' for a 'CollectionViewSource' object - see line 107.

The 'view' then made from this oCVS object ( see line 108 ) seems to be suitable for filtering. Line 113 shows how we can

write Vulcan code to write a callback, which is used to determine if an item is suitable for inclusion in the view.

Here is what the SDK docs have to say :-

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

So in order to make filtering work for us we will need to write a couple of callback methods. One simple one you can see

at the head of the following image, entitled 'Young'.

Checkout the Vulcan syntax shown below :-

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

Notice that the top list box display has a much reduced set of records / rows, on show.

The detail in the lower data grid (of the same image) shows that the top list box entries are just those persons with age less than 30. It

is the 'Age' property which is being filtered.

The method which is used by the system is shown in lines 90 through 95 of the above image.

In the following image we see that we can easily adjust our code to get us a bunch of 'Old guys' - see below :-

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

So we have created another callback method - this time called 'Old'.

Please do remember that the filter condition is on (or applied to) the 'view' and other GUI controls bound to the

same underlying Collection will display what their particular 'view' is set to - if it is indeed a different 'view'.

The underlying collection has not changed, it still has the same number of items within it. And the order of the collection

also remains as it was when it was first created.

Now have a look at the following filter condition which simply gives us the 'Jones' persons. Or as we would say locally

here in Wales UK, "the Jonesies" ;-0))

Check this out :-

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

[ .. Whoops! - line 107 should have been changed to 'all the Joneses in the list' .. ]

In the above image our data is just suitable enough to show that within the list of Jones guys we have a sort order

determined by 'Firstname' A to Z.

Here is why - the Vulcan code :-

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

Note it is the 'ListBox' element which is displaying this sorted 'view'.

Also remember from earlier work that if there is more than a single sort added to the set, we get a "Multi-Key" sort, where

one property is sorted before the next, and so on. The major sort orders are read from left to right, or first to last.

Things that need saying !? :

Okay then folks, unlike a lot of textbooks, we need to "dot a few i's and cross a few t's".

Although we can apply more than one sort description to make a 'sort' we can in fact only apply one filter condition

to make a 'filter'. It does not make sense to be able to see the Old and the Young at the same time. So remember

we can only apply one 'mask' at once, as that is what a filter is, as "data mask" !

However, while we are on the subject, we can Group by more than one 'group description' at a time. So 'grouping' is

similar to 'sorting' in this respect. We get a clue about this when we look in the SDK docs - see below :-

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

Yep! we see details about setting a collection of 'group description' objects - similar to what we saw for sorting.

However, we don't see a collection of filter descriptions !!

I think we should leave things at that for now. Filtering is not difficult to do if you copy and adapt the code you

see in this subsection - and remember to always start with the correct object(s) and then everything will work for you.

Time to move on.

See you in the next subsection ..................

PHH - 6th. November 2011.


Grouping

If we look at the SD documentation we see that we can set both Group and Sort descriptions at the

'CollectionViewSource' level as well as the 'view' level when we have an object based on 'ICollectionView'.

Study the following carefully :-

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

And now the same stuff but from the 'view' class :-

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

If we can we need to find out what is the difference (if any) of setting Sort, Group, and even Filter at the apparently higher

level than the 'view' level. If indeed CSV is at a high level in the tree than ICV.

For now we will continue to work (as we did for Sorting) with the 'view' object.

Grouping on the 'view' object:

First of all, before we group our items we need to go back to our Vulcan code and make our basic data more

suitable for doing tests.

below I have mixed up more thoroughly our Jones guys with our Smith guys. Check it out :-

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

As you can see from the above image the default view - with no groups, filters, or sorts is as in our code.

To produce the above runtime image I set my Vulcan coding to the following - check this out :-

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

Yes, I know, I have commented code lines to try out at both levels, but let us just try un-commenting the 'view'

one on line number 225.

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

Now we can see in the above image that we have 'blocks' of Jones and then Smith.

However, adding a second group description may not do as you may think or imagine. Look at what we

get when we add a GD based on the 'Firstname' property - see below :-

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

As you can see above the names within the existing 'blocks' do not get arranged as in a sort.

To get blocks within blocks (or a group within a group) we need some 'improved' data.

For the moment let us look at sorting the 'Firstnames' while we have the group set on Surname.

Sorting and Grouping :

In the following image we apply a sort "over the top" as it were, of the group description.

To check this out look at the alphabetic order of the Smith and Jones 'block', at runtime. It ought to be in

order of A-Z on the first name - see below :-

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

Okay, I can hear you shout that if the other names were moved out of the way we could see the results better. And I

agree, so we will filter these out below, as well as adding some more suitable names, and mixing them up in the code

for the List<Person> object.

Checkout the following - line 245 in particular :-

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

The persons NOT Jones or Smith have been removed from display, and as you can see by comparison,

the default data order below is now quite different, and not what is displayed in the list box :-

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

If we comment out lines 236 and 237 below we return to the data order of the code, except that the 'blocks' defined

by the group description is still valid - so we get blocks of Jones and Smith, but the other way around.

Check this out below :-

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

I will leave it up to you to work out why the groups are now Jones then Smith, when they had been the other way when

the sort description for 'Firstname' had been set on.

Summary - how good are we now ? :

Okay then guys, you probably missed it above, when we had all three types of ordering taking place at once.

Yes! in the image two before the last one, we had a Sort, a Filter, and group all working together.

So to my mind we have come a long way. Well done ;-0))

Before we leave this subsection let us see how we can place the Group Description objects in a collection at the CVS one.

Grouping on the Collection View Source object:

Up till now we have been using the Filter property on the 'view' object itself - in our case this was a object of the class ICV

or ICollectionView.

However, we can also use the Filter property of the CVS object (CollectionViewSource) as show in the following image :-

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

Notice that we have the three Smith persons together - BUT - as we well know from previous testing work,

our data needs to be improved - or made better.

I will enlarge the list box so we can see the result better below :-

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

Yes, we can see Jones together further down the list, but we need better data and a filter too.

Below is the same grouping applied to the improved code, where extra data has been inserted etc. :-

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

Guess what, I think I am part way to answering the thorny question - where to place the Sort/Group/Filter, and why !!

If we change the above code simply to change lines 245 and 246 then we get a different order displayed.

Check this out below :-

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

Although the difference is obvious, I am not too sure what to make of it, except to say that the description placed in

the higher tree object level has priority over the order description of the lower level.

I think it is time to leave this alone - enough for one day, and one conference too ;-0))

See you in the next subsection ....................

PHH - 7th. November 2011.

Addendum :

Just a couple of tests to try out settings at both Tree levels.

The lines 245 & 246 are changed around as to which group descriptions they have.

Checkout the following two images below :-

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

And now when we swap over the oPGD and oPGD2 we get this :-

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

This once again suggests that if there is a setting at the higher tree level then it takes priority.

I feel there is much more to research in this topic area, and much more needs exploring - BUT - we are writing code which works,

and we are doing it regularly, even if we do NOT fully understand our results.

PHH - 7th. November 2011.


Further Filtering

We should be aware by now that we can filter at both 'CollectionViewSource' level as well as the 'view' level.

This subsection is important simply because the Vulcan code syntax for filtering on the CVS class of

object is fundamentally different to what we saw with the ICollectionView class.

So, another way to set a filter, and this time using an event handler.

Study the following thoroughly, before we see how to use it in Vulcan code / syntax :-

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

Okay then, not that difficult, we have done stuff like this before - BUT - notice that this is done to the property which

is directly part of the 'CollectionViewSource' itself, NOT the 'view' which is associated with it.

Before we go into the fine details of what the difference is doing things this way, let us first see how we get the code

to work correctly for us :-

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

So line 150 is the key line to study, that and the bunch of lines numbered 118 through 128.

Also we need to remind ourselves that just as a ' += ' makes an association, a ' --= ' removes one. This means

that the following Vulcan code will effectively have no filter condition set - check this out :-

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

Yep! - it is lines 153 and 154, which effectively cancel each other out!

When to Filter, Where ? :

And so to that 'thorny' question, which of these filter approaches to take, and when, and for why ???

Do we filter at the 'view' level, of at the CVS level ?

Just be very careful, it is easy to very mixed up and confused with all this, so before you go launching off

wishing to apply filters, sorts and groups, think again.

I suggest that until you become experienced you take one step at a time. I would also suggest you test out thoroughly

what you do "code up" into a solution - just to see that it actually does what you intend it to.

My second thought on the two filter approaches ( I scrapped my first thought, it was WRONG) is that you use the filter on the

'source' if you do not have at hand a separate 'view'. This seems a tame idea, but if I set filters with both methods the one set

last seems to 'win' and so both must be accessing the data at the same 'level'.

Before we get anymore confused, let us say ALWAYS make a 'view' and always set you Filter, Sort and Group on the 'view'

unless you need to do otherwise.

You need to appreciate that we have learned a lot in this section and far more than is easily available in even 'good' textbooks.

I suggest we use it for real for a period, and then revisit the topic to learn more later.

Time to move on, see you in the next section .................

PHH - 7th. November 2011.

Addendum :

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

Only one filter can be applied - even at different levels.

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

Wow ! - different result for a different code order - only lines 263 & 264 have been swapped around.

I need to think and have a strong cup of tea !!

PHH - 7th. November 2011.


Section H : Serializable Classes

Start with a Simple Truth :

After you have coded for a short while in dotNet and Vulcan you will fully appreciate the benefits of being

easily able to take an in-memory custom object and 'dump' it to a disk file.

It goes without saying that you will be even happier when you can also easily 'point' at the disk file and re-make

a custom object in memory.

This process is called "Serialization and Deserialization" (SnD).

So far in these eNotes we have seen the use of 'SnD' to keep configuration details for SQL server connections.

However, we can design many classes of object to be serializable. We just need to know how to go about it.

How to Specify Our Own Classes :

If our classes are comprised of simple, or basic, dotNet data types then life is easy indeed. It is just when we

start to get fields and properties which are complex data types, that we need to take some care - and do some

initial testing too.

Let us view the serializable class I made for my invoices in the Small Trader app. - study the detail carefully :-

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

Sorry it's so longwinded, but then that's the reality of coding !

Notice the data types are ... String, Double, Boolean, DateTime and Int. Nothing fancy here

at all, in the invoice class for serializing.

Let us now look at how I have used this in the application to dump and invoice object from Small Trader, so it may be

picked up and used from another application called 'BESTinvoicePrinting'.

In the image following, we see both of the two EXE files, one which serializes the invoice object, the other which then

deserializes it for use in the printing process :-

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

You may have realised that the XML file called "Inv2Prnt" is the serialized file, and it's name stands for

'Invoice to print'.

Now let us move on to see how the Small Trader creates the XML file.

How to Use a Serializable Class :

If we study what has been done to print an invoice in the Small Trader application we will start to get a feel for

what needs to be done.

Let us first look at the short method which controls the dumping of the invoice data to file.

Checkout the following image :-

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

The above image shows that we first create an object of a serializable 'type'.

We then populate the fields of this object from suitable and available data objects.

Then, when the serializable object is complete, we process it in a serializable way.

In this example, the last thing we do is to start an external process which is to launch the second application

to carry out the printing.

The purpose of me doing things this way was quite simple, I already had the code module to print an invoice, it

was part of another application I use regularly. So lazy or time saving, whichever you care to label me ;-0))

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

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

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

How to Deserializise from a File :

Obviously we need to have the serializable Class available in our second application code, it is

needed for the deserialization process.

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

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

PHH - 24th. August 2011.


Possible 'Gotcha' !

Although Serialization works for me in Vulcan.net, and I have been getting good result for a number of years,

since Paul Piko introduced me to it, I have just found a situation in which i got 'strange' and unacceptable results.

Below is an image of a Serializable class where the properties have been commented out. Study it carefully :-

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

I found that with the properties defined (Access and Assign uncommented) I got a double entry in the XML file

of the data I expected.

Although I thought Serialization only 'dumped' the data in the fields, I found that I had an XML file section for both

Seats and aSeats. This was also true for Parties and aParties.

I had first thought me problems were to do with me having the data in ArrayList as alSeats and alParties, before

I changed this. But my problem persisted.

There are a few differences in having to deal with ArrayList, and I will show you later how to write the code to Serialize

these. I got my inspiration from Alwyn's PDF book on Vulcan - very useful it is too.

Now that I have removed the cause for my duplicated data I see the XML file to look as follows :-

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

Above is the start of the file, and an early entry in the aSeats 'typed' array.

Below is the end of this file and we see an entry at the end of the aParties 'typed' array. See below :-

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

If you look at the image below you will see that the XML file is of size 132 KBytes and not the double of this

that I was initially getting - see below :-

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

This behaviour strongly suggests to me that it is better to design a class specifically for the job of serializing. By this I mean

that I would create a class solely for the purpose of serializing, and that it would have Export (or Public) fields, and Constructors

only - no methods and properties. Remember it would need to have an 'empty' constructor whatever else I provide - this is

one of the requirements for serializing.

Take the Customer class as an example. I would design and create a Customer class for general use, but then another one,

possibly called 'S_Customer' which would only have the fields for the data.

What I would then do is write some code that would turn the general class of Customer object into an instantiated object

of the S_Customer class. Then I would serialize using this 'S_' class object. Then hopefully any duplication would not occur.

PHH - 24th. October 2011.


Example 1 - Hall Venue Seat Booking

Arrays and ArrayLists :

If we are going to do this then we may as well also make any ArrayList fields into Arrays, as this is easier and more likely to

succeed - as far as us getting the code syntax correct.

Before we move on to using ArrayLists study the code shown below which is used to serialize the class without ArrayLists.

Study the following carefully :-

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

Notice in particular lines 410 through 413.

Getting the Syntax Correct for ArrayLists :

First of all let us look at some success, where we have an XML file made from ArrayList data.

The file is overall in size about the same as previously, but there are some extra characters because of the small changes made.

Study the following image carefully, where we have made a new file called 'HallVenueAL.xml' :-

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

The calling code was used when the main window was closed - check the following :-

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

So it would seem we now need to examine closely the method called 'SerializeHallVenueAL', which

is shown below in detail :-

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

In the above code image we can see that at lines 410 through 413 we have had to add some extra information.

The new class of 'HallVenueAL' does not now have any reference to what type of class of object is being held in

the array lists, and so we need to supply this in an alternative overload to the 'XmlSerializer' class.

It would also appear as though the following code also produces the same XML file content - checkout the following :-

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

But if we leave out one of the classes within the ArrayLists we get the resulting error shown below :-

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

Maybe I got carried away by using Alwyn's example too literally - here is what I was following :-

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

So if we make sure we supply the extra class types of Seat and Party, all is well for us.

Now we had better take a look at how the new adapted class called 'HallvenueAL' looks - see below :-

=====================================================================================

Although we seem to understand a lot more about serialization, and we have some more complex successes, be

warned that when we get ArrayLists within objects in an ArrayList we have a more difficult and complex situation to

deal with. It may just work fine by adding more class types to the 'aTypes' input parameter - BUT - my feeling

is that making specific simplified classes just for the purpose of serialization is more likely NOT to fail, or let us

down when we need to succeed.

So good luck with your serialization, and remember to Deserialize with the same class as you used for Serialization.

Time to move on, see you in the next subsection ..............

PHH - 24th. October 2011.


Section I : Finding Intances of SQL server

=====================================================================================================

============================================================================================================

========================================================================================================

============================================================================================

==========================================================================================================================================

PHH - 24th. August 2011.


Section J : Installing MS SQL server

PHH - 2nd. September 2011.


Section K : Collections in WPF

Okay then you guys, are you ready for this topic section ?

In some ways this is a difficult section and in other ways an easy one.

If we don't get too carried away about wishing to know all the classes that can be termed as a 'Collection' class,

and instead take more the approach as 'how can they be best used' to solve the developer's coding problems - then

we are well on the way to getting to grips with 'Collections' - and having some fun too.

Often throughout the SDK documentation the word 'collection' is used to mean a group of data objects.

So we can think of collections as being the data 'group' contents of everything from System.Arrays right through

ArrayLists, to the much more modern collections of dotNet 4.0 and WPF related GUI controls, like ObservableCollections(T)).

The well discussed 'ItemsSource' collections of the WPF controls on the GUI, are often what developers today think

of as 'collections'.

So we will need to deal with all of these as we progress through the topic. Let us first reflect on some history.

General background to Collections - the History :

Let us 'stand back' mentally form any specific details of dotNet and consider 'point A and point B'.

Point 'A' is the world we are leaving behind, and point 'B' is the world into which we are moving.

We used to have some structures into which we put data and now it would appear that 'objects' are everywhere.

Before objects we extensively use the array structure to hold just about everything - I know I did ;-)

Now I rarely, or never even, use it as I find objects generally much more useful.

I am NOT saying that there is not a use for the array, with its index access, and in Vulcan we are blessed with more than most.

However, in the modern world objects are king ! So it is not surprising that Microsoft are making it easier and easier for us to

deal with objects and groups of objects too.

I would guess that when we talk of 'collections' we usually think of a group of similar objects, BUT, many of the modern

collections can have quite mixed object groups, simply because it see each item in the group as just that, an 'object'.

It would also appear that objects are some to be with us in the world of 'Denali' - MS SQL Server 2012, as well as with Entity

Framework 4.0 (which is here already) and with LINQ to support stuff too.

On top of all this we have had WPF and object related GUI technology for years now, with wonderful GUI data binding and

a comprehensive Dependency Property system to back it up.

Using collections the modern way we should be spared writing all those tedious For/Next loops and extracting elements like that.

We do seem to have to give up the idea of knowing exactly where each item is in a collection (unlike arrays) but this is made up

for by the ease of which we can sort, filter and group on the 'view' of any collection.

So just in the way that we don't know where our SQL table rows are, and don't expect them back in any order (unless we specify it),

we do the same for our collections and items - sort and filter as we see fit, over and above the collection itself.

Lets move on a pace .....

What have they got in common ?:

Well one simple thing is that they can all be 'enumerated' in some shape or another - certainly if they are similar in nature.

Meaning we can easily move through the 'group' of items in the collection, in a 'Read Only' fashion, and do as we

please with them as they become the 'current' item.

They all take a 'View' as well - the 'View' being an object which effectively wraps around the data source, or collection source.

Collections have a 'view' whether we make one or not - it is the default view.

If we choose to use the 'view' for our own purposes we can do some nice things - but one is still their in place, even if we think

we have ignored it.

It would seem to me that the WPF and dotNet system needs a view to be in place for a collection to work - it must be part of the

underlying design. SO if it is good enough for Microsoft then it should be even better for us - let us always use the 'view' we

have available to us for our collection.

Be aware however, that like all things in the developing world of dotNet, their are older types of 'view' and those more modern

ones with greater functionality and possibly greater flexibility.

What should we be aiming at using ? :

Well, I realise that we may not wish to go the whole 'hog' each time we use a collection - BUT - issue related to hardware performance

are to some degree 'old hat' more that we have such powerful PC platforms and such large memories.

So to my mind I will try and use a type of collection which as as much power and flexibility in it use as possible. Quite often we don't

know exactly what we demand from our collection when we code it, so have plenty of spare performance, in terms of functionality, seems

to me like good sense.

When I create a business object class these days I always try to make it a dependency object and code all meaningful properties of

that class as DPs (dependency properties).

Also, with the new functionality in the forthcoming version of Vulcan (208) I will also be able to code into my BO classes the

'INotifyPropertyChanged' event for use with each property. This will mean I can use the objects of such classes just about anywhere

in my GUI binding and expect a LOT of their related behaviour.

Also, if and when I do group my BOs into collections I will try and make the Observable Collections - see below for more details :-

================================================================================================================================

[ .. please note that DataGrid should also be included in the 'ItemsControl' list of GUI elements, just that at the time these notes ]

[ were written the DataGrid was still at the development stage or in the Toolbox ! ..]

The following image suggest my hunch is right - not easy to find on the web however ! See below :-

======================================================================

I have highlighted the 'ItemsControl' line of the hierarchy, just to show that DataGrid is as I said above.

And as if you may need more evidence of the truth in all this, compare the inheritance hierarchy of the ListBox control

which I have illustrated in the following image :-

=================================================================

Okay then guys, time to move on to the next subsection, see you there .......................

[ ** Note ** it is not easy to put the subsections into one sequential order which makes total sense so be understanding ! ]

PHH - September 16th. 20011.

Addendum :

I thought the following SDK documentation may be of help to us trying to understand Collections.

However, please realise that I found it in the .NET 3.5 documentation and so the 'ObservableCollection' (OC) type was not

around when these notes were produced.

So this means that failure to mention OC does not mean that it may now be a better one to use for WPF data binding.

Study the following carefully :-

=======================================================================================================

Then the following information comes in the notes just after that shown above.

Read these thoroughly :-

======================================================================================================

I suggest you locate these notes for yourself and then follow them up by clicking on some of the more interesting 'hot links'.

Good Luck,

PHH - 29th. October 2011.


Ten Points about Collections

Ten points about using Collections :

- whenever possible us the 'ObservableCollection' (OC) type, it is the most modern, and also flexible to use

- make your OC from a generic list object (List<T>) or directly by adding custom items to the OC itself

- always use a 'view' and always make this a unique 'view' by using the 'CollectionViewSource' instantiation approach

- try NOT to use 'CollectionViewSource.GetDefaultView' if at all possible, especially when doing complex GUI design

- bind the collection to the GUI controls in dotNet code and by associating the 'DataContext' property with the 'view' of the collection object

- to sort the GUI control data rows, use the 'sort descriptions' on the 'view' object

- filtering can also be done via the 'view' object

- navigating can be done at both 'view' and 'collection' levels using enumerators

- the current item of the 'view' is shown in the GUI control by highlighting of one of the rows

- removal and addition of items to and from the collection will be reflected in any and all 'views' including such items in their sort descriptions

==========================================================================================================

[ extras - the OC type has enough suitable event handling capability to work correctly in all necessary places within the GUI]

If using Child Forms (or Child Windows), make sure you set the 'Owner' property of the Child form to b 'SELF' in the parent calling code

Make sure that all custom class objects in the collection have suitable DPs declared, and also an OnPropertyChanged event plus handler too

PHH - 30th. October 2011.


What are they how are they used ?

What are they ? :

Well, as we alluded to in the previous subsection, we can think of collections as groups of objects.

Most often we will groups of similar objects - so we can say the collection is made from items of the same

type, or class.

This above doesn’t always have to be the case, but in our business logic we often see for example a

collection of 'Invoice' objects, or 'Orders' and we can imagine many others too.

So let us concentrate on the case where our 'group' is similar, and ignore for the moment any special

situation (which does exist) in which we have invoices, order and customers all mixed together.

How are they used ? :

Most often in our GUI forms we will have controls which are 'ItemsControls' like Lists, Trees and Grids.

These GUI controls we need to bind to collections of our Business Objects, such that we get for example

a drop list of Customers. The Customers in today's modern IT world will be 'customer' objects and what is

displayed in the list or other controls depends on what we make as the display property, or to get more complex,

it depends on the XAML data template we create for the GUI element.

We can do some nice things with items displays and the item template - to illustrate this look at the WorldTime 4

app from DevShare 2011 - see below :-

Yep! a long way from the ComboBox controls I used to have in VO.

[ .. What a collection of 'mug shots' ! ..]

More often than not we will have a table or grid display and from the displayed item collections select one

which is then shown in full detail within other elements, or controls.

In the following example I think we also had the grid/table Read Only and the TextBox controls editable.

Check this out below :-

Moving on to parts of the Small Trader (ST) application itself, we see other common examples and illustrations.

More often than not however, you will see GUI stuff like that shown below :-

============================================================================

In the above image we have a collection of customers - the data source is an ArrayList - and the displayed member

is a string concatenated property probably called something like 'Fullname'. (yes, just checked the code PRG.)

Note that in this image the same collection is bound to the data grid and the drop list.

However, because I designed and made the 'Customer' class some time ago (18 months plus) I did not make it a

dependency object with Vulcan code dependency properties - as I would if done today.

Also, there is no 'INotifyPropertyChanged' event, which if coded on Monday next week there may be even in VN2,

as there is a new Vulcan release planned which will have an upgraded compiler which will provide the coder / programmer

to use new events as required to do the above. Then I will no longer need to dip into C# just to make a standard BO class.

Collections and Enumeration :

Quite often we use collections by binding to GUI controls, and the interaction is done by the user, selection from list, trees

and grids etc.. However, with a view we can also choose an order of the collection, and even grouping, as well as filtering

of the items as well.

If on top of the 'view' we cycle through the 'viewed' collection we get each item in the 'view' but also in order that we have set

within the 'view'. So why worry about any default order when we can control exactly what we want to see as far as order is

concerned.

To me, it seems that use of an 'enumerator' is very much a 'collection thing'. I am starting to use them more and more when

selection is required outside of the user interactive 'selection' or screen choice.

Remember, enumerator use is a Read Only activity. Edit and change the items of a collection via data binding and a suitable

GUI control like a 'TextBox' element.

For more information on Enumerators and enumeration generally, see the subsection at the end of this main section 'K'.

Quite often Enumeration and Navigation (as we got to call it in Win Forms) overlap in what we wish to do and see happen.

In the modern 'view' classes we can quite often navigate without need to call up, or create a special enumerator.

Lets have fun with all of this stuff later, seeing examples of doing things both ways.

Now we need to move on to see some suitable examples.

See you in the next subsection .....................

PHH - 17th. September 2011.


Samples to Try

A good number of useful downloadable examples from Microsoft :-

http://msdn.microsoft.com/en-us/library/ms771560%28v=VS.90%29.aspx


Items Collection

===========================================================================

==============================================================================================================================

PHH - 31st. August 2011.


ItemsControl GUI elements

PHH - 17th. September 2011.


'Changing' Collections

=========================================================================================================================

===============================================================================================================

=========================================================================================================================

system collection ?

List(T)

Collection(T)

BindingList(T)

PHH - 29th. August 2011.


Data Sources - historical approach

Let us ask ourselves a simple question.

"Which data sources can we use for effective 'Data Binding' ?"

Meaning, what classes within dotNet can we use to hold our data, and still have effective Data Binding.

Well, the answer to this means that we need to take an 'interesting' approach, that of a historian.

If we understand when the classes came about, and how they fit into the general time progression of the

development of dotNet, then we are part way to know how to use them and what they will do for us.

Let us take two classes to start with, the Array and the ArrayList - both been around since before dotNet 2.0

Here is what the SDK has to say for Array :-

==========================================================================================================

And then following this a bit further we see :-

=============================================================================================================

The reason I start so far back, and look at the above notes, is that they suggest to me a change from dotNet version(s) one,

through to 2.0 for the Array, and also we see the word collection starting to be used.

This gives us a toehold into understanding how Microsoft keeps all this dotNet stuff moving forward. They use interfaces

to provide modern functionality for some older classes, it would seem.

And finally for the Array, note below that we see Enumerators being mentioned. And these are a feature of collections :-

==========================================================================================================

Now, moving on to the ArrayList and List<T> classes.

Well, ArayList was around before dotNet 2.0 and List<T> was new to 2.0 - see the following images :-

============================================================================================

But from the same SDK documentation we see the following for ArrayList :-

============================================================================================

Notice above that there is no red typeface and no suggestion of it being new to dotNet version 2.0.

What we do see in the above image is that 'ArrayList' is in the System.Collections namespace.

So we are talking about, and dealing with - a 'Collection'.

Note that in the SDK documentation List(T) and ArrayList are often discussed as if they are VERY similar, and

this can hide that fact that they appeared at different times in the dotNet evolution.

We had better run our eyes over what is said about these two classes - checkout below :-

=============================================================================================================

The ArrayList class is in the System.Collections namespace as shown immediately below, but the List<T> class

in in the System.Collections.Generic namespace as seen a further image below this.

Check them both out :-

========================================================================================================

It is important to know the interfaces provided to support these classes :-

========================================================================================================

And now we need to look at the historically more recent 'generic' class types - see below :-

[ I don't know how many of these first appeared in dotNet 2.0 .. but since Queue is also new I would suggest the ]

[ whole namespace itself ... ]

==========================================================================================================

Here is the 2.0 SDK documentation entry for Queue :-

======================================================================================================

And we must not get too diverted by our history lesson, we still need to know about List<T> so study the

following image details :-

==============================================================================================================

Okay then, enough of history, lets move on ..........

Most Recent of the Collections :

In the most recent version of dotNet, that is version 4.0, we see a new class called 'ObservableCollection' and this

is the namespace 'System.Collections.ObjectModel'.

When we look at the SDK 'blurb' below we notice that we immediately get information about 'Dynamic data Collection'.

Check out the following for this new and modern collection class :-

====================================================================================================================

I suggest you read carefully once more, all the above information, it is VERY important to how things will work in

data binding at the most 'feature full' - by this I mean if we need to have items added to and removed from our collection,

and changes reflected in other bindings to other GUI elements, etc. etc., then we need to tap into all binding features and

functionalities.

The very last line is 'crucial' - that about 'INotifyPropertyChanged'.

It would appear to me that the 'ObservableCollection' class has a lot to offer most of us developers.

I for one, intend to use it a lot. lets move on and see some action ;-)

Working Example :

It is always easier to understand things when we see them in action.

Let us first look at a suitable collection which is made within a STATIC method of the 'PersonsCollections' class

of my own making - see below :-

=======================================================================================================

The above image shows that a number of Person objects are instantiated and then placed into the collection.

In fact I see that 14 items have been added to the previously empty collection.

Here is the outline of the very simple class called 'Person' :-

============================================================================================================

Notice that we have no Dependency Properties declared and defined, and no INotifyPropertyCahnged events

being used either.

However, this simple class when used within the 'ObservableCollection' seems to do a lot of what may be

required in many WPF forms.

Here is what it looks like at run time :-

As well as good navigation and even sorting of the columns, we also get full functionality when

we add new rows to the grid, and hence items to the collection.

Study the following image where the latest (and best?) code is shown as well as some commented trial

stuff which shows what worked but did not always provide the required interactive functionality.

If we miss out using the 'view' with the TextBlocks we do not see the navigation taking place.

Check this out :-

=============================================================================================================

Interestingly enough, we can make the 'view' from any of the three STATIC methods and the

functionality seems the same for each - but this is before we really push the functions of adding and removing

collection items via the GUI.

After all, "we only get what we pay for!" ;-0))

Let us see the XAML script lines for this :-

==============================================================================================================================

Since the script shows binding for Grid and TextBlocks, we then need to associate the 'DataContext' property for

each in both cases :-

=========================================================

Since we will see more about the 'ObservableCollection' class in a subsection a few on from here, it is probably a

good time to move on, after saying "don't forget your history" !

See you in the next section .......................

PHH - 20th. September 2011.


All Different - All the same !?

One aspect I did not focus on in the main subsection on data sources, and their type, was the sorts of

object that is gathered into the 'collection'.

Quite a common situation for us developer guys using collections is that we need each and every item in

our collection be be similar. That is, the the items are the same class.

So a collection of customers, invoices and orders is commonplace but one where we have a mix of types

is probably not so common to us.

Yes, we can thing of good examples - like the 'Removal Van' (RV) class, where we need to add items which are

quite different, like table, chairs, washing machine, plant pot, and dog bowl. All are quite different classes

of object, but all are contained within the RV object.

List<T> and ObservableCollection <T> are both useable for the first scenario - all similar objects.

However, the second scenario that of the RV is not solved by using the above, BUT, the good old ArrayList can

have a collection of mixed types.

It would appear to me that the first scenario is better covered by the available classes, since we also have

Collection<T>, BindingList<T> and probably a few more too.

I will need to research to find if there is a modern replacement or update for the ArrayList, or whether it still is

as good as it was - simply I imagine - because lots of possible functionality is missing BECAUSE the items

in the collection are all different, or at least mixed.

Watch this space :-

PHH - 20th. September 2011.


DataTable to Collection

PHH - 20th. September 2011.


CollectionViewSource class

PHH - 29th. August 2011.


Views and their use - Navigation

If I remember correctly in WinForms there was one, or more, specialised control to handle navigation

related to binding on the form.

In WPF there is a much simpler solution.

The 'view' that is made from the data source collection has already got all the necessary members to allow

us to easily manage our own navigation.

We can quite quickly move from doing simple back and forth movement of the 'pointer' to the current item in

the collection, to more complex stuff using position selectors, using drop lists, spinners and even sliders.

Let us start with a couple of push buttons - as simple as it gets - see below :-

===========================================================================================================

For any GUI control to which the collection is bound, we need to set a property to make the pointer movement

synchronize correctly.

Check this out :-

===============================================================================================================================

For some reason the default is less than helpful to the developer :(( - and other authors lament as well !!

In the above images the content controls display the property values of the selected item in the list box element,

BUT, there is an alternative syntax available, and that allows us to bind to the item which is 'current', or to

which the collection 'pointer' focused.

Here are samples of both approaches - check this out below :-

=======================================================================================================================

Obviously, it is the 'slash' or ' / ' syntax following which relates to the 'pointer' :-

====================================================================================================

===================================================================================================================================

As yet we have not go enough test / sample record items to allow us to checkout the method entitled "MoveCurrentToPosition".

We can do this later, and perhaps bind a slider to the min/max values of position.

PHH - 14th. September 2011.


Enumerators and how to Enumerate

PHH - 28th. October 2011.


the List<T> class and collections

Read the following SDK documentation to learn why we all ought to be using List<T> and giving up ArrayList.

"The ArrayList is dead, long live List<T>" ;-0))

Study the following carefully :-

=======================================================================================================================================

My reading of the above is that I should now abandon using the ArrayList class and use List<T> in its place.

I have no problems with this at all - lets get to it I say !

One simple benefit is I won't have to keep casting my 'ArrayList' items, and also Serialization should work easier simply

because I won't have to specify the class of the contained objects. Well, I hope this is the case, we will have to see what

our experiments throw up.

So if performance is not an issue to worry about, we had better just get on and checkout the syntax requirements for

our coding in Vulcan.

What to test for in code :

First of all let us write some Vulcan code which instantiates - or creates - a new object, of the class 'List<T>'.

Notice that the syntax throughout the method is similar to using the 'ArrayList' class :-

==============================================================================================

So having made a List<T> object ( called 'generic list' ) we can then use it as shown below :-

=========================================================================================

Note that lines 31, 37, 38 are the crucial ones to this simple test of counting the items in our new collection.

In the images shown below we will go further with our simple tests, let us now checkout the Enumerator :-

=======================================================================================

Above we have seen how to create an object which is an Enumerator, and we will see this used a little later

in this notes subsection.

For the present, let us try out the array index syntax, to see how that works for us - see below :-

Okay then, above we see us accessing the fourth item of the collection, that with index value '3'.

So if we wished we could cycle through the collection using array syntax, or as shown below, use the power

of the Enumerator.

Check this out :-

============================================================================================

For the simple test shown above we are using the custom 'ToString' method which I made for the Person class.

To remind us what we have, study the following image :-

==============================================================================================================

If we wish to access the properties of the collection items during enumeration we need to code with

syntax like that shown below :-

[ .. checkout lines 57 & 58 particularly .. ]

====================================================================================================

Yes, it does look disappointing to my eyes - why? - because we have had to cast the 'Current' item as it

is seen just as an object otherwise.

I had been hoping that because we have a strongly type collection, by having a generic list, that the system

or compiler would be clever enough to do the casting for us :(

Shown below are a couple of screen shots of what we get as error messages from the compiler when we fail

to make a cast in our code - check these out :-

===========================================================================

And then if we use a specific variable declared as type Person :-

=====================================================================================

Either way, the above images suggest to me that we need to cast when using the enumerator.

Let us keep our fingers crossed that we get a better result when we try Serialization, and that we don't

have to provide a type there as well. remember with serialization we are trying to get an improvement

over the way 'ArrayList' is handled, where we have to declare list of types for the serialization to work.

So finally, below we see a simple test of accessing and using two separate fields from our cast item

which we are accessing via an Enumerator.

=====================================================================================================

Now we need to see if the access we have to the enumerated items is limited to R/O (read only) or if

we can write to the properties, and so do changes 'on the fly'.

Let us set up a simple test and see for ourselves - checkout the following :-

=========================================================================================================

And later in the execution of the application we see the main screen looking like this shown below :-

The above image details proves that we have certainly made lasting changes to our collections items, so

the access to the Enumerator elements is certainly not R/O in this case.

However, take into account that this may be different with using other Enumerators, in different circumstances. You

will need to carry out tests for yourself as part of your own application development process.

Converting the Generic List - to an 'ObservableCollection' :

Okay then you guys, one of the great things about using the generic list is that the 'ObservableCollection' (OC) class has a

Constructor which takes a generic list as an input parameter.

Because the OC class is later, and only part of .NET 4.0 it seems to have superior performance and functionality.

So if we have a List<T> we can make a new OC object as easy as pie !

In fact if you have been looking at the code shown above, in any detail, you will have noticed that this is what we are doing

here in our little test app.

The important lines are those shown below :-

===========================================================================================

[ .. Whoops! - line 38 is 'dead' and should be commented out ! .. ]

Notice that line 39 is the most important one, as this is where we do the conversion.

Well, I suppose it is more of a 'Clone' than a conversion, as the original still stays exactly as it is, AND we get

a new OC object as well - extra to the original object.

Proof of what we think !? :

Okay then guys, you know my approach by now, I am a scientist and like to make experiments, and do tests

designed to prove or disprove things.

Let us now do a simple test to see if the OC object is in fact totally independent of the original generic list.

Checkout carefully the following image, in all its details :-

[ Yes, I know, I have done a little screen trickery by running multiple instances - but each is genuine and real ! ]

==============================================================================================================================================================

[ .. Whoops! - line 38 is 'dead' and should be commented out ! .. ]

In the above image you can see that after creating the OC object from the generic list, we reset the generic list back

to an empty one.

So by the time the execution process gets to lines 44 and below, we have a genuine List<Person> object which is empty

of collection items.

However, the main screen (shown in the background) still has all the original Person items showing in the GUI controls.

Remember the GUI controls are bound to the OC object.

Why make an 'ObservableCollection' ? :

You may have wondered while following the previous notes and seeing the experiments I made, why we are wishing to be

able to make an OC object from our generic list.

Well, the answer is quite simple and straightforward.

The OC class is a new dotNet 4.0 class and is very good for handling bound collections, and when we have complex GUI

control binding on our WPF forms, and also with Child forms involved as well.

If we wish our Collection and item Property binding to work as we require, we need the correct collection type to follow collection

changes - and keep in synchronisation - as well as having any item property changes shown up too.

I think we need to do a parent / child form test where we have an OC object bound to many GUI controls, and across the forms,

and see how this behaves at run time.

In the following image we have such a test scenario - one grandparent (in the very background, then the child (in the middle ground)

and then the child of the child (right in the very front).

Study the image carefully and also the detail within the GUI controls :-

[ .. note that one extra row, or record, has been added - that for Philip Hepburn - and the collection is sorted by Age descending .. ]

When I made the addition in the grandchild form, once I moved off the 'current' row (that being added) the addition

immediately showed in all the other GUI controls, on all of the Forms - Grandparent, Parent and Child.

Now we need to see some code which allowed me to do this - see below :-

==========================================================================================================

One consequence of the child forms having an owner is that when they are opened they appear in front of the

parent Form - so no more hidden windows ;-0)

============================================================================================

Notice that in the above code we are not yet using 'views' - apart that is from the default 'view' that the collection will

be using. Later we need to experiment with many more views, and apply then separately to the GUI controls. This

way we can have different sort orders in various and different controls, while underneath keeping the same collection

object.

For the moment, lets look at an output which comes from the above test application when we open a whole host of

cascading child forms - each from the last Child which appears on screen - check this out :-

One nice thing about how this is coded and structured, in order to clear the monitor of all Forms I just need click the red

close icon on the very first window - the one at the VERY back of the group. Then BINGO ! - all is gone !!

Why bind to 'ItemsSource' instead of 'DataContext' property ? :

We are going to explore these two properties by consulting the SDk docs for the ListBox control - see below :-

========================================================================================================================================

And now what is said about the 'ItemsSource' property :-

========================================================================================================================================

For now I will stick with use 'DataContext' whenever I need to bind.

It has served me well and has seemed to work under many different circumstances.

Time to move on to another subsection I feel - see you there .........................

PHH - 28th. October 2011.


the 'ObservableCollection'

At this moment in time, when we are using the latest public release of dotNet which is 4.0, the most powerful

and flexible Collection for us to use is probably the 'ObservableCollection' class.

When we read the SDK documentation it seems to offer us the most in solving any data binding problems while

also using Collections of our custom class objects.

Here is some SDK blurb from the .NET 4.0 documentation :-

================================================================================================================

And we had better also read carefully the following too :-

================================================================================================================

So where does this leave us ? :

It seems quite simple to me, we need to know just how we create and fill 'ObservableCollection' (OC) objects.

Earlier in another subsection we saw one way to make an OC class and object, but we need to explore others.

Let us try and instantiate our own 'empty' collection.

Before we can do this we need to choose a simple custom class, so I have added the Person type of my own, that

I used some months back in Pearls.

See below - for how to instantiate - the code does compile and run! :-

[ WARNING ... the name of my Window class is 'ObservableCollection' simply by my own choice, 'silly me' - yes, possibly a

VERY bad choice under the circumstances - sorry !! ]

=====================================================================================================================

The reason we have tried the above code is because for some of the related collection 'stuff' we are told we

can't instantiate an 'abstract' class so it is as best to try these things for ourselves first ;-0))

Now let us add some items to our collection - checkout the following details :-

===================================================================================================================

We now also need to look at the XAML script created to go with our dotNet code - see below :-

=======================================================================================================================================

So now we can see why we had surname entries in our list box control.

We can improve things simply by defining a 'ToString' method which concatenates 'Firstname' with 'Surname', and by also removing

the 'DisplayMemberPath' property association from the script section.

Lets try this now - see below :-

=================================================================================================================================================

And here is the extra couple code lines which were added to the Person class :-

====================================================

"So far so good" guys, but we appear to have a few overloads of the Constructor for the 'ObservableCollection' class.

Study the following documentation and then we can try out a couple of tests :-

================================================================================================================

It would appear that we need to investigate (or explore) the overload which takes a specified collection as an

input parameter at instantiation.

First let us see how we make a couple of such simpler collections, one and ArrayList and another generic list.

First study the ArrayList or Person objects :-

=====================================================================================================================

Yes, above we saw we could display the collection in the ListBox control.

Now let us try the generic list - a List<person> - see below :-

========================================================================================

And yes, this too shows the data in the list box.

Now then, let us try to make an 'ObservableCollection' object using this other overload, where we

use the generic list as the collection input parameter - check out the following image :-

=======================================================================================

And once again, you can see that our data is displayed nicely within the ListBox control.

The following code snippet shows the method to make the generic list, and how it is used to create the

new OC object - see below :-

===================================================================================================

*** However *** all is not right (or correct) all of the time :(

When I tried the same idea, but with a parameter input of the ArrayList object, the approach (and code) did

not work. We did not get a display within the list box.

===================================================================================================================

The above image shows even more reason why we ought to give up using the ArrayList in place of List<T> unless for

some reason we need to mix our class types.

So using an OC works nicely for both our ListBox control and the DataGrid control, no matter if we make the OC object

directly from code and adding person objects, or if we instantiate and use the overload which tales a generic list object

which has already been created - still of Person type objects.

===============================================================================================================================================

How does the Collection behave ? :

Okay then guys, now that we have an OC how do we use it in our GUI as a collection.

By this I mean can we add and remove items, and also can we edit and change items to have the changes show up elsewhere, in

other GUI elements bound to the same collection ?

Let us now try out a few of these collection operations.

Adding 'Extra' rows / records :

At the end of the list of items in the DataGrid control we have a special row which allows us to make a full entry from a previously

'blank' line - or row.

Below we will use this row to add a set of cell details suitable to the Person class structure.

Note also that the RHS (right hand side) shows the added row which the appears to be moved up one, simply because a second

new record has been added to the grid - that for Alwyn.

Checkout below the full details :-

=================================================================================================================================================================

Okay the so we can do addition - and this is reflected in the ListBox entries, you will need to take my word for this ;-0)

So what about removal, or row deletions ? Checkout the images below :-

Removal :

Note below that I have removed a bunch of rows for Persons previously between Jane Smith and Dan Logan.

======================================================================================

We need to see what XAML script detail we have to add to allow the data grid object to be changed interactively at

runtime.

Checkout below what we can add, even though some of the properties are set OK by default.

Note also that although AutoGenerateColumns is set to false, we often have set this in code to True. However, in

the subsection coming soon (belwo) we see a need to make our own DataGrid columns so that DP generated stuff

we don't want, gets hidden, or left out of sight.

Fist of all checkout the user interactive script settings :-

=============================================================================================================================

Views and stuff :

See the next subsection .................

PHH - 28th. October 2011.


Person class - code

//=========================================================

class Person

private cSurname as string

private cFirstname as string

private nAge as int

private nOwes as double

private dtDoB as DateTime

constructor()

SUPER()

self:Initialize()

return

constructor(cS as string, cF as string, nA as int, nO as double, dtD as DateTime)

SUPER()

self:Initialize()

self:Age := nA

self:Owes := nO

//----- check that DT is valid !? -----

self:DoB := dtD

self:Surname := cS

self:Firstname := cF

return

//-------------------------------------

METHOD ToString() AS STRING

RETURN SELF:Firstname+" "+SELF:Surname

//-------------------------------------

method Initialize() as void

self:Age := -1

self:Owes := 0.0

self:DoB := DateTime{1900,01,01}

self:Surname := ""

self:Firstname := ""

return

//-------------------------------

access Age() as int

return self:nAge

assign Age(nN as int) as void

self:nAge := nN

RETURN

//-------------------------------

access Owes() as double

return self:nOwes

assign Owes(nN as double) as void

self:nOwes := nN

return

//-------------------------------

access DoB() as DateTime

return self:dtDoB

assign DoB(dtD as DateTime) as void

self:dtDob := dtD

return

//-------------------------------

access Surname() as string

return self:cSurname

assign Surname(cS as string) as void

self:cSurname := cS

return

//-------------------------------

access Firstname() as string

return self:cFirstname

assign Firstname(cF as string) as void

self:cFirstname := cF

return

//-------------------------------

end class

//==============================================================


Person DP class - code

//=========================================================

class Person INHERIT DependencyObject IMPLEMENTS System.ComponentModel.INotifyPropertyChanged

STATIC PUBLIC SurnameProperty := DependencyProperty.Register("Surname", typeof(STRING), typeof(VODCdataBinding.Person), PropertyMetadata{""}) AS DependencyProperty

STATIC PUBLIC FirstnameProperty := DependencyProperty.Register("Firstname", typeof(STRING), typeof(VODCdataBinding.Person), PropertyMetadata{""}) AS DependencyProperty

STATIC PUBLIC AgeProperty := DependencyProperty.Register("Age", typeof(INT), typeof(VODCdataBinding.Person), PropertyMetadata{-1}) AS DependencyProperty

STATIC PUBLIC DoBProperty := DependencyProperty.Register("DoB", typeof(DateTime), typeof(VODCdataBinding.Person), PropertyMetadata{DateTime{1900,01,01}}) AS DependencyProperty

STATIC PUBLIC OwesProperty := DependencyProperty.Register("Owes", typeof(double), typeof(VODCdataBinding.Person), PropertyMetadata{}) AS DependencyProperty

//----- CARE : if put 0 as default (value) in metadata for double it throws and exception at compile time !? -----

//----- Events -----

EVENT PropertyChanged AS System.ComponentModel.PropertyChangedEventHandler

constructor()

SUPER()

self:Initialize()

return

constructor(cS as string, cF as string, nA as int, nO as double, dtD as DateTime)

SUPER()

self:Initialize()

self:Age := nA

self:Owes := nO

//----- check that DT is valid !? -----

self:DoB := dtD

self:Surname := cS

self:Firstname := cF

RETURN

//----- Methods -----

protected method OnPropertyChanged(name as string) as void

local pChanged as System.ComponentModel.PropertyChangedEventHandler

pChanged := self:PropertyChanged

if (pChanged != null)

pChanged:Invoke(self, System.ComponentModel.PropertyChangedEventArgs{name})

endif

RETURN

//-------------------------------------

METHOD ToString() AS STRING

RETURN SELF:Firstname+" "+SELF:Surname

//-------------------------------------

method Initialize() as void

self:Age := -1

self:Owes := 0.0

self:DoB := DateTime{1900,01,01}

self:Surname := ""

self:Firstname := ""

return

//-------------------------------

access Age() as int

return (int)self:GetValue(AgeProperty)

assign Age(nN as int) as void

SELF:SetValue(AgeProperty,nN)

self:OnPropertyChanged("Age")

RETURN

//-------------------------------

access Owes() as double

return (double)self:GetValue(OwesProperty)

assign Owes(nN as double) as void

SELF:SetValue(OwesProperty,nN)

self:OnPropertyChanged("Owes")

return

//-------------------------------

access DoB() as DateTime

return (DateTime)self:GetValue(DoBProperty)

assign DoB(dtD as DateTime) as void

SELF:SetValue(DoBProperty,dtD)

self:OnPropertyChanged("DoB")

return

//-------------------------------

access Surname() as string

return (string)self:GetValue(SurnameProperty)

assign Surname(cS as string) as void

SELF:SetValue(SurnameProperty,cS)

self:OnPropertyChanged("Surname")

return

//-------------------------------

access Firstname() as string

return (string)self:GetValue(FirstnameProperty)

assign Firstname(cF as string) as void

SELF:SetValue(FirstnameProperty,cF)

self:OnPropertyChanged("Firstname")

return

//-------------------------------

end class

PHH - 30th. October 2011.


Extra properties - DP objects

===========================================================================================================================================

[ .. CARE - if you type in line 15 'Surnname' for the binding you will get an empty column ! .. ]

PHH - 30th. October 2011.


working script

<Window x:Class="VODCdataBinding.ObservableCollection"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Observable Collection :" Height="300" Width="300" Background="Azure">

<Grid>

<ListBox Margin="12,14,12,0" Name="listBox1" ItemsSource="{Binding}" Height="98" VerticalAlignment="Top"

IsSynchronizedWithCurrentItem="True" SelectionMode="Single"/>

<DataGrid AutoGenerateColumns="False" Margin="12,147,12,12"

Name="dataGrid1" ItemsSource="{Binding}" CanUserAddRows="True" CanUserDeleteRows="True"

CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True" >

<DataGrid.Columns>

<DataGridTextColumn Header="Firstname" Binding="{Binding Firstname}" Width="80"/>

<DataGridTextColumn Header="Surname" Binding="{Binding Surname}" Width="80"/>

<DataGridTextColumn Header="Age" Binding="{Binding Age}" Width="50"/>

<DataGridTextColumn Header="Owes" Binding="{Binding Owes}" Width="60"/>

<DataGridTextColumn Header="DoB" Binding="{Binding DoB}" Width="80"/>

</DataGrid.Columns>

</DataGrid>

<Button Content="Launch Child" Height="23" HorizontalAlignment="Left" Margin="90,118,0,0" Name="pbLaunchChild"

VerticalAlignment="Top" Width="100" Click="pbLaunchChild_Click" />

</Grid>

</Window>

=======================================================================================================================

PHH - 30th. October 2011.


Views and Sorting - multiple controls

Sorting the Controls - using Separate Views :

This is a very important subsection.

When we have multiple controls on a form and its children (descendants) we quite often wish to show and display

the records (or data rows) in different orders.

This can be frustrating and tricky, if you do not know exactly what you are doing. However, I think I have solved

the problem and will try and make it clear to you in the notes below.

Let us first look at some visual success - checkout the image below :-

Above is our test application for exploring the 'ObservableCollection' class.

Notice that in the list box we have the records in order of property 'Firstname' A-Z (ascending), whereas in

the data grid control the rows are sorted by property 'Surname' Z-A (descending).

By clicking the mouse in the header section of columns in the DataGrid we can get a variety of different data

record orders, while the list box stays as it is.

The following image shows the data grid rows in order of property 'Age' ascending - see below :-

So it would appear that we have quite independent behaviour as far as sorting and viewing the two Items controls

in our WPF form is concerned.

Let us know move on to see what the Vulcan code looks like which made this happen for us.

Study the following code which is used to open the main parent screen - we will look at 'Initialize2' later on.

Checkout these details :-

===============================================================================================

In the above code we do our sorting via 'views' and not by the collection class. The collection class does allow

us to handle the navigation, but sort order and filtering are the domain of the View object.

However, what we MUST avoid is getting what appears to be two 'view' objects which are in fact based on the

same 'default' view, and are therefore pointers to the same basic 'view' - this can and does cause confusion,

I know, I have just been there ;-0))

Where can we go wrong ? :

Well, it is quite easy to overlook what we are doing when we make a view in the common way.

Let us look at what we have coded in these notes up to now, when we wanted to make a single view.

Check this out :-

=============================================================================================

Okay, it reads easily - "make a view called 'oView' of type 'ICollectionView' and point this at the existing view which

is the default view of the supplied 'data source' - in this case 'SELF:oMySecondOC2'.

The whole operation is carried out by a STATIC method - and its name which starts with 'Get' gives us a clue as to

what is happening - 'GetDefaultView' does NOT suggest that a new 'view' is made similar to the default one !!!

So this means that after the execution of the above code, 'oView' is actually the same object in memory as the 'default' view.

What does this really mean then ?

Well, look at the following code and we can explain better using it as an example :-

==============================================================================================

Above we appear to have made three 'views' for ourselves - however, they are in fact the same 'view'.

All three are just pointers to the 'default' view and so any sort order set on one would be set on the other, and this

is actually the default which is having an order set on it.

This results in all GUI controls having the same order displayed, and that would be the one which was last set on

any of the the 'views', including the 'default' which may be interactively set by column header mouse clicking.

How to get out of this confusion and 'mess' !? :

To me this is quite simple - don't use the STATIC method ! - well for anything other than the trivial case.

For more complex GUI situation, where we need quite separate 'views' for use with each and every Items control,

make individual and 'separate' view - each with there own pointer.

We do NOT need to know anything about the pointers used, and what is going on under the hood, so to speak. We

simply need to know that if we use code like that shown below, we get a pointer (and separate object) for each

instantiation - checkout this code detail :-

=====================================================================================

Using the 'CollectionViewSource' class as shown above, we get a quite separate 'view' object each time. So we get

different pointers, to separate and different memory objects - every time we instantiate and set a source, then use

the 'View' property.

** be Aware ! ***

The following image shows that we can't instantiate the 'ICollectionView' class - checkout the detail :-

=========================================================================================================

Okay then, lets move on to use these ideas to do something very useful.

Many Sorts from one Collection :

I have now tweaked the code (made some small adjustments) so that when we open the Child form from the

main form we get four different sort orders on display.

Checkout the following image and realise that Elizabeth is the youngest at 16 :-

[ .. sort orders are Firstname A-Z (hence Alan), Surname Z-A (hence van der Hulst), Age ascending (hence Elizabeth)

and Owes ascending (hence -8.13) .. ]

Well we seem to have done as we wished originally, and gotten ourselves four separate 'views' on the same collection.

If we remove a few records from the collection, using the child 'DataGrid' control, we ought to still have the same orders

but with less items, or rows, on display - lets give this a try :-

Studying the above image we see a whole bunch of guys have been deleted, or removed.

However, Alan is still first in order of Firstname, Elizabeth is still the youngest, Robert (van der Hulst) is still

at the end of the Surname list, but now zero ( 0 ) is the least value 'Owed', as Fredderick Jones has be deleted.

Also I have thoroughly checked out the interactive 'Column Header' clicking behaviour, to set the order on the Items within

the related GUI control, and all seems to work independently as we required.

So we have had more success, and feel as if we are getting to be a little more in control of both our coding

and our application design.

Time to move on, see you in the next subsection .......................

PHH - 30th. October 2011.


working code and script

===================================================================================

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

BEGIN NAMESPACE VODCdataBinding

/// <summary>

/// Interaction logic for ObservableCollection.xaml

/// </summary>

PARTIAL CLASS ObservableCollection INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

SELF:Initialize()

RETURN

PRIVATE METHOD pbLaunchChild_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoLaunchChild()

RETURN

END CLASS

END NAMESPACE

===================================================================================

<Window x:Class="VODCdataBinding.ObservableCollection"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Observable Collection :" Height="300" Width="300" Background="Azure">

<Grid>

<ListBox Margin="12,14,12,0" Name="listBox1" ItemsSource="{Binding}" Height="98" VerticalAlignment="Top"

IsSynchronizedWithCurrentItem="True" SelectionMode="Single"/>

<DataGrid AutoGenerateColumns="False" Margin="12,147,12,12"

Name="dataGrid1" ItemsSource="{Binding}" CanUserAddRows="True" CanUserDeleteRows="True"

CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserSortColumns="True" />

<Button Content="Launch Child" Height="23" HorizontalAlignment="Left" Margin="90,118,0,0" Name="pbLaunchChild"

VerticalAlignment="Top" Width="100" Click="pbLaunchChild_Click" />

</Grid>

</Window>

===================================================================================

<Application x:Class="VODCdataBinding.App"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

StartupUri="ObservableCollection.xaml">

<Application.Resources>

</Application.Resources>

</Application>

===================================================================================

#using System

#using System.Collections.Generic

#using System.Configuration

#using System.Data

#using System.Linq

#using System.Windows

BEGIN NAMESPACE VODCdataBinding

/// <summary>

/// Interaction logic for App.xaml

/// </summary>

PARTIAL CLASS App INHERIT Application

END CLASS

END NAMESPACE

===================================================================================

// partObservableCollection.prg

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

//----------------------------------

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

BEGIN NAMESPACE VODCdataBinding

/// <summary>

/// Interaction logic for ObservableCollection.xaml

/// </summary>

PARTIAL CLASS ObservableCollection INHERIT Window

PUBLIC oMyOC AS ObservableCollection<Person>

//PUBLIC oMyOC2 AS ObservableCollection<Person>

PUBLIC oMyAL AS ArrayList

PUBLIC oMySecondOC AS ObservableCollection<Person>

PUBLIC oMySecondOC2 AS ObservableCollection<Person>

PUBLIC aPerson AS Person[]

PUBLIC oListT AS List<Person>

PUBLIC oMyOC_child AS ObservableCollection<Person>

PRIVATE oView AS ICollectionView

CONSTRUCTOR(oOC as ObservableCollection<Person>)

SUPER()

self:oMyOC_child := oOC

InitializeComponent()

SELF:Initialize2()

RETURN

METHOD Initialize2() AS VOID

TRY

SELF:oMySecondOC2 := SELF:oMyOC_child

//-------------- Create a separate View ----------------

LOCAL oCVS AS CollectionViewSource

oCVS := CollectionViewSource{}

oCVS:Source := SELF:oMySecondOC2

SELF:oView := oCVS:View

SELF:listBox1:DataContext := SELF:oView

//-------------- Set a Sort order -------------

LOCAL oSD2 AS SortDescription

oSD2 := SortDescription{"Age", ListSortDirection.Ascending}

self:oView:SortDescriptions:Add(oSD2)

//---------------------------------------------

SELF:listBox1:DataContext := SELF:oView

SELF:oView:MoveCurrentToPosition(3)

//=============================================================

SELF:oMySecondOC := self:oMyOC_child

//-------------- Create a separate View ----------------

LOCAL oView2 AS ICollectionView

LOCAL oCVS2 AS CollectionViewSource

oCVS2 := CollectionViewSource{}

oCVS2:Source := SELF:oMySecondOC

oView2 := oCVS2:View

//-------------- Set a Sort order -------------

LOCAL oSD1 AS SortDescription

oSD1 := SortDescription{"Owes", ListSortDirection.Ascending}

oView2:SortDescriptions:Add(oSD1)

//---------------------------------------------

SELF:dataGrid1:DataContext := oView2

oView2:MoveCurrentToPosition(4)

//----------- both work, which is better for me ? ------------

self:dataGrid1:AutoGenerateColumns := true

self:dataGrid1:AlternationCount := 2

self:dataGrid1:AlternatingRowBackground := Brushes.LightCoral

SELF:dataGrid1:SelectedItem := 0

CATCH oEX AS SystemException

MessageBox.Show(oEX:Message,"My Error :")

END TRY

RETURN

METHOD Initialize() AS VOID

try

SELF:CreateListT()

SELF:oMySecondOC := ObservableCollection<Person>{SELF:oListT}

//-------------- Create a separate View ----------------

LOCAL oCVS AS CollectionViewSource

oCVS := CollectionViewSource{}

oCVS:Source := SELF:oMySecondOC

SELF:oView := oCVS:View

SELF:listBox1:DataContext := SELF:oView

//-------------- Set a Sort order -------------

LOCAL oSD2 AS SortDescription

oSD2 := SortDescription{"Firstname", ListSortDirection.Ascending}

self:oView:SortDescriptions:Add(oSD2)

//---------------------------------------------

SELF:listBox1:DataContext := SELF:oView

SELF:oView:MoveCurrentToPosition(1)

//-------------- Create a View ----------------

LOCAL oView2 AS ICollectionView

LOCAL oCVS2 AS CollectionViewSource

oCVS2 := CollectionViewSource{}

oCVS2:Source := SELF:oMySecondOC

oView2 := oCVS2:View

//-------------- Set a Sort order -------------

LOCAL oSD1 AS SortDescription

oSD1 := SortDescription{"Surname", ListSortDirection.Descending}

oView2:SortDescriptions:Add(oSD1)

//---------------------------------------------

oView2:MoveCurrentToPosition(2)

SELF:dataGrid1:DataContext := oView2

//------------ settings on the DataGrid --------------

self:dataGrid1:AutoGenerateColumns := true

self:dataGrid1:AlternationCount := 2

self:dataGrid1:AlternatingRowBackground := Brushes.LightCoral

self:dataGrid1:SelectedItem := 0

//---------------------------------------------

CATCH oEX AS SystemException

MessageBox.Show(oEX:Message,"My Error :")

END TRY

RETURN

METHOD DoLaunchChild() AS VOID

LOCAL oChild AS VODCdataBinding.ObservableCollection

oChild := VODCdataBinding.ObservableCollection{SELF:oMySecondOC}

oChild:Owner := self

//oChild:ShowDialog()

//----- with non modal forms you can adjust GUI controls on parents -----

oChild:Show()

RETURN

METHOD CreateAL() AS VOID

oMyAL := ArrayList{}

oMyAL:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

oMyAL:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

oMyAL:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

oMyAL:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

oMyAL:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

oMyAL:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

oMyAL:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

oMyAL:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

oMyAL:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

oMyAl:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

oMyAL:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

oMyAL:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

oMyAL:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

RETURN

METHOD CreateListT() AS VOID

oListT := List<Person>{}

oListT:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

oListT:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

oListT:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

#region {.. for display purposes ..}

oListT:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

oListT:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

oListT:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

oListT:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

oListT:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

oListT:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

oListT:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

oListT:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

#endregion

oListT:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

oListT:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

RETURN

METHOD CreateOC() AS VOID

oMyOC := ObservableCollection<Person>{}

oMyOC:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

oMyOC:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

oMyOC:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

oMyOC:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

oMyOC:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

oMyOC:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

oMyOC:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

oMyOC:Add(Person{"Logan","Dan", 39, 7.77, DateTime{1970, 06, 29}})

oMyOC:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

oMyOC:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

oMyOC:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

oMyOC:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

oMyOC:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

RETURN

END CLASS

//=========================================================

class Person

private cSurname as string

PRIVATE cFirstname AS STRING

#region

private nAge as int

private nOwes as double

PRIVATE dtDoB AS DateTime

#endregion

constructor()

SUPER()

self:Initialize()

return

constructor(cS as string, cF as string, nA as int, nO as double, dtD as DateTime)

SUPER()

self:Initialize()

self:Age := nA

self:Owes := nO

//----- check that DT is valid !? -----

self:DoB := dtD

self:Surname := cS

self:Firstname := cF

return

//-------------------------------------

METHOD ToString() AS STRING

RETURN SELF:Firstname+" "+SELF:Surname

//-------------------------------------

method Initialize() as void

self:Age := -1

self:Owes := 0.0

self:DoB := DateTime{1900,01,01}

self:Surname := ""

self:Firstname := ""

return

//-------------------------------

access Age() as int

return self:nAge

assign Age(nN as int) as void

self:nAge := nN

RETURN

//-------------------------------

access Owes() as double

return self:nOwes

assign Owes(nN as double) as void

self:nOwes := nN

return

//-------------------------------

access DoB() as DateTime

return self:dtDoB

assign DoB(dtD as DateTime) as void

self:dtDob := dtD

return

//-------------------------------

access Surname() as string

return self:cSurname

assign Surname(cS as string) as void

self:cSurname := cS

return

//-------------------------------

access Firstname() as string

return self:cFirstname

assign Firstname(cF as string) as void

self:cFirstname := cF

return

//-------------------------------

end class

END NAMESPACE

===================================================================================

PHH - 30th. October 2011.


Section L : Extended Toolkit WPF

This is an interesting section and takes us forward into the future ;-)

Here is the all important link :-

http://wpftoolkit.codeplex.com/

BEWARE however, this is NOT directly from Microsoft so issues could occur in the future.

I suggest you try it and use only the - "must haves" - or "can't do withouts".

This way you won't get burned later when you have forgotten you installed and used it !!

I suggest we look at what Willie Moore has made available to us through "RightSLE" for WPF.

===============================================================================================================================

===============================================================================

=======================================================================================================================================================

==============================================================================================================================

========================================================================================================

PHH - 7th. September 2011.


Installation and Use

=====================================================================================================================

================================================


Section M : DataTable, general use

Okay you guys, we need to make some clear statements regarding DataTables (DTs), and the world in which

they originated.

The point of making some comparisons of the IT world of yesteryear, and where it is headed today, and

what is planned for tomorrow, is simply so we can (as developers) best decide on the technologies we need to

choose to learn for our future use.

It would appear to me after quite some time and effort with dotNet 2.0 and then dotNet 4.0, that DataTables,

although very useful and easy to work with at times, do belong in the past - yes, they are old hat !

DTs belong to the earlier world of SQL Server, where tables, columns, rows, and relationships were the

order of the day - the thing to do. And the DT allowed the same technology to be carried out in memory etc..

Now that Microsoft have eventually go their heads into the fact that we developers like business, and custom

objects, objects of all kinds and like to do all things 'objectively', they have turned the thing around.

With EF (Entity Framework 4.0) and the soon to be released 'Denali', or the SQL Server 2012 version, we

are seeing an objective approach by them - to all parts of the solutions we can design. LINQ technology also

fits into this approach, so that SQL script errors do not pass compilation stage - previously a bane for SQL use.

WPF and XAML, with dotNet 4.0 and the 'Data Binding' potentials of the new GUI, supported by the DP

(Dependency Property) system, mean that we can have our business objects directly used with our GUI

designs and creations,

I have had a small exploratory session with EF4 and it does promise to put the data source end of our coding

to rights - in that we can deal far more (entirely?) with custom or business objects. However, I have not yet

tried their 'Code First' approach which is designed to take existing dotNet coded BOs and 'go backwards' in

our old way of thinking - meaning make the database tables mapped from an existing code class of object.

This emerging approach to developing solutions for our customers keeps us more at arms length from the SQL

database and the server, and allows us to think and deal in objects throughout our whole solution.

Some of you may be delighted to hear this, particularly if you are new to all things SQL ;-) You may think that

most of it will just 'go away'.

However, the for new guys, the downside of this seems to be that we all still need to have the skills and knowledge

to be able to script (using T-SQL) query statements, and think in 'set based' algebra as opposed to processing

rows by the old cyclic row pointer approach. This in itself is still quite a challenge.

To get the most from SQL, and some would say even just to 'use' SQL, we all need to work hard at developing the

required abilities. I have been at it some years now and feel I am at about 'reasonable survivor' level. But I have

made some eNotes for you (available in ClickStart) so that you waste as little time as possible while learning.

SDK - documentation :

Let us pause a while to see a few useful things which the SDK has to teach us. Note DTs are very much part of the

ADO.NET approach of dotNet 2.0.

Also notice that a DT is part of a larger structure - known as a 'DataSet' (DS). If a DS is a group of DTs which may be

related (via primary and foreign keys) then a DS is effectively a database (DB) as we know it in SQL. Okay, the real SQL

DB has other objects attached, like functions and procedures, but essentially they are similar.

Checkout the following details :-

========================================================================================================================================

Okay then, read and re-read the above till it makes some sort of sense, then consider the summary below.

Note that a 'schema' is essentially a the table structure, as we think of things in SQL, and for the SQL database,

Summary :

DataTables will still be around for quite a while - we need to know about them.

Although they belong to the old world of WinForms they can be used with WPF, but will not tap into all its potential.

DataTables belong to an IT world which has gone.

WPF and 'data binding' in the GUI is best suited to business objects in collections.

BOs to be bound, are best designed to be Dependency Property (DP) objects and have suitably defined DP properties.

For new work forget DTs and use BOs in collections instead - then you can get the most our of dotNet 4.0, EF, and 'Denali'.

It is easier as a developer / coder to totally embrace the new world of dotNet 4.0 than try to mix older ideas, approaches,

classes, and existing VO type code, with the new WPF world. I read lots of News Group (NG) posts, were I see the

problems others seem to make for themselves by gradually coming to new things from the VO type world. This is

simply the hard way to do things.

DataTables are still fun, and at least we can wrap modern 'Views' around them. So if you are to use them use dotNet 4.0

View classes immediately, and at all times. This way you can at least still use skills and knowledge which applies to

the modern world of dotNet coding.

The Fun ! :

Okay then guys, having told you the truth, let us move on to enjoying the fun of working with the older DTs. I do have a

soft spot for them, and they did get me really into dotNet 2.0 and beginning SQL, so I ought to be thankful ;-0))

See you in the next subsection ..................

PHH - 11th. September 2011.


Creating a Structure

Making a Start :

Now we get down to business, and start by defining the columns of our new table.

Let us start by seeing the documentation for this process :-

============================================================================================================================

And before we study in detail what this all means, and see the code to do it, let us cast

our eyes onto an image of success - see below :-

==================================================================

As you can see in the above image we have a grid showing six columns.

Notice the column headers, or captions - these will be discussed a little later.

Okay then, how do we code things to achieve this ?

Making Columns :

We first need to see and study the top of the relevant PRG file in our little test project.

================================================

The highlighted file is the one we will study in the image below :-

================================================================

In the above image lines 17 and 18 show the extra namespaces required over and above the standard 'stub' ones

provided by the VN2 integration within VS2010.

Notice that in the code we have declared two table parameters, oDT and oDT2.

So having instantiated an 'empty' table and entitled it 'VODC11_DT' we then proceed on line 36 to pass the

table into a suitable method for structure creation.

Now we need to study the following image of the creation of the first column :-

=============================================================================

The first of our columns is a bit special, or different to the others. This is simply due to our design, and

no other reason. We are choosing to give it a few special property settings shown in line 94,95 and 96.

The column will be a unique valued column which is both non-editable and also auto-incrementing, once it has

been seeded with a starting value when the first row is created.

Notice that line 99 shows we have many other column definitions, but these are are similar so have been collapsed

for display purposes.

Let us now look at a couple of these when we expand the section - see below :-

=============================================================================

In the above image we see typical columns of the non-primary key type. Standard data columns.

Note they are capable of being edited, as well as NOT being unique. Take particular attention of the values of

the captions in lines 105 and 116, we will later compare these values to those we see in our grids on the GUI.

As you can see these are typical 'string' type data fields, nothing special at all.

Other Data Type Fields :

Our trial or demo DT has been chosen by me to have both an integer field as well as a 'double' data type field, as

well as a possibly more potentially 'difficult' or 'problematic' DateTime field. I say this simply because DateTime

values and objects seem to cause us developers a lot more hassle than simple numeric types.

Study the differences below :-

==================================================================================

As you can see above, not a lot really, just that we need line 146 to declare the 'DataType' property as one

of type 'DateTime'.

The name and caption are of course quite arbitrary since they do nor affect the behaviour of the column at all.

How does this look ? :

So then, having defined our six fields, or columns, and made up a column collection, what does it look like

before we add any row data ?

Simply check out the image below to answer that one :-

===================================================================

As you can see, we get 'not a lot' !

So it would appear that we need to add some row data to start making things happen.

In the following image we have made and added the first row, one which is a little different to the others.

Checkout the details below :-

==================================================================

==================================================================================================================

As the above image suggests, we need to study the Person class which we are starting to use for creating

our rows - but still in the old fashioned way - simply to pass values.

Here we have the Person class details shown below.

The Person Class :

Okay then, lets look at the class we have used with our DataTable example app so far. I say so far simply

because we may well improve and develop this same class towards a full DP class which can give us fuller WPF

functionality with other similar applications.

Check out the simplest of Vulcan.Net classes below :-

==========================================================

Above we have a simple class, but one with five different data types.

Each class field has related properties, all of which can be both read from and written to.

PHH - 11th. September 2011.


Making and Adding Rows

First in this subsection - let us study the 'CreateFirstRow' method - see below :-

===================================================================================

Remember the above first row is a little special, in that we need to provide a value for the 'id' property

since it is the 'seed' for the rest to follow.

If we do not provide a value then we get the default of zero - see below :-

=====================================================================================================

Likewise a higher values does as expected - see below :-

====================================================================

I have remembered to put the code back to seed at one - ' 1 ' - before I forget ;-)

Subsequent Rows :

This part is quite easy, it is simply built upon code and ideas used to create the first row.

However, because all subsequent rows are not special like the first one, we can write a method to help out.

Here is the method along with the data input 'CreateRows' method :-

=============================================================================================================================

[ .. don't worry if the ages of the people and their dates of birth do not quite agree - I just guessed at them !! .. ]

And the result of all this effort is seem in the following image :-

================================================================

Notice in the image above that the column headers are not apparently the same as the DT captions we defined.

It seems that we need to look more closely at how we have displayed our table and its data.

PHH - 11th. September 2011.


Displaying in a GUI Grid

Displaying the Table :

Let us start at the GUI end of things. Remember we are all about WPF these days - no more WinForms for me, that

was well over two years ago.

Let us see part of the Designer surface in the WPF/XAML editor 'shell' :-

====================================================================================================================

Shown above we have a DataGrid control from the Toolbox, and it has it 'ItemsSource' property set to be bound at runtime.

[ Remember :-- a DataGrid control, like a ListBox and ComboBox, is an 'Items' control. That is they all have a collection ]

[ ======== of items - as opposed to a single 'thing' for a 'content' control. ]

By declaring a binding which is unspecified (see line 9) we need to provide the object in our dotNet runtime code.

======================================================================

Above we see the code line numbered 41 which both sets the object link to the 'ItemsSource' property, as well as a couple of

other things we can view below :-

=======================================================================================

In the above image, line 218 associates the data table object with the data source for the DataGrid element.

If we did use the auto generate feature through checking line 219 then we would need to make our own columns

as we did for the table.

Since our captions of 'My Surname' and 'My Firstname' are not showing it would appear as though the DT column

names are used in place of those which are captions.

I am quite happy with this, and all we have done so far - now to look at the Person class - see the following

relevant information.

PHH - 11th. September 2011.


Filtering and Sorting

By the time we get to filtering and sorting the DataTable (DT) directly, we are starting to have fun ;-)

It has been a bit difficult finding out the full details for this as many text books and also web blogs on the

matter, simply do NOT go far enough, and/or include all common data types.

To get us into this section lets start by studying what the SDK documentation has to say on the matter.

We will start by looking at the 'SELECT' method and its four possible overviews - see below :-

=========================================================================================================================

As this suggests, if we supply certain parameter inputs we can determine the array of 'DataRow' objects which is returned.

If we neglect to pass in any parameter details we get this - study the following :-

=====================================================================================================

Since the most complex overload, that requiring three input parameters, is the most difficult to get to work correctly

for us, I have chosen to focus on this one in our demo / test application. If we get this to function in a suitable manner

then we can handle all other eventualities.

Before we get into coding and scripting, let us see what the SDK has to say about Sorting and Filtering generally.

Checkout the following information :-

=========================================================================================================================================

The above image has information which relates to one of the overloads (fourth one) which will 'SELECT' a set of row items from

the original DT row set, and return it as an array.

The 'select' method of the DT class can take up to three parameters, and this overload is that which I will use throughout our demo

application as we can learn more from its use.

Lets checkout some details for this overload - see below :-

=======================================================================================================================

As you can see in the above image we need to supply two string values and a third which is an enumeration.

The enumeration is of the 'DataViewRowState' which was shown in the images earlier in this sunsection.

Before we study just what needs to be done in code and XAML script to get everything to work, let

us first look at what our test applet looks like as we have it working at the moment.

Study in particular the three drop lists (combo box controls) to the right of the WPF form :-

Firstly, in the above image, we see the set of possible enumerations, these are not offered to us as string values.

However, for the filter conditions we have a bunch of string values in our drop list - see below :-

==========================================================

Similarly, we have another bunch of string values in our 'Sort' drop list which represent sorting conditions.

Checkout the image shown below :-

=================================================

And now finally we can see in the blue striped grid at the bottom of the WPF form the returned set of

data rows - which have been placed into a second 'DataGrid' control.

In the following image see that the results on view match the input strings for Filter and Sort :-

The label which acts as a heading to the lower grid, and the results on display, should be changed to fit the current

state of our application - the above was for an earlier phase of the development.

See below for the more appropriate scripting :-

================================================================================================================

Note the XML way to include a special character - embed it within th text string. Others will be seen for the sort and

filter conditions. Note the '&' is used like an 'escape' it has nothing to do here with the fact that we need an '&' to shown

on the screen. That information is within the 'amp' which is sort hand for the full correct name of 'ampersand'.

Filling the Drop Lists - 'ComboBoxes' :

Okay then you guys, let us see how we can fill the easier Sort and Filter drop list controls.

All we need to do is include a list of string values and test them to see that they both work, and also do not

cause any compilation or runtime errors.

Let us look at how easy it is to put this into the XAML section of our WPF form - see below :-

================================================================================================================

As you can see in the above image, these script lines create the label and the drop list for the sort condition.

And now we need to see the filter conditions, as they appear in the XAML script :-

=======================================================================================================================

There are a couple of things to notice in the above image, one is the use of the '&' to allow the alternative form of

the ' < ' symbol (said less than) as otherwise this would be taken as part of the XAML script syntax.

Check out lines 41,42,45,46 and 47 in the image above.

Now then, although we seemed to have used ' > ' (said greater than) to good effect in our script previously, we can

also use an 'escaped' type format, or XAML syntax.

See below for example :-

========================================================================

Applying the Selected Condition Text :

In order to help us clearly understand both the script and the code, I have changed the default names of the drop list

controls to suit the situation - see below for an example :-

===============================================================================================================

Notice in the above image that the 'Name' property is now "cbxFilter" - instead of the default 'comboBox1'..

This name change is also reflected by the changes I had to make in the dotNet code - see below :-

============================================================================================================

And also the 'DoChangeFilter' method as well - checkout the following :-

========================================================================================================

I will now make similar changes to script and code for the other two drop list - those for Sort and 'RowState'.

Let us now see the 'ApplyFnS' method to see how we are using the string input parameters. (Be aware that

here we are using the two parameter overload, and that will very soon change.)

=================================================================================

Line 206 in the image above shows the two parameter overload in use. Two string inputs, one for

the filter and the other for the sort.

In extensive tests I have carried out during development of this DataTable applet I have found this code above to work well,

and give results as would be expected from the data shown above.

Filling the Drop List for the Enumeration :

Okay then guys, are you ready for a more complex piece of script to allow us to fill the combo box control with enumerations

for the 'DataViewRowState' (DVRS) enumeration ?

However, although the procedure is more complex, it does fall in line with how to fill any drop list control with any system

enumerations, so a small amount of new learning goes a long way ;-)

Let us first remind ourselves of what we have in the DVRS set of options - see below :-

=======================================================================================================================

*** PLEASE *** Do not be tempted to provide string values as we did for Sort and Filter !!

The beauty of WPF and the GUI controls is that 'Items' controls accept any type of object quite nicely. So let us use

the fact and fill the 'ComboBox' control accordingly.

To get us going in the right direction study the following image - checkout the drop list values :-

==============================================================================================================

As you can see in the above image we have an exact match between the drop list and the members of the

enumeration itself.

We are going to see how we can bind the collection of row states to the GUI control.

Here is the way to setup the binding on the 'ComboBox' - see below :-

==================================================================================================================

In the above image it is line 70 which does the binding - here we are binding to a resource (static) which we must setup

in a 'Window' or 'Grid' Resources section - well, that makes the most sense anyway.

Let us use the widest scope for this WPF form, by using 'Window Resources'.

This is what we need to script in the XAML section of the application :-

=================================================================================================================================

In the image displayed above, we firstly need to examine lines 6 and 7.

These follow the guidelines written in 'Section C' earlier in these eNotes.

We are pointing to the required namespaces, and making an association between them and our local 'prefixes'

called 'sys' and 'data' - take care to use the correct case, and to follow this through in all your script lines.

In order to make line 11 work we need the 'sys' prefix setup in line 6.

We can see what is going on if we look at the intellisense from the Vulcan.net editor - see below :-

=============================================================================================================

===================================================================

This looks like a static method called 'System.Enum.GetValues' which takes an existing system enumeration type

and returns an array. yes, an array not a collections - smells of old fashioned ?

And in order to make the correct list of values we need line 7 - where the prefix will help us point at the namespace

which holds our required enumeration. Remember that the DVRS is in the System.Data namespace.

Looking at line 13 above, this is what it is doing, providing the type for the static method to use to make the array.

So the result is a System.Array of DVRS enumerations.

DON'T ... DON'T ......

Just do NOT try the following - this gives string values within 'ComboBoxItem' objects :-

===========================================================================================================================================

Now lets move on to see how the interaction works.

Selecting from the DVRS drop list :

There is a simple start to the combo box selection process - see below :-

==========================================================================================================

So what does the method on line 86 do for us - the 'DoChangeRowState' method.

Study the following code carefully :-

========================================================================================

Yes, we need two string type inputs and an enumeration.

The filter and sort strings are class variables and will have been set previously, either interactively or in the Initialize() method.

In order to get the interactively selected enumeration we simply have to make the correct cast - see line 91.

Remember, in the drop list we are bound to a strongly typed array of DVRS items and not a collection of ComboBoxItems, so

the cast is quite different to that of Sort and Filter.

Almost finally, we need to use the 'SELECT' method of the DT members, to apply the three parameters - see below :-

==============================================================================================================

And the FINALLY, we can see how to display the results array in a second DT - the blue one at the bottom of the form.

Here is the result - before we look at the code :-

In the above image we see a second data table, and one with the same structure as the first one.

It may have escaped your notice in earlier images to see a couple of lines which copied the structure by 'Cloning'

the first DT just after it had been created.

Lets look at this now - see below :-

================================================

Although the first table has rows as well as a structure, it is only the structure itself which gets copied in

the 'cloning' process.

So now it will make sense if we study the 'MakeResultsTable' method shown below :-

=====================================================================

Above we see code to remove any existing rows from the results data table, and the we construct a set

of new rows from the array returned from the SELECT method.

And then lastly of all we display the result table as shown in the following image :-

=====================================================================================

The only difference being that we use a View instead of binding the DT itself - even though this will produce a default

view.

I like to make a view of my own, so I can then apply sort, grouping and filtering on the view. This is a better WPF approach

it seems to me than directly binding data source objects.

In this final image notice the XAML script does not bind to anything, this means we can code the property

called 'ItemsSource' - checkout below :-

=========================================================================================================================

I leave it to you to rename the element more suitably - like 'dgResults'.

Time to move on, see you in the next subsection ................................................

PHH - 10th. September 2011.


On-Screen Interaction - Add, Edit and Sort

PHH - 11th. September 2011.


Wrapping a View around

"One view or two madam ? "

What on earth do I mean by the above heading ?

Well, quite simply we can use the standard (old fashioned or early dotNet) way or indeed we

can use a more up to date dotNet 4.0 approach.

One way allows us certain functionality, like being able to enumerate the collection or data source, and

the modern way gives far greater functionality and flexibility to the coder / developer.

DateView or ICollectionView ? :

PHH - 11th. September 2011.


Relations, a Table too far !?

=========================================================================================================================================

PHH - 11th. September 2011.


Section N : Full (small) example

Now that we have learned so much regarding Data Binding, Collections, and a few important other things

as well, it seems a good idea to try and make a new demo application - or applet - which shows clearly

some of the major aspects of DB in WPF forms.

In the following applet we use sorting and filtering as well as a little reflection to get our custom class

properties fro the right hand drop list.

Have a careful look at what we will be studying - see below :-

In the next subsection we will go into full detail - see you there ..........................

PHH - 10th. November 2011.


Custom Class

The class of object we will use to make our 'collection' is a custom class of mine called 'Person'.

I have tried to make the 'Person' class as fully DP friendly as possible. By this I simply mean I have

designed and coded the class so it has all the features required of a class that will be fully capable of

binding using DPs (dependency properties).

Take a careful look at the following 'collapsed' form of iur class :-

=======================================================================================================================

PHH - 10th. November 2011.


General Details

PHH - 10th. November 2011.


working code and script

 Top Previous

=======================================================================================

<Application x:Class="CollectionItemDisplay.App"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

StartupUri="Window1.xaml">

<Application.Resources>

</Application.Resources>

</Application>

=======================================================================================

#using System

#using System.Collections.Generic

#using System.Configuration

#using System.Data

#using System.Linq

#using System.Windows

BEGIN NAMESPACE CollectionItemDisplay

/// <summary>

/// Interaction logic for App.xaml

/// </summary>

PARTIAL CLASS App INHERIT Application

END CLASS

END NAMESPACE

=======================================================================================

<Window x:Class="CollectionItemDisplay.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ListBox Ordering and Filtering :" Height="400" Width="500" Background="Cornsilk">

<Grid>

<ListBox Height="301" HorizontalAlignment="Left" Margin="12,12,0,0" Name="listBox1" VerticalAlignment="Top"

Width="187" ItemsSource="{Binding NotifyOnSourceUpdated=True}" IsSynchronizedWithCurrentItem="True" SelectionMode="Single"

Background="Beige" BorderBrush="Plum" BorderThickness="2" DisplayMemberPath="FullDetail"/>

<ComboBox Height="23" HorizontalAlignment="Left" Margin="215,44,0,0" Name="comboBox1" VerticalAlignment="Top"

Width="111" ItemsSource="{Binding}" SelectionChanged="comboBox1_SelectionChanged" DisplayMemberPath="PropertyName"/>

<ComboBox Height="23" HorizontalAlignment="Left" Margin="215,105,0,0" Name="comboBox2" VerticalAlignment="Top"

Width="111" ItemsSource="{Binding}" SelectionChanged="comboBox2_SelectionChanged" />

<Label Content="Sort Orders :" Height="28" HorizontalAlignment="Left" Margin="215,12,0,0" Name="label1"

VerticalAlignment="Top" Width="120" Foreground="#FF13B752"/>

<Label Content="Filter Conditions :" Height="28" HorizontalAlignment="Left" Margin="215,73,0,0" Name="label2"

VerticalAlignment="Top" Width="120" Foreground="red"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="336,161,0,0" Name="textBox1" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=listBox1, Path=SelectedItem.Firstname, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="336,192,0,0" Name="textBox2" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=listBox1, Path=SelectedItem.Surname, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="336,225,0,0" Name="textBox3" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=listBox1, Path=SelectedItem.Age, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="336,257,0,0" Name="textBox4" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=listBox1, Path=SelectedItem.Owes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="336,290,0,0" Name="textBox5" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=listBox1, Path=SelectedItem.DoB, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2" />

<Label Content="Firstname :" Height="28" HorizontalAlignment="Left" Margin="241,156,0,0" Name="label3"

VerticalAlignment="Top" Width="94" Foreground="#FF8434E2" />

<Label Content="Surname :" Height="28" HorizontalAlignment="Left" Margin="241,185,0,0" Name="label4"

VerticalAlignment="Top" Width="94" Foreground="#FF8434E2" />

<Label Content="Age :" Height="28" HorizontalAlignment="Left" Margin="241,220,0,0" Name="label5"

VerticalAlignment="Top" Width="94" Foreground="#FF8434E2" />

<Label Content="Owes :" Height="28" HorizontalAlignment="Left" Margin="241,252,0,0" Name="label6"

VerticalAlignment="Top" Width="94" Foreground="#FF8434E2" />

<Label Content="DoB :" Height="28" HorizontalAlignment="Left" Margin="241,285,0,0" Name="label7"

VerticalAlignment="Top" Width="94" Foreground="#FF8434E2" />

<TextBox Height="24" HorizontalAlignment="Left" Margin="215,325,0,0" Name="textBox6" VerticalAlignment="Top"

Width="252" Text="{Binding ElementName=listBox1, Path=SelectedItem.FullDetail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

Background="Beige" BorderBrush="Plum" BorderThickness="2"/>

<Label Content="Full Detail :" Height="28" HorizontalAlignment="Left" Margin="117,321,0,0" Name="label8"

VerticalAlignment="Top" Width="96" Foreground="#FF8434E2"/>

<ComboBox Height="23" HorizontalAlignment="Left" Margin="353,44,0,0" Name="comboBox3" VerticalAlignment="Top"

Width="114" ItemsSource="{Binding}" SelectionChanged="comboBox3_SelectionChanged" >

</ComboBox>

<Label Content="Display Member :" Height="28" HorizontalAlignment="Left" Margin="353,12,0,0" Name="label9"

VerticalAlignment="Top" Width="114" Foreground="Chocolate"/>

</Grid>

</Window>

=======================================================================================

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

BEGIN NAMESPACE CollectionItemDisplay

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD comboBox1_SelectionChanged( sender AS System.Object, e AS System.Windows.Controls.SelectionChangedEventArgs ) AS System.Void

self:DoSelectionChangedSort()

RETURN

PRIVATE METHOD comboBox2_SelectionChanged( sender AS System.Object, e AS System.Windows.Controls.SelectionChangedEventArgs ) AS System.Void

self:DoSelectionChangedFilter()

RETURN

PRIVATE METHOD comboBox3_SelectionChanged( sender AS System.Object, e AS System.Windows.Controls.SelectionChangedEventArgs ) AS System.Void

self:DoSelectionChangedMember()

RETURN

END CLASS

END NAMESPACE

=======================================================================================

// partWindow1.prg

#using System

#using System.Collections.Generic

#using System.Linq

#using System.Text

#using System.Windows

#using System.Windows.Controls

#using System.Windows.Data

#using System.Windows.Documents

#using System.Windows.Input

#using System.Windows.Media

#using System.Windows.Media.Imaging

#using System.Windows.Navigation

#using System.Windows.Shapes

//----------------------------------

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

#using System.Reflection

BEGIN NAMESPACE CollectionItemDisplay

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

CLASS MyPropertyInfoSort IMPLEMENTS IComparer

VIRTUAL method Compare (o1 as object, o2 as object) as int

LOCAL oPI1, oPI2 AS PropertyInfo

oPI1 := (PropertyInfo)o1

oPI2 := (PropertyInfo)o2

//----- normally we compare first with second -----

//----- for an ascending sort order -----

LOCAL nResult AS INT

DO CASE

CASE oPI1:Name < oPI2:Name

nResult := -1

CASE oPI1:Name > oPI2:Name

nResult := +1

OTHERWISE

nResult := 0

END CASE

RETURN nResult

END CLASS

//==========================================================================

CLASS MySortDesc

PRIVATE oSD AS SortDescription

PRIVATE cKnownBy AS STRING

//PRIVATE cDirection AS STRING

CONSTRUCTOR()

SUPER()

self:Initialize()

RETURN

CONSTRUCTOR(cKB as string, oS as SortDescription)

SUPER()

SELF:oSD :=oS

self:cKnownBy := cKB

self:Initialize()

RETURN

METHOD Initialize() AS VOID

RETURN

//-------------------------------

access KnownBy() as string

return cKnownBy

assign KnownBy(cK as string) as void

SELF:cKnownBy := cK

return

//-------------------------------

access SD() as SortDescription

return oSD

assign SD(oS as SortDescription) as void

SELF:oSD := oS

return

//-------------------------------

END CLASS

//=====================================================================================

CLASS MyFilterCond

PRIVATE oFC AS Predicate<OBJECT>

PRIVATE cKnownBy AS STRING

//private cDirection as string

CONSTRUCTOR()

SUPER()

self:Initialize()

RETURN

CONSTRUCTOR(cKB as string, oF as Predicate<OBJECT>)

SUPER()

SELF:oFC :=oF

self:cKnownBy := cKB

self:Initialize()

RETURN

METHOD Initialize() AS VOID

RETURN

//-------------------------------

access KnownBy() as string

return cKnownBy

assign KnownBy(cK as string) as void

SELF:cKnownBy := cK

return

//-------------------------------

access FC() as Predicate<OBJECT>

return oFC

assign FC(oF as Predicate<OBJECT> ) as void

SELF:oFC := oF

return

//-------------------------------

END CLASS

//=====================================================================================

PARTIAL CLASS Window1 INHERIT Window

PRIVATE oListT AS List<Person>

PRIVATE oView1 AS ICollectionView

PRIVATE oSD AS SortDescription

PRIVATE oSD1 AS SortDescription

PRIVATE oSD2 AS SortDescription

PRIVATE oSD3 AS SortDescription

PRIVATE oSD4 AS SortDescription

PRIVATE oListSD AS List<SortDescription>

PRIVATE oListPO AS List< Predicate<OBJECT> >

PRIVATE oListMFC AS List<MyFilterCond>

PRIVATE oListMSD AS List<MySortDesc>

PRIVATE aProperties AS PropertyInfo[]

METHOD Initialize() AS VOID

SELF:CreateMySortDescs()

SELF:CreateMyFilterList()

//----- initial Sort applied in 'Setup' -----

SELF:SetupListBox1()

SELF:comboBox1:DataContext := SELF:oListMSD

SELF:comboBox1:DisplayMemberPath := "KnownBy"

SELF:oView1:Filter := NULL

SELF:oView1:Filter := SELF:oListMFC[5]:FC

SELF:comboBox2:DataContext := SELF:oListMFC

SELF:comboBox2:DisplayMemberPath := "KnownBy"

SELF:comboBox2:SelectedIndex := 5

SELF:FindPropertiesList()

SELF:comboBox3:DataContext := SELF:aProperties

SELF:comboBox3:DisplayMemberPath := "Name"

SELF:comboBox3:SelectedIndex := 3

RETURN

METHOD FindPropertiesList() AS VOID

LOCAL oType AS Type

oType := typeof(Person)

//------------------------------------------------

local aProps AS PropertyInfo[]

aProps := oType:GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)

//----- remove all those from the System - not mine ! -----

self:aProperties := PropertyInfo[]{6}

ARRAY.Copy(aProps, SELF:aProperties, 6)

ARRAY.Sort(SELF:aProperties, MyPropertyInfoSort{})

//MessageBox.Show(SELF:aProperties:Length:ToString(),"Array Length :")

RETURN

METHOD CreateMySortDescs() AS VOID

self:oListMSD := List<MySortDesc>{}

self:oListMSD:Add(MySortDesc{"Firstname A-Z",SortDescription{"Firstname", ListSortDirection.Ascending}})

self:oListMSD:Add(MySortDesc{"Firstname Z-A",SortDescription{"Firstname", ListSortDirection.Descending}})

self:oListMSD:Add(MySortDesc{"Surname A-Z",SortDescription{"Surname", ListSortDirection.Ascending}})

SELF:oListMSD:Add(MySortDesc{"Surname Z-A",SortDescription{"Surname", ListSortDirection.Descending}})

self:oListMSD:Add(MySortDesc{"Age S-L",SortDescription{"Age", ListSortDirection.Ascending}})

SELF:oListMSD:Add(MySortDesc{"Age L-S",SortDescription{"Age", ListSortDirection.Descending}})

self:oListMSD:Add(MySortDesc{"Owes S-L",SortDescription{"Owes", ListSortDirection.Ascending}})

SELF:oListMSD:Add(MySortDesc{"Owes L-S",SortDescription{"Owes", ListSortDirection.Descending}})

RETURN

METHOD CreateMyFilterList() AS VOID

self:oListMFC := List<MyFilterCond>{}

SELF:oListMFC:Add(MyFilterCond{"Smith & Jones", Predicate<OBJECT>{SELF, @SmithAndJones()}})

SELF:oListMFC:Add(MyFilterCond{"In Credit", Predicate<OBJECT>{SELF, @InCredit()}})

SELF:oListMFC:Add(MyFilterCond{"Young", Predicate<OBJECT>{SELF, @Young()}})

SELF:oListMFC:Add(MyFilterCond{"Old", Predicate<OBJECT>{SELF, @Old()}})

SELF:oListMFC:Add(MyFilterCond{"The Joneses", Predicate<OBJECT>{SELF, @Joneses()}})

SELF:oListMFC:Add(MyFilterCond{"All Persons", Predicate<OBJECT>{SELF, @AllPersons()}})

RETURN

METHOD SetupListBox1() AS VOID

SELF:CreateListT() //----- makes 'SELF:oListT' -----

//-------------- Create a separate View ----------------

LOCAL oCVS AS CollectionViewSource

oCVS := CollectionViewSource{}

oCVS:Source := ObservableCollection<Person>{SELF:oListT}

SELF:oView1 := oCVS:View

SELF:listBox1:DataContext := SELF:oListT

//---------------------------------------

SELF:SetSortOrderByIndex(0)

self:comboBox1:SelectedIndex := 0

//---------------------------------------

SELF:listBox1:DataContext := SELF:oView1

SELF:oView1:MoveCurrentToPosition(0)

RETURN

METHOD SetSortOrderByIndex(n as int) AS VOID

//-------------- Set a Sort order -------------

self:oView1:SortDescriptions:Clear()

SELF:oView1:SortDescriptions:Add(self:oListMSD[n]:SD)

//---------------------------------------------

RETURN

METHOD SetFilterByIndex(n AS INT) AS VOID

SELF:oView1:Filter := NULL

//-------------- Set a Filter condition -------------

SELF:oView1:Filter := SELF:oListMFC[n]:FC

//---------------------------------------------

RETURN

METHOD CreateListT() AS VOID

try

oListT := List<Person>{}

oListT:Add(Person{"Jones","Jessy", 19, 0, DateTime{1995, 08, 16}})

#region { .. for display only ..}

oListT:Add(Person{"Smith","Elizabeth", 16, 9.11, DateTime{1995, 08, 16}})

oListT:Add(Person{"Jones","Fredderick", 41, -8.13, DateTime{1975, 04, 21}})

oListT:Add(Person{"Smith","Jane",21, 5.66, DateTime{1989, 05, 23}})

oListT:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

oListT:Add(Person{"Smith","Alan",32, 0, DateTime{1979, 11, 03}})

oListT:Add(Person{"Jones","Mary", 47, 111.33, DateTime{1973, 05, 30}})

oListT:Add(Person{"Smith","Jannine",21, 5.66, DateTime{1989, 05, 23}})

oListT:Add(Person{"Jones","James", 36, 3.78, DateTime{1995, 08, 16}})

oListT:Add(Person{"Jones","Jessy", 19, 0, DateTime{1993, 06, 22}})

oListT:Add(Person{"Jones","Jamie", 37, 7.65, DateTime{1975, 02, 28}})

oListT:Add(Person{"Jones","Fredderick", 49, -18.67, DateTime{1977, 07, 27}})

oListT:Add(Person{"Smith","Jane",34, 25.19, DateTime{1979, 10, 7}})

oListT:Add(Person{"Smith","Elizabeth", 19, 23.45, DateTime{1995, 05, 1}})

oListT:Add(Person{"Logan","Dan", 39, -7.77, DateTime{1970, 06, 29}})

oListT:Add(Person{"Moore","Willie", 29, 6.66, DateTime{1969, 05, 27}})

oListT:Add(Person{"van der Hulst","Robert",37, 5.55, DateTime{1967, 04, 25}})

oListT:Add(Person{"Piko","Paul", 41, 4.44, DateTime{1966, 03, 24}})

#endregion

oListT:Add(Person{"Feldman","Brian", 31, 3.33, DateTime{1965, 02, 23}})

oListT:Add(Person{"Crispien","Dieter", 21, 2.22, DateTime{1965, 01, 22}})

//MessageBox.Show(oListT[13]:Surname,"Ordinal Access :")

CATCH oEX AS SystemException

MessageBox.Show(oEX:Message,"My ListT Error :")

end try

RETURN

METHOD DoSelectionChangedSort() AS VOID

SELF:SetSortOrderByIndex(self:comboBox1:SelectedIndex)

RETURN

METHOD DoSelectionChangedFilter() AS VOID

SELF:SetFilterByIndex(self:comboBox2:SelectedIndex)

RETURN

METHOD DoSelectionChangedMember() AS VOID

SELF:listBox1:DisplayMemberPath := ((PropertyInfo)SELF:comboBox3:SelectedItem):Name

RETURN

METHOD SmithAndJones(oP as object) AS Boolean

LOCAL lResult AS Boolean

LOCAL oPerson := (Person)oP AS Person

//----- return All people called 'Smith'or'Jones' -----

if oPerson:Surname == "Jones"

lResult := TRUE

ENDIF

if oPerson:Surname == "Smith"

lResult := TRUE

ENDIF

RETURN lResult

METHOD InCredit(oP as object) AS Boolean

LOCAL lResult AS Boolean

LOCAL oPerson := (Person)oP AS Person

//----- return people who have paid in -----

lResult := oPerson:Owes < 0

RETURN lResult

METHOD Young(oP as object) AS Boolean

LOCAL lResult AS Boolean

LOCAL oPerson := (Person)oP AS Person

//----- return people who are '30' or less in age -----

lResult := oPerson:Age <= 30

RETURN lResult

METHOD Old(oP as object) AS Boolean

LOCAL lResult AS Boolean

LOCAL oPerson := (Person)oP AS Person

//----- return people who are more than '38' in age -----

lResult := oPerson:Age >= 39

RETURN lResult

METHOD Joneses(oP as object) AS Boolean

LOCAL lResult AS Boolean

LOCAL oPerson := (Person)oP AS Person

//----- return All people called 'Jones' -----

lResult := oPerson:Surname == "Jones"

RETURN lResult

METHOD AllPersons(oP AS OBJECT) AS Boolean

RETURN TRUE

END CLASS

//=========================================================

CLASS Person INHERIT DependencyObject IMPLEMENTS System.ComponentModel.INotifyPropertyChanged

STATIC PUBLIC FullDetailProperty := DependencyProperty.Register("FullDetail", typeof(STRING), typeof(CollectionItemDisplay.Person), PropertyMetadata{""}) AS DependencyProperty

STATIC PUBLIC SurnameProperty := DependencyProperty.Register("Surname", typeof(STRING), typeof(CollectionItemDisplay.Person), PropertyMetadata{""}) AS DependencyProperty

STATIC PUBLIC FirstnameProperty := DependencyProperty.Register("Firstname", typeof(STRING), typeof(CollectionItemDisplay.Person), PropertyMetadata{""}) AS DependencyProperty

STATIC PUBLIC AgeProperty := DependencyProperty.Register("Age", typeof(INT), typeof(CollectionItemDisplay.Person), PropertyMetadata{-1}) AS DependencyProperty

STATIC PUBLIC DoBProperty := DependencyProperty.Register("DoB", typeof(DateTime), typeof(CollectionItemDisplay.Person), PropertyMetadata{DateTime{1900,01,01}}) AS DependencyProperty

STATIC PUBLIC OwesProperty := DependencyProperty.Register("Owes", typeof(double), typeof(CollectionItemDisplay.Person), PropertyMetadata{}) AS DependencyProperty

//----- CARE : if put 0 as default (value) in metadata for double it throws and exception at compile time !? -----

//----- Events -----

EVENT PropertyChanged AS System.ComponentModel.PropertyChangedEventHandler

constructor()

SUPER()

self:Initialize()

return

constructor(cS as string, cF as string, nA as int, nO as double, dtD as DateTime)

SUPER()

self:Initialize()

self:Age := nA

self:Owes := nO

//----- check that DT is valid !? -----

self:DoB := dtD

self:Surname := cS

SELF:Firstname := cF

self:FullDetail := self:MakeFullDetailsProperty()

RETURN

//----- Methods -----

protected method OnPropertyChanged(name as string) as void

local pChanged as System.ComponentModel.PropertyChangedEventHandler

pChanged := SELF:PropertyChanged

if (pChanged != null)

pChanged:Invoke(self, System.ComponentModel.PropertyChangedEventArgs{name})

endif

RETURN

//-------------------------------------

METHOD ToString() AS STRING

RETURN SELF:Firstname+" "+SELF:Surname+" "+SELF:Age:ToString()+" >> "+SELF:Owes:ToString()

METHOD ToString2() AS STRING

RETURN SELF:Firstname+" "+SELF:Surname

//-------------------------------------

method Initialize() as void

self:Age := -1

self:Owes := 0.0

self:DoB := DateTime{1900,01,01}

self:Surname := ""

SELF:Firstname := ""

self:FullDetail := self:MakeFullDetailsProperty()

RETURN

METHOD MakeFullDetailsProperty() AS STRING

LOCAL cResult := "" AS STRING

cResult := SELF:Firstname+" "+SELF:Surname+" "+SELF:Age:ToString()+" >> "+SELF:Owes:ToString()

return cResult

//-------------------------------

ACCESS FullDetail() AS STRING

RETURN (STRING)self:GetValue(FullDetailProperty)

ASSIGN FullDetail(cD AS STRING) AS VOID

SELF:SetValue(FullDetailProperty,cD)

self:OnPropertyChanged("FullDetail")

RETURN

//-------------------------------

access Age() as int

return (int)self:GetValue(AgeProperty)

assign Age(nN as int) as void

SELF:SetValue(AgeProperty,nN)

self:FullDetail := self:MakeFullDetailsProperty()

self:OnPropertyChanged("Age")

RETURN

//-------------------------------

access Owes() as double

return (double)self:GetValue(OwesProperty)

assign Owes(nN as double) as void

SELF:SetValue(OwesProperty,nN)

self:FullDetail := self:MakeFullDetailsProperty()

self:OnPropertyChanged("Owes")

return

//-------------------------------

access DoB() as DateTime

return (DateTime)self:GetValue(DoBProperty)

assign DoB(dtD as DateTime) as void

SELF:SetValue(DoBProperty,dtD)

self:FullDetail := self:MakeFullDetailsProperty()

self:OnPropertyChanged("DoB")

return

//-------------------------------

access Surname() as string

return (string)self:GetValue(SurnameProperty)

assign Surname(cS as string) as void

SELF:SetValue(SurnameProperty,cS)

self:FullDetail := self:MakeFullDetailsProperty()

self:OnPropertyChanged("Surname")

return

//-------------------------------

access Firstname() as string

return (string)self:GetValue(FirstnameProperty)

assign Firstname(cF as string) as void

SELF:SetValue(FirstnameProperty,cF)

self:FullDetail := self:MakeFullDetailsProperty()

self:OnPropertyChanged("Firstname")

return

//-------------------------------

end class

END NAMESPACE

=======================================================================================

PHH - 10th. November 2011.