Introduction

 Top Next

Now that I have had the benefit of using WPF for a couple of months, I am now in a better

position to make some simple statements which may help both you and me come to terms

with 'what is'!

First of all, WPF and XAML are quite separate things, or even issues.

WPF is a whole set of dotNet classes, in its own namespace, which allow the next generation

of GUI, after Forms 2.0, to be created.

As you will see later in these notes, it is possible to use no XAML markup at all, and only Vulcan

code to create a WPF window. However, I don't intend to do that for very long ;-)

What was called a 'Form' is now called a 'window'. So we are moving from 'forms' to 'windows' by going

into WPF coding.

Since WPF is a set of dotNet classes, then all coding is possible in Vulcan. And some more enlightened

web article sources encourage anyone calling themselves a 'professional' developer, to get proficient at

writing code to do all the necessary supporting WPF stuff. We will need to do it sometime, so don't run away.

Actually, since most people coming to WPF will be comfortable with dotNet syntax, and even may be quite

proficient in using dotNet, it is the easy way in to WPF to use the designer to place WPF controls on the

window, within a grid, and do the rest in Vulcan code.

I have managed to do a lot in WPF while keeping my XAML creation down to a minimum, and left it up to

the designer to do the XAML 'bit' for the moment. yes, I intend to master XAML when the time is right

to focus on it, I just want to gain some successes in WPF first.

When I address the matter of writing XAML, I will also look quite seriously at Expression Blend. But

firstly I realise that I need to master some WPF coding in Vulcan.

Once you realise that XAML is a separate issue entirely, it becomes the obvious question, "what does WPF

bring to us the developers, that Forms did not?"

If we ask the question this way, and in light of our realisation about XAML, then we are much more likely

to see what we have been missing by not using WPF earlier.

In order to keep this short, so we can get into the meaty part of these notes, I will simply say that WPF is

Microsoft's next attempt at getting a modern GUI environment, which is totally consistent in many ways

that Forms was not. It has all the functionality built into it from the start, and does not have parts 'bolted' on

afterwards. This is most obvious when we come to Data Binding in WPF, things are a lot more straight

forward, logical, and consistent.

The consistent and persistent use of objects and collections is also very obvious to me, and I am starting

to use collections to a level I did not previously. It does make life easier, after a little mental adjustment.

Finally, I can say that all modern approaches to documents, display, and printing have been addressed,

so don't try and do everything the old way, before you find and see the benefits of the new ways.

And don't forget, if all else fails, and you want a Forms control functionality (say like a Rich Text Box) then

you can host it in a host control on your WPF window.

More excitingly, if you want some new functionality which comes with WPF, then you can host the WPF

control on a Form, even one of your existing forms. This can be useful for flow documents, which were

just not thought about when Forms was designed and created.

Enough of this, lets get learning ;O))

PHH - February 2010.


What is WPF ?

 Top Previous Next

Well the offline beta Framework 4 documentation (CHM format) has this to say about WPF :-

[.. checkout chapter 4 to see how to download and use this.. ]

Windows Presentation Foundation (WPF) provides developers with a unified programming model for building

rich Windows smart client user experiences that incorporate UI, media, and documents.

WPF Fundamentals support the overall programming model. These include features that extend CLR concepts

like properties, events, input, commanding and other programming model features like styles, templates,

threading, resources, and working with an element tree.

The WPF development platform is built on a core programming system, which is extended to support a broad

set of application development features, including the application model itself, resources, controls, graphics,

layout, data binding, documents, and security.

The core of WPF is a resolution-independent and vector-based rendering engine that is built to take advantage

of modern graphics hardware. WPF extends the core with a comprehensive set of application-development

features that include Extensible Application Markup Language (XAML), controls, data binding, layout, 2-D and

3-D graphics, animation, styles, templates, documents, media, text, and typography. WPF is included in the

Microsoft .NET Framework, so you can build applications that incorporate other elements of the .NET

Framework class library.

Windows Presentation Foundation (WPF) is a next-generation presentation system for building Windows client

applications with visually stunning user experiences. With WPF, you can create a wide range of both standalone

and browser-hosted applications.

Programming with WPF :

WPF exists as a subset of .NET Framework types that are for the most part located in the System.Windows

namespace. If you have previously built applications with .NET Framework using managed technologies like

ASP.NET and Windows Forms, the fundamental WPF programming experience should be familiar; you instantiate

classes, set properties, call methods, and handle events, all using your favorite .NET Framework programming

language, such as C# or Visual Basic.

To support some of the more powerful WPF capabilities and to simplify the programming experience, WPF

includes additional programming constructs that enhance properties and events: dependency properties and

routed events. For more information on dependency properties, see Dependency Properties Overview. For more

information on routed events, see Routed Events Overview.

Markup and Code-Behind :

WPF offers additional programming enhancements for Windows client application development. One obvious

enhancement is the ability to develop an application using both markup and code-behind, an experience that

ASP.NET developers should be familiar with. You generally use Extensible Application Markup Language (XAML)

markup to implement the appearance of an application while using managed programming languages

(code-behind) to implement its behavior. This separation of appearance and behavior has the following benefits:

- Development and maintenance costs are reduced because appearance-specific markup is not tightly coupled

with behavior-specific code.

- Development is more efficient because designers can implement an application's appearance simultaneously

with developers who are implementing the application's behavior.

- Multiple design tools can be used to implement and share XAML markup, to target the requirements of the

application development contributors; Microsoft Expression Blend provides an experience that suits designers,

while Visual Studio 2005 targets developers.

- Globalization and localization for WPF applications is greatly simplified (see WPF Globalization and Localization

Overview).

The following is a brief introduction to WPF markup and code-behind. For more information on this programming model,

see XAML Overview and Code-Behind and XAML.

So don't just think of WPF as an upmarket version of Forms ;-)

I think it is the two word "unified programming" which may have as much impact on many of us as anything in WPF.

Also if I had to hazard a guess it would be "unified programming" that forces a XAML type solution on the Microsoft

designers of WPF.

To have one way of programming for all scenarios, we need to have the ability to have the GUI code run

on a machine which is physically distant from the server machine, and even NOT running Windows.

Any developer who has spent some time getting his/her head into the world of dotNet would expect many

of the other improvements to the WPF facilities / functions. Like all things being based on objects.

It is not surprising to me that documents are now objects, and as such can be nicely rolled up into

XML for transporting over distances.

It is also not surprising to me that vastly improved 'media' features have been included in WPF. However,

it would appear that certain 'presentations' using these media features - by personnel not understanding what

WPF was all about - has in many ways given WPF an early "bad press".

I suggest you forget any silly OTT demonstrations of WPF and consider it an essential part of development

in the future, and something which will make your life both easier and richer.

The sooner we all get to grips with WPF the better for us all ;O)

If I had to make a generality about WPF and Forms, based on my research so far to date, I would say that

it would appear that most 'corners' of WPF have all been designed in a modern object fashion, and in a consistent

style across all the classes in the whole namespace. This in itself makes it a joy to use.

Finally, I would also add that don't let XAML script get foremost in your thinking about WPF. I have found with

my application and project successes to date, that much can be done with WPF without getting "heavily" into

XAML scripting. Yes, you pick bits up as you use the Properties Pane, and also much can be done in

Vulcan code itself - once you know how.

We will explore more on XAML in the following subsections.

See you there.

PHH - January 2010.


Why use WPF - or Not !

As you may realise if you read the Introduction as it now stands, this is an easy question

to answer.

The modern facilities built into WPF controls for the new GUI surface make application development

a lot easier and more flexible too.

If your reason not to use WPF is because you were scared off by XAML, then XAML is not an issue

as I explained earlier.

Quite honestly, from my limited experience to date, I will use WPF from now on, simply to access

the power and simplicity of the data binding, if nothing else.

Oh! here is a small example of what 'extras' you get, the 'TextBox' control has a property which allows

you to turn on a spell checker facility. This is very handy, and easy to use.

Even if your target development app is to be a stand alone app and therefore no XAML is needed as such,

you will benefit from accessing the modern facilities of the WPF controls, and the WPF platform

generally.

Also, there is another way of looking at this, and that is that the VS WPF designer is not C# based

and therefore we as Vulcan.NET users are getting as close to a well designed and widely tested tool

as possible. There ought to be far less specific Vulcan integration required for WPF window design than

with Forms.

PHH - February 2010.


What is XAML ?

XAML is 100% XML !

The following section is from the beta documentation of Framework 4 - CHM format.

XAML is an XML-based markup language that is used to implement an application's appearance declaratively. It is

typically used to create windows, dialog boxes, pages, and user controls, and to fill them with controls, shapes,

and graphics.

Since XAML is XML-based, the UI that you compose with it is assembled in a hierarchy of nested elements known

as an element tree. The element tree provides a logical and intuitive way to create and manage UIs. For more

information, see "Trees in WPF".

Definition :-

XAML, Extensible Application Markup Language, is Microsoft's XML-based language for creating a rich GUI, or

graphical user interface.

XAML supports both vector and bitmap types of graphics, as well as rich text and multimedia files. XAML was

introduced in 2003 as the language behind the Windows Presentation Foundation (WPF), which is Microsoft's

GUI design tool for the upcoming Windows Vista operating system.

XAML files can be produced in either Visual Studio, Microsoft's development tool for the .NET Framework, or in

Microsoft's Expression toolset (Blend) for designers, before exporting them to the Windows Presentation Foundation.

This allows designers, who focus on the front end of an application, and developers, who focus on the code that defines

the front end, to work from the same code base, which improves productivity.

XAML and BAML :

XAML Source :

When you build a WPF application inside Visual Studio the compiler creates a Binary Application Markup (BAML) file.

This is a binary representation of XAML auto-genned by Visual Studio. I was happy to hear that Reflector can show me

the XAML for these files.

This is great news.

See my more recent post for details on how to setup up Reflector correctly to recognize BAML files.

[ http://blog.wpfwonderland.com/2007/01/23/latest-version-of-reflector-can-dissemble-baml-to-xaml/ ]

[ http://blog.wpfwonderland.com/2007/01/27/reflector-and-wpf-baml-disassembler-revisited/ ]

Silverlight is VERY much related to WPF - nee, even 'part' of it you may say !

Silverlight (a subset of WPF) which is designed and optimized for web scenario.

Microsoft Silverlight is a free runtime that powers rich application experiences and delivers high quality, interactive

video across multiple platforms and browsers, using the .NET framework. Learn why Silverlight is right for your

business.

Chris Andersen's :-

http://www.simplegeek.com/permalink.aspx/100aec62-3352-4c35-b471-f3f2fa5fac5a

Here introduces one of our current debates - is XAML a programming language or a resource format?

With XAML you can define new types, define interactivity with "def:Code", and specify member variables of the type

using "ID". XAML also defines the UI appearance of something, and is used for localization scenarios.

XAML lacks any imperative constructs (branching, etc.).

XAML is more of a "programming model" rather than a language, so we have left it at that - "XAML is a

declarative programming model".

The general rules for XAML go something like this:

1.

Tag names are class names. The CLR namespace and assembly is discovered by an XML namespace mapping scheme (more later).

2.

XML Elements are mapped to CLR properties or events

3.

Sub-elements of a tag can either be other class instances or "compound" properties (of the form Type.PropertyName)

There are a few more advanced rules around the "Definition" namespace and Style.VisualTree, but basically

these three rules are it.

Property values are converted from strings in markup to code using TypeDescriptors (just like in ASP.NET).

Rule #3 has some complications to it - there are several ways that a child object gets attached to a

parent (IAddChild, IList, etc.).

There is a fourth rule:

4. The root tag of a markup file is the base type of the class the markup file defines.

Thus a file like this:

<Window xmlns="..." xmlns:def="..." def:Class="Window1">
<Button>Hello</Button>
</Window>

Would define a new type called "Window1" that derives from "Window".

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

Let us make some simple statements to help us on our way :

XAML belongs to Microsoft !

Where did it Come From ?

Microsoft dreamed it up and created it.

Because it is not 'Public Domain' you will not find it covered in any detail at all in the 'w3schools' web site for learning.

The W3 Consortium covers XML, HTML, and many other related markup and script technologies, including JavaScript, but

you will see no mention 'at all' on their 'schools' learning site of XAML.

This is a pity for a couple of reasons, firstly it would help people like me trying to get my head around all this new 'stuff'. And

secondly, they do such a good job of explaining things simply and clearly, and without fuss - it would be a good place to start

learning XAML if it was under their wing.

However, now I know that I need to look elsewhere, that in itself is a help - I suppose. And it does confirm that I can simply

say - "XAML belongs to Microsoft."

What is it ?

We need to be careful answering this question, as we may give the wrong impression. I have read some (in my mind)

misleading comments recently while trying to understand this myself ;-)

It is often said to be a markup language, and is based on XML. There is no need to refer to anything else, or throw in

other similarities!

Because XAML is XML code, it should be "well formed" when written correctly, and can be checked out as such.

Microsoft have added a whole list of tags, or markup elements which they say are part of XAML. We will explain more

of this a little later, but we need to know that what elements exist are simply due to Microsoft's decisions.

So if a piece of software specifically designed for creating, editing and saving XAML code is used, it will also be able to

check that no incorrect tags are used - by referring to 'internal' lists available to it.

Although XAML is XML based, we can't add our own elements (or tags) as we can in XML, otherwise all things would be

possible, and not checking could therefore be made for correctness.

So to summarize, XAML is an extensive markup language which has been designed and 'built' by Microsoft, we can use

the elements they had included (and presumably they may include others in the future).

If we are to create anything ourselves using XAML, we are likely to use a designer application. Although it is straight

forward to learn and write XAML, it does lend easily to comprehensive designer tools to make the life of developer and

GUI designer so much richer. More of this later.

What does it do ?

Well in one sense it does nothing, in the sense that XML does nothing.

[ On the other hand you could say that it makes WPF possible, and if that is the case then it makes .NET possible. ]

It actually contains all the declarative information to describe a GUI which is to support Microsoft's .NET technologies.

Think of the XAML for a screen, and the GUI itself, in the same way as a shopping list and the pile of goodies themselves.

[ It is very easy to transport the shopping list !]

If you will, it is a 'plan' for the User Interface. This plan can be easily transmitted across the web, and be employed at a

remote terminal - the business logic residing on the web server.

So once again we need to consider the 'separation' model. Remember our discussion on 'layering' earlier ? Well, XAML

allows the layering of the GUI and the business logic to take place.

Remember, the EXE file is a thing of yesteryear. The execution file has splintered into function parts, those required

locally and those which may stay at a distance. This reduces the traffic of transmitted code and information at this

same time as vastly improving the experience of the users of web based services.

Summarise this as - "only send the bits you have to" !

What is it for ?

Once again we need to be careful when answering this. Don't lets throw in too many rough comparisons, let us get a

few 'critical' ones - which are totally correct.

Microsoft would say 'UI' the Unified Interface.

At a very useful 'free' Microsoft presentation at their Thames Valley site, they said that WPF was best approached

and thought about as solving the Unified Interface (UI) problem. I believe four older and separate technologies could

be replaced with one. And that would also remove a lot of barriers to developers, and improve experiences for

users too.

With WPF and XAML, we can have the same rich technology creating local application GUI screens as that creating

remote and web based improved interactive screens.

So simply it is to allow separation of GUI and application logic, such that the same solution can run locally as applications

did, as well as remotely as web services. In fact the new program model is really a web service model.

Once we know what it is for, we can see other benefits, like being able to separate the designers who make the XAML

for the rich GUI experience, from the developers writing the application logic.

However, be careful when you read some source material, it is often suggested that this is the reason for XAML - not

so ! this is a nice side benefit ;O)

If we (by design) have separated the GUI from the application logic at runtime then it is not difficult to see that we can

have separation of these two at design time.

Why ?

If you have a business eye view to how MS works, you could say Bill Gates was tired of paying large sums of cash

to support four different, and hardly overlapping technologies, attempting to solve the same problem.

Others may say that it is just a natural progression of the development of the IT world we live in.

Myself ? I feel it may be a bit of both. Certainly, the waste of resources must have helped focus the mind on pushing

to carry out what seems like a necessary 'next step'

How does it Affect Developers ?

"Simply", in my book.

We can't ignore it. We therefore must either retire of embrace it.

For those who may wish to think about a rush to retirement, I would suggest they research whether this exciting new

raft of technologies rolled into .NET are actually an 'excellent' way to enjoy retirement.

Take it from me (a standard computer guy) this stuff is a whole lot easier than it may at first appear. Your in-built fears

are doing you a disservice!

I know, I've been there - and escaped! No retirement for me, I am just intending to enjoy my coding and development

for years to come - God willing ;O)

Can we as Developers Ignore the new Challenge ?

Well, first of all don't panic!

The answer to this is both yes and no.

The existing world of Forms 2.0 and EXE files, and GDI+ pixellated graphics is going to be around for a while. Also you

programs won't stop running over night.

[ I see the new book (arrived yesterday) I am reading on XAML was published in March 2006. ]

Because of the complexity and nature of the this newer world of IT we have long run in times. The wise amongst us will

see this as plenty of time to engage it developing new skills, and gradually introducing them into our solutions, as and

when we see fit.

The foolish amongst us will see this as an easy way of putting off until tomorrow, or even next week.

Being a bit close to retirement myself, I see these changes in the following way. My age and experience means that for

years in the past, we has had to do things the had way as developers and program writers. At time the experience was

far from 'rich', sometimes closer to monotonous and unexciting.

So when the world is getting colourful, rich, and more exciting, why run away now ?

In fact as well as the above sentiments being true, it is also true to say that the modern .NET coding approach is far, far,

more productive, It has been estimated to be five times faster than previous experiences, and after two years 'at it' I would

agree indeed.

Sources of Information :

XAML in a Nutshell - O'Reilly. By Lori MacVittie ( and interesting and experienced lady - and its shows ).

ISBN-10 : 0-596-52673-3

This is also Safari 'Books Online' enabled. Includes Free 45-Day Online Edition - I will try this !

try >> http://safari.oreilly.com

Mapping XAML to the .NET Framework :

Every XAML tag corresponds directly to a .NET Framework class.

The attributes of the tag (or element), correspond directly with the properties of that class.

So a specific property may be 'set' through the inclusion of a specific XML attribute. Remember XAML is XML, and at certain

time we will refer to the 'XMLness' of the XAML script. the existence of elements (tags) and attributes, is an XML feature.

Properties that are 'read only' are NOT exposed through XAML. Properties which are public, and which have both a 'Set and a

Get' methods, are accessible to developers through XAML.

Let's see how this works out with a simple code example :-

<Button Name="MyPress" Height="50" Width="200" Background="Red" Foreground="White" FontFamily="Times New Roman" FontSize="14" Content="Red Button" />

PROTECT MyPress AS Button{}

MyPress:Background := Brushes.Red

MyPress:Foreground := Brushes.White

MyPress:Content:= "Select"

MyPress:FontFamily := "Times New Roman"

MyPress:FontSize := "14"

MyPress:Height = "50"

MyPress:Width = "200"

We will see how to include reference to the event handler a little later.

The live test of our first XAML script line is shown above.

I have used XamlPad to input and display our code. This obviously check for 'well formed' XML and also correctness

of tags and attributes. We say there has been a 'parse check', or the script is parsed.

As you can see there is an applet toggle function which allows parsing and rendering on keyboard entry. This can get

a bit annoying once you know what you are doing, so it is easy to turn off and on at will of the user.

See below to see what we get if I change the code and render, and then add a 'typo' and reparse. The error in the text

entered is indicated with a small vertical grey marker, and all the text is displayed in red.

Check this out for yourself :-

We must do something about the layout of the XamlPad applet !

Let's see what can be done to improve both readability and display :-

I think you will agree that it has improved somewhat ;-)

If I consult the back of the book of Lori's book (XAML in a Nutshell) I see some predefined colours I can

use with brush elements.

See below for the changes :-

So our output get more compact as it gets more interesting ;-)

Notice we have changed the colours of the brushes, and increased the size quite a lot, as well as adding

a style of italic.

[ I have yet to find out why it would not take "Bold". ]

Fine, just before we complete this part of our XAML introduction, lets see what happens if we include an

attribute in our script in XamlPad which relates to an event and an event handler.

This is what we get :-

It looks as though we need to look at visual Studio itself for this inclusion. And maybe a taste of

Expression Blend too.

See you in the next sub-section.

PHH - January 2010.


What is BAML ?

http://www.codeplex.com/wikipage?ProjectName=reflectoraddins&title=BamlViewer&referringTitle=Home

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

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

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

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

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

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

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

PHH - January 2010.


How much code, how much script ?

The Answer :

No XAML script at all if you so wish, some if you are sensible, and a great deal if that seems to suit you!

Some of you may say that this is not a helpful answer, but if we look a little closer at it we may see that

it has merit.

The no script at all part just reinforces the fact that WPF stands on its own as dotNet 'stuff' and does

not need XAML as such.

The last part about using a great deal is a sensible remark which applies to any guy wishing to move

to web services and Silverlight, were the Unified Interface (UI) is a main goal. Since at the moment

Silverlight is a subset of WPF, then any initialization of GUI screens with extensive XAML scripting may

pay benefits as far as time and web transfer to a distant client platform is concerned.

I suspect that many of us starting out with WPF for the first time will choose the middle option of the

simple answer. We will use the WPF designer to create and edit our GUI surface, use the Properties

Pane (PP) a little, but do a lot more in Vulcan code.

I suspect that as time goes on, excellent tools will provide for those of us who wish to make more XAML

without hand coding it. A bit like the tools to allow guys like me to make web site stuff without getting

deep into HTML and the likes.

However, I can't see any tools arriving which will do the Vulcan code creation for you ;-)

For your interest, the approach taken in much of these eNotes is the middle one. As we get more

experienced I will show you how to write the script which does what we by then know how to code

in Vulcan.

But do remember, not all code can be written in XAML - XAML is only a declarative programming 'model'.

It can't handle imperative stuff, and logical branching. Also, as far as I can tell at the moment, you only

get one shot at running the XAML script file, so later stuff has to be coded.

I may find that this is not the case but at the moment it looks true enough.

General Discussion :

This is a new problem to many of us.

While creating a Forms GUI the designer created Vulcan code for the 'Code Behind' section. This

section could be read by the developer as the project progressed. [ This was also the section we

were advised to stall well clear of, and do little or zero editing in the same PRG file. ]

To put it simply, anything that was done on the design surface, and in the input boxes of the PP

(Properties Pane), ended up in Vulcan code in the 'code behind' PRG file.

In Visual Objects and Forms, it was a matter of 'personal choice' as to how much was left to be done

by the designer (and I include the properties entry as part of this), and how much was actually hand

coded by the developer.

I like using the graphical interactive aspect of the window designer in VO, and even more so the rich features

of the Forms designer. However, I have never 'over used' the PP in Forms. I still liked to be able to see my

Vulcan code in my own partial class, and how things were initialized in my own method of the same name.

I even know some 'macho' developers who liked to be able to by-pass the designer altogether and write

everything as hand coded stuff. Strange guys these ;-)

"Okay", I can hear some of you say, "that is no big deal ...", and that is true - BUT - in the world of WPF things

would appear to be VERY different, and we need a good mental model upon which to base what we do from now

on.

The WPF Designer - What it Does for Us ? :

When we move from VO and Forms to WPF, we find that the work done in the designer, and from data input into

the properties pane (PP) creates a XAML script as the 'code behind'. The XAML script is placed in a PRG file

and does not get translated into Vulcan code.

So the more work we do at a designer level and PP input, the larger the amount of XAML we have generated.

[ *** Note *** if you are using Blend 3 as a 'super' designer, then the same argument applies for it as the

designated 'designer' in place of that built into Visual Studio. ]

So what is the big issue ? Not that we are try to get away (escape) from XAML itself, as you will soon find out it is

straight forward enough to write and understand.

Well the problem comes as part of the advantages of the dotNet 'programming model'. The fact that the XAML

script is to be run by the browser on a client (or target) machine at a distance to the application EXE file running

on a server machine.

It would be foolish of us to take the approach that since we don't intend to write web services, then we will not

concern ourselves with anything other than effectively the stand alone type of programming model. And just

do what we need to to get things to work - in the way that it personally suits us.

On a personal level, I think I would prefer to know what is required to code a WPF solution for BOTH situations

so that I can develop one style of development coding, which suit all situations. Much in the same way as I

now always write my data accessing projects using the multi user model, even when I know the applet

concerned is hardly ever likely to be used by more than one user at a time.

So when we use the designer to select tools from the toolbox, and the PP to give values to any chosen

property, this generates XAML script entries in the 'code behind' file. Classes turn out as tags, and properties as

attributes, in the XAML code. This is a 100% correspondence, or mapping of the two sides of things.

What Does the WPF Designer NOT do for Us ? :

The facilities in the designer do not tell us what to do, and how far to keep going.

If the XAML script we generate is to run at a distance, then we will have to make sure that all code required at the

client end is present in XAML, since any Vulcan code will be being run at the server side of things - which could

be as far away has half a world !

Similarly, we don't wish to have server side Vulcan code trying to access 'stuff' which is on the client machine.

Since this is all new to me, I don't know the answers, but am aware of the fact that we may need to find out more

of this aspect of WPF programming / development.

So it would seem a simple rule - make sure the WPF project, in XAML script, has available to it locally, everything

that the script refers to, and does not refer to anything which it can't 'get hold of'.

Also make sure that your Vulcan code running 'server side' does not refer to anything only available at the client

machine.

Where to Now, or How Far do We Take This ? :

And now the human 'bit' !

Having being careful to lay out clearly what we are aiming at, I am now going to say lets forget most of it for now.

I think we have a lot of new things to learn, and so we need to go 'one step at a time'. As long as we don't

forget where we are headed ;-)

Till we get more experienced and successful we need to mix the XAML script and the Vulcan code at a balanced

'level' which is simply based on 'get it working at all costs' approach.

We can worry about future aims of creating web services with VN and ASP.Net etc. at a much later stage.

My Approach :-

So far, with just a small amount of research, I have found editing the XAML script a touch 'fragile' when the developer

is not experienced. Yes, I can see that it is quick, powerful, and effective, but also quite easy to mess up when

you get too ambitious, too early.

So until I get more 'into the swing' of things, I will probably write more Vulcan code - especially since I am needing to

learn about classes and properties of the new WPF controls.

See you in the next subsection.

PHH - January 2010.


What will Happen to Forms ?

I expect Forms to be still around for years to come.

Forms will continue to be widely used, and luckily for all of us, Forms and WPF may be

used side by side, quite happily.

However, having said that, I feel that as developers get more familiar and comfortable with WPF ways

of doing things, they will tend to gravitate to WPF alone.

It would seem to me, after my small amount of research, that most things in WPF have been thought through

more clearly, and implemented more 'correctly' in WPF. Here we may like to use the binding of data as

an example, apparently WPF has moved this into a different 'comfort zone', and I for one intend to get

the most from it ;O)

Certainly, for VO guys coming to the world of dotNet I would suggest exploring Forms thoroughly before

wishing to rush off in the WPF direction.

Find out just how you can combine VO screens and Forms to get the most from your old transported

applications. Then add WPF windows to your solution, and maybe even host WPF controls on some of

your Forms too - to get enhanced functionality.

PHH - January 2010.


Peaceful Coexistence !

Having Your Cake and Eating it ! :

The good news for guys coming to WPF is that you can "both have your cake, and eat it" !

Keep your knowledge intact, and your productivity high, by continuing to use Forms productively.

However, branch out into using WPF when your needs are such that WPF better serves your purpose.

So 'needs driven' is what I am saying will be my guide. If something is best done in WPF and I have the

time to develop it, and make it work satisfactorily, then I will. And this goes for 'needs driven' at a control

or tool level, not just a GUI level.

By this I mean that a Forms control (like RTB for example) may be what I require on my WPF window. If

that is the case then I can easily host it and use it - just like magic !

Alternatively, if I wish to have some enhanced WPF functionality on my Form I can also host a control

there too. We will see later how we for example can have a Flow Document viewer on a Form. There

is no other way to allow us to display 'Flow Document' objects on Forms - there are no standard Forms

controls, is what I am saying really.

Here is an image of such a 'cross GUI' solution to a problem :-

The above is a standard Form with a WPF custom control (CC) 'hosted'. There are two standard WPF controls

on the CC, one a RichTextBox, the other a FlowDocumentReader.

It would appear that we can really mix and match our Forms and WPF GUI, at both a screen level, as

well as a control level - which is great news indeed.

Drag and Drop :

Since DnD is a Windows 'thing' we should have no problem getting this to work across the GUI platforms.

Chapter 8 will deal in specific details of how to do this, and build on the work which I did for the

'ClickStartDnD' eNotes.

PHH - January 2010.


Useful Interoperability !!

One Note of Caution ! :

PHH - January 2010.


Chapter 1 -

If WPF was just a new version of Forms, and was simply to support locally run applications, then

there would not be a XAML. There would be no need for it !

However, because Microsoft need to have GUI running at a distance from other parts of an application,

on the standard .NET model of things, it simply has to exist.

Obviously, the script needs to be XML, and since there needs to be tags and attributes held within it

to 100% map to the classes and properties of WPF namespace, then XAML has to belong to Microsoft.

XML and HTML are open, but XAML belongs to Microsoft. They (MS) develop and maintain the namespace.

At the moment a subset of WPF is available to run in a "cross browser" mode, and this is called Silverlight.

Originally, Silverlight was termed "WPF Everywhere" (WPF/E) - we are now up to version 3.0 and more will

follow I am sure of that. The latest version is far less of a subset than the original ;-)

I am guessing when I say that in years to come developers will use one style of software app creation, and

develop to run locally and with GUI separated. In a similar way to the approach all sensible developers use and

create apps which are multi user, whether they are intended for stand alone use, or over a network.

So to summarize, on the web services dotNet model of things, GUI runs at a different PC which can be as far as

half a globe. The GUI is supported by Silverlight and Windows does not need to be present on the target PC.

The web services app will run on the server, and the data can be on an SQL server any other place.

Of course, all three of these could actually be on the same PC. We frequently run SQL server and local

Windows apps on the same box. So they may be separated, but don't have to be.

Making a Start with XAML :

PHH - January 2010.


Chapter 2 -

In this chapter we will look at the Visual Studio 2008 'window designer'. When I say 'window

designer' I mean the parts of VS which allow the user to create and edit WPF interfaces.

Here we will look at how it works, and how we can make life easier for ourselves. However, we

will leave production of working apps in WPF until chapter 4.

For the moment lets look at what we have available to us, and how we may best use the tools on offer.

PHH - December 2009.


Controls - Comparison of Forms & WPF

Lets Start with What is New :

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

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

PHH - January 2010.


Chapter 3 -

PHH - January 2010.


Chapter 4 -

In this chapter we will build upon the knowledge we gained in chapter 2.

Now that we are aware of what the WPF window designer is in VS2008, we can use it to

start making some simple, but working, applications.

Remember WPF is a huge topic in its own rights, so we need to go steadily, and gradually build

up both our skills and our knowledge.

Before we get to designing windows which will 'flow', we need to just see how to place some simple

controls on the user interface itself, and provide some content too.

Quite soon after getting apps to build and run WPF we will need to start adding our handlers for chosen

events - so we can get our app to respond appropriately.

At this moment, not all of the available WPF controls appear in the toolbox in VS2008. There are some in Blend 3

which are not in VS. However, we have a whole bunch of useful stuff to choose from, and put into an

app or two ;-)

As we develop our WPF skills and gain confidence in what we do, we need to be on the lookout for

differences in functionality of controls in Forms compared to WPF.

Okay then, lets move onto the next subsection where we make a start.

PHH - December 2009.


Making a Start

Open Visual Studio 2008 (VS) and make sure any previous solution is closed.

Now use 'File, New, Project...' as shown below :-

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

In the New Project dialog select the Vulcan.NET project type, and the template entitled

"WPF Application".

Provide a suitable name for the project, and if you wish change the solution name to something you

require. I have changed mine to 'ClickStartWPFfirst' after I took the screen shot.

If you do everything correctly you should see something like the image shown below :-

Notice the toolbox looks different to that in Forms - for experienced Form users ;-)

In the Solution Explorer (SE) pane you can see my change solution name. Don't worry about the XAML script

in the XAML editor, at the moment. We will get around to that shortly.

For the moment, lets just see if we can build and run our app, as simple as it is ;-) I have used Ctrl+F5 to do this.

Below is what I get when I build and run :-

Simple Controls from the Toolbox :

Okay then, lets focus our attention to the toolbox, and finding some simple tools.

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

In the image above, one or two seem useful for our purpose of making a start to WPF coding etc.

Let us start with a label.

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

Above is what we get after just dragging the mouse onto the design surface.

Now lets go to the Properties Pane (PP) and change a few settings to our liking.

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

I have floated my PP onto my third screen (Right hand Side (RHS)) and have more room in which to work.

Notice in the image above that I have edited the 'content' property [ yep! no longer 'Text' as in Forms ] as well

as changing the name to 'olbOurFirst'.

And you should know me by now, lets try and build and run - nothing like regular success to spur one into

learning more.

So a 'Ctrl+F5' for start without debugging will do nicely :-

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

And the result of this is :-

Don't laugh, we have to start somewhere, and simple is as good start as any ;-)

Now lets change a couple more properties to get some colour into our label. Below is how the

changes look on the design surface :-

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

Fine, lets take a look at what we get after building and running. Please notice that in the image

below I have elongated the window by dragging its RHS out further to the right :-

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

And also for the first time we are looking at the whole of the XAML code which the designer has created for us.

Notice the 'VerticalAlignment', this is set to top as things stand, but if I add another constraint as shown below

we get something different :-

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

[ Please note that the red 'property' strings have been stacked vertically by me clicking my mouse etc. -

normally the designer just makes a long single line, which is great for general coding, but poor for

displaying in eNotes! ]

Just by clicking in the design surface to add the lower, or bottom, constraint line, we get the XAML code

changed and the 'VerticalAlignment' has been removed, and when the app builds and runs, we can get a large

label indeed ;O)

We will need to do a lot more work, and some serious thinking too, on the way WPF controls may move

and 'flow' with changes in the size of the containing window. There is a lot more functionality built into

WPF than we had available to the developer in Forms.

Before we move on to grander things in the next subsection, like filling list and combo boxes with objects,

lets us stay simple for just a while longer and add a push button and a text box control.

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

And now I will go and change some properties settings using the PP so that we have something

more interesting than what is shown above. After a few changes we have what is shown below :-

And now what the XAML code looks like - still all created by the designer, I just formatted it for

ease of reading :-

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

Now to see if it builds and runs (no need to cross our fingers yet, haven't done too much! ) :-

Well, it looks nice, even if it doesn't yet do anything - 'event less' is how I would describe it.

I did learn a couple of things while using the VS properties pane, and one is the font weight and style had to be

manually entered for the TextBox control - no drop down options !

Secondly, the font family had no options available either - no drop down options. I suspect this situation will improve

in VS 2010, and certainly everything will be there for use in Blend 3.

This is enough for one subsection, either go to the very next section to see how we may fill some list controls, or

move on to the "next but one", where we will add some event handling to this first project.

See you soon, to learn some more WPF ;-)

PHH - December 2009.


Going a little Further

Okay then guys we seem to know enough to try and go just that bit further.

Let us start a new project by right clicking the solution line in the Solution Explorer (SE) pane as

follows :-

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

In the New Project dialog box supply the project name of "ListsExplore" and leave the solution name

as it is.

Collapse the first project in the SE pane and also set the new one as 'StartUp' project, like so :-

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

Now from the toolbox drag a ComboBox control onto the window1 grid and using the Properties Pane (PP)

set the name of the object to "ocbListExp01".

Build and run the app and you should see something like this :-

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

I have aligned the 'property' names (called attributes in XAML script) to help the reader.

Also I have made another PRG file called "ListsExploreInFront" and placed in it my 'InFront'

part of the partial class for 'Window1'.

It is empty at present and looks like this :-

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

Fine, things are looking good, lets try and take a simple approach to filling our first drop list.

We will definitely need to find a better way of doing this quite soon, but lets go for success while

it is so easily at hand :-

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

Okay, I hear you shouting, but the answer is you can't use AddRange() in WPF. Things have changed

to accommodate superior Data Binding I believe, so we will just have to learn the new ways - slowly.

Well, in fact my idea of slowly, turned out to be a bit quicker than I anticipated ;-) In fact the name of the

property is "ItemsSource" and we can achieve similar results as in the Forms 'AddRange' method - although

I feel it is more akin to the 'DataSource' property used in binding. We will see as we learn more and things unravel.

However, when we do some research we may well find that there is more power and functionality to

this new approach. For now lets see the results :-

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

It would appear to me that we can fruitfully investigate adding a list of custom objects in this fashion.

When I come back in the morning we will do just that. Make a second drop list, and add some person

objects, after creating a simple person class. Time to sleep on it ;-)

Okay, fresh with ideas that seem to work, lets move on to a more complex list.

Custom Objects as Items :

Before we can build our list of items, we need to create our new custom class for this object.

Let us make simple three field object called 'Person', and it is shown below :-

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

For the moment I have just added the minimum set of methods to allow our list to be made and displayed in

the chosen list control. We can add 'Assign' and other methods later if we require them.

Notice the 'ToString' method, we will refer back to this later.

Fine, now we have a class of custom object, lets see how we can use it to make a test list for our demo app.

The image shown below has both the necessary code to generate the working app, as well as the 'live' and

running applet itself :-

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

As you can see, it is quite simple, but does work nicely.

Our list displays with the 'Surname' property as the 'ToString' method provides this.

If with the current simple code we change this so we use the 'commented' 'ToString' method above, we

will get a list of heights as shown below :-

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

Okay, I know we all wish to be able to display any field in a way similar to the setting the 'DisplayMember' property

in Forms, but we have not quite got there yet ;-) I need to find out how to do this, so we can switch display items

interactively - possibly with a drop down selection approach.

For now, we need to extend this demo app so that we can respond to an event or two related to the behaviour

of the combo box itself.

Handling an Event of the 'ComboBox' Control :

With the WPF designer tab selected, and the 'ComboBox' control in focus, we need to go to the PP and select the

yellow lightning icon (for the events category list). See below :-

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

Double clicking in the input box for the default event offered by the IDE, we get what is shown above :-

The extra line of XAML script which has been added by the designer is shown below :-

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

Actually, I find it easier to read and recognize if the highlighting is left off, so here it is again :-

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

So much for the event association, but what of the event handler 'stub' itself ?

What has been added to the designer 'code behind' partial class PRG ? See below :-

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

Note that once again I have reformatted the code to make it more presentable to you, the reader.

We now need to add own own code to get some response to occur to the event itself.

Lets keep it simple, and just print out a message :-

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

I got the live output by first selecting the 1.69 value and then trying to change this to 1.78 - just

before the new value was displayed, I got the message box shown.

Obviously, I still have the second 'ToString' method being used, and not the surname 'variety'.

Changing the code so that we pick up which item has been selected, and put details of that in the

message, is shown below (even though some improvements may still be made) :-

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

It may be best for the purposes of learning, and the workings of this demo app, that we don't use

the message box output, and place a label on the WPF window itself.

Lets try this :-

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

Above you will see the newly added label control, and as usual I have aligned the XAML class attributes, or the

object property names, to make it easier for us to read.

Lets first see what results we get when our new code lines are added :-

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

This time the label content is displayed as soon as the list item is selected, and both the

'Williams' names appear at apparently the same time - just as we required.

A Point of Interest :

I have found something interesting which is worthy of note here.

While researching how to display different properties of the object in the list, I found the 'Text' property of

the 'ComboBox', which may be set if the 'IsEditable' property is 'TRUE'. Look at the code below :-

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

This looks as though it could be useful, so remember it.

However, in fiddling and fumbling to get the above to work, I have found out how to display what we

were after earlier.

Displaying Different Properties in the List :

In the image shown below, although we have a 'ToString' method which returns the 'Surname' property,

the list shows as 'Ages', see below :-

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

As you can see from the Vulcan code above, it is the line 43 which does the trick.

You can also see the commented lines which indicates my further testing to see that I can get all

the properties to display as required.

Now that we can display different properties in our combo box, we can move onto the next subsection

where we will allow interactive changing of what is displayed within a 'ListBox' control, and will use a set

of 'RadioButton's to do this - even though it is a rather simplistic method of doing so.

See you in 'Moving On', the title of the next part.

PHH - December 2009.


Moving On

Lets us still use our current 'Window1' within the 'ListsExplore' project.

In order to prepare the designer surface for our extra work, click on the comb box control,

and with the sift key held down also click the label control.

Now you have a multiple section, and both these can be moved to the bottom left of the window

using the arrow keys.

Also resize the controls to be a little smaller and you should have something like what is shown

in the image below :-

Now we have room to add a 'ListBox' control, a 'GroupBox', and three 'RadioButton' controls.

To clear things even more for our new coding, use the PP to set the 'Visibility' property to 'hidden' for both the

combo box and the label. This is to be found under the 'Appearance' category, near the top of the pane, when

the list is displayed by category.

[ Obviously, if you are displaying alphabetically, using the AZ icon in the icon bar, then this will be near the end. ]

Now, lets work back from what I have on screen after building and running the app. - see below :-

When placing from the toolbox, the nee elements on the design surface, don't panic if you appear to

get a grid within the GroupBox. That's just the way things are ;-)

The new XAML script added by the designer should be something like that shown below, after you tidy

the formatting - if you wish to.

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

Now we need to use the PP to add some colour to the background for window, list box and group box as shown :-

You may also like to change the foreground colours of the group box and the radio buttons.

When changing the radio buttons, multiple select by either clicking with the mouse and holding down the shift key,

or by marquee selection which is to click the mouse with the pointer outside the first radio button, and while holding

down the left mouse button drag the selection 'rectangle to cover some or most of the three controls.

The first method produces this after the last click :-

while the marquee selection looks like this (before the mouse button is released).

Either way, once the three controls are in focus, go to the PP and change the foreground colour setting.

I made it 'DarkBlue', and for the group box 'Chocolate' - which is quite appropriate for Christmas ;-)

Having add my good friends John Parker and Paul Piko to our list if items, we should see the following

when we build and run the app. :-

Look at the Vulcan code to create and associate the list of items :-

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

This is the lower part of the Initialize method, just the parts which have changed. Other parts have been

removed to my code dumping method called 'CodeDump()' where I place old or unwanted code and

comment it out, so as not to bring the compiler into play ;-)

Making the Last Few Steps :

Okay then, now that we have set things up nicely, let us add the 'spice' to the applet !

We wish to make the radio button selection change the item property data which is displayed

in our list box.

Because we still have very little experience to build upon, let us do it simply. We will make an

event association for each radio button to be triggered when the 'state' is checked.

If you are experienced with Forms, you will see that it is easier to do in WPF than it was previously.

The event triggers when the state of the button changes to 'checked'.

This is easier than it sounds.

First lets look at the Vulcan code I have written :-

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

And now the 'stub' code created by the designer itself :-

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

And finally the XAML script showing the attributes added to the radio button tags :-

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

Most of this has not changed, so look at the last line in each 'RadioButton' tag section, for the attribute 'Checked'.

These are the event handler association lines added by the designer.

Okay, last of all, lets build, run, and test our applet :-

You will have to take it from me that all the right things happen when we click the radio buttons.

Enough for now I suggest. the next subsection is one where we can enhance our Person class, to

add more properties, as well as picking up all the properties using reflection.

This is not as difficult as it may sound, and we can then populate a drop list with all available properties.

Selecting an item from the new drop list will change our list box display to that chosen.

That is the plan, so lets move on. See you there.

PHH - December 2009.


Enhancing with some Reflection!

Let us start with a nice colourful image, and one which can encourage us to greater things !

Below is actually a Forms app., which was prepared fro VODC 2009. It was called 'Hands on' and has

some very useful functionality (and Vulcan) code which can easily borrow and use in a WPF window.

Firstly, this app does with custom object lists just what we are requiring. The two drop lists hold identical

lists of all the available properties of the custom class. One is for specifying that which is to be displayed

in the list box, and the other to specify which data field will be returned as 'value' when a selection is made

from the list box.

The properties list is made using 'Reflection' - this approach is much more flexible and robust, simply because

properties may be removed and added, or also changed, and the code will still compile and run correctly.

Notice that the value selected (bottom centre) is a date. The date s the one to go with 'Chris' who has been

selected from the list by a mouse click or two.

Now, before we get into looking at code and how to do the necessary things with the WPF designer, let's

see what I have got working so far in WPF :-

All I have done so far is 'beef up' or improve, our custom class to include a couple more properties, as

well as using the few code lines required to find these by 'Reflection'.

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

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

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

Above are the code lines required to find the list of properties by 'Reflection'. There is more explaining this

sort of 'stuff' in the 'ClickStartF2D' eNotes.

Line 62 is extra, and is just a nicety to make sure we have an alphabetic (A to Z) list. The class used is

held in a separate PRG file called 'PI_Sort'.

This is what it looks like :-

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

Okay then, most of this is general dotNet stuff, and not WPF related.

Before we look at the WPF code and designer stuff, lets get out of the way the need to view the changes to

the Person class, and the way the original data list is created.

Firstly we will look at the new 'Initialize' method :-

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

It has to be said that the code is rather neat, and does a lot for us in a few lines ;-)

Now let us look at the newly 'shaped Person class :-

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

Remember the 'CodeDump' method is just a repository for my old and possibly redundant code.

It is all commented out stuff so won't hurt the building process.

What Still needs Adding ? :

At the moment, the app does not respond to any events. What we need to try and add is the code

to make a drop list selection of a property, be applied to the list box so that the correct field data is

displayed.

We can then add a further drop list which although shows the same properties, the one selected from

this second drop list will determine which value is return when the list box has a selection made.

Where are We Headed ? :

Let us see the visual impact of the finished app - how it looks when it is working :-

Add we also need to see the drop list when extended, to view the available properties :-

The first thing you may notice is that we have an extra property called 'JoinDateShort', from the

Vulcan code we studied earlier.

The second thing you may notice is that we now have a push button which causes a second

window to be launched, and this WPF form / window shows the full data set - displayed in a very

simple fashion. Sufficient for the job in hand.

If we are concerned in this small subsection, about where we are headed, then we need to

realise that before we can do much in the way of adding functionality, we need to first of all find

the available properties by reflection.

We need a list of all of the properties that exist for our class of objects which are to be held

in the list box collection. Let us see how this is done :-

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

In our "Window1" class we have a few class variables shown above.

The aDisplay and aValue variables are strongly typed arrays, and will hold a list of

'PropertyInfo' objects.

The coding to acquire the array of PropertyInfo objects is simpler than you may have feared. Look

at the code below, and study it carefully if it is at all new to you.

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

In the code lines shown above, the 'type' is the type of our class 'in focus' - for us this is the

'Person' class we saw earlier.

The 'GetProperties' method retrieves the list we require and assigns this first to the aDisplay array,

and then the aValue array. yep! as easy as one line of code.

Just to tidy things up a little, I wished the property names to be A-Z in order when the drop lists are

displayed to the user. In order to do this we have to create sorting class called 'MyPropertyInfoSort'.

For my own convenience I have placed this in a separate PRG file called 'PI_Sort'. Look at the present

content of the Solution Explorer pane (SE) :-

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

Now even more good news, the class is quite short, even though it does a useful job, AND, is

quite adaptable so that it may be used in many other places, with suitable changes made to make

it do as you require.

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

Finally, for this subsection, now that we have the two arrays of 'property info' objects, we need

to fill the combo box controls, and have a suitable display be shown for each also.

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

This code is quite easy to understand, the 'ItemsSource' is the array we have built up, and the

member to be displayed is the 'Name' property of the list of objects - which we have said are of

type 'PropertyInfo'.

So if you use the SDK documentation to look up PropertyInfo, you will see it has a property called

'Name' which is most suitable for us in this demo app.

If we choose a different property (as in the image below) we get a different list shown, and for the

"MemberType" property it is not a lot of use ;-)

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

Responding to Events, and Writing Handler Methods :

So far, so good - we now need to be able to select a property from the top combo box control,

and have this property applied to the list box so that it is this property which is displayed.

In the designer and properties pane we can set the event to be as shown below :-

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

When you are setting or viewing events, make sure the correct control is in focus, and if

you are having any trouble selecting the control, remember to use the DO screen.

DO is Document Outline and looks like this for our demo app. :-

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

Selecting an element in the DO screen brings it into focus in both the design surface and

the XAML code tab :-

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

I used the Properties Pane in the designer to add four event associations with handler methods. These

are shown below, and are for three list 'SelectionChanged' events, and one button click event.

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

When the events are chosen using the PP the relevant script lines are inserted into the XAML code, and

this becomes part of the BAML (compiled) script which is used at run time. You can't see anything

more than this below in the XAML script :-

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

Note it is the last line of script which set the association of the event to the handler method.

So this last line is referring to the method at code line 27 shown above but one :-

You will also see that I have linked this to run a method in my code 'InFront' PRG file called

'DoSelectionOfProperty'.

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

Vulcan Code - Setting and Breaking Associations :

Finally, it is very convenient at times to be able to use Vulcan code to 'set' and 'unset' our own

event associations. If we leave things as they are, then the system sets them at window instantiation time,

and these are fixed for the lifetime of the window.

I have now gotten quite used to using the 'stub' created in the ".xaml.prg" file to create my own

Vulcan coded association, as it has all the necessary detail one needs to know. Stydy what I

have here, and that seems to work fine :-

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

Obviously, as I did throughout the ClickStartF2D eNotes on Forms design and creation, I use the

Initialize() method to first 'Unset' and then 'Set' the associations, so that my initialization does not trigger

any events while setting up the app reading for use.

It is about time we now looked at the Initialize() method :-

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

I think you will see it all makes good sense ;O)

We have already had a brief look at the handler method called 'DoSelectionOfProperty' and so we

will now look at the slightly more complex method which allows the user to select and change the

value returned from the list box after section of a list box 'Person' item :-

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

The only reason that this is more complex than the equivalent for 'display' is because I had some

problems with errors at run time. The above code reflects my attempts and struggles to overcome

these errors.

Basically, my feelings after much testing are that there is an issue with the ListBox control in WPF.

The error comes when one particular order of selecting the properties takes place. I can select Age,

then Height, and then JoinDate, and the correct values are both returned and shown in the TextBox.

However, once a string field (property) has been selected - like Surname - then the re-selection of

Age, height & JoinDate give and error - quite odd. Sorry, I have no answer, and I have spent ages

looking into this.

Selecting a Person Item :

This is actually quite simple, as the code below demonstrates :-

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

Although when we select from the Persons list, we could pick-up the selected item and then process that,

we don't need do that in order to get a value, as we have already 'set' which value is to be returned.

So we get whatever has been selected from the 'Value' drop list - providing we got no runtime error ;-)

Creating the Data - Filling the List :

These two operations, creating and filling, on and with the data, are now done in separate methods as so :-

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

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

So as you can see I have set at start-up the value selected to be 'Age', this way I can check and test

all forms of the value, as long as I leave the string properties till last. Remember the error problem ?

The Second Window :

Finally, we can get to looking at the second window which is launched when we click the long button.

'View Full Data Set' is the button in focus, and this is very simple indeed - a grid on a window, which

contains a TextBox control. the XAML code is shown below :-

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

And the designer surface (when reduced in size a little) looks like this below :-

Here is my 'InFront' code which supports the new window class called 'ViewDataSet' :-

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

All quite simple, the most complex thing involved is the way the ArrayList object is passed into the

overload of the Constructor with a signature of such an single parameter input.

Obviously, to instantiate the new class of window we need to pass the data array list as shown below :-

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

Also, if you wish to keep the data on screen while you work wit the list selections, then just

change the line 69 code to oVDS:Show() instead, then it will no longer be a dialog screen / window.

I will now leave it to you to use the code provided in the 'Some Useful Code' subsection, and make your

own working app. Doing it for yourself will gain you valuable experience with the designer and its tools.

Good Luck, and see you in the next section.

PHH - December 2009.


Some Useful Code

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

<Window x:Class="HandsOnWPF.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Item Lists : Display and Value Members :" Height="371" Width="453" Background="AliceBlue">

<Grid Height="330" Width="429">

<ListBox Background="Wheat"

HorizontalAlignment="Left"

Margin="14.443,36.663,0,128.876"

Name="olbxSecondList"

Width="177" SelectionChanged="olbxSecondList_SelectionChanged" ToolTip="Click to select an object, see the returned value in the thistle coloured TextBox !" />

<ComboBox Name="ocbxProperties"

Height="23"

IsEditable="True"

Margin="211.09,36.663,67.771,0"

Text="Display Property :"

VerticalAlignment="Top"

SelectionChanged="ocbxProperties_SelectionChanged" />

<TextBox Height="23"

Margin="14.443,0,43.132,57.772"

Name="otbSelectionResult"

VerticalAlignment="Bottom" Background="Thistle">.. result will be shown ..</TextBox>

<Label Margin="12,2.222,0,0"

Name="olbSelectionDetails"

Background="AliceBlue"

Foreground="Chocolate"

Height="28"

VerticalAlignment="Top"

HorizontalAlignment="Left"

Width="177"

Visibility="Visible">Details of 'Person' Objects :</Label>

<Label Height="28"

Margin="211.09,3.333,98.879,0"

Name="olbDisplay"

VerticalAlignment="Top"

Background="Beige">Display Property :</Label>

<ComboBox Height="23"

Margin="211.09,109.989,71.104,0"

Name="ocbxValueProperty"

VerticalAlignment="Top" IsEditable="True" Text="Value Property :" SelectionChanged="ocbxValueProperty_SelectionChanged" ToolTip="CARE .. after a string field has been selected, others then seem to give an error !?" />

<Label Height="28"

Margin="211.09,75.548,97.768,0"

Name="olbValueMember"

VerticalAlignment="Top"

Background="Beige">Value Member :</Label>

<Label Margin="14.443,0,0,84.436"

Name="olbResult"

Background="Beige" Height="27.808" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="120.142">Result of Selection :</Label>

<Button Height="23" Margin="211.09,0,43.132,128.876" Name="opbViewDataSet" VerticalAlignment="Bottom" Click="opbViewDataSet_Click">View Full Data Set</Button>

</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 HandsOnWPF

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD ocbxProperties_SelectionChanged( sender AS Object,;

e AS SelectionChangedEventArgs ) AS Void

self:DoSelectionOfProperty(sender, e)

RETURN

PRIVATE METHOD olbxSecondList_SelectionChanged( sender AS Object,;

e AS SelectionChangedEventArgs ) AS Void

self:DoSelectionFromPersonList()

RETURN

PRIVATE METHOD ocbxValueProperty_SelectionChanged( sender AS Object,;

e AS SelectionChangedEventArgs ) AS Void

self:DoSelectionOfValue()

RETURN

PRIVATE METHOD opbViewDataSet_Click( sender AS Object,;

e AS RoutedEventArgs ) AS Void

self:DoViewFullDataSet()

RETURN

END CLASS

END NAMESPACE

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

// HandsOnWPFinFront.prg

//----- created January 7th. 2010. -----

//----- for ClickStartWPF eNotes volume -----

#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.Reflection

BEGIN NAMESPACE HandsOnWPF

/// <summary>

/// Vulcan code 'InFront' for Window1.xaml

/// or my partial class for the WPF window

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private alPerson as ArrayList

private aDisplay AS PropertyInfo[]

private aValue AS PropertyInfo[]

private cValue, cDisplay as string

method Initialize() as void

self:UnsetMyEventHandlers()

self:CreateMyCollection()

self:FillPersonListBox()

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

self:FindPropertiesList()

self:FillPropertiesDropList()

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

self:FillValuePropertiesDropList()

self:SetMyEventHandlers()

return

method SetMyEventHandlers() as void

self:ocbxProperties:SelectionChanged += SelectionChangedEventHandler{self, @ocbxProperties_SelectionChanged()}

self:olbxSecondList:SelectionChanged += SelectionChangedEventHandler{self, @olbxSecondList_SelectionChanged()}

self:ocbxValueProperty:SelectionChanged += SelectionChangedEventHandler{self, @ocbxValueProperty_SelectionChanged()}

self:opbViewDataSet:Click += RoutedEventHandler{self, @opbViewDataSet_Click()}

return

method UnsetMyEventHandlers() as void

self:ocbxProperties:SelectionChanged -= SelectionChangedEventHandler{self, @ocbxProperties_SelectionChanged()}

self:olbxSecondList:SelectionChanged -= SelectionChangedEventHandler{self, @olbxSecondList_SelectionChanged()}

self:ocbxValueProperty:SelectionChanged -= SelectionChangedEventHandler{self, @ocbxValueProperty_SelectionChanged()}

self:opbViewDataSet:Click -= RoutedEventHandler{self, @opbViewDataSet_Click()}

return

method DoViewFullDataSet() as void

//MessageBox.Show("Will launch the second window soon ....","View Data :")

local oVDS as ViewDataSet

oVDS := ViewDataSet{alPerson}

oVDS:ShowDialog()

return

method DoSelectionOfValue() as void

local cVal as string

try

self:olbxSecondList:SelectedValuePath := ""

cVal := (((PropertyInfo)self:ocbxValueProperty:SelectedItem):Name):Trim()

if cVal:Length = 0

self:otbSelectionResult:Text := "ZERO length property name !?"

return

endif

self:cValue := cVal

self:olbxSecondList:SelectedValuePath := cVal

self:olbxSecondList:DisplayMemberPath := self:cDisplay

self:olbxSecondList:SelectedValuePath := self:cValue

self:otbSelectionResult:Text := "Value member :- "+cVal

catch oEx as SystemException

MessageBox.Show(oEX:message+CRLF+">"+cVal+"<"+CRLF,"Changing Value Member :")

end try

return

method DoSelectionFromPersonList() as void

local cVal as string

cVal := self:olbxSecondList:SelectedValue:ToString()

self:otbSelectionResult:Text := "Item Value : "+cVal+" [ "+self:cValue+" ]"

return

method FillPropertiesDropList()as void

self:ocbxProperties:Items:Clear()

self:ocbxProperties:ItemsSource := self:aDisplay

self:ocbxProperties:DisplayMemberPath := "Name" //----- 'Name' is a property of the 'PropertyInfo' class -----

//self:ocbxProperties:DisplayMemberPath := "MemberType"

return

method FillValuePropertiesDropList() as void

self:ocbxValueProperty:Items:Clear()

self:ocbxValueProperty:ItemsSource := self:aValue

self:ocbxValueProperty:DisplayMemberPath := "Name" //----- 'Name' is a property of the 'PropertyInfo' class -----

return

method FillPersonListBox() as void

self:olbxSecondList:Items:Clear()

self:olbxSecondList:ItemsSource := self:alPerson

self:cValue := "Age"

self:cDisplay := "Fullname"

self:olbxSecondList:DisplayMemberPath := self:cDisplay

self:olbxSecondList:SelectedValuePath := self:cValue

return

method CreateMyCollection() as void

self:alPerson := ArrayList{}

self:alPerson:Add(Person{"Jones","Albert", 33, 1.85,DateTime{1984,04,24}})

self:alPerson:Add(Person{"Smith","Deliah", 41, 1.78,DateTime{1993,05,21}})

self:alPerson:Add(Person{"Williams","Hank", 29, 1.91,DateTime{1999,11,11}})

self:alPerson:Add(Person{"Pyrgas","Chris", 31, 1.69,DateTime{2002,07,09}})

self:alPerson:Add(Person{"van der Hulst","Robert", 44, 1.71,DateTime{1984,04,24}})

self:alPerson:Add(Person{"Logan","Dan", 39, 1.82,DateTime{2005,12,17}})

self:alPerson:Add(Person{"Parker","John", 46, 1.73,DateTime{1984,04,24}})

self:alPerson:Add(Person{"Piko","Paul", 45, 1.77,DateTime{1977,02,15}})

return

method DoSelectionOfProperty( sender AS Object, e AS SelectionChangedEventArgs) as void

local cProp as string

cProp := ((PropertyInfo)self:ocbxProperties:SelectedItem):Name

self:cDisplay := cProp

self:olbxSecondList:DisplayMemberPath := self:cDisplay

self:otbSelectionResult:Text := "Display property :- "+cProp

return

method FindPropertiesList() as void

local oType as Type

oType := typeof(Person)

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

self:aDisplay := oType:GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)

Array.Sort(self:aDisplay, MyPropertyInfoSort{})

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

self:aValue := oType:GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)

Array.Sort(self:aValue, MyPropertyInfoSort{})

return

method DoMyListSelectionChanged(sender AS System.Object, e AS SelectionChangedEventArgs )AS Void

local oP as Person

oP := (Person)self:olbxSecondList:SelectedItem

local cText as string

cText := oP:ToString()

self:olbSelectionDetails:Content := "New selection is :- "+cText

return

END CLASS

class Person

private cSurname as string

private cFirstname AS STRING

private nAge as int

private nHeight as double

private dJoin AS DateTime

CONSTRUCTOR()

SUPER()

self:cSurname := ".. empty .."

self:cFirstname := ".. empty .."

self:nAge := 0

self:nHeight := 0.0

self:dJoin := DateTime{0001,01,01}

self:Initialize()

return

CONSTRUCTOR(cS as string, cF as string, nA as int, nH as double, dJ as DateTime)

SUPER()

self:cSurname := cS

self:cFirstname := cF

self:nAge := nA

self:nHeight := nH

self:dJoin := dJ

self:Initialize()

return

method Initialize() as void

//----- do whatever I need to -----

return

ACCESS Surname as string

return self:cSurname

ACCESS Firstname AS STRING

RETURN SELF:cFirstname

ACCESS Age as int

return self:nAge

/*ACCESS cAge as string

return self:nAge:ToString()*/

ACCESS Height as double

return self:nHeight

ACCESS JoinDate AS DateTime

RETURN SELF:dJoin

ACCESS JoinDateShort AS string

RETURN SELF:dJoin:ToShortDateString()

method ToString() as string

return self:cSurname

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

ACCESS Fullname AS STRING

RETURN SELF:cFirstname+" "+self:cSurname

end class

END NAMESPACE

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

// PI_Sort.prg

//----- created October 20th. 2009 -----

//----- for VODC Hands-On session -----

#using System.Collections

#using System.Reflection

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

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

<Window x:Class="HandsOnWPF.ViewDataSet"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ViewDataSet" Height="293.607" Width="340.875">

<Grid>

<TextBox Name="otbDataSetView" Background="Wheat" />

</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 HandsOnWPF

/// <summary>

/// Interaction logic for ViewDataSet.xaml

/// </summary>

PARTIAL CLASS ViewDataSet INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

END CLASS

END NAMESPACE

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

// ViewDataSetInFront.prg

//----- created January 7th. 2010 -----

//----- for ClickStartWPF -----

#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

BEGIN NAMESPACE HandsOnWPF

/// <summary>

/// Interaction logic for ViewDataSet.xaml

/// </summary>

PARTIAL CLASS ViewDataSet INHERIT Window

CONSTRUCTOR(alP as ArrayList)

SUPER()

InitializeComponent()

self:Initialize(alP)

RETURN

method Initialize() as void

return

method Initialize(alP as ArrayList) as void

local oP as Person

local cText, cTab as string

cText := ""

cTab := " "

local nCount, i as int

nCount := alP:Count

MessageBox.Show("Number of records >> "+nCount:ToString(),"Data List :")

for i := 0 upto nCount-1

oP := (Person)alP[i]

cText += oP:Firstname+cTab

cText += oP:Age:ToString()+cTab

cText += oP:Surname+cTab

cText += oP:Height:ToString()+cTab

cText += oP:JoinDateShort+cTab

cText += CRLF

next

self:otbDataSetView:Text := cText

return

END CLASS

END NAMESPACE

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

PHH - January 2010.


Handling some Events

PHH - December 2009.


First Application

Okay then lets try to use some events, and make some methods to handle what is to happen when

the events selected are triggered.

See the 'First App.' as it stands now :-

So to get ourselves into WPF interactivity, let us get the push button to change the text in the TextBox control.

If we are even smarter, we can get a second click of the button to change the 'content' property of the label.

*** Remember *** the label does not have a 'Text' property.

On the design surface, select the push button control to bring it into focus. Then move to the PP and with the

mouse click the yellow lightning icon to display the event list.

Forms users remember that name of the control is now at the very top of the PP and that for this control, the

click event is separate to the others when we choose the categories icon. Seems odd, one in one category

and all the others in one other.

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

Anyway, what has the VS designer and IDE done for us, when we double clicked in the 'Click' even input box ?

Well it placed the default name of "opbWPFtest_Click" into the input box, and created some XAML script, as well as

some Vulcan code. Lets look at the XAML script first :-

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

Notice the last line, which is the new event association. So the 'click' event is how to be handled by the

method called "opbWPFtest_Click".

The Vulcan.NET code 'stub' for this method has been created in the file called "Window1.xaml.prg" file, which

is open on another tab in the design area. The 'stub' is empty of content, but has the necessary structure to make

it operate correctly once we add our own code to it.

Lets see what this VN code file looks like before we add any code, I have just improved the formatting.

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

Taking this 'step by step', let us first just add a single line of code to this handler method, one to

display a message :-

And now lets see the code :-

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

Remember that the MessageBox class is now within the Windows.Media namespace, not that we may be used

to from the Windows.Forms namespace.

So, now that we know that the event is causing the method to be run, lets add code to change the TextBox content.

I say 'content' in a general sense (term) as it is actually the 'Text' property. Many other controls in WPF have what

used to be the 'Text' property in Forms, called the 'Content' property in WPF. We will get the hang of things as we go

along.

The results - a success ! :-

We did not have to do much as you will notice below :-

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

What may throw Forms guys is that the only place we can go to see the names of the controls we have

in our 'Window1' class is in the XAML code itself.

Also, you will notice I forgot to change the control name from the default to something more meaningful, so

we are stuck with 'textBox1' as this is now displayed in the images and code previous included in this subsection.

Oh! well, I am just getting the hang of this as well go along. I will get better, I am sure of that.

Now to add some more functionality as we planned at the start :-

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

Well the code works, even though after each control has been changed we can't see any thing happening.

I will leave it to you to improve things so we have at least one change of text string on each control - and

can therefore see it going back and forth, from one to the other.

Alternative Idea :

I will now show you an alternative to my idea above, and that is to add a second event handler. What if we

add an event handler to respond to when the contents of the TextBox control changes ?

So in the WPF designer, select the TextBox to bring it into focus.

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

So we get an extra line of XAML script as follows [ view the last line ] :-

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

The new Vulcan code is shown below, along with a live result. You will just have to believe me that it works.

I am aware however, that after two clicks the behaviour, or use, of the app has a lot to be desired ;-)

But, the point was to respond to events, and that we have started to do.

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

I have tidied the code layout slightly, as the designer has a mind of its own when it comes to

laying out and formatting the 'Window1' class.

I think I will develop later in the eNotes, a way of keeping my code (code 'InFront' ) from that the

designer likes to mess with - or should I say which belongs to it (code behind).

So if I had to summarize anything it would be to say we have learned that the label in WPF has a

content property in place of the old Forms text property.

Also, that event handling is no more difficult it would seem that for Forms. I have taken to it quite

easily because of my Forms experience.

Now I suggest you move on to see the more advanced event handling in the next subsection, where

we do some useful things with lists and selection, and the likes.

See you there.

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

Addendum :

I have had a little play around and have come up with a 'separated' PRG approach to what we have

done above.

Have a look at this for an improvement, the details are shown below.

First the 'code behind' belonging to the designer :-

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

And now the extra PRG I have made, which I term 'code in front' - just like I did in the Forms designer

ClickStartF2D eNotes I did for VODC.

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

Now at least the code in this last PRG will not be reformatted each time I add new handlers.

See you in the lists subsection, next.

PHH - December 2009.


Second Application

PHH - January 2010.


Designing 'Flow' into WPF windows

PHH - January 2010.


Chapter 5 -

PHH - January 2010.


Chapter 6 -

PHH - January 2010.


Chapter 7 - Data Binding with WPF

It is most instructive to see what is written by Adam Nathan at the start of Chapter 13 in his recent book on

WPF 4.

Read this through both carefully, and thoroughly - then read it again !

[ >> In WPF, the term data is generally used to describe an arbitrary .NET object.

You can see this naming pattern in terms such as data binding, data templates, and data triggers.

A piece of data could be a collection object, an XML file, a web service, a database table, a custom object,

or even a WPF UI element such as a button.

Therefore, data binding is about tying together arbitrary .NET objects.

The classic scenario is providing a visual representation (for example, in a 'ListBox' or 'DataGrid' ) of items in

an XML file, a database, or an in-memory collection.

For example, instead of iterating through a data source and manually adding a 'ListBoxItem' to a 'ListBox' for

each one, it would be nice to say, "Hey, 'ListBox'! Get your items from over here. And keep them up to date,

please. Oh yeah, and format them to look like this."

Data binding enables this and much more. << ]

I thoroughly recommend you acquire a copy of this text on WPF, it contains a LOT of good stuff.

Binding in Code :

Here is some help and clear information form the web link :-

http://msdn.microsoft.com/en-us/magazine/cc700358.aspx

Most explanations of WPF data binding only show how to work with bindings in XAML. Since anything that is

possible to do in XAML is also possible to achieve in code, it is important that professional WPF developers

learn how to work with data binding programmatically as well as declaratively.

In many situations, it is more convenient and expedient to declare bindings in XAML. As systems become more

complex and dynamic, it sometimes makes more sense to work with bindings in code. Before going further,

let's first review some common classes and methods involved with programmatic data binding.

WPF elements inherit both the SetBinding and GetBindingExpression methods, from either FrameworkElement

or FrameworkContentElement. These are just convenience methods that call into methods with the same names

in the BindingOperations utility class. The following code illustrates how to use the BindingOperations class to

bind the Text property of a textbox to a property on another object:

static void BindText(TextBox textBox, string property)

{

     DependencyProperty textProp = TextBox.TextProperty;

     if (!BindingOperations.IsDataBound(textBox, textProp))

     {

         Binding b = new Binding(property);

         BindingOperations.SetBinding(textBox, textProp, b);

     }

}

You can easily unbind a property by using the code seen here:

static void UnbindText(TextBox textBox)

{

   DependencyProperty textProp = TextBox.TextProperty;

   if (BindingOperations.IsDataBound(textBox, textProp))

   {

       BindingOperations.ClearBinding(textBox, textProp);

   }

}

By clearing out the binding, you will also remove the bound value from the target property.

Declaring a data binding in XAML conceals some of the underlying details.

When you start working with bindings in code, those details begin to come out of the woodwork.

One of those details is the fact that the relationship between a binding source and target is actually

maintained by an instance of the BindingExpression class, not Binding itself.

The Binding class contains high-level information that multiple BindingExpressions can share,

but the enforcement of a link between two bound properties comes from an underlying expression.

The following code shows how you can use BindingExpression to programmatically check if

the Text property of a textbox is being validated:

static bool IsTextValidated(TextBox textBox)

{

   DependencyProperty textProp = TextBox.TextProperty;

   var expr = textBox.GetBindingExpression(textProp);

   if (expr == null)

       return false;

   Binding b = expr.ParentBinding;

   return b.ValidationRules.Any();

}

Since a BindingExpression does not know if it is being validated, you'll need to ask its parent binding. I'll

examine input validation techniques later on.

Working with Templates :

An effective user interface presents raw data in such a way that the user can intuitively discover meaningful

information in it. This is the essence of data visualization. Data binding is just one piece of the data visualization

puzzle. All but the most trivial WPF programs require a means to present data in a more powerful way than just

binding one property on a control to one property on a data object. Real data objects have many related values,

and those various values should aggregate into one cohesive visual representation. This is why WPF has data

templates.

The System.Windows.DataTemplate class is just one form of template in WPF. In general, a template is like a

cookie cutter that the WPF framework uses in order to create visual elements that aid in rendering objects that

have no intrinsic visual representation. When an element attempts to display an object that does not have an

intrinsic visual representation, such as a custom business object, you can tell the element how to render the

object by giving it a DataTemplate.

The DataTemplate can generate as many visual elements as necessary to display the data object. Those

elements use data bindings to display the data object's property values. If an element does not know how to

display the object it is told to render, it simply calls the ToString method on it and displays the result in a

TextBlock.

Suppose you have a simple class called FullName, which stores a person's name. You want to display a list

of names and have each person's last name appear more prominently than the other parts of the name. To do

this you could create a DataTemplate that describes how to render a FullName object. The code listed in

Figure 1 shows the FullName class and the codebehind for a window that will display a list of names.

Figure 1 Display FullNames with a DataTemplate

public class FullName

{

public string FirstName { get; set; }

public char MiddleInitial { get; set; }

public string LastName { get; set; }

}

public partial class WorkingWithTemplates : Window

{

// This is the Window's constructor.

public WorkingWithTemplates()

{

InitializeComponent();

base.DataContext = new FullName[]

{

new FullName

{

FirstName = "Johann",

MiddleInitial = 'S',

LastName = "Bach"

},

new FullName

{

FirstName = "Gustav",

MiddleInitial = ' ',

LastName = "Mahler"

},

new FullName

{

FirstName = "Alfred",

MiddleInitial = 'G',

LastName = "Schnittke"

}

};

}

}

As seen in Figure 2, there is an ItemsControl in the window's XAML file. It creates a simple list of items that the

user cannot select or remove. The ItemsControl has a DataTemplate assigned to its ItemTemplate property,

with which it renders each FullName instance created in the window's constructor.

You should notice how most

of the TextBlock elements in the DataTemplate have their Text property bound to properties on the FullName

object that they represent.

Figure 2 Display FullName Objects Using a DataTemplate

<!-- This displays the FullName objects. -->

<ItemsControl ItemsSource="{Binding Path=.}">

<ItemsControl.ItemTemplate>

<DataTemplate>

<StackPanel Orientation="Horizontal">

<TextBlock FontWeight="Bold" Text="{Binding LastName}" />

<TextBlock Text=", " />

<TextBlock Text="{Binding FirstName}" />

<TextBlock Text=" " />

<TextBlock Text="{Binding MiddleInitial}" />

</StackPanel>

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

When this demo app runs, it looks like Figure 3. By using a DataTemplate to render the name, it is easy to

accentuate each person's last name because the corresponding TextBlock's FontWeight is bold. This simple

example demonstrates the fundamental relationship between WPF data binding and templates.

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

PHH - January 2010.


a Simple Example in XAML

Source of this sample script is :-

http://msdn.microsoft.com/en-us/magazine/cc163299.aspx

Well, lets start with some colourful success :-

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

The demo applet as built and run to produce the above results, is completely written in XAML script.

There are two previous simpler binding approaches which are commented out. and are seen in green.

We shall try to reduce the amount of XAML script, and increase the amount of Vulcan code in proportion.

It would appear to be useful knowledge - and acquired skill - to be able to code in Vulcan when the content

is more than just interactive design elements placing and basic property 'filling'.

PHH - January 2010.


a Vulcan Coded Example

So far we have seen how easy it is to find examples of WPF data binding written in XAML.

There are countless suitable examples of one kind or another, many of which are scripted using

XAML, completely.

http://msdn.microsoft.com/en-us/magazine/cc700358.aspx

The link above however, takes us to a very useful MSDN article by Josh Smith.

Lets start off by looking at his introduction :-

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

Now let us see what Josh has to say about developing skills so we can code data binding 'stuff' in our own

sections of app, outside of the XAML script related to the GUI and the available controls :-

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

Okay then, it would appear that we need to know what our Vulcan code syntax is to create and maintain our

data bindings.

Its fine to set up some initial bindings using the slightly more intuitive syntax of XAML script, and using relevant

attributes, however there comes a time when we need to clear bindings and make new ones. This we have to

do in our code, especially if we need conditional code. remember XAML script is simply declarative - you can

only state what is!

So to summarise so far, we have to use what little C# code we can get our hands on, to translate into Vulcan

syntax, so that we can have an alternative to using XAML - well, who ever said that life gets easier ;-)

Start with Some Colourful Success :

Below is the screen image of a Vulcan code working applet.

The XAML script which is created by the designer, and used by the compiler, is just simple stuff related to

me sliding suitable (and necessary) controls onto the window.

We shall see the XAML script afterwards, for now study the GUI :-

The solution explorer (SE) pane looks like this for the completed app. :-

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

What of the Data ? :

Since the applet is based on data, and displaying properties of data objects, we first need to see just

what the simple data class looks like :-

[ I apologise for the choice of variable names, it was done as a consequence of following some SDK

documentation in the Beta .NET 4 Framework CHM file. ]

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

Yes, as you can see it is simplicity itself - could be more straightforward.

If we now look at the Initialize() method in my 'InFront' code we will see how some data objects are

created :-

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

You will notice that it is the first of these data objects which seems to be bound to the three TextBlock

controls at the time when the applet is initialised, ready for use.

Let us now see the code to set the bindings to the 'myDataObject' on line 38 :-

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

So it would appear that it is the 'SetUpBinding()' method which we need to study :-

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

Once we have the data object instantiated, we can pass this into the method to have its three

properties bound to the three TextBlocks - using their 'Text' property.

[ *** NOTE *** Please remember that TextBlock controls do NOT allow editing. ]

So after this code is executed, we have added three new bindings to the list the system maintains

when our applet is running.

Notice that each binding has a name - and sorry, this too is not very imaginatively named ! My next

examples will get better - as we have total freedom over what we create from scratch.

Maintaining, and Changing, the Registered Bindings :

Since we have just seen how to create and 'register' new bindings with the system, so they are available

during runtime. We ought to say now, that it is important to 'clear' any bindings before making fresh ones

which involve the same control and property.

So study the following code to see the Vulcan code syntax for removing registered bindings :-

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

Cycling Around the Single Objects :

So far so good. We have seen how our first single object has been bound to the three TextBlock controls.

The earlier image shows that we have a green background (actually Chartreuse), and the text content is

related to 'Fun' things.

What happens when we click the large button, entitled 'Change Data Source' ?

For this simple app I have just made an event association through the Properties Pane of the designer. So

we have a click event to handle.

Here is the stub with my inserted method call :-

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

In the PRG file I created called "BindingEGchmInFront", we now see how the handler method is coded :-

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

I think you will find this quite easy to follow.

A class variable (Boolean) is used to track which of the two objects is being displayed (and hence is bond).

Depending on this logical variable value, the code switches bindings from one single object to the other.

So the operation is cyclical.

What of the Drop List ? :

Lets turn our attention to the drop list, and the array list of custom objects held within.

Here is how it looks during operation :-

We appear to have just four custom objects, and these are also of the same type as the two single

ones we were dealing with earlier.

Lets see how the list is created and filled - by Vulcan code :-

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

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

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

Nothing particularly difficult or special.

As you can see from the image of the Vulcan code above, the array list is created and then

supplied as the 'ItemsSource' property of the drop list control created in the designer (line 47).

Adding Interactivity to the Drop List :

Its all very well having a drop list, but we need the event association to allow the selection of an

element to cause something to happen.

Let us start with the stub created by the designer, and to which I have added the call to my

handler method :-

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

And now the handler method in the 'InFront' prg file :-

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

Yes, I know we could have saved a few lines of code - if that's what's 'wets your whistle' - but

basically, we pick up the custom object and supply it as a parameter to the method entitled

'SetUpBindings'.

This method we have used previously, as it sets up the bindings for any custom object with

which it is provided.

Finally, just to give a visual cue at a GUI level, the background colour of the three TextBlocks

is then changed to a 'SkyBlue' colour. Here is what it looks like in use :-

In the image above, the F,H & C item has previously been selected, and I am in the process

of selecting Ginny and Co. as a second selection.

Explanation of the Binding Process :

Up till now we have focused on the general Vulcan code to make all this work - on an

application basis.

Now let us focus for a brief moment on the registration of the binding itself.

Let us remind ourselves of the the relevant code lines :-

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

In the code shown above, a new binding object is made.

Don't mistake the string supplied with it being a name for the binding ! It is in fact the name of the

property belong to the class :-

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

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

So once the source property has been assigned in line 74, we have a binding object which basically

describes the 'source', and that is the data, and the property belonging to it.

Now we need to focus on the target. The MSDN notes and SKD offerings, suggest that we always

think in terms of Target and Source, when we are dealing with data bindings.

So, on line 75. the use of the static method 'BindingOperations.SetBinding', allows us to associate (or connect),

the 'Target object' and then the 'Target property', with the binding. Remember the binding as both source object

and source property already specified.

To summarise, there is a registration made with the system, of a target object and property, with a source object

and property.

In order to bind another source with an already bound target object and property, the existing registration needs

to be removed using the BindingOperations.ClearBinding() static method.

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

After studying the code you will notice that it is only the 'link' or 'association' with the target which is broken.

The original binding still exists, and only the connection to the target object/property has been deleted or removed.

If you wished to prove this programmatically, you could change the code so that after clearing all the

binding registrations, we remade them with the original three binding objects, BUT, in the reverse order.

I have tried this and it seems to work OK, study the image below :-

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

And here is the extra method to achieve the reversing :-

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

Fine, we have come a long way, made some useful code do a real task, and also have started

to understand what registration of bindings is all about.

See you in the next subsection where I promise to improve my selection of variable and object names,

as well as class names also ;-)

PHH - January 2010.

Addendum :

Sorry, I forgot to show you the XAML script - that is good, we can have a whole section and

not talk about XAML ;O))

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

*** Note *** the 'Click' and 'SelectionChanged' attributes in the Button and ComboBox tagged sections.

These specify the event associations with the handler 'stub' methods made by the designer.


Some Useful Code

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

<Window x:Class="BindingEGchm.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="First Binding Success :" Height="304" Width="336">

<Grid Background="BurlyWood" Height="268" Width="314" VerticalAlignment="Stretch"

SnapsToDevicePixels="False" ClipToBounds="False">

<TextBlock Height="21" Margin="43,67,131,0" Name="mytext"

VerticalAlignment="Top" Background="Chartreuse" />

<TextBlock Height="21" Margin="87,104,88,0" Name="mytext2"

VerticalAlignment="Top" Background="Chartreuse" />

<TextBlock Margin="0,0,25,109" Name="mytext3" Background="Chartreuse"

Height="24" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="122" />

<Label Height="28" Margin="12,12,12,0" Name="olbHeader"

VerticalAlignment="Top"

Background="PowderBlue">Three properties of the MyData class bound !</Label>

<Button Height="23" Margin="138,0,12,12" Name="opbChangeSource"

VerticalAlignment="Bottom" IsCancel="True"

Click="opbChangeSource_Click">Change Data Source</Button>

<ComboBox Height="23" Margin="12,0,141,61" Name="ocbxMyDataObjects"

VerticalAlignment="Bottom" SelectionChanged="ocbxMyDataObjects_SelectionChanged" />

</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 BindingEGchm

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD opbChangeSource_Click( sender AS Object, e AS RoutedEventArgs ) AS Void

self:DoChangeDataSource()

RETURN

PRIVATE METHOD ocbxMyDataObjects_SelectionChanged( sender AS Object,;

e AS SelectionChangedEventArgs ) AS Void

self:DoDropListSelection()

RETURN

END CLASS

END NAMESPACE

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

// BindingEGchmInFront.prg

//----- created January 8th. 2010 -----

//----- for ClickStartWPF eNotes -----

#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

BEGIN NAMESPACE BindingEGchm

/// <summary>

/// my 'InFront' part .....

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private myDataObject as MyData

private myDataObject2 as MyData

private myBinding as Binding

private myBinding2 as Binding

private myBinding3 as Binding

private lShowFirst as Boolean

private alMyData as ArrayList

method Initialize() as void

self:olbHeader:Background := SolidColorBrush{Colors.PaleGoldenrod}

myDataObject := MyData{"Amuzement?","Fun-Stuff!","Shear Hilarity!?"}

myDataObject2 := MyData{"Tom","Dick","Harry"}

self:lShowFirst := true

self:SetUpBinding(myDataObject)

//============ De-Register and New Rigistartion ==============

self:ClearBindings()

self:ReverseTheBindings(myDataObject)

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

self:SetBackGround(Colors.Chartreuse)

self:SetUpMyDataList()

self:ocbxMyDataObjects:ItemsSource := self:alMyData

return

method SetBackGround(oC as Color) as void

self:MyText:Background := SolidColorBrush{oC}

self:MyText2:Background := SolidColorBrush{oC}

self:MyText3:Background := SolidColorBrush{oC}

return

method DoDropListSelection() as void

local oMD as MyData

oMD := (MyData)self:ocbxMyDataObjects:SelectedItem

self:ClearBindings()

self:SetUpBinding(oMD)

self:SetBackGround(Colors.SkyBlue)

return

method SetUpMyDataList() as void

alMyData := ArrayList{}

alMyData:Add(MyData{"Sally","Mary","Jane"})

alMyData:Add(MyData{"Brian","Robert","Chris"})

alMyData:Add(MyData{"Ginny","Bev","Pam"})

alMyData:Add(MyData{"Faith","Hope","Charity"})

return

method SetUpBinding(myDataObject as MyData) as void

myBinding := Binding{"MyDataProperty"}

myBinding:Source := myDataObject

BindingOperations.SetBinding(self:myText, TextBlock.TextProperty, myBinding)

//---------------- going a step further ------------------

myBinding2 := Binding{"MySecondProperty"}

myBinding2:Source := myDataObject

BindingOperations.SetBinding(self:myText2, TextBlock.TextProperty, myBinding2)

//---------------- going another step further ------------------

myBinding3 := Binding{"MyThirdProperty"}

myBinding3:Source := myDataObject

BindingOperations.SetBinding(self:myText3, TextBlock.TextProperty, myBinding3)

return

method ReverseTheBindings(myDataObject as MyData) as void

BindingOperations.SetBinding(self:myText, TextBlock.TextProperty, myBinding3)

BindingOperations.SetBinding(self:myText2, TextBlock.TextProperty, myBinding2)

BindingOperations.SetBinding(self:myText3, TextBlock.TextProperty, myBinding)

return

method ClearBindings() as void

BindingOperations.ClearBinding(self:myText, TextBlock.TextProperty)

BindingOperations.ClearBinding(self:myText2, TextBlock.TextProperty)

BindingOperations.ClearBinding(self:myText3, TextBlock.TextProperty)

return

method DoChangeDataSource() as void

//MessageBox.Show("About to change the data source ...","Information :")

self:ClearBindings()

self:SetBackGround(Colors.Chartreuse)

if self:lShowFirst

self:SetUpBinding(myDataObject2)

self:lShowFirst := false

else

self:SetUpBinding(myDataObject)

self:lShowFirst := true

endif

return

method CodeDump() as void

return

end class

class MyData

private cMyDataProperty as string

private cMySecondProperty as string

private cMyThirdProperty as string

CONSTRUCTOR()

self:cMyDataProperty := ".. empty .."

self:cMySecondProperty := ".. empty .."

self:cMyThirdProperty := ".. empty .."

self:Initialize()

return

CONSTRUCTOR(cP1 as string, cP2 as string, cP3 as string)

self:cMyDataProperty := cP1

self:cMySecondProperty := cP2

self:cMyThirdProperty := cP3

self:Initialize()

return

method Initialize() as void

return

method ToString() as string

return self:cMyDataProperty+", "+self:cMySecondProperty+", "+self:cMyThirdProperty

access MyDataProperty as string

return self:cMyDataProperty

access MySecondProperty as string

return self:cMySecondProperty

access MyThirdProperty as string

return self:cMyThirdProperty

end class

END NAMESPACE

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

PHH - January 2010.


Another Vulcan Coded App.

Welcome back !

In this section we will take our knowledge of data binding with WPF a little further, and see

how we can make the binding 'Two Way'.

By this I mean that the information displayed in an editable control (like the TextBox control)

will have the changes reflected in the data source, and any other control to which it may also be

bound.

Yes, it is possible to have one data binding used to bind a data source property to more than one

control property.

Also we will see how we can set things up so we can see every character edit change in the

other controls bound to the property being edited.

Downside to Seeing Immediate Changes :

They is just one problem at the moment, and I mean with Vulcan version 160 which I am using. That

is that the declaration of our event necessary in our 'custom' class, is not yet possible - as it is if

we code our class in C#.

Since we need to be able to use Visual Studio on a more general, and 'broad' basis, we will happily

use such a custom data class to see how the rest of the Vulcan code looks.

How to Make a C# Custom Class :

Right click the solution entry at the top of the SE pane.

Add a 'New Project', and you should see the following :-

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

When the 'Add New Project' dialog screen appears, as shown below :-

Select C# in the tree view pane (on the left) and then 'Class Library' from the templates.

Before you click OK, make sure you name the 'DLL' with a suitable name - I chose for this demo the

name 'ClassLibraryDemo33'.

You will then get a new entry in the SE pane looking something like this below :-

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

I have already made a suitable class library, and it is called 'ClassLibraryPerson', and this is shown

as it looks in the SE pane, see below :-

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

Okay then, we are getting somewhere. Now let us look at the' PersonCS' class I have

created after consulting the Beta .NET 4 eNotes in CHM format (see chapter 11 for details).

Below is what I started with in the notes :-

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

I simply used my acquired knowledge from many hours of fumbling in VS and dotNet, to get me through ;-)

My new class of 'PersonCS' has a wider range of fields and properties. Which makes it more suitable

to use in a Vulcan code example of two-way data binding.

Please notice the 'OnPropertyChanged' event at the bottom of this example, as this is what makes our

eventual Vulcan app work 'like magic' !

Enough of the 'magic' for now, let's just look at the code I have for the custom class I describe. Note

in the code shown below that there is no real need to have as many construct overloads. There are as

many as there are simply because I gradually added a field and a property at a time - then tested etc.

Here is the custom 'PersonCS' class we will use in our demo to come :-

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

Remember when you make a class library, you need to compile (or build) the project before you try to

use it in your other Vulcan project.

Creating a New Vulcan Project, from which to use the Class Library :

Okay then, now let us start a new Vulcan WPF project from which we can access our new C# class

library.

After creating the new project we will see how to add settings so that it has direct access to the class

library shown above, and can then find its namespace and other relevant 'things'.

Right click the solution line at the top of the SE pane, and use the context menu to 'Add a New Project'.

See below to see what I have done, and this is real as I have not yet got a project for this demo app we

shall develop together in these notes. You should see something like :-

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

When your new entry appears in the SE pane, remember to unload any unnecessary project that may be

hanging around in your solution.

Then right clcik the new one (in my case BindingEGs3) and set this project as the 'StartUp project'

like so below :-

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

The bold 'styling' should now be applied to the new project as shown :-

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

Making the Necessary Settings :

Remember to build your class library (dll) when you have this in focus in the editor.

Before you add the extra settings the new Vulcan project 'Reference' section will appear as so :-

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

We need to do things so that our new 'dll' is included in the 'References' list.

Right click the 'References' item in the SE pane to get the following :-

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

Then in the 'Add Reference' dialog screen click the 'Browse' tab to get something like this :-

Now you need to navigate to the correct folder, in which the '.dll. file exists.

Here I am choosing the 'ClassLibraryPerson' folder and we need to open this up, by clicking on

it in the dialog screen - let's do that :-

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

And finally, select the 'bin' folder and then the 'Release' one, as I had release chosen

when I built the library file - it should look like this if all goes well :-

Either double click the '.dll', or select and press the OK button.

At my end of things I get this :-

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

And expanding the properties pane (PP) to see the full path we get this :-

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

Although we have not quite finished setting this up as well need, it is a good idea to do a build

and run of the new Vulcan app, so press 'Ctrl+F5' and you should get this :-

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

At least we have no errors ;-)

Finally, before we get into adding lots of interesting Data Binding code, we need to make a new Vulcan

code file (.prg), which I usually term my code 'InFront'. To this we can add the correct namespace of our

class library.

First add a new code file - right click the 'Binding EGs3' element in the SE pane, like so :-

If you have been successful you will see the following - or similar :-

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

At the moment our new code file (.prg) looks a little empty ;O) - it looks like this :-

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

So go to the tab showing 'Window1.xaml.prg' and you will see this Vulcan code :-

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

I usually copy and paste this into my new 'InFront' file, and then edit it to look something like

the following :-

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

And quite finally, we need to link the 'code behind' partial section, with our own new 'code in front'

partial section by adding the single line in the code the designer creates :-

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

Now build and run and hopefully you should also have a running / working app like I have

at this end of things.

Designing and Creating the GUI with its Required Controls :

Before we start to place controls on our design surface, we need to briefly discuss what it is we

are trying to do.

We wish the GUI to show five text box elements, one for each of the properties - Firstname, Surname,

Age, Height and JoinDate.

We also want to have the same number of text block elements which are also bound to the same set

of properties. These will reflect all the changes which take place, while the text boxes are used for the

editing process itself.

We also need to have more than one PersonCS object (probably three or four) and so a button is needed

to allow us to cycle through the few objects we have.

When a new object comes into focus, the field data will be shown through the relevant properties on

both the TextBox and TextBlock controls.

Hopefully, when changes are made, they will persist to when the same object is displayed at a later time.

Lets see what this set of requirements looks like in the designer :-

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

I have placed all the Text Block controls right next to each other, so that when the data is displayed

it looks as if we are getting a complete data 'record' as opposed to five properties.

To this basic layout we will probably add labels and some other bells and whistles later. But for now

it should get us on our way to doing some data binding.

Please notice that the XAML code created by the designer is not really an issue for us - we simply

need to know what the names of the controls have been defined as.

Getting it to Function with One Object :

For the moment, we only need worry ourselves about using one PersonCS object, with which to check

out our binding code.

Once we get the applet working for one object we can add others and the event and association to carry out

the cycling - with the push button.

Creating the Data Binding Objects :

For each pair of TextBox and TextBlock controls, we will make a single binding. S lets declare the bindings

as shown below :-

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

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

It is about time we looked at the Initialize() method to see what we are doing ar window launch time :-

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

As you can see in the image above, once we have made the binding objects, we use the new

method called 'BindToControls' to do exactly that.

We bind each pair of controls (one box, and one block - of 'text' variety) to each of the five data

binding objects. The Vulcan code looks like this :-

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

That is basically it ;-)

If we now build and run the applet we get this :-

Okay, we could do with a few labels, and tooltips perhaps, but lets first see if we have a two-way

functionality !

I have used the text boxes to edit Mike to Michael, and change the age and height properties, as

well as change the join date details too.

At the moment the image was captured, i was typing in "MacIntosh" to replace 'Jones'. Please

notice (as best you can) that each character changed is immediately reflected in the second of the

pairings of the controls.

So at the moment 'MacInto' is in both controls, see below for the two-way editing to become

obvious to you :-

Adding Multiple Custom Objects :

It should be easy to add a few more PersonCS objects, but before we can cycle through them we

will need to add an event association to the 'Next Person' push button.

First the extra data :-

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

We now have five single objects as you can see in the image above :-

The Initialize() method now looks like this :-

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

So what does the event association code look like ?

Remember, to get an empty 'stub' in the designer Vulcan 'code behind' file, you just double click the

button control on the designer surface, or use the PP and the yellow lightning icon approach.

Look for the 'click' event, which I seem to recall is offered as a default in the input box list. Just

double click in the input box if using the PP way of doing things, when adding an event association

handler code line.

Here is the 'stub' :-

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

So simply, we need to add the method called 'DoNextPersonDisplay' into our code 'in front' section.

here is what I have added and tested to see that it works :-

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

Yes, I know, it is a VERY simple approach, and we can do far better than this - but, one step at a time ;-)

It is more important at the moment that we look at how to clear or remove the binding 'links', before we try

to make fresh ones.

Let us see the 'ClearBindingLinks' method which is used to good effect in the code above.

This is what appears to work fine :-

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

Oh! I nearly forgot, I had to change the coding slightly for the way we create the binding objects.

I had to pass in the data source object as a parameter, so that the cycling code would work.

Here is the start of the new syntax :-

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

Okay then, that's about it for the functionality. Now we just need to check that any changes made

are reflected in the data objects in memory, AND, very importantly that they persist event when the

custom object is out of focus.

How to test fro Persistence :

This is quite easy. Just go to the record for Robert van der Hulst, and change Robert to 'Robby',

as well as his surname to 'the Dutchman', and then cycle through the whole object set, clicking the

next button five times.

If you see the detail of 'Robby' and 'the Dutchman', as I do, then you have got your two-way data binding

to work. Well done if you get my results also.

I know this does not prove anything, but it looks nice, so here is my result :-

Remember, the text block controls are not editable at all. There is not a Read Only property setting.

Its now time for me to do some tidying and improving.

However, we will leave the use of a proper list control, and accessing custom objects from an array, or collection, to

a later section.

For now, we ought to be very happy with our progress, and we also ought to be able to use Fabrice's Reflector plug-in

to show us the code we will be able to use when the Team add the necessary event capability to a subsequent

version of Vulcan.

Here is what Fabrice Foray gives us through his reflector tool :-

[ just use File Open, and point to the '.dll. file ]

Study the output above to see the red text from Fabrice, showing the unsupported event 'stuff'.

Now if we expand the methods (see bottom of image) we get this shown below :-

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

For now, it is good practice with Visual Studio, to be able to mix and match dotNet languages in the one

application we are developing.

This 'mixed' approach allows us to take advantage of many tools previously developed in other dotNet

languages, by other developers.

PHH - January 2010.


Some Useful Code

Enter topic text here.


Lists and Grid - Going Further

Well, you know my style by now, when starting an exciting section it is often good to see some

colourful 'success' before we get our fingers into coding and design etc.

Let us take a look at the next applet we will develop :-

In the above example we have just one data object, a 'view', which is bound to all of the four control 'areas.

We have text boxes, text blocks, a list box, and a data grid - all showing data from the 'view'.

Changes in the editable controls (grid and text boxes) show up in all the others too.

One function of the 'view' object is that we can apply sorting, as well as filtering, and have this reflected

in what is displayed on screen.

To see what I mean let me click the 'Old Guys' push button :-

*** Note *** when sorting or filtering conditions are applied to the 'view' object, the underlying data

collection is not changed. Only the way it is displayed on screen is changed.

PHH - January 2010.


Using Collections of Objects

Doing Things the Easy Way :

Now that we are a little familiar with what data binding is all about, it may well be time to look at

it from the 'other end' as it were.

What I mean is, instead of starting with simple ideas and simple data types, and gradually 'bolting'

new ideas and bits of code onto our simple beginnings, let us look at the better ways of doing things,

right from a 'clean' start.

Once we see the better ways of doing data binding we can then just copy and adapt, and everything

will work fine.

One very good approach to "Data Binding" is to use an 'observable collection' of business objects.

Before we go any further, let us see what we mean by 'Observable Collection' by looking at some

code from a working application. Study the following Vulcan code :-

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

The code shown above is to allow us to have two observable collections for use within our app. One

for customers and one for invoices. Also notice that along with the collections is declared a 'view' also.

'Views' make life easy as well, and views are like a wrapper which goes around the collection. It allows

us to apply filtering conditions and apply sort too, without changing the data in the collection.

Some to put it simply, we have a strongly type system array of the custom (or business) object in question,

and then an observable collection declared for this object type, and thirdly a 'view' which will be used with

the collection. Notice the 'view' is more specifically termed an 'ICollectionView' object.

As with previous examples, the custom or business objects need to have an "INotifyPropertyChanged" interface

to allow Data Binding (DB) to function correctly, and reflect editing changes in all bound controls.

For technical reasons mentioned previously, I have used C# to define my class of 'CustomerCS', as well

as 'InvoiceCS', as Vulcan does not support the required interface at the moment.

Looking at Success :

Before we go into how we create the collections, let us look at what we get when the customer collection

is created and used in an application.

Below is part of the main screen of the 'BEST invoices' application :-

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

The combo box is holding the customer collection, and individual objects when they come into focus are

displayed in the seven TextBlock controls - or at least some of their properties are.

Let us look at the drop list now :-

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

And when we select Andrew in the list we get this below :-

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

Okay, we can't do everything at once, so we will leave the Add and Edit customer windows till a little

later in this section, where the same 'view' object made from the collections is used with editable

TextBox controls - in place of these uneditable TextBlock controls.

If you wish to have a closer look at the CustomerCS class then look at the full code in the subsection

called 'Some Useful Code' which follows this section.

For now here is the first few lines :-

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

And now a few more lines to show a property :-

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

You may remember that the crucial method from our DB point of view is as follows :-

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

This is what allows us to see editing changes in other bound controls for the property in focus

at the time of editing.

Making a Single Customer Object :

Okay then, we are doing just fine. However, we need to be able to make a single customer object

before we can make a list of then, and then a collection. So how do we make a single object from

a record in a DBF file for example ?

Study the code below to see how :-

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

Because the C# 'CustomerCS' class was designed with the DBF structure in mind, it is not too

difficult to create the single object from each of the database fields.

Now that we know how to make a single customer object, let us see how we make an array of

them :-

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

At the point where we left the code listing, the whole set of DBF records have been added to a strongly

typed list of customer objects. I have left in the commented lines which proved very useful while creating and

testing the code.

So then, having created our customer 'group' on line 1378, how do we populate it ?

Well the answer is quite straight forward as you will see in the code below :-

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

I remember form Forms experience, that it is a good idea to check that a list is empty (or cleared) before

we fill it with a new list or array - hence line 1404.

To make the collection line 1405 does the job fine. And while testing, printing the 'Count' property after

creation is useful - image below is a real live result of this :-

So we ought to have 38 entries in our customer drop list.

So where have we got to ? Well we have created out observable collection, and the view object which encapsulates

it, ready for use.

Notice too that we have supplied our collection as the 'ItemsSource' for our drop list, as well as specifying that

the displayed property will be the 'Company' property. (Lines 1409 and 1411.)

What we have yet to do is bind the properties of the object in focus in the drop list, to the seven TextBlock

controls within the group box for customers.

Creating and Applying the Bindings :

Let us look at the controls in detail :-

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

Since we have seven different TextBlocks, it would be convenient to have seven separate binding objects.

So here is part of the class variable declaration :-

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

You may guess that I have used 'C' in these to denote 'customer', as opposed to invoice.

So in a similar way to that done previously, we can make bindings like shown below :-

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

Notice that by passing the observable collection in as a parameter we can shorten the name, which makes

it easier to code, if not so much here, then in other examples.

Line 138 is left in, even though commented, so that we are reminded that this is an option we may wish to use

another time. That is, that the editing changes only show up in other bound controls, after the edit control has

lost focus - rather than every edit change and subsequent correction etc. being displayed everywhere the binding

is in effect.

So then now we h ave the bindings, how do we apply them?

See below to see code which is familiar to us from earlier sections :-

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

You may well be aware that there are other customer properties which have not been used in the DB to

the TextBlock controls. This is quite simply because not all data fields will appear on the printed invoice.

So it is a visual 'thing', or issue, for the computer user - they only need to see these fields, and we will

make the Vulcan code use only these to create the invoice, before printing.

To Summarize :

We have made a simple binding to the uneditable TextBlock controls so effectively this is a one-way DB.

If we wish to see two way DB in action we need to look at the GUI which is placed on screen after both

the Add and Edit green buttons are clicked.

Lets look at these now .

Add and Edit Customer Screen :

before we go into detail over the code required to allow editing of existing, and addition of new customers,

let us take a look at the screen - which is of course a WPF window :-

[ By the way, Andrew does not live at the displayed address ;-) ]

And if we choose to add rather than edit we get this :-

Yes, it is the same screen, but it has been setup, or initialised, to be used for a new customer input,

rather than an existing customer edit / change.

Notice that because this screen is effectively 'linked' directly to the data file, it needs to have enough

editable controls so that all data fields may be changed or added.

The only one which is different in the unique customer number, as this is in control of the system, and

should not be changeable by the user.

As you can see, we seem to have a slight problem ;-) if we have 38 customers so far, then why is a

value of 779 showing for a new one - which ought to be the next running number ! I shall have to follow

that one up - I suspect it is because I was using 777 as a test, and two trial entries are still remaining

in the file.

Look at the image below to see that this is indeed the case :-

Anyway, enough of that, lets get back to the two way binding.

Since we are interested in DB in this section, we can simply follow the code for the edit option. There

is no need to look at the add facility here. The whole app will be available as code later in the eNotes so

don't worry.

Let us study the method which sets up and calls the extra WPF window :-

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

In the code displayed above, the current item in the 'view' (the one selected in the drop list) is found

and cast as appropriate, then passed into the constructor call to the new window.

'CustomerWin' is the class name of the new WPF window, and this is part of the same project as

the main invoice screen. So in effect it is the second window within the project.

Notice that the constructor takes three parameters, after the customer in focus object, the second one is a flag,

and the third one the customer DBF server.

It seemed a good idea to make the second window a dialog one, since while editing or adding a customer it

seems reasonable that no other tasks should take place. And we don't need two windows open editing the

same customer either !

Notice before we move to the edit screen and its details, that once the edit window is closed down then the

customer list is cleanly refilled so that any changes may be seen there too.

When the edit screen closes, any changes are saved to disk file if required.

Since the newly displayed drop list is made directly from data file on disk, it is a good way of checking that there

is persistence in the new changes. If you can't see it, then it won't be in the file - well for the seven displayed

fields anyway. Re-launching the edit screen for the appropriate customer will allow checking of the other fields

for persistence.

Data Binding in the Add - Edit Screen :

Now that we have seen what we needed to do before calling the second WPF window, lets see what happens

when the constructor is called into action :-

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

Notice that the three parameters are present, customer, flag and server. BUT, I have done one of those coding

'naughties' as I have swapped around a previously used variable name.

*** NOTE *** in the class variables, 'oCust' is now the customer object, and the DBF Server is 'oDBSC'. The

cMode variable is the flag to denote add or edit.

Line 52 shows we have a logic variable to keep track of whether we make any text changes in the editable fields.

Lets see the Initialize() method to follow these points through :-

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

Lines 70 and 71 are required to keep track of whether changes are made, as well as whether it is the first attempt

to save details to disk.

It is possible that we add a new customer, save details to disk (the first time) and then continue to make more

changes afterwards. Obviously, the first time a record needs to be appended, but subsequently we just need to

update the fields.

Creating the Bindings :

OK then, this next bit is necessary, but not very exciting ;-)

Lets see the class declarations for the bindings :-

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

Note that line 49 shows that we have kept binding number thirteen separate from the others, quite simply

because it is different. It will be read only and the others editable.

We will have twelve bindings with TextBox controls and one with a TextBlock.

Here we have the start of the 'CreateCustomerBindings' method.

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

I have chopped out the other ten similar code sections, so we can now jump to the last part of this method.

Here we have it :-

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

*** Remember *** that the DependencyProperty options for any control are shown in the 'Fields' section in the SDK

documentation. Below is shown that for the TextBlock control - notice the orange 'TextProperty' element :-

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

Surprisingly, the number of options for the TextBox control are fewer as you can see below :-

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

For our DB in this example we are using only the 'TextProperty' of each control. No doubt we may use others for

other purposes when we get a little more experienced.

Testing to See Data Binding is Working :

So things seem fine so far. There was not a whole lot to do to put DB in place, but does it work as we require it?

We now need to test to see if the binding part of our example app works. That really means if we edit text content

within the second Add-Edit screen, will it show up in the first and main invoice screen ?

Lets give it a go :-

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

Well, above is the live imagery to prove that as I edit the first address line, the [.... testing 1,2,3 ... ] extra text string has also

shown up in the original TextBlock control (uneditable).

This only happens because we have the 'UpdateSourceTrigger' enumeration set to 'PropertyChanged'. In order to demonstrate

the difference I have changed the code for this one control as follows :-

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

Now lets repeat the editing test as before, but this time I will include an extra 'again' in the text entered.

Study the image below to see that nothing has yet shown in the first control :-

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

You need to remind yourself that the source is NOT the first text block control, but the collection view object. The

text block is simply bound to the source as is the editable text box. But once a change is sent back to the source, any

bond control will reflect the changes.

To prove this I have simply clicked the mouse into the empty 'contact' field above, and this is what I saw :-

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

I hope this demonstration is useful to give you a flavour of what is going on.

To Summarise :

None of the control have been coded to specifically deal with a chosen filed or property of the source object. We have

kept things as general as possible, except we have named the labels appropriately ;-)

However, we could have an interactive input (check box / radio buttons) which allows the user to change the

BindingOperations.SetBinding details so that different data properties appear in the controls in focus.

In fact a useful exercise for you is to make an app where a single object with five string field properties is bound

to a single TextBlock or TextBox. At the click of a button the binding changes to the next property in the cycle - yes,

remember to cycle, so from 5 we go back to 1.

If you are really good, you can use a drop list to allow the user to select any of the five properties, then change the

binding. In fact, if you want a gold star for your work, you can even have the text on the label change with selection too.

If you wish to see more code from this app look at the useful code section.

See you in the next section on using DataTable objects for Data Binding.

PHH - February 2010.


Some Useful Code

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

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

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

using System.ComponentModel;

namespace ClassLibraryCustomer

{

public class CustomerCS : INotifyPropertyChanged

{

private int custrefno;

private string person;

private string company;

private string add1;

private string add2;

private string add3;

private string postcode;

private string phone;

private string web;

private string email;

private string fax;

private string details1;

private string details2;

// Declare the event

public event PropertyChangedEventHandler PropertyChanged;

public CustomerCS()

{

this.Initialize();

}

public CustomerCS(int nRef)

{

this.Initialize();

this.custrefno = nRef;

}

protected void Initialize()

{

this.custrefno = 0;

this.person = "";

this.company = "";

this.add1 = "";

this.add2 = "";

this.add3 = "";

this.postcode = "";

this.phone = "";

this.web = "";

this.email = "";

this.fax = "";

this.details1 = "";

this.details2 = "";

}

public int CustRefNo

{

get { return custrefno; }

set

{

custrefno = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("CustRefNo");

}

}

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 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 Web

{

get { return web; }

set

{

web = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("Web");

}

}

public string Email

{

get { return email; }

set

{

email = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("Email");

}

}

public string Fax

{

get { return fax; }

set

{

fax = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("Fax");

}

}

public string Details1

{

get { return details1; }

set

{

details1 = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("Details1");

}

}

public string Details2

{

get { return details2; }

set

{

details2 = value;

// Call OnPropertyChanged whenever the property is updated

OnPropertyChanged("Details2");

}

}

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

// Create the OnPropertyChanged method to raise the event

protected void OnPropertyChanged(string name)

{

PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)

{

handler(this, new PropertyChangedEventArgs(name));

}

}

}

}

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

// CustomerWinInFront.prg

//----- created February 6th. 2010 -----

//----- for DevShare 2010, and ClickStart -----

#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 ClassLibraryCustomer

#using ClassLibraryInvoice

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

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

#using System.Data

#using System.IO

BEGIN NAMESPACE BESTinvoicesWPF

/// <summary>

/// ... my InFront code part for 'CustomerWin' ...

/// </summary>

PARTIAL CLASS CustomerWin INHERIT Window

private oCust as CustomerCS

private oBindC1 as Binding

private oBindC2 as Binding

private oBindC3 as Binding

private oBindC4 as Binding

private oBindC5 as Binding

private oBindC6 as Binding

private oBindC7 as Binding

private oBindC8 as Binding

private oBindC9 as Binding

private oBindC10 as Binding

private oBindC11 as Binding

private oBindC12 as Binding

private oBindC13 as Binding

private cMode as string

private lChanges as Boolean

private oServerC as DBServer

private cFolderData as string

private nRef as int

private lFirstTime as Boolean

CONSTRUCTOR(oC as CustomerCS, cM as string, oDBSC as DBServer)

SUPER()

InitializeComponent()

self:oCust := oC

self:cMode := cM:Trim()

self:oServerC := oDBSC

self:Initialize()

RETURN

method Initialize() as void

self:UnsetMyEventHandlers()

self:lChanges := false

self:lFirstTime := true

//MessageBox.Show(self:oCust:CustRefNo:ToString())

self:CreateCustomerBindings(self:oCust)

self:SetMyEventHandlers()

return

method SetMyEventHandlers() as void

self:opbSave:Click += RoutedEventHandler{self, @opbSave_Click()}

self:textBox1:TextChanged += TextChangedEventHandler{self,@textBox1_TextChanged()}

self:textBox2:TextChanged += TextChangedEventHandler{self,@textBox2_TextChanged()}

self:textBox3:TextChanged += TextChangedEventHandler{self,@textBox3_TextChanged()}

self:textBox4:TextChanged += TextChangedEventHandler{self,@textBox4_TextChanged()}

self:textBox5:TextChanged += TextChangedEventHandler{self,@textBox5_TextChanged()}

self:textBox6:TextChanged += TextChangedEventHandler{self,@textBox6_TextChanged()}

self:textBox7:TextChanged += TextChangedEventHandler{self,@textBox7_TextChanged()}

self:textBox8:TextChanged += TextChangedEventHandler{self,@textBox8_TextChanged()}

self:textBox9:TextChanged += TextChangedEventHandler{self,@textBox9_TextChanged()}

self:textBox10:TextChanged += TextChangedEventHandler{self,@textBox10_TextChanged()}

self:textBox11:TextChanged += TextChangedEventHandler{self,@textBox11_TextChanged()}

self:textBox12:TextChanged += TextChangedEventHandler{self,@textBox12_TextChanged()}

return

method UnsetMyEventHandlers() as void

self:opbSave:Click -= RoutedEventHandler{self, @opbSave_Click()}

self:textBox1:TextChanged -= TextChangedEventHandler{self,@textBox1_TextChanged()}

self:textBox2:TextChanged -= TextChangedEventHandler{self,@textBox2_TextChanged()}

self:textBox3:TextChanged -= TextChangedEventHandler{self,@textBox3_TextChanged()}

self:textBox4:TextChanged -= TextChangedEventHandler{self,@textBox4_TextChanged()}

self:textBox5:TextChanged -= TextChangedEventHandler{self,@textBox5_TextChanged()}

self:textBox6:TextChanged -= TextChangedEventHandler{self,@textBox6_TextChanged()}

self:textBox7:TextChanged -= TextChangedEventHandler{self,@textBox7_TextChanged()}

self:textBox8:TextChanged -= TextChangedEventHandler{self,@textBox8_TextChanged()}

self:textBox9:TextChanged -= TextChangedEventHandler{self,@textBox9_TextChanged()}

self:textBox10:TextChanged -= TextChangedEventHandler{self,@textBox10_TextChanged()}

self:textBox11:TextChanged -= TextChangedEventHandler{self,@textBox11_TextChanged()}

self:textBox12:TextChanged -= TextChangedEventHandler{self,@textBox12_TextChanged()}

return

method DoDetailsChanged() as void

self:lChanges := true

return

method DoSaveToDisk() as void

MessageBox.Show("will save to disk .... mode is :- "+cMode,"Action :")

if self:lChanges

MessageBox.Show("Changes have been made ...")

else

MessageBox.Show("NO changes have been made ...")

return

endif

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

self:cFolderData := "C:\Users\BEST\Documents\Visual Studio 2008\ProjectsWPF\BESTinvoicesWPF\BESTinvoicesWPF\Data"

SELF:oServerC:SetIndex(self:cFolderData+"customers.cdx" )

if SELF:oServerC:Reindex()

//MessageBox.Show("Went OK !!","REINDEX :")

else

MessageBox.Show("Failed ?!","REINDEX :")

endif

if !SELF:oServerC:SetOrder("custrefno")

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

MessageBox.Show("NOT set to 'CustRefNo' ...","ORDER:")

else

//MessageBox.Show("Set to 'CustRefNo' ...","ORDER:")

endif

SELF:oServerC:GoTop()

self:nRef := self:oCust:CustRefNo

//MessageBox.Show(self:nRef:ToString())

if self:oServerC:Seek(self:nRef)

MessageBox.Show("Customer record found ...."+self:nRef:ToString(),"FOUND :")

self:UpdateExistingRecord()

else

MessageBox.Show("Customer record NOT found "+self:nRef:ToString()+" !!??","No Find :")

//----- give option to add a record -----

//----- may already exist ? -----

if self:cMode == "Add"

MessageBox.Show("Add as new customer ??","Action :")

if self:AddNewRecord()

else

endif

else

MessageBox.Show("This customer record ought to exist - seek HELP !!","CARE :")

if self:UpdateExistingRecord()

else

endif

endif

endif

return

method UpdateExistingRecord() as Boolean

local lResult as Boolean

lResult := false

if self:TransferFieldData()

lResult := true

endif

return lResult

method AddNewRecord() as Boolean

local lResult as Boolean

lResult := false

if self:oServerC:Append()

if self:TransferFieldData()

lResult := true

self:lFirstTime := false

endif

endif

return lResult

method TransferFieldData() as Boolean

local lResult as Boolean

lResult := false

if self:oServerC:LockCurrentRecord()

if self:cMode == "Add"

if self:lFirstTime

self:oServerC:FIELDPUT("custrefno", self:oCust:CustRefNo)

endif

endif

self:oServerC:FIELDPUT("company", self:oCust:Company)

self:oServerC:FIELDPUT("person", self:oCust:Contact)

self:oServerC:FIELDPUT("add1", self:oCust:Add1)

self:oServerC:FIELDPUT("add2", self:oCust:Add2)

self:oServerC:FIELDPUT("add3", self:oCust:Add3)

self:oServerC:FIELDPUT("postcode", self:oCust:PostCode)

self:oServerC:FIELDPUT("phone", self:oCust:phone)

self:oServerC:FIELDPUT("web", self:oCust:Web)

self:oServerC:FIELDPUT("email", self:oCust:Email)

self:oServerC:FIELDPUT("fax", self:oCust:Fax)

self:oServerC:FIELDPUT("details1", self:oCust:Details1)

self:oServerC:FIELDPUT("details2", self:oCust:Details2)

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

self:oServerC:UnLock()

lResult := true

endif

return lResult

method CreateCustomerBindings(oC as CustomerCS) as void

oBindC1 := Binding{"Company"}

oBindC1:Source := oC

oBindC1:Mode := BindingMode.TwoWay

oBindC1:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

//----- alternative - when focus changes to a.n. other element -----

//oBindC1:UpdateSourceTrigger := UpdateSourceTrigger.LostFocus

oBindC2 := Binding{"Contact"}

oBindC2:Source := oC

oBindC2:Mode := BindingMode.TwoWay

oBindC2:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC3 := Binding{"Add1"}

oBindC3:Source := oC

oBindC3:Mode := BindingMode.TwoWay

oBindC3:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

//oBindC3:UpdateSourceTrigger := UpdateSourceTrigger.LostFocus

oBindC4 := Binding{"Add2"}

oBindC4:Source := oC

oBindC4:Mode := BindingMode.TwoWay

oBindC4:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC5 := Binding{"Add3"}

oBindC5:Source := oC

oBindC5:Mode := BindingMode.TwoWay

oBindC5:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC6 := Binding{"PostCode"}

oBindC6:Source := oC

oBindC6:Mode := BindingMode.TwoWay

oBindC6:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC7 := Binding{"Phone"}

oBindC7:Source := oC

oBindC7:Mode := BindingMode.TwoWay

oBindC7:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC8 := Binding{"Web"}

oBindC8:Source := oC

oBindC8:Mode := BindingMode.TwoWay

oBindC8:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC9 := Binding{"Email"}

oBindC9:Source := oC

oBindC9:Mode := BindingMode.TwoWay

oBindC9:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC10 := Binding{"Fax"}

oBindC10:Source := oC

oBindC10:Mode := BindingMode.TwoWay

oBindC10:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC11 := Binding{"Details1"}

oBindC11:Source := oC

oBindC11:Mode := BindingMode.TwoWay

oBindC11:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC12 := Binding{"Details2"}

oBindC12:Source := oC

oBindC12:Mode := BindingMode.TwoWay

oBindC12:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindC13 := Binding{"CustRefNo"}

oBindC13:Source := oC

oBindC13:Mode := BindingMode.TwoWay

oBindC13:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

local odpTBKT as DependencyProperty

odpTBKT := TextBlock.TextProperty

BindingOperations.SetBinding(self:textBlock1, odpTBKT, oBindC13)

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

local odpTBXT as DependencyProperty

odpTBXT := TextBox.TextProperty

BindingOperations.SetBinding(self:textBox1, odpTBXT, oBindC1)

BindingOperations.SetBinding(self:textBox2, odpTBXT, oBindC2)

BindingOperations.SetBinding(self:textBox3, odpTBXT, oBindC3)

BindingOperations.SetBinding(self:textBox4, odpTBXT, oBindC4)

BindingOperations.SetBinding(self:textBox5, odpTBXT, oBindC5)

BindingOperations.SetBinding(self:textBox6, odpTBXT, oBindC6)

BindingOperations.SetBinding(self:textBox7, odpTBXT, oBindC7)

BindingOperations.SetBinding(self:textBox8, odpTBXT, oBindC8)

BindingOperations.SetBinding(self:textBox9, odpTBXT, oBindC9)

BindingOperations.SetBinding(self:textBox10, odpTBXT, oBindC10)

BindingOperations.SetBinding(self:textBox11, odpTBXT, oBindC11)

BindingOperations.SetBinding(self:textBox12, odpTBXT, oBindC12)

return

END CLASS

END NAMESPACE

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

// MainInvInFront.prg

//----- created February 2nd. 2010 -----

//----- for DevShare 2010 (May 20-22) -----

#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 ClassLibraryCustomer

#using ClassLibraryInvoice

#using System.Collections

#using System.ComponentModel

#using System.Collections.ObjectModel

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

#using System.Data

#USING System.Xml.Serialization

#USING System.IO

#using System.Diagnostics

//#using ClassLibrarySettingsForm

//#using ClassLibraryBESTdocument

//#using ClassLibraryInvoicePrinting

BEGIN NAMESPACE BESTinvoicesWPF

/// <summary>

/// my partial code 'InFront' for Window1

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private oCust as DBServer

private oInv as DBServer

private cFolderData as string

private nVAT as Double

private aNewRefNos as int[]

private alNewRefNos as ArrayList

private aExistRefNos as int[]

private alExistRefNos as ArrayList

private nLimitStart, nLimitEnd as int

private nHighest, nNext as int

private nNextCustRef as int

private aCustomerCS as CustomerCS[]

private oCustomerGroup as List<CustomerCS>

private oViewC as ICollectionView

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

private aInvoiceCS as InvoiceCS[]

private oInvoiceGroup as List<InvoiceCS>

private oViewI as ICollectionView

private oBindC1 as Binding

private oBindC2 as Binding

private oBindC3 as Binding

private oBindC4 as Binding

private oBindC5 as Binding

private oBindC6 as Binding

private oBindC7 as Binding

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

private oBindV1 as Binding

private oBindV2 as Binding

private oBindV3 as Binding

private oBindV4 as Binding

private oBindV5 as Binding

private oBindV6 as Binding

private oBindV7 as Binding

private oBindV8 as Binding

private oBindV9 as Binding

private oBindV10 as Binding

private oBindV11 as Binding

private oBindV12 as Binding

private oBindV13 as Binding

private oBindV14 as Binding

private oBindV15 as Binding

private oBindV16 as Binding

private oBindV17 as Binding

private oBindV18 as Binding

private oBindV19 as Binding

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

private oNV as InvoiceCS

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

private oBindN1 as Binding

private oBindN2 as Binding

private oBindN3 as Binding

private oBindN4 as Binding

private oBindN5 as Binding

private oBindN6 as Binding

private oBindN7 as Binding

private oBindN8 as Binding

private oBindN9 as Binding

private oBindN10 as Binding

private oBindN11 as Binding

private oBindN12 as Binding

private oBindN13 as Binding

private oBindN14 as Binding

private oBindN15 as Binding

private oBindN16 as Binding

private oBindN17 as Binding

private oBindN18 as Binding

private oBindN19 as Binding

private oBindN20 as Binding

private oBindN21 as Binding

private oBindN22 as Binding

private oBindN23 as Binding

private oBindN24 as Binding

private oBindN25 as Binding

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

private oBindN26 as Binding

private oBindN27 as Binding

private oBindN28 as Binding

private oBindN29 as Binding

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

export cWhich as string

export Landscape as Boolean

EXPORT oppcTestBD AS System.Windows.Forms.PrintPreviewControl

private oBindD99 as Binding

export oSI as SerializableInvoice

method CreateCustomerBindings(oC as List<CustomerCS>) as void

oBindC1 := Binding{"Company"}

oBindC1:Source := oC

oBindC1:Mode := BindingMode.TwoWay

oBindC1:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

//----- alternative - when focus changes to a.n. other element -----

//oBindC1:UpdateSourceTrigger := UpdateSourceTrigger.LostFocus

oBindC2 := Binding{"Add1"}

oBindC2:Source := oC

oBindC2:Mode := BindingMode.TwoWay

oBindC2:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC3 := Binding{"Add2"}

oBindC3:Source := oC

oBindC3:Mode := BindingMode.TwoWay

oBindC3:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC4 := Binding{"Add3"}

oBindC4:Source := oC

oBindC4:Mode := BindingMode.TwoWay

oBindC4:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC5 := Binding{"PostCode"}

oBindC5:Source := oC

oBindC5:Mode := BindingMode.TwoWay

oBindC5:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC6 := Binding{"Phone"}

oBindC6:Source := oC

oBindC6:Mode := BindingMode.TwoWay

oBindC6:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindC7 := Binding{"Email"}

oBindC7:Source := oC

oBindC7:Mode := BindingMode.TwoWay

oBindC7:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

local odpTBKT as DependencyProperty

odpTBKT := TextBlock.TextProperty

BindingOperations.SetBinding(self:textBlock1, odpTBKT, oBindC1)

BindingOperations.SetBinding(self:textBlock2, odpTBKT, oBindC2)

BindingOperations.SetBinding(self:textBlock3, odpTBKT, oBindC3)

BindingOperations.SetBinding(self:textBlock4, odpTBKT, oBindC4)

BindingOperations.SetBinding(self:textBlock5, odpTBKT, oBindC5)

BindingOperations.SetBinding(self:textBlock6, odpTBKT, oBindC6)

BindingOperations.SetBinding(self:textBlock7, odpTBKT, oBindC7)

return

method CreateInvoiceBindings(oV as List<InvoiceCS>) as void

oBindV1 := Binding{"InvRefNo"}

oBindV1:Source := oV

oBindV1:Mode := BindingMode.TwoWay

oBindV1:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV2 := Binding{"Customer"}

oBindV2:Source := oV

oBindV2:Mode := BindingMode.TwoWay

oBindV2:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV3 := Binding{"Company"}

oBindV3:Source := oV

oBindV3:Mode := BindingMode.TwoWay

oBindV3:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV4 := Binding{"Contact"}

oBindV4:Source := oV

oBindV4:Mode := BindingMode.TwoWay

oBindV4:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV5 := Binding{"Parts1"}

oBindV5:Source := oV

oBindV5:Mode := BindingMode.TwoWay

oBindV5:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV6 := Binding{"Parts2"}

oBindV6:Source := oV

oBindV6:Mode := BindingMode.TwoWay

oBindV6:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV7 := Binding{"Parts3"}

oBindV7:Source := oV

oBindV7:Mode := BindingMode.TwoWay

oBindV7:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV8 := Binding{"Parts4"}

oBindV8:Source := oV

oBindV8:Mode := BindingMode.TwoWay

oBindV8:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV9 := Binding{"Parts5"}

oBindV9:Source := oV

oBindV9:Mode := BindingMode.TwoWay

oBindV9:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV10 := Binding{"Parts6"}

oBindV10:Source := oV

oBindV10:Mode := BindingMode.TwoWay

oBindV10:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV11 := Binding{"Parts7"}

oBindV11:Source := oV

oBindV11:Mode := BindingMode.TwoWay

oBindV11:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV12 := Binding{"Parts8"}

oBindV12:Source := oV

oBindV12:Mode := BindingMode.TwoWay

oBindV12:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV13 := Binding{"Parts9"}

oBindV13:Source := oV

oBindV13:Mode := BindingMode.TwoWay

oBindV13:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindV14 := Binding{"Amounts1"}

oBindV14:Source := oV

oBindV14:Mode := BindingMode.TwoWay

oBindV14:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV15 := Binding{"Amounts2"}

oBindV15:Source := oV

oBindV15:Mode := BindingMode.TwoWay

oBindV15:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV16 := Binding{"Amounts3"}

oBindV16:Source := oV

oBindV16:Mode := BindingMode.TwoWay

oBindV16:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV17 := Binding{"Amounts4"}

oBindV17:Source := oV

oBindV17:Mode := BindingMode.TwoWay

oBindV17:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV18 := Binding{"Amounts5"}

oBindV18:Source := oV

oBindV18:Mode := BindingMode.TwoWay

oBindV18:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindV19 := Binding{"Amounts6"}

oBindV19:Source := oV

oBindV19:Mode := BindingMode.TwoWay

oBindV19:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

local odpTBKT as DependencyProperty

odpTBKT := TextBlock.TextProperty

/*BindingOperations.SetBinding(self:textBlock1, odpTBKT, oBindC1)

BindingOperations.SetBinding(self:textBlock2, odpTBKT, oBindC2)

BindingOperations.SetBinding(self:textBlock3, odpTBKT, oBindC3)

BindingOperations.SetBinding(self:textBlock4, odpTBKT, oBindC4)

BindingOperations.SetBinding(self:textBlock5, odpTBKT, oBindC5)

BindingOperations.SetBinding(self:textBlock6, odpTBKT, oBindC6)

BindingOperations.SetBinding(self:textBlock7, odpTBKT, oBindC7)*/

return

method Initialize() as void

self:UnsetMyEventHandlers()

/*self:MinHeight := Self:Height

self:MaxHeight := Self:Height

self:MinWidth := Self:Width

self:MaxWidth := Self:Width*/

self:odpCreateNew:SelectedDate := DateTime.Now

//------ settings ------

self:nVAT := 17.5

self:nLimitStart := 2255

//self:nLimitStart := 2300

self:nLimitEnd := 2600

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

RDDSetDefault("DBFCDX")

//SetExclusive(false)

SetExclusive(true)

self:cFolderData := "C:\Users\BEST\Documents\Visual Studio 2008\"

self:cFolderData += "ProjectsWPF\BESTinvoicesWPF\BESTinvoicesWPF\Data\"

//MessageBox.Show("am here OK ...","NO PROBLEM :")

try

if self:OpenCustomerDBF()

self:MakeCustomerCollectionFromDBF()

else

MessageBox.Show("Can't open customer data file !?","PROBLEM :")

return

endif

if self:OpenInvoiceDBF()

self:MakeInvoiceCollectionFromDBF()

else

MessageBox.Show("Can't open invoice data file !?","PROBLEM :")

return

endif

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

//MessageBox.Show("am here OK ...","NO PROBLEM :")

self:CreateCustomerBindings(self:oCustomerGroup)

self:CreateInvoiceBindings(self:oInvoiceGroup)

//MessageBox.Show("am here OK ...","NO PROBLEM :")

self:FindNewRefNos()

self:FillNewRefNos()

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

self:otbxParts1:SpellCheck:IsEnabled := true

self:otbxParts2:SpellCheck:IsEnabled := true

self:otbxParts3:SpellCheck:IsEnabled := true

self:otbxParts4:SpellCheck:IsEnabled := true

self:otbxParts5:SpellCheck:IsEnabled := true

self:otbxParts6:SpellCheck:IsEnabled := true

self:otbxParts7:SpellCheck:IsEnabled := true

self:otbxParts8:SpellCheck:IsEnabled := true

self:otbxParts9:SpellCheck:IsEnabled := true

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

self:otbxDate1:SpellCheck:IsEnabled := true

self:otbxDate2:SpellCheck:IsEnabled := true

self:otbxDate3:SpellCheck:IsEnabled := true

self:otbxDate4:SpellCheck:IsEnabled := true

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

self:SetMyEventHandlers()

return

method FindNewRefNos() as void

self:alExistRefNos := ArrayList{}

local oV as InvoiceCS

local nCount, i, nLen, nRef as int

nCount := self:oInvoiceGroup:Count

nLen := nCount-1

for i := 0 upto nLen

oV := self:oInvoiceGroup:Item[i]

nRef := oV:InvRefNo

if nRef < self:nLimitStart .or. nRef > self:nLimitEnd

loop

endif

self:alExistRefNos:Add(nRef)

next

//MessageBox.Show(oV:InvRefNo:ToString())

self:alExistRefNos:Sort()

self:nHighest := (int)self:alExistRefNos:Item[self:alExistRefNos:Count-1]

//MessageBox.Show(self:nHighest:ToString())

self:aExistRefNos := int[]{0}

self:aExistRefNos := (int[])self:alExistRefNos:ToArray(typeof(int))

//MessageBox.Show(self:aExistRefNos:Length:ToString(),"EXIST :")

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

self:alNewRefNos := ArrayList{}

for i := self:nLimitStart upto self:nLimitEnd

if self:alExistRefNos:Contains(i)

loop

else

self:alNewRefNos:Add(i)

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

endif

next

self:aNewRefNos := int[]{0}

self:aNewRefNos := (int[])self:alNewRefNos:ToArray(typeof(int))

//MessageBox.Show(self:aNewRefNos:Length:ToString(),"NEW :")

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

return

method FillNewRefNos() as void

self:ocbxNewRefNos:ItemsSource := self:aNewRefNos

self:nNext := self:nHighest+1

self:ocbxNewRefNos:SelectedItem := self:nNext

return

method SetMyEventHandlers() as void

self:opbClose:Click += RoutedEventHandler{self, @opbClose_Click()}

self:opbViewPDF:Click += RoutedEventHandler{self, @opbViewPDF_Click()}

self:opbViewXPS:Click += RoutedEventHandler{self, @opbViewXPS_Click()}

self:opbAddCustomer:Click += RoutedEventHandler{self, @opbAddCustomer_Click()}

self:wMainInvoice:Closing += CancelEventHandler{self, @wMainInvoice_Closing()}

self:opbEditRemove:Click += RoutedEventHandler{self, @opbEditRemove_Click()}

self:opbClear:Click += RoutedEventHandler{self, @opbClear_Click()}

self:opbStartNew:Click += RoutedEventHandler{self, @opbStartNew_Click()}

self:opbTestFill:Click += RoutedEventHandler{self, @opbTestFill_Click()}

self:opbCalculate:Click += RoutedEventHandler{self, @opbCalculate_Click()}

self:opbSaveToFile:Click += RoutedEventHandler{self, @opbSaveToFile_Click()}

self:opbSettings:Click += RoutedEventHandler{self, @opbSettings_Click()}

self:opbPrintNewInvoice:Click += RoutedEventHandler{self, @opbPrintNewInvoice_Click()}

self:opbSerialize:Click += RoutedEventHandler{self, @opbSerialize_Click()}

self:otbxAmount1:TextChanged += TextChangedEventHandler{self, @otbxAmount1_TextChanged()}

self:otbxAmount2:TextChanged += TextChangedEventHandler{self, @otbxAmount2_TextChanged()}

self:otbxAmount3:TextChanged += TextChangedEventHandler{self, @otbxAmount3_TextChanged()}

self:otbxAmount4:TextChanged += TextChangedEventHandler{self, @otbxAmount4_TextChanged()}

self:otbxAmount5:TextChanged += TextChangedEventHandler{self, @otbxAmount5_TextChanged()}

self:otbxAmount6:TextChanged += TextChangedEventHandler{self, @otbxAmount6_TextChanged()}

self:otbxAmount7:TextChanged += TextChangedEventHandler{self, @otbxAmount7_TextChanged()}

self:otbxAmount8:TextChanged += TextChangedEventHandler{self, @otbxAmount8_TextChanged()}

self:otbxAmount9:TextChanged += TextChangedEventHandler{self, @otbxAmount9_TextChanged()}

self:otbxAmount1:LostFocus += RoutedEventHandler{self, @otbxAmount1_LostFocus()}

self:otbxAmount2:LostFocus += RoutedEventHandler{self, @otbxAmount2_LostFocus()}

self:otbxAmount3:LostFocus += RoutedEventHandler{self, @otbxAmount3_LostFocus()}

self:otbxAmount4:LostFocus += RoutedEventHandler{self, @otbxAmount4_LostFocus()}

self:otbxAmount5:LostFocus += RoutedEventHandler{self, @otbxAmount5_LostFocus()}

self:otbxAmount6:LostFocus += RoutedEventHandler{self, @otbxAmount6_LostFocus()}

self:otbxAmount7:LostFocus += RoutedEventHandler{self, @otbxAmount7_LostFocus()}

self:otbxAmount8:LostFocus += RoutedEventHandler{self, @otbxAmount8_LostFocus()}

self:otbxAmount9:LostFocus += RoutedEventHandler{self, @otbxAmount9_LostFocus()}

//self:odpCreateNew:SelectedDateChanged += SelectedChangedEventHandler{self, @odpCreateNew_SelectedDateChanged()}

self:odpCreateNew:SelectedDateChanged += EventHandler<SelectionChangedEventArgs>{self, @odpCreateNew_SelectedDateChanged()}

return

method UnsetMyEventHandlers() as void

self:opbClose:Click -= RoutedEventHandler{self, @opbClose_Click()}

self:opbViewPDF:Click -= RoutedEventHandler{self, @opbViewPDF_Click()}

self:opbViewXPS:Click -= RoutedEventHandler{self, @opbViewXPS_Click()}

self:opbAddCustomer:Click -= RoutedEventHandler{self, @opbAddCustomer_Click()}

self:opbEditRemove:Click -= RoutedEventHandler{self, @opbEditRemove_Click()}

self:opbClear:Click -= RoutedEventHandler{self, @opbClear_Click()}

self:opbStartNew:Click -= RoutedEventHandler{self, @opbStartNew_Click()}

self:opbTestFill:Click -= RoutedEventHandler{self, @opbTestFill_Click()}

self:opbCalculate:Click -= RoutedEventHandler{self, @opbCalculate_Click()}

self:opbSaveToFile:Click -= RoutedEventHandler{self, @opbSaveToFile_Click()}

self:opbSettings:Click -= RoutedEventHandler{self, @opbSettings_Click()}

self:wMainInvoice:Closing -= CancelEventHandler{self, @wMainInvoice_Closing()}

self:opbPrintNewInvoice:Click -= RoutedEventHandler{self, @opbPrintNewInvoice_Click()}

self:opbSerialize:Click -= RoutedEventHandler{self, @opbSerialize_Click()}

self:otbxAmount1:TextChanged -= TextChangedEventHandler{self, @otbxAmount1_TextChanged()}

self:otbxAmount2:TextChanged -= TextChangedEventHandler{self, @otbxAmount2_TextChanged()}

self:otbxAmount3:TextChanged -= TextChangedEventHandler{self, @otbxAmount3_TextChanged()}

self:otbxAmount4:TextChanged -= TextChangedEventHandler{self, @otbxAmount4_TextChanged()}

self:otbxAmount5:TextChanged -= TextChangedEventHandler{self, @otbxAmount5_TextChanged()}

self:otbxAmount6:TextChanged -= TextChangedEventHandler{self, @otbxAmount6_TextChanged()}

self:otbxAmount7:TextChanged -= TextChangedEventHandler{self, @otbxAmount7_TextChanged()}

self:otbxAmount8:TextChanged -= TextChangedEventHandler{self, @otbxAmount8_TextChanged()}

self:otbxAmount9:TextChanged -= TextChangedEventHandler{self, @otbxAmount9_TextChanged()}

self:otbxAmount1:LostFocus -= RoutedEventHandler{self, @otbxAmount1_LostFocus()}

self:otbxAmount2:LostFocus -= RoutedEventHandler{self, @otbxAmount2_LostFocus()}

self:otbxAmount3:LostFocus -= RoutedEventHandler{self, @otbxAmount3_LostFocus()}

self:otbxAmount4:LostFocus -= RoutedEventHandler{self, @otbxAmount4_LostFocus()}

self:otbxAmount5:LostFocus -= RoutedEventHandler{self, @otbxAmount5_LostFocus()}

self:otbxAmount6:LostFocus -= RoutedEventHandler{self, @otbxAmount6_LostFocus()}

self:otbxAmount7:LostFocus -= RoutedEventHandler{self, @otbxAmount7_LostFocus()}

self:otbxAmount8:LostFocus -= RoutedEventHandler{self, @otbxAmount8_LostFocus()}

self:otbxAmount9:LostFocus -= RoutedEventHandler{self, @otbxAmount9_LostFocus()}

//self:odpCreateNew:SelectedDateChanged -= SelectionChangedEventHandler{self, @odpCreateNew_SelectedDateChanged()}

self:odpCreateNew:SelectedDateChanged -= EventHandler<SelectionChangedEventArgs>{self, @odpCreateNew_SelectedDateChanged()}

return

method DoSerializationToFile() as void

MessageBox.Show("will eventually SERIALIZE invoice ...")

if self:oNV = NULL_OBJECT

MessageBox.Show("There is no invoice object at present !","CARE :")

return

endif

self:CopyCustomerFieldsToInvoice()

self:MakeSerializableObject()

if self:DoSerialization()

MessageBox.Show("XML file appears to have been created !","Success :")

endif

return

method MakeSerializableObject() as void

self:oSi := SerializableInvoice{}

self:oSI:cAdd1 := self:oNV:Add1

self:oSI:cAdd2 := self:oNV:Add2

self:oSI:cAdd3 := self:oNV:Add3

self:oSI:nInvRefNo := self:oNV:InvRefNo

self:oSI:cCompany := self:oNV:Company

self:oSI:cContact := self:oNV:Contact

self:oSI:cCustomer := self:oNV:Customer

self:oSI:cParts1 := self:oNV:Parts1

self:oSI:cParts2 := self:oNV:Parts2

self:oSI:cParts3 := self:oNV:Parts3

self:oSI:cParts4 := self:oNV:Parts4

self:oSI:cParts5 := self:oNV:Parts5

self:oSI:cParts6 := self:oNV:Parts6

self:oSI:cParts7 := self:oNV:Parts7

self:oSI:cParts8 := self:oNV:Parts8

self:oSI:cParts9 := self:oNV:Parts9

self:oSI:nAmount1 := self:oNV:Amount1

self:oSI:nAmount2 := self:oNV:Amount2

self:oSI:nAmount3 := self:oNV:Amount3

self:oSI:nAmount4 := self:oNV:Amount4

self:oSI:nAmount5 := self:oNV:Amount5

self:oSI:nAmount6 := self:oNV:Amount6

self:oSI:nAmount7 := self:oNV:Amount7

self:oSI:nAmount8 := self:oNV:Amount8

self:oSI:nAmount9 := self:oNV:Amount9

self:oSI:nCost := self:oNV:Cost

self:oSI:nVAT := self:oNV:VAT

self:oSI:nTotal:= self:oNV:Total

self:oSI:cPostCode := self:oNV:PostCode

self:oSI:cPhone := self:oNV:Phone

self:oSI:cEmail := self:oNV:Email

self:oSI:cDate1 := self:oNV:Date1

self:oSI:cDate2 := self:oNV:Date2

self:oSI:cDate3 := self:oNV:Date3

self:oSI:cDate4 := self:oNV:Date4

self:oSI:dDateMade := self:oNV:DateMade

self:oSI:dDateModify := self:oNV:DateModify

self:oSI:dInvDate := self:oNV:InvDate

self:oSI:lPrinted := self:oNV:Printed

return

method DoSerialization() as Boolean

local lResult as Boolean

lResult := false

LOCAL serial AS XmlSerializer

LOCAL oSW AS StreamWriter

LOCAL cFile AS STRING

cFile := "C:\Users\BEST\Documents\Visual Studio 2008\ProjectsWPF\BESTinvoicesWPF\BESTinvoicesWPF\Data\Inv2Prnt.xml"

//----- Save object to XML file -----

serial := XmlSerializer{typeof(SerializableInvoice)}

oSW := StreamWriter{cFile}

try

serial:Serialize(oSW,self:oSI)

lResult := true

catch oEx as Exception

self:MyInnerMessage(oEx)

end try

oSW:Close()

return lResult

method MyInnerMessage(oEX as Exception) as void

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)

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

MessageBox.Show(cText)

return

endif

return

method DoPrintNewInvoice() as void

MessageBox.Show("will eventually PRINT invoice ...")

local oProcess as Process

oProcess := Process{}

oProcess:StartInfo:FileName := "BESTinvoicePrinting.exe"

oProcess:StartInfo:WorkingDirectory := "..\data\"

oProcess:StartInfo:UseShellExecute := TRUE

oProcess:Start()

return

method DoViewEditSettings() as void

//MessageBox.Show("will launch settings Form ...")

local oVS as InvoiceSettings

oVS := InvoiceSettings{}

oVS:ShowDialog()

return

method DoChangedSelectedDate() as void

//MessageBox.Show("Will show new date selection ...")

local cText as string

local oDT as DateTime

oDT := (DateTime)self:odpCreateNew:SelectedDate

cText := oDT:ToShortDateString()

self:olbInvDate:Content := "[ Inv. Date "+cText+" ]"

if !self:oNV = NULL_OBJECT

self:oNV:InvDate := oDT

endif

return

method DoSaveToFile() as void

if self:oNV = NULL_OBJECT

MessageBox.Show("Problem, no invoice object !?","No Invoice :")

return

else

MessageBox.Show("Trying to save to file ...","File Save :")

self:CopyCustomerFieldsToInvoice()

endif

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

try

if self:DoCopyInvoiceFieldsToDBF()

MessageBox.Show("Append OK ...")

self:oInvoiceGroup:Add(self:oNV)

else

MessageBox.Show("NO append OK ??")

endif

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

return

method CopyCustomerFieldsToInvoice() as void

local oC as CustomerCS

oC := (CustomerCS)self:oViewC:CurrentItem

self:oNV:Company := oC:Company

self:oNV:Contact := oC:Contact

self:oNV:Add1 := oC:Add1

self:oNV:Add2 := oC:Add2

self:oNV:Add3 := oC:Add3

self:oNV:PostCode:= oC:PostCode

self:oNV:Phone := oC:Phone

self:oNV:Email := oC:Email

return

method DateTimeToOldDate(oDT as DateTime) as date

local dResult as date

local cDate as string

local nDay, nMonth, nYear as int

local cDay, cMonth, cYear as string

nDay := oDT:Day

nMonth := oDT:Month

nYear := oDT:Year

cYear := nYear:ToString()

cMonth := nMonth:ToString()

cDay := nDay:ToString()

if nDay < 10

cDay := "0"+cDay

endif

if nMonth < 10

cMonth := "0"+cMonth

endif

cDate := cDay+"/"+cMonth+"/"+cYear

//MessageBox.Show(cDate)

dResult := ctod(cDate)

return dResult

method DoCopyInvoiceFieldsToDBF() as Boolean

local lResult as Boolean

lResult := false

//----- MUST do check on existence of Unique Ref No. first -----

if !self:oInv:Append()

return lResult

endif

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

self:oInv:FIELDPUT("invdate",DateTimeToOldDate(self:oNV:InvDate))

self:oInv:FIELDPUT("datemade",DateTimeToOldDate(self:oNV:DateMade))

self:oInv:FIELDPUT("datemodify",DateTimeToOldDate(self:oNV:DateModify))

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

self:oInv:FIELDPUT("invrefno",self:oNV:InvRefNo)

self:oInv:FIELDPUT("customer",self:oNV:Customer)

self:oInv:FIELDPUT("company",self:oNV:Company)

self:oInv:FIELDPUT("person",self:oNV:Contact)

self:oInv:FIELDPUT("add1",self:oNV:Add1)

self:oInv:FIELDPUT("add2",self:oNV:Add2)

self:oInv:FIELDPUT("add3",self:oNV:Add3)

self:oInv:FIELDPUT("postcode",self:oNV:PostCode)

self:oInv:FIELDPUT("email",self:oNV:Email)

self:oInv:FIELDPUT("phone",self:oNV:Phone)

self:oInv:FIELDPUT("date1",self:oNV:Date1)

self:oInv:FIELDPUT("date2",self:oNV:Date2)

self:oInv:FIELDPUT("date3",self:oNV:Date3)

self:oInv:FIELDPUT("date4",self:oNV:Date4)

self:oInv:FIELDPUT("parts1",self:oNV:Parts1)

self:oInv:FIELDPUT("parts2",self:oNV:Parts2)

self:oInv:FIELDPUT("parts3",self:oNV:Parts3)

self:oInv:FIELDPUT("parts4",self:oNV:Parts4)

self:oInv:FIELDPUT("parts5",self:oNV:Parts5)

self:oInv:FIELDPUT("parts6",self:oNV:Parts6)

self:oInv:FIELDPUT("parts7",self:oNV:Parts7)

self:oInv:FIELDPUT("parts8",self:oNV:Parts8)

self:oInv:FIELDPUT("parts9",self:oNV:Parts9)

self:oInv:FIELDPUT("amount1",self:oNV:Amount1)

self:oInv:FIELDPUT("amount2",self:oNV:Amount2)

self:oInv:FIELDPUT("amount3",self:oNV:Amount3)

self:oInv:FIELDPUT("amount4",self:oNV:Amount4)

self:oInv:FIELDPUT("amount5",self:oNV:Amount5)

self:oInv:FIELDPUT("amount6",self:oNV:Amount6)

self:oInv:FIELDPUT("amount7",self:oNV:Amount7)

self:oInv:FIELDPUT("amount8",self:oNV:Amount8)

self:oInv:FIELDPUT("amount9",self:oNV:Amount9)

self:oInv:FIELDPUT("cost",self:oNV:Cost)

self:oInv:FIELDPUT("vat",self:oNV:VAT)

self:oInv:FIELDPUT("total",self:oNV:Total)

lResult := true

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

return lResult

method DoCalculate() as void

if self:oNV = NULL_OBJECT

MessageBox.Show("First start a new invoice !","CARE :")

return

endif

//MessageBox.Show("will now calculate ...")

local nCost as Double

nCost := 0.0

nCost += oNV:Amount1

nCost += oNV:Amount2

nCost += oNV:Amount3

nCost += oNV:Amount4

nCost += oNV:Amount5

nCost += oNV:Amount6

nCost += oNV:Amount7

nCost += oNV:Amount8

nCost += oNV:Amount9

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

oNV:Cost := Math.Round(nCost,2)

oNV:VAT := Math.Round((nCost*0.01*self:nVAT),2)

oNV:Total := Math.Round((oNV:Cost + oNV:VAT),2)

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

self:AdjustNumberText(otbxCost)

self:AdjustNumberText(otbxVAT)

self:AdjustNumberText(otbxTotal)

return

method DoRoundAmount(sender AS Object, e AS RoutedEventArgs) as void

local oTBX as TextBox

oTBX := (TextBox)sender

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

self:AdjustNumberText(oTBX)

return

method AdjustNumberText(oTBX as TextBox) as string

local nD as Double

local cText as string

cText := oTBX:Text

if cText == "" .or. cText == "."

return "0.00"

endif

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

nD := Convert.ToDouble(cText)

nD := Math.Round(nD,2)

cText := nD:ToString()

if cText:IndexOf(".") < 0

cText += ".00"

endif

oTBX:Text := String.Format("{0:F2}",nD)

return cText

method DoTestFill() as void

self:TestFillNewInvoiceObject()

self:CreateBindingsForNewInvoice(self:oNV)

return

method DoStartNewInvoice() as void

local cText as string

cText := self:odpCreateNew:SelectedDate:ToString()

//if self:odpCreateNew:SelectedDate < DateTime{2009,01,01}

/*cText := "No date has been selected ...."+CRLF

cText += "A new invoice will be started ...."+CRLF

MessageBox.Show(cText,"Creation :")

else*/

cText := "Selected date is >> "+cText+CRLF

cText += "A new invoice will be started ...."+CRLF

MessageBox.Show(cText,"Creation :")

//endif

local oDT as DateTime

oDT := (DateTime)self:odpCreateNew:SelectedDate

//cText := self:odpCreateNew:SelectedDate:ToShortDateString()

cText := oDT:ToShortDateString()

self:olbInvDate:Content := "[ Inv. Date "+cText+" ]"

//self:MakeNewInvoiceObject(self:nNext)

if !self:ocbxNewRefNos:SelectedItem = NULL_OBJECT

self:MakeNewInvoiceObject((int)self:ocbxNewRefNos:SelectedItem)

else

self:MakeNewInvoiceObject(self:nNext)

//----- will need to recalculate nNext when each invoice is created -----

endif

self:CreateBindingsForNewInvoice(self:oNV)

return

method DoClearNewInvoiceDetails() as void

if self:oNV = NULL_OBJECT

MessageBox.Show("No new invoice exists yet !","CARE :")

return

endif

MessageBox.Show("Will remove all the detail existing at the moment !!","WARNING :")

oNV:DateMade := DateTime.Now

oNV:DateModify := DateTime.Now

//oV:InvDate := oInv:FIELDGET("invdate")

oNV:Printed := false

oNV:Customer := ""

oNV:Company := ""

oNV:Contact := ""

oNV:Add1 := ""

oNV:Add2 := ""

oNV:Add3 := ""

oNV:PostCode := ""

oNV:Phone := ""

oNV:Email := ""

oNV:Date1 := ""

oNV:Date2 := ""

oNV:Date3 := ""

oNV:Date4 := ""

oNV:Parts1 := ""

oNV:Parts2 := ""

oNV:Parts3 := ""

oNV:Parts4 := ""

oNV:Parts5 := ""

oNV:Parts6 := ""

oNV:Parts7 := ""

oNV:Parts8 := ""

oNV:Parts9 := ""

oNV:Amount1 := 0.0

oNV:Amount2 := 0.0

oNV:Amount3 := 0.0

oNV:Amount4 := 0.0

oNV:Amount5 := 0.0

oNV:Amount6 := 0.0

oNV:Amount7 := 0.0

oNV:Amount8 := 0.0

oNV:Amount9 := 0.0

//MessageBox.Show(oNV:Amount1:ToString())

oNV:Cost := 0.0

oNV:VAT := 0.0

oNV:Total := 0.0

return

method DoWindowClosing() as void

self:CloseAllFiles()

return

method CloseAllFiles() as void

if !self:oCust = NULL_OBJECT

self:oCust:Close()

endif

if !self:oInv = NULL_OBJECT

self:oInv:Close()

endif

return

method OpenInvoiceDBF() as Boolean

local lResult as Boolean

lResult := false

local cFilePath as string

cFilePath := self:cFolderData+"Invoices.dbf"

if File.Exists(cFilePath)

//MessageBox.Show("OK found :- "+cFilePath,"File Locate :")

else

MessageBox.Show("Can't find :- "+cFilePath,"File Locate :")

endif

try

SELF:oInv := DBServer{cFilePath}

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

if !SELF:oInv:Used

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

MessageBox.Show("Problem - not opened right !?")

return lResult

else

//MessageBox.Show("Opened customers file!")

lResult := true

endif

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

SELF:oInv:GoTop()

return lResult

method OpenCustomerDBF() as Boolean

local lResult as Boolean

lResult := false

local cFilePath as string

cFilePath := self:cFolderData+"Customers.dbf"

if File.Exists(cFilePath)

//MessageBox.Show("OK found :- "+cFilePath,"File Locate :")

else

MessageBox.Show("Can't find :- "+cFilePath,"File Locate :")

endif

try

SELF:oCust := DBServer{cFilePath}

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

if !SELF:oCust:Used

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

MessageBox.Show("Problem - not opened right !?")

return lResult

else

//MessageBox.Show("Opened customers file!")

lResult := true

endif

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

SELF:oCust:GoTop()

return lResult

method MakeInvoiceCollectionFromDBF() as Boolean

local lResult as Boolean

lResult := false

try

local nLen as int

nLen := oInv:Reccount

self:oInvoiceGroup := List<InvoiceCS>{}

self:aInvoiceCS := InvoiceCS[]{nLen}

local oV as InvoiceCS

local nCount as int

nCount := 1

oInv:GoTop()

do while !oInv:eof

oV := self:MakeInvoiceObject()

//MessageBox.Show(oV:InvRefNo:ToString())

//----- add to collection -----

self:aInvoiceCS[nCount] := oV

nCount +=1

oInv:Skip(+1)

enddo

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

//MessageBox.Show(self:aInvoiceCS:Length:ToString(),"Array Length :")

try

self:oInvoiceGroup:Clear()

self:oInvoiceGroup:AddRange(self:aInvoiceCS)

//MessageBox.Show(self:oInvoiceGroup:Count:ToString(),"List Length :")

self:oViewI := CollectionViewSource.GetDefaultView(self:oInvoiceGroup)

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

self:ocbxInvoices:ItemsSource := self:oViewI

self:ocbxInvoices:DisplayMemberPath := "InvRefNo"

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

return lResult

method CreateBindingsForNewInvoice(oN as InvoiceCS) as void

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

oBindN1 := Binding{"InvRefNo"}

oBindN1:Source := oN

oBindN1:Mode := BindingMode.TwoWay

oBindN1:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN2 := Binding{"Customer"}

oBindN2:Source := oN

oBindN2:Mode := BindingMode.TwoWay

oBindN2:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN3 := Binding{"Company"}

oBindN3:Source := oN

oBindN3:Mode := BindingMode.TwoWay

oBindN3:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN4 := Binding{"Contact"}

oBindN4:Source := oN

oBindN4:Mode := BindingMode.TwoWay

oBindN4:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindN5 := Binding{"Parts1"}

oBindN5:Source := oN

oBindN5:Mode := BindingMode.TwoWay

oBindN5:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN6 := Binding{"Parts2"}

oBindN6:Source := oN

oBindN6:Mode := BindingMode.TwoWay

oBindN6:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN7 := Binding{"Parts3"}

oBindN7:Source := oN

oBindN7:Mode := BindingMode.TwoWay

oBindN7:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN8 := Binding{"Parts4"}

oBindN8:Source := oN

oBindN8:Mode := BindingMode.TwoWay

oBindN8:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN9 := Binding{"Parts5"}

oBindN9:Source := oN

oBindN9:Mode := BindingMode.TwoWay

oBindN9:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN10 := Binding{"Parts6"}

oBindN10:Source := oN

oBindN10:Mode := BindingMode.TwoWay

oBindN10:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN11 := Binding{"Parts7"}

oBindN11:Source := oN

oBindN11:Mode := BindingMode.TwoWay

oBindN11:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN12 := Binding{"Parts8"}

oBindN12:Source := oN

oBindN12:Mode := BindingMode.TwoWay

oBindN12:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN13 := Binding{"Parts9"}

oBindN13:Source := oN

oBindN13:Mode := BindingMode.TwoWay

oBindN13:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindN14 := Binding{"Amount1"}

oBindN14:Source := oN

oBindN14:Mode := BindingMode.TwoWay

oBindN14:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN15 := Binding{"Amount2"}

oBindN15:Source := oN

oBindN15:Mode := BindingMode.TwoWay

oBindN15:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN16 := Binding{"Amount3"}

oBindN16:Source := oN

oBindN16:Mode := BindingMode.TwoWay

oBindN16:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN17 := Binding{"Amount4"}

oBindN17:Source := oN

oBindN17:Mode := BindingMode.TwoWay

oBindN17:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN18 := Binding{"Amount5"}

oBindN18:Source := oN

oBindN18:Mode := BindingMode.TwoWay

oBindN18:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN19 := Binding{"Amount6"}

oBindN19:Source := oN

oBindN19:Mode := BindingMode.TwoWay

oBindN19:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN20 := Binding{"Amount7"}

oBindN20:Source := oN

oBindN20:Mode := BindingMode.TwoWay

oBindN20:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN21 := Binding{"Amount8"}

oBindN21:Source := oN

oBindN21:Mode := BindingMode.TwoWay

oBindN21:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN22 := Binding{"Amount9"}

oBindN22:Source := oN

oBindN22:Mode := BindingMode.TwoWay

oBindN22:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindN23 := Binding{"Cost"}

oBindN23:Source := oN

oBindN23:Mode := BindingMode.TwoWay

oBindN23:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN24 := Binding{"VAT"}

oBindN24:Source := oN

oBindN24:Mode := BindingMode.TwoWay

oBindN24:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN25 := Binding{"Total"}

oBindN25:Source := oN

oBindN25:Mode := BindingMode.TwoWay

oBindN25:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

oBindN26 := Binding{"Date1"}

oBindN26:Source := oN

oBindN26:Mode := BindingMode.TwoWay

oBindN26:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN27 := Binding{"Date2"}

oBindN27:Source := oN

oBindN27:Mode := BindingMode.TwoWay

oBindN27:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN28 := Binding{"Date3"}

oBindN28:Source := oN

oBindN28:Mode := BindingMode.TwoWay

oBindN28:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

oBindN29 := Binding{"Date4"}

oBindN29:Source := oN

oBindN29:Mode := BindingMode.TwoWay

oBindN29:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged

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

local odpTBKT as DependencyProperty

odpTBKT := TextBlock.TextProperty

BindingOperations.SetBinding(self:otbkInvRef, odpTBKT, oBindN1)

local odpTBXT as DependencyProperty

odpTBXT := TextBox.TextProperty

BindingOperations.SetBinding(self:otbxCost, odpTBXT, oBindN23)

BindingOperations.SetBinding(self:otbxVAT, odpTBXT, oBindN24)

BindingOperations.SetBinding(self:otbxTotal, odpTBXT, oBindN25)

BindingOperations.SetBinding(self:otbxParts1, odpTBXT, oBindN5)

BindingOperations.SetBinding(self:otbxParts2, odpTBXT, oBindN6)

BindingOperations.SetBinding(self:otbxParts3, odpTBXT, oBindN7)

BindingOperations.SetBinding(self:otbxParts4, odpTBXT, oBindN8)

BindingOperations.SetBinding(self:otbxParts5, odpTBXT, oBindN9)

BindingOperations.SetBinding(self:otbxParts6, odpTBXT, oBindN10)

BindingOperations.SetBinding(self:otbxParts7, odpTBXT, oBindN11)

BindingOperations.SetBinding(self:otbxParts8, odpTBXT, oBindN12)

BindingOperations.SetBinding(self:otbxParts9, odpTBXT, oBindN13)

BindingOperations.SetBinding(self:otbxAmount1, odpTBXT, oBindN14)

BindingOperations.SetBinding(self:otbxAmount2, odpTBXT, oBindN15)

BindingOperations.SetBinding(self:otbxAmount3, odpTBXT, oBindN16)

BindingOperations.SetBinding(self:otbxAmount4, odpTBXT, oBindN17)

BindingOperations.SetBinding(self:otbxAmount5, odpTBXT, oBindN18)

BindingOperations.SetBinding(self:otbxAmount6, odpTBXT, oBindN19)

BindingOperations.SetBinding(self:otbxAmount7, odpTBXT, oBindN20)

BindingOperations.SetBinding(self:otbxAmount8, odpTBXT, oBindN21)

BindingOperations.SetBinding(self:otbxAmount9, odpTBXT, oBindN22)

BindingOperations.SetBinding(self:otbxDate1, odpTBXT, oBindN26)

BindingOperations.SetBinding(self:otbxDate2, odpTBXT, oBindN27)

BindingOperations.SetBinding(self:otbxDate3, odpTBXT, oBindN28)

BindingOperations.SetBinding(self:otbxdate4, odpTBXT, oBindN29)

return

method TestFillNewInvoiceObject() as void

self:oNV := InvoiceCS{9999}

MessageBox.Show(oNV:InvRefNo:ToString())

//----- and further details -----

//----- need special treatment of Dates ! -----

oNV:DateMade := DateTime.Now

oNV:DateModify := DateTime.Now

//oV:InvDate := oInv:FIELDGET("invdate")

oNV:Printed := false

oNV:Customer := ".. empty .."

oNV:Company := ".. empty .."

oNV:Contact := ".. empty .."

oNV:Add1 := ".. empty .."

oNV:Add2 := ".. empty .."

oNV:Add3 := ".. empty .."

oNV:PostCode := ".. empty .."

oNV:Phone := ".. empty .."

oNV:Email := ".. empty .."

oNV:Date1 := "last week of"

oNV:Date2 := "December 2009"

oNV:Date3 := "first week of"

oNV:Date4 := "January 2010"

oNV:Parts1 := "Supply suitable hardware and software services as agreed on the phone ."

oNV:Parts2 := "Install extra drivers for scanner and printers."

oNV:Parts3 := "Provide wireless router and repeater."

oNV:Parts4 := "Five copies of Windows 7 - upgrade (Pro version)."

oNV:Parts5 := "Extra toner cartridges for colour laser printer."

oNV:Parts6 := "Three days 'on-site' support for install and testing."

oNV:Parts7 := "Continued on-line support for three months."

oNV:Parts8 := "External USB devices for regular backing up."

oNV:Parts9 := "Extra external batteries for extended use of devices."

oNV:Amount1 := 0

oNV:Amount2 := 69.17

oNV:Amount3 := 103.17

oNV:Amount4 := 777.77

oNV:Amount5 := 256.23

oNV:Amount6 := 390.00

oNV:Amount7 := 123.88

oNV:Amount8 := 56.37

oNV:Amount9 := 89.89

//MessageBox.Show(oNV:Amount1:ToString())

oNV:Cost := 0.0

oNV:VAT := 0.0

oNV:Total := 0.0

return

method MakeNewInvoiceObject(nRef as int) as void

self:oNV := InvoiceCS{nRef}

MessageBox.Show(oNV:InvRefNo:ToString())

//----- and further details -----

//----- need special treatment of Dates ! -----

oNV:DateMade := DateTime.Now

oNV:DateModify := DateTime.Now

oNV:InvDate := (DateTime)self:odpCreateNew:SelectedDate

oNV:Printed := false

oNV:Customer := ""

oNV:Company := ""

oNV:Contact := ""

oNV:Add1 := ""

oNV:Add2 := ""

oNV:Add3 := ""

oNV:PostCode := ""

oNV:Phone := ""

oNV:Email := ""

oNV:Date1 := ""

oNV:Date2 := ""

oNV:Date3 := ""

oNV:Date4 := ""

oNV:Parts1 := ""

oNV:Parts2 := ""

oNV:Parts3 := ""

oNV:Parts4 := ""

oNV:Parts5 := ""

oNV:Parts6 := ""

oNV:Parts7 := ""

oNV:Parts8 := ""

oNV:Parts9 := ""

oNV:Amount1 := 0.0

oNV:Amount2 := 0.0

oNV:Amount3 := 0.0

oNV:Amount4 := 0.0

oNV:Amount5 := 0.0

oNV:Amount6 := 0.0

oNV:Amount7 := 0.0

oNV:Amount8 := 0.0

oNV:Amount9 := 0.0

//MessageBox.Show(oNV:Amount1:ToString())

oNV:Cost := 0.0

oNV:VAT := 0.0

oNV:Total := 0.0

return

method MakeInvoiceObject() as InvoiceCS

local oV as InvoiceCS

oV := InvoiceCS{oInv:FIELDGET("invrefno")}

//MessageBox.Show(oC:InvRefNo:ToString())

//----- and further details -----

//----- need special treatment of Dates ! -----

/*oV:DateMade := oInv:FIELDGET("datemade")

oV:DateModify := oInv:FIELDGET("datemodify")

oV:InvDate := oInv:FIELDGET("invdate")*/

oV:Printed := oInv:FIELDGET("printed")

oV:Customer := oInv:FIELDGET("customer")

oV:Company := oInv:FIELDGET("company")

oV:Contact := oInv:FIELDGET("person")

oV:Add1 := oInv:FIELDGET("add1")

oV:Add2 := oInv:FIELDGET("add2")

oV:Add3 := oInv:FIELDGET("add3")

oV:PostCode := oInv:FIELDGET("postcode")

oV:Phone := oInv:FIELDGET("phone")

oV:Email := oInv:FIELDGET("email")

oV:Date1 := oInv:FIELDGET("date1")

oV:Date2 := oInv:FIELDGET("date2")

oV:Date3 := oInv:FIELDGET("date3")

oV:Date4 := oInv:FIELDGET("date4")

oV:Parts1 := oInv:FIELDGET("parts1")

oV:Parts2 := oInv:FIELDGET("parts2")

oV:Parts3 := oInv:FIELDGET("parts3")

oV:Parts4 := oInv:FIELDGET("parts4")

oV:Parts5 := oInv:FIELDGET("parts5")

oV:Parts6 := oInv:FIELDGET("parts6")

oV:Parts7 := oInv:FIELDGET("parts7")

oV:Parts8 := oInv:FIELDGET("parts8")

oV:Parts9 := oInv:FIELDGET("parts9")

oV:Amount1 := oInv:FIELDGET("amount1")

oV:Amount2 := oInv:FIELDGET("amount2")

oV:Amount3 := oInv:FIELDGET("amount3")

oV:Amount4 := oInv:FIELDGET("amount4")

oV:Amount5 := oInv:FIELDGET("amount5")

oV:Amount6 := oInv:FIELDGET("amount6")

oV:Amount7 := oInv:FIELDGET("amount7")

oV:Amount8 := oInv:FIELDGET("amount8")

oV:Amount9 := oInv:FIELDGET("amount9")

oV:Cost := oInv:FIELDGET("cost")

oV:VAT := oInv:FIELDGET("vat")

oV:Total := oInv:FIELDGET("total")

return oV

method MakeCustomerObject() as CustomerCS

local oC as CustomerCS

oC := CustomerCS{oCust:FIELDGET("custrefno")}

//MessageBox.Show(oC:CustRefNo:ToString())

//----- and further details -----

oC:Contact := oCust:FIELDGET("person")

oC:Company := oCust:FIELDGET("company")

oC:Add1 := oCust:FIELDGET("add1")

oC:Add2 := oCust:FIELDGET("add2")

oC:Add3 := oCust:FIELDGET("add3")

oC:PostCode := oCust:FIELDGET("postcode")

oC:Phone := oCust:FIELDGET("phone")

oC:Web := oCust:FIELDGET("web")

oC:Email := oCust:FIELDGET("email")

oC:Fax := oCust:FIELDGET("fax")

oC:Details1 := oCust:FIELDGET("details1")

oC:Details2 := oCust:FIELDGET("details2")

return oC

method MakeCustomerCollectionFromDBF() as Boolean

local lResult as Boolean

lResult := false

try

local nLen as int

nLen := oCust:Reccount

self:oCustomerGroup := List<CustomerCS>{}

self:aCustomerCS := CustomerCS[]{nLen}

local oC as CustomerCS

local nCount as int

nCount := 1

oCust:GoTop()

self:nNextCustRef := oCust:FIELDGET("CustRefNo")

do while !oCust:eof

oC := self:MakeCustomerObject()

//MessageBox.Show(oC:CustRefNo:ToString())

//----- add to collection -----

self:aCustomerCS[nCount] := oC

if oCust:FIELDGET("CustRefNo") > self:nNextCustRef

self:nNextCustRef := oCust:FIELDGET("CustRefNo")

endif

nCount +=1

oCust:Skip(+1)

enddo

self:nNextCustRef += 1

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

//MessageBox.Show(self:aCustomerCS:Length:ToString(),"Array Length :")

//MessageBox.Show(self:nNextCustRef:ToString())

try

self:oCustomerGroup:Clear()

self:oCustomerGroup:AddRange(self:aCustomerCS)

//MessageBox.Show(self:oCustomerGroup:Count:ToString(),"List Length :")

self:oViewC := CollectionViewSource.GetDefaultView(self:oCustomerGroup)

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

self:ocbxCustomers:ItemsSource := self:oViewC

//self:ocbxCustomers:DisplayMemberPath := "CustRefNo"

self:ocbxCustomers:DisplayMemberPath := "Company"

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

return lResult

method DoViewPDF() as void

return

method DoViewXPS() as void

return

method ReFillCustomer() as void

self:oCustomerGroup:Clear()

self:MakeCustomerCollectionFromDBF()

self:CreateCustomerBindings(self:oCustomerGroup)

return

method DoAddCustomer() as void

//----- make a blank customer object and pass in to oWin -----

//----- set 'new' flag -----

local oC as CustomerCS

oC := CustomerCS{self:nNextCustRef}

//oC := CustomerCS{777}

local oWin as CustomerWin

oWin := CustomerWin{oC,"Add",self:oCust}

oWin:ShowDialog()

//----- NO need to increment the Next customer value -----

//----- this will be done automatically when the -----

//----- drop list is refilled -----

self:ReFillCustomer()

return

method DoEditRemove() as void

//----- pass customer object in focus into oWin -----

//----- set 'edit' flag -----

local oC as CustomerCS

oC := (CustomerCS)self:oViewC:CurrentItem

local oWin as CustomerWin

oWin := CustomerWin{oC,"Edit",self:oCust}

oWin:ShowDialog()

//----- could be useful to check persistence ! -----

self:ReFillCustomer()

return

method DoClose() as void

self:Close()

return

END CLASS

[Serializable] class SerializableInvoice

export nInvRefNo as int

export cCompany as String

export cContact as String

export cCustomer as string

export dDateMade as DateTime

export dDateModify as DateTime

export dInvDate as DateTime

export lPrinted as Boolean

export cAdd1 as string

export cAdd2 as string

export cAdd3 as string

export cPostcode as string

export cPhone as string

export cEmail as string

export cDate1 as string

export cDate2 as string

export cDate3 as string

export cDate4 as string

export cParts1 as string

export cParts2 as string

export cParts3 as string

export cParts4 as string

export cParts5 as string

export cParts6 as string

export cParts7 as string

export cParts8 as string

export cParts9 as string

export nAmount1 as Double

export nAmount2 as Double

export nAmount3 as Double

export nAmount4 as Double

export nAmount5 as Double

export nAmount6 as Double

export nAmount7 as Double

export nAmount8 as Double

export nAmount9 as Double

export nCost as Double

export nVAT as Double

export nTotal as Double

CONSTRUCTOR()

SUPER()

nInvRefNo := 0

cCompany := ""

cContact := ""

cCustomer := ""

dDateMade := DateTime{1001,01,01}

dDateModify := DateTime{1001,01,01}

dInvDate := DateTime{1001,01,01}

lPrinted := false

cAdd1 := ""

cAdd2 := ""

cAdd3 := ""

cPostcode := ""

cPhone := ""

cEmail := ""

cDate1 := ""

cDate2 := ""

cDate3 := ""

cDate4 := ""

cParts1 := ""

cParts2 := ""

cParts3 := ""

cParts4 := ""

cParts5 := ""

cParts6 := ""

cParts7 := ""

cParts8 := ""

cParts9 := ""

nAmount1 := 0.0

nAmount2 := 0.0

nAmount3 := 0.0

nAmount4 := 0.0

nAmount5 := 0.0

nAmount6 := 0.0

nAmount7 := 0.0

nAmount8 := 0.0

nAmount9 := 0.0

nCost := 0.0

nVAT := 0.0

nTotal := 0.0

return

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 BESTinvoicesWPF

/// <summary>

/// Interaction logic for CustomerWin.xaml

/// </summary>

PARTIAL CLASS CustomerWin INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD opbSave_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoSaveToDisk()

RETURN

PRIVATE METHOD opbClose_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

RETURN

PRIVATE METHOD opbQuit_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

RETURN

PRIVATE METHOD textBox1_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox2_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox3_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox4_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox5_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

RETURN

PRIVATE METHOD textBox6_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox7_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox8_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox9_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox10_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox11_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

PRIVATE METHOD textBox12_TextChanged( sender AS System.Object, e AS System.Windows.Controls.TextChangedEventArgs ) AS System.Void

self:DoDetailsChanged()

RETURN

END CLASS

END NAMESPACE

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

<Window x:Class="BESTinvoicesWPF.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="BEST invoices WPF :" Height="730" Width="760" Name="wMainInvoice" Background="Cornsilk" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" Closing="wMainInvoice_Closing">

<Grid Height="694">

<Button Height="23" HorizontalAlignment="Right" Margin="0,272,18,0" Name="opbClose" VerticalAlignment="Top" Width="65" Click="opbClose_Click" Background="Red" Foreground="Gold" FontWeight="Bold">Close</Button>

<GroupBox Header="Existing Invoices :" Height="68" HorizontalAlignment="Right" Margin="0,6,12,0" Name="ogbxExistingInvs" VerticalAlignment="Top" Width="344" BorderThickness="3">

<Grid Background="BlanchedAlmond">

<ComboBox Margin="16,10.04,0,11.96" Name="ocbxInvoices" HorizontalAlignment="Left" Width="79" />

<Button Margin="121,10.04,0,11.96" Name="opbViewPDF" Click="opbViewPDF_Click" HorizontalAlignment="Left" Width="74" Background="Thistle">View PDF</Button>

<Button Margin="220,10,0,12" Name="opbViewXPS" HorizontalAlignment="Left" Width="75" Click="opbViewXPS_Click" Background="Thistle">View XPS</Button>

</Grid>

</GroupBox>

<GroupBox Header="Create new Invoice :" Height="93" HorizontalAlignment="Right" Margin="0,100,12,0" Name="ogbxCreateNew" VerticalAlignment="Top" Width="344" BorderThickness="3">

<Grid Background="BlanchedAlmond">

<ComboBox Height="23" Margin="136,0,107,9" Name="ocbxNewRefNos" VerticalAlignment="Bottom" />

<Label Margin="134,8,101,35" Name="olbNewRefNo" Foreground="Red">New Ref. No. :</Label>

<my:DatePicker Margin="7,0,0,7" Name="odpCreateNew" xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"

Height="24.04" VerticalAlignment="Bottom"

ToolTip="Select the date for the invoice - NOT necessarily todays date !"

HorizontalAlignment="Left" Width="115"

SelectedDateChanged="odpCreateNew_SelectedDateChanged" />

<Label Margin="7,8,0,35" Name="olbInvoiceDate" Foreground="Red" HorizontalAlignment="Left" Width="105">Invoice date :</Label>

<Button Height="23" HorizontalAlignment="Right" Margin="0,0,14,9" Name="opbStartNew" VerticalAlignment="Bottom" Width="75" Click="opbStartNew_Click" Background="LightGreen">Start New</Button>

</Grid>

</GroupBox>

<GroupBox Header="Company / Contact :" Margin="8,5,0,0" Name="ogbxCompanyContact" Height="290" VerticalAlignment="Top" HorizontalAlignment="Left" Width="363" BorderThickness="3" FontSize="12">

<Grid Background="BlanchedAlmond">

<Label Height="28" Margin="6,6,146,0" Name="olbSelectCustomer" VerticalAlignment="Top" Foreground="Green">Select a Company or Contact :</Label>

<ComboBox Height="23" HorizontalAlignment="Left" Margin="6,32,0,0" Name="ocbxCustomers" VerticalAlignment="Top" Width="168" IsSynchronizedWithCurrentItem="True" />

<Button Height="23" HorizontalAlignment="Right" Margin="0,10,15,0" Name="opbAddCustomer" VerticalAlignment="Top" Width="100" Click="opbAddCustomer_Click" ToolTip=".. wiil show all customer details .." Background="LightGreen">Add Customer</Button>

<TextBlock Height="21" Margin="90,78,15,0" Name="textBlock1" VerticalAlignment="Top" Background="AliceBlue" />

<TextBlock Height="21" Margin="90,102,15,0" Name="textBlock2" VerticalAlignment="Top" Background="AliceBlue" />

<TextBlock Background="AliceBlue" Margin="90,127,15,119" Name="textBlock3" />

<TextBlock Background="AliceBlue" Height="21" Margin="90,0,15,95" Name="textBlock4" VerticalAlignment="Bottom" />

<TextBlock Background="AliceBlue" Height="21" Margin="90,0,15,70" Name="textBlock5" VerticalAlignment="Bottom" />

<TextBlock Background="AliceBlue" Height="21" Margin="90,0,15,46" Name="textBlock6" VerticalAlignment="Bottom" />

<TextBlock Background="AliceBlue" Height="21" Margin="90,0,15,21" Name="textBlock7" VerticalAlignment="Bottom" />

<Button Height="23" HorizontalAlignment="Right" Margin="0,45,15,0" Name="opbEditRemove" VerticalAlignment="Top" Width="100" Click="opbEditRemove_Click" Background="LightGreen">Edit / Remove</Button>

<Label Height="30" HorizontalAlignment="Left" Margin="6,71,0,0" Name="label1" VerticalAlignment="Top" Width="78" FontSize="12" VerticalContentAlignment="Center" SnapsToDevicePixels="False">Company :</Label>

<Label HorizontalAlignment="Left" Margin="6,98,0,0" Name="label2" Width="78" Height="27.04" VerticalAlignment="Top">Add 1 :</Label>

<Label HorizontalAlignment="Left" Margin="7,123,0,116" Name="label3" Width="73">Add 2 :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="7,0,0,91.04" Name="label4" VerticalAlignment="Bottom" Width="78">Add 3 :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="8,172,0,0" Name="label5" VerticalAlignment="Top" Width="76">Post Code :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="9,196.04,0,0" Name="label6" VerticalAlignment="Top" Width="75">Tele :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="9,0,0,18" Name="label7" VerticalAlignment="Bottom" Width="74">Email :</Label>

</Grid>

</GroupBox>

<GroupBox Header="New Invoice :" Margin="8,301,11,12" Name="ogbxNewInvoice" BorderThickness="3" Foreground="Blue">

<Grid Background="BlanchedAlmond">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0*" />

<ColumnDefinition Width="707*" />

</Grid.ColumnDefinitions>

<TextBox Height="23" HorizontalAlignment="Left" Margin="6,33,0,0" Name="otbxDate1" VerticalAlignment="Top" Width="97" Grid.Column="1" />

<TextBox Height="23" Margin="0,33,128,0" Name="otbxParts1" VerticalAlignment="Top" Grid.Column="1" HorizontalAlignment="Right" Width="461" />

<TextBox Height="23" HorizontalAlignment="Right" Margin="0,33,17,0" Name="otbxAmount1" VerticalAlignment="Top" Width="79" Grid.Column="1" TextChanged="otbxAmount1_TextChanged" TextAlignment="Right" LostFocus="otbxAmount1_LostFocus" />

<Label Height="33" HorizontalAlignment="Right" Margin="0,0,356,0" Name="olbInvoiceDetails" VerticalAlignment="Top" Width="233" FontSize="18" FontWeight="Bold" Foreground="Blue" Grid.Column="1">Invoice Details :</Label>

<TextBox Grid.Column="1" Height="23" Margin="0,58,128,0" Name="otbxParts2" VerticalAlignment="Top" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,84,128,0" Name="otbxParts3" VerticalAlignment="Top" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,110,128,0" Name="otbxParts4" VerticalAlignment="Top" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,136,128,0" Name="otbxParts5" VerticalAlignment="Top" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Margin="0,162,128,174" Name="otbxParts6" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,0,128,148" Name="otbxParts7" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,0,128,122" Name="otbxParts8" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="461" />

<TextBox Grid.Column="1" Height="23" Margin="0,0,128,96" Name="otbxParts9" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="461" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="6,58,0,0" Name="otbxDate2" VerticalAlignment="Top" Width="97" Grid.Column="1" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="6,84,0,0" Name="otbxDate3" VerticalAlignment="Top" Width="97" Grid.Column="1" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="6,110,0,0" Name="otbxDate4" VerticalAlignment="Top" Width="97" Grid.Column="1" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,58,17,0" Name="otbxAmount2" VerticalAlignment="Top" Width="79" TextChanged="otbxAmount2_TextChanged" TextAlignment="Right" LostFocus="otbxAmount2_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,83,17,0" Name="otbxAmount3" VerticalAlignment="Top" Width="79" TextChanged="otbxAmount3_TextChanged" TextAlignment="Right" LostFocus="otbxAmount3_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,60" Name="otbxCost" VerticalAlignment="Bottom" Width="96" TextAlignment="Right" IsReadOnly="True" Background="AliceBlue" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,35" Name="otbxVAT" VerticalAlignment="Bottom" Width="96" TextAlignment="Right" IsReadOnly="True" Background="AliceBlue" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,10" Name="otbxTotal" VerticalAlignment="Bottom" Width="96" TextAlignment="Right" IsReadOnly="True" IsUndoEnabled="True" Background="AliceBlue" />

<Button Height="23" HorizontalAlignment="Left" Margin="8,0,0,96.52" Name="opbClear" VerticalAlignment="Bottom" Width="61" Click="opbClear_Click" Grid.Column="1">Clear</Button>

<Label Height="28" HorizontalAlignment="Left" Margin="8,6.473,0,0" Name="olbDatePeriod" VerticalAlignment="Top" Width="95" Foreground="Blue" Grid.Column="1">Date / Period :</Label>

<Label Grid.Column="1" Height="28" HorizontalAlignment="Right" Margin="0,8,17,0" Name="olbAmountBP" VerticalAlignment="Top" Width="78" Foreground="Blue">Amount £ :</Label>

<Button Grid.Column="1" Height="23" Margin="0,0,502,6" Name="opbSaveToFile" VerticalAlignment="Bottom" Click="opbSaveToFile_Click" Background="LightSalmon" HorizontalAlignment="Right" Width="87">Save to File</Button>

<Button Grid.Column="1" Height="23" Margin="0,0,347,6" Name="opbPrintNewInvoice" VerticalAlignment="Bottom" Click="opbPrintNewInvoice_Click" HorizontalAlignment="Right" Width="119" Background="LightSalmon">Print New Invoice</Button>

<Button Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,217,6" Name="opbCalculate" VerticalAlignment="Bottom" Width="67" Click="opbCalculate_Click" Background="LightGreen">Calculate</Button>

<Label Grid.Column="1" Height="28" HorizontalAlignment="Right" Margin="0,0,119,55" Name="olbCost" VerticalAlignment="Bottom" Width="55" Foreground="Blue">Cost :</Label>

<Label Grid.Column="1" HorizontalAlignment="Right" Margin="0,300.04,123,30" Name="olbVAT" Width="49" Foreground="Blue">V.A.T. :</Label>

<Label Grid.Column="1" Height="28" HorizontalAlignment="Right" Margin="0,0,121,6" Name="olbTotal" VerticalAlignment="Bottom" Width="51" Foreground="Blue">Total :</Label>

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,109,17,0" Name="otbxAmount4" VerticalAlignment="Top" Width="79" TextChanged="otbxAmount4_TextChanged" TextAlignment="Right" LostFocus="otbxAmount4_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,136,17,0" Name="otbxAmount5" VerticalAlignment="Top" Width="79" TextChanged="otbxAmount5_TextChanged" TextAlignment="Right" LostFocus="otbxAmount5_LostFocus" />

<TextBox Grid.Column="1" HorizontalAlignment="Right" Margin="0,162,17,174" Name="otbxAmount6" Width="79" TextChanged="otbxAmount6_TextChanged" TextAlignment="Right" LostFocus="otbxAmount6_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,148" Name="otbxAmount7" VerticalAlignment="Bottom" Width="79" Foreground="Black" TextChanged="otbxAmount7_TextChanged" TextAlignment="Right" LostFocus="otbxAmount7_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,121" Name="otbxAmount8" VerticalAlignment="Bottom" Width="79" TextChanged="otbxAmount8_TextChanged" TextAlignment="Right" LostFocus="otbxAmount8_LostFocus" />

<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Right" Margin="0,0,17,95" Name="otbxAmount9" VerticalAlignment="Bottom" Width="79" TextChanged="otbxAmount9_TextChanged" TextAlignment="Right" LostFocus="otbxAmount9_LostFocus" />

<TextBlock Grid.Column="1" Height="21" Margin="276,9,0,0" Name="otbkInvRef" VerticalAlignment="Top" HorizontalAlignment="Left" Width="60" Text=" 1234" TextAlignment="Center" />

<Button Grid.Column="1" HorizontalAlignment="Left" Margin="8,162,0,0" Name="opbTestFill" Width="61" Height="22.04" VerticalAlignment="Top" Click="opbTestFill_Click">Test Fill</Button>

<Label Grid.Column="1" Height="28" Margin="342,3,245,0" Name="olbInvDate" VerticalAlignment="Top" Foreground="Blue">.. invoice date ..</Label>

<Button Grid.Column="1" Height="23" Margin="0,0,502,62" Name="opbSerialize" VerticalAlignment="Bottom" Background="Gold" Click="opbSerialize_Click" HorizontalAlignment="Right" Width="86" ToolTip="Use this before printing - creates a disk image of an invoice object.">Serialize</Button>

</Grid>

</GroupBox>

<Button Height="23" HorizontalAlignment="Right" Margin="0,272,271,0" Name="opbSettings" VerticalAlignment="Top" Width="79" Click="opbSettings_Click" Background="Thistle">Settings</Button>

</Grid>

</Window>

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

<Window x:Class="BESTinvoicesWPF.CustomerWin"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="WPF : Customer Details : Add, Edit, Remove :" Height="438" Width="414" Name="winCustomer" ResizeMode="NoResize">

<Window.ContextMenu>

<ContextMenu />

</Window.ContextMenu>

<Grid Background="Khaki">

<Label FontSize="12" Height="30" HorizontalAlignment="Left" Margin="11,40,0,0" Name="label1" SnapsToDevicePixels="False" VerticalAlignment="Top" VerticalContentAlignment="Center" Width="78">Company :</Label>

<Label Height="27.04" HorizontalAlignment="Left" Margin="12,69,0,0" Name="label2" VerticalAlignment="Top" Width="76">Contact :</Label>

<Label Height="28.04" HorizontalAlignment="Left" Margin="11,94,0,0" Name="label3" VerticalAlignment="Top" Width="67">Add 1 :</Label>

<Label HorizontalAlignment="Left" Margin="11,120,0,0" Name="label4" Width="78" Height="25" VerticalAlignment="Top">Add 2 :</Label>

<Label HorizontalAlignment="Left" Margin="12,148,0,0" Name="label5" Width="76" Height="28" VerticalAlignment="Top">Add 3 :</Label>

<Label HorizontalAlignment="Left" Margin="13,173,0,0" Name="label6" Width="75" Height="28" VerticalAlignment="Top">Postcode :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="13,198,0,0" Name="label7" VerticalAlignment="Top" Width="74">Phone :</Label>

<TextBox Height="23" Margin="98,45,0,0" Name="textBox1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox1_TextChanged" />

<TextBox Height="23" Margin="98,70,0,0" Name="textBox2" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox2_TextChanged" />

<TextBlock Height="21" Margin="98,19,0,0" Name="textBlock1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="71" Background="AliceBlue" />

<TextBox Height="23" Margin="98,96,0,0" Name="textBox3" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox3_TextChanged" />

<TextBox Height="23" Margin="98,122,0,0" Name="textBox4" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox4_TextChanged" />

<TextBox Margin="98,148,0,0" Name="textBox5" Height="23" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox5_TextChanged" />

<TextBox Height="23" Margin="98,174,0,0" Name="textBox6" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox6_TextChanged" />

<TextBox Height="23" Margin="98,200,0,0" Name="textBox7" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox7_TextChanged" />

<TextBox Height="23" Margin="98,226,0,0" Name="textBox8" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox8_TextChanged" />

<Label HorizontalAlignment="Left" Margin="14,224,0,100" Name="label8" Width="76">Web :</Label>

<TextBox Height="23" Margin="98,251,0,0" Name="textBox9" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox9_TextChanged" />

<TextBox Height="23" Margin="98,277,0,0" Name="textBox10" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox10_TextChanged" />

<TextBox Height="23" Margin="98,303,0,0" Name="textBox11" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox11_TextChanged" />

<Label Height="28" HorizontalAlignment="Left" Margin="14,250,0,0" Name="label9" VerticalAlignment="Top" Width="69">Email :</Label>

<TextBox Height="23" Margin="98,329,0,0" Name="textBox12" VerticalAlignment="Top" HorizontalAlignment="Left" Width="263" TextChanged="textBox12_TextChanged" />

<Label Height="28" HorizontalAlignment="Left" Margin="15,275,0,0" Name="label10" VerticalAlignment="Top" Width="63">Fax :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="15,301,0,0" Name="label11" VerticalAlignment="Top" Width="70">Details 1 :</Label>

<Label Height="28" HorizontalAlignment="Left" Margin="15,327,0,0" Name="label12" VerticalAlignment="Top" Width="76">Details 2 :</Label>

<Button Height="23" HorizontalAlignment="Right" Margin="0,0,12,8" Name="opbSave" VerticalAlignment="Bottom" Width="130" Click="opbSave_Click" Background="LightGreen">Save Changes to File</Button>

<Button Height="23" HorizontalAlignment="Right" Margin="0,12,11,0" Name="opbQuit" VerticalAlignment="Top" Width="60" Background="Red" Click="opbQuit_Click">Quit :</Button>

<Label Height="28" HorizontalAlignment="Left" Margin="12,16,0,0" Name="label13" VerticalAlignment="Top" Width="76">Ref. No. :</Label>

<Button Height="23" HorizontalAlignment="Left" Margin="12,0,0,8" Name="opbClose" VerticalAlignment="Bottom" Width="75" Click="opbClose_Click" Background="Thistle">Close</Button>

</Grid>

</Window>

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

PHH - February 2010.


Using Data Table Objects

Some Amazing Capabilities Before Data Binding is Applied :

To many of us just getting things to work in dotNet can be a struggle in itself. So what I am about

to explain may bring a broad smile to your face ;-)

I am about to show a Form and a WPF window, displaying date via the same 'view' object. When cells

are edited in the form and the row in focus changed, the editing changes are reflected in the data shown

in the DataGrid control on the window.This works in both dircetions

In the image shown below I will be editing the 'ITEMNO' field, for the product "Z5500" - the first of the pair.

I will edit the yellow / beige Form, so this is a DataGridView control in use. The screen behind is the WPF

window, with the DataGrid showing alternate purple / white in colour :-

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

In the image above, notice the pencil icon to the left of the cell being edited. this actually indicates the row that is

being edited, it just happens to be next to the cell in focus for our tests.

The 'xyz' which I have added has not yet shown up in the WPF window, as I have not moved off the row in focus.

However, once I click outside of this row we see this :-

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

And now the changes are shown in both.

If we do this the other way around, editing in a cell in the WPF DataGrid control, then we see the following :-

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

This time the behaviour is slightly different, simply because just moving focus to another application

to capture the screen image, caused the changes to show in the Form control. Obviously the WPF Data

grid is responding to a different event - similar, but different.

So examine this to see what I mean :-

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

So to summarize, we can get an interesting degree of interactivity between a Form and a WPF window even

without any binding in place. As I said, this may be expected by some, but it is great news to many

standard guys like me ;-)

Extra Functionality :

Now, without labouring a point, let me say that the degree of 'cooperation' - if you will allow me to put it like that -

does not stop at editing.

If we click in the header of the WPF grid we set an order for the data rows, and not only do we get the same order

shown in the Form's grid, but the sort icon appears correctly as well - see below for an example :-

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

Here is Barcode set to ascending in the WPF header :-

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

We now have to be careful as to what make of our next test, that of trying to set an order by clicking in the header

of the From grid. It appears that is some ways the WPF grid has a slightly over-riding 'control'. So first I need to

set the sort to 'NULL' in the WPF grid.

See the image below for what happens after the sort order has been removed at the WPF grid :-

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

In the image above, once the 'null' order had been applied / set in the WPF window, then this seemed to free things

up nicely for me to get a positive response while clicking in the header of the Form grid.

The sorting works fine, but this way round the icon is not displayed in the WPF grid header.

Still, considering we have hardly done anything, it is amazing what interoperability, or interactivity, there is.

And we have not finished yet ;O))

Setting the filter conditions as shown below, is reflected immediately in the Form's grid. In the

example shown you can compare the 'ITEMNO' column to see that this is so :-

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

What to do Next :

Fine, we have come a long way to see just how a standard DataTable (familiar to Forms ClickStart readers) can be used

to make a standard DataView object and have this successfully display in both a DataGrid control on a window and a

DataGridView control on a Form.

All functionality seems to be present for both, once we know how to leverage it !!

What we need now is a list control on each GUI screen and us to attempt to bind the 'view' to each. remember the view is

really the wrapped up DataTable object.

Let us change the code so we can see if we can get even more to happen.

PHH - February 2010.


Chapter 8 - Drag 'n' Drop with WPF

In this chapter we will take some time to examine the 'built-in' behaviour of some of the WPF controls,

as far as' Drag and Drop' is concerned.

We will then move on to add some functionality of our own to enhance more complex control in ways

we would wish for our custom applications. This will be mainly centred on list controls - to start with anyhow.

One aspect of interest is how DnD has improved over what was available in Forms 2.0, and how we have

some useful behaviour already there for us just use - right out of the box ;-)

On a more complex note, we do have to deal with events 'bubbling' up through 'levels' in WPF. So I suggest

we start by seeing what we can easily get our hands on in the Drag Drop arena.

Lets get to work ;O))

PHH - February 2010.


the TextBox control

To start this new section we are simply going to see what default functionality is available to us

as developers in a couple of the common controls - those which we may well expect to use with

Drag and Drop (DnD) in many scenarios. I am talking specifically of the TextBox and the RichTextBox

controls from the WPF toolbox.

I am going to assume that you the reader is familiar with general Windows DnD behaviour. And

know how the modifier key ('Ctrl') changes a 'Move' to a 'Copy', when held down at any time during

the drag operation.

Hopefully, you may well have looked at and studied the ClickStart eNotes on DnD too, as they are

a good grounding for what we are about to try in WPF.

Starting a New Solution / Project :

Before we start to explore DnD with the TextBox control, let us make a new VS solution so that

we keep all our DnD development work in one place.

Close the previous work, using 'File' and 'Close Solution'.

Start a new solution and project with 'File', 'New', 'Project' ... as shown below :-

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

Now select Vulcan.NET and WPF Application as shown in the following screen capture :-

Out of general interest, notice that we do not see an option yet for .NET Framework 4.0 even though

we appear to have it on the computer, and are using it for development :-

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

I suspect we just need to wait a few more weeks to have VS2010 (version 10) to try out to see the

full integration, from both Microsoft and Grafxsoft.

Let us now see what we have in the Solution Explorer (SE) pane :-

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

OK, it seems to have gone as we wanted, and note that as we make progress with DnD

we will add further projects to this solution, calling then names like .....

"WPF_DnD_ExampleX" or "WPF_DnD_OwnCode" or similar.

For now we will just use the one we have to start our investigation into what the default behaviour

has to offer us with TextBox and RichTextBox controls.

The Text Box :

To our very simple window let us add a single TextBox control from the toolbox, and place it

within the grid boundaries, using the Designer. You should see something like this :-

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

And the XAML markup the Designer creates for us is like this :-

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

Notice that I am using the Add-In tool called 'MetalScroll' which is shown in place of the vertical scrollbar on the

right hand side of the editor tab panel.

Also notice that I have added a return before the 'Name' so that the script displays easier for us when we

need to show it in these notes.

The default name for the text box has been supplied by the Designer, and we need to change it from

"textBox1" to something more suitable. Let us use the Properties Pane (PP) to do this :-

Before :

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

After :

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

So the Designer has changed the XAML script to :-

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

Also notice that we have no attributes relating to allowing drop to be allowed, and yet this

behaviour would appear to be default.

If we look at the relevant part of the PP when the text box is in focus in the Designer, we

see the following :-

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

If on the other hand we override the default property, and set 'AllowDrop' to false by

unchecking it in the PP we see this :-

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

Now we can learn something new, if we re-check the box in PP we see that the Designer does NOT remove

the attribute entry in the XAML script, but sets it to true, see below :-

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

I think you will find that if you edit the XAML markup so that the text string for the attribute is removed, then

this will return to the original situation, where the default of 'true' is taken or implied.

You make like to try this for yourself, I have just tried it and it works as expected.

Oh! and if you really want to feel in control of things, YES! I did add an extra carriage return before the 'AllowDrop'

red text in the editor, so that it would display nicely for us.

Time to Test :

[ *** CAUTION *** Notepad does not support either Drag or Drop, so do NOT use it for testing ! ]

Fine, lets now build and run our little app, and see how our text box behaves, remember after

all our changing of properties etc., we need to test the condition where we have the default of

'AllowDrop' is true.

Below are some images from a couple of tests using WordPad in Windows 7 (W7) :-

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

Notice the mouse cursor icon shows we have not got the modifier (Ctrl) key down at point of release

of the mouse button, so we get the following result :-

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

The highlighted text has been 'Moved' from WordPad to our text box on our little app.

Or, more correctly, it has been copied, and then removed (deleted) from the original or source control.

Try this for yourself, to see that when we hold down 'Ctrl' we get a copy. Below is a suitable image but I

have done a little screen MAGIC to make it appear like this :-

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

Notice the shape of the muse cursor icon, it has a plus sign showing a copy, and not a move as

previously.

Okay then, we have tested out the behaviour of our WPF TextBox control as far as the 'Drop' property

is concerned, we now need to know just how it behaves when we try to use it as the source of a

Drag Drop operation. So how does is 'Drag' ?

Drag Testing :

For this next test cycle, I have changed the background colour of the text box to 'Beige' and also

the 'Tile' property of the WPF window to something more suitable.

I will enter into the text box the string "Now testing drag!"

So the image below shows how we look before the test starts :-

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

I will now try to highlight the newly entered string and drag it to the WordPad work area, with the modifier

key held down, hoping to get a copy of the string.

Lets see what happens :-

At the source we are informed by the mouse cursor icon that a copy is initiated.

Now lets examine the target when we hover with the mouse cursor :-

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

The cursor icon over the target also confirms a copy will take place - in other words NOT removing the

source text.

And now what do we get when I release the left mouse button ?

Lets see :-

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

Well, we seem to have achieved a copy as we required. And as indicated by the cursor shapes prior to release

of the mouse button.

Let us now clear the WordPad content and try once more with the modifier key un-pressed, so we are

attempting a 'Move'.

This is what happens :-

At the source we see the cursor indicate a 'Move' will take place, as the cursor icon has no positive

('+') sign in it.

But what happens when we get to the target and hover, still not touching the modifier key ('Ctrl') :-

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

It would appear that the WordPad in-built functionality has changed this and now a 'Copy' is

being indicated.

So what actually happens ? Lets see shall we :-

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

So our tests show that we can certainly drag from a TextBox control in WPF, without needing to do

anything much it would seem.

But that we may need to be careful as to what we think is going on because Drag Drop is a 'contract'

between two objects, or entities, and overall behaviour of the DnD action may be changed by on of those

in the 'contract' when we may expect different behaviour.

It would appear as if WordPad is programmed NOT to delete data from the source, whatever it is.

Checking Against Itself :

We may get a better idea of just how the WPF TextBox control functions under DnD situations if we

use two instances of our new app.

In the image below I have added a second copy of the app by using Windows Explorer (WE) to find the

compiled EXE file for our app, and then double clicking it to carry out a 'Shell Execute'.

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

See the results :-

So let us now test what happens when I try to highlight the text in the top instance of our app, and

then 'Move' it to the second instance below it. (No modifier key is being pressed!)

Here is what we get :-

It appears as if the source control is expecting a 'Move' to take place, judging by the cursor icon.

Lets now move the cursor so it hovers over the target control :-

This too would indicate that a 'Move' is to take place.

Let us see what actually happens when we release the left mouse button :-

Yep! just as I expected it to, the 'Move' has taken place. This is what I would call genuine

DnD behaviour - it has not been changed by code unseen to the user.

Testing the 'Copy' with our Double Test Arrangement :

OK then, if the 'Move' is genuine, and as we may well wish it to be, we had better test to see

that the 'Copy' is as we would like it to be as well.

Since our two instances are equal, or the same, we can now try the 'Copy' from the bottom

instance to the top one :-

So far the source control and functionality would appear to suggest a 'Copy' will take place.

Lets move the cursor to hover over the target (remember the target is now the top instance!) :-

Yes, it would appear that the target control and functionality also thinks we will get a 'Copy'.

So what does happen? Lets see :-

Well, it would seem we have as we expected, a 'Copy' indicated by both the signs of the source

and target controls, and a result to match this also.

Anyone coming from the world of Forms 2.0 programming will realise that this is a big improvement

to the situation we had there, when we needed to add our own functionality for DnD when it seemed

to most of us that is was a basic requirement of the TextBox control.

I for one am delighted in this improved functionality, and will use it more in my apps.

*** Note ***

Just in case you forget, we need to remind ourselves that by default the TextBox control has a basic

context menu, as shown below :-

So in fact we can copy and paste to achieve the same results as the recent DnD operation.

Here is the second context menu just before pasting :-

Take it from me that the paste action worked 100% and we hade text showing in both controls.

Two Controls in One App. :

Fine, we are making progress, so let us move on. Let us try to have two TextBox controls

in the one app, and see how that works.

Although we have not tested yet what changes when we set 'AllowDrop' to false and also

set the 'ReadOnly' property to true, we will move on to having two TextBox controls in the

one simple app.

In fact, with two controls in one app we can probably do these further tests more easily. So

here we go.

Close down all the test simple apps, and go to the Designer in VS2008, and add a second

TextBox control.

Change the name in the PP to 'otbDnDsecond' and also the background colour to 'Bisque'.

Leave the 'AllowDrop as the default value of 'true'.

You should have something like this :-

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

Now build and run the app, and resize the window to suit the situation.

This is what I have here :-

Place the cursor with the mouse in the top control, and at the keyboard enter the text shown below.

In the image the entered text has been highlighted and the modifier key pressed as a drag operation

is being carried out with the lower control as the target :-

I suppose by now it does not surprise you to hear that it copies the text just fine.

Try it for yourself to check this out.

So we have 'standard' DnD behaviour between apps and within the same app. Very neat !

Let us go on to complete our behaviour tests.

Functionality when 'AllowDrop' is 'False' :

Since we now have two controls to be able to use for testing, we can change the properties of

the top one while using the lower, or bottom one, as it is now, with full functionality in place.

Close down any running apps and go to the Designer in VS.

Click on the upper TextBox control to give it focus.

In the PP uncheck the 'AllowDrop' property.

Build and run. Resize to suit.

Type in the text as shown, and try a copy or move to the top control.

Look at what I get here :-

No matter what I do with the modifier key (it goes plus and no plus in the source control),

the mouse cursor icon in the target control shows the 'No Entry' symbol. As expected no doubt.

However, if we use the context menu we can cut and paste the text so we start like this :-

Now, with the 'AllowDrop' property set as it was (to false), let us see if we can still drag the content

of the control to the lower one - which still has all its DnD functionality :-

Here is the result of a 'Copy' :-

And also now a 'Move' :-

Yes we can still drag, and also copy and paste with the clipboard and the context menu.

Oh! as well as the keys for cut, copy and paste as well ;-)

Let us move on to test what we get when the 'ReadOnly' property is changed.

Functionality when control is Read Only :

Close down all instances of the running app.

In the Designer, go to the PP and change the 'AllowDrop' property back to 'true' for the upper control.

Now find the 'IsReadOnly' property near the bottom of the PP list and check this as shown below :-

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

Change the 'Text' property in the PP to say "this is Read Only now !!"

You should see this :-

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

Build and run the app. - resize to suit.

We should both see this below :-

Enter some text in the lower control and try to drag it to the top one :-

Try to copy and paste the text to the top control - you will now see that the context menu

will not display, so the Cut,Copy and Paste options don't even show. Greyed out or otherwise.

[*** Note *** this is not the case if we highlight text in the top control, as a copy to clipboard

is still possible. Check the context menu behaviour for yourself. ]

Now then, it is not surprising to see that a control set to RO does not allow text changes, in any

way. But what will happen when we try to use it as a source for a drag action / operation ?

Lets see :-

Yes, this looks promising, a copy operation is being attempted.

That is fine, and is as we may well expect. However, what happens when we try a drag with 'Move' ?

That means not pressing the modifier key ('Ctrl') - let see :-

Well, the cursor icon is telling us that we will get a copy and not a move. So functionality is being

true to the property settings.

No text in the upper control should be able to be changed by DnD or cut and Paste - but, we may use

the text as a data source. Data being text in this case.

And for the sake of completion, here is the result :-

I think this just about wraps up the TextBox as far as default and in-built DnD behaviour is concerned.

Let us now move on to the next section where we examine the RichTextBox control in some details also.

PHH - February 2010.


the RichTextBox control

RichTextBox control

Let us continue with the app we were developing in the last subsection.

After the two text box controls, let us add a new RichTextBox just below the second text box.

You should get something like that shown below :-

Now, in the PP change the name of this new control to "ortbDnDtop", and check that the XAML script looks

like this below :- (notice the 'AllowDrop' prpoerty too )

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

Once again, note that although the AllowDrop property is set 'true' it is not shown as this is the default

setting and it has not been changed in the PP from a user set value of 'false'.

To make things easier to see let us set its background colour to something nice and restful, like 'Cornsilk'.

Now we can build and run the app, it looks like this :-

Testing for Default Functionality :

Ok then, lets get testing.

Remember the top text box is R/O, but can be copied.

So after highlighting all the text string, attempt to drag it into the new RTB control. You should see

something like this :-

And surprisingly, I am not holding down the moderator key (Ctrl), so lets see if the suggested 'Copy'

action is what follows :-

[ *** Note *** if the top control is R/O a 'Move' can't be completed as this would be against the property

setting. ]

So what do we get if we attempt to hold down the modifier key ? Lets see shall we :-

Yes, we again get the cursor icon showing a 'Copy', but that is not so surprising since I am holding down

the Ctrl key.

So what will we get ? Well first look at the position of the line cursor and then see below :-

We seem to be getting somewhere, but we can keep going, as we ought to be able to drag the text string

from the lower control (RTB) into the middle one, a text box.

Lets start by trying a 'Move' - that is no modifier key. Here is what happens :-

Part way through the action the cursor icon is suggesting we will get a 'Move' and not a 'Copy' as the

cursor icon has no plus (positive) sign showing.

Lets complete the action :-

Yep, the whole lot has moved into the middle control.

Problem now is that the text box can't show it all - well unless we enlarge the window that is ;-)

Finally, lets try to copy half the middle text string into the RTB control twice, adding a

return character manually, after the first Drag Drop :-

[ *** note *** the words above do NOT mean that the middle and bottom controls are R/O ....

..... they are just text from text from the first control which is R/O !!! ]

Okay, but for some reason it looks as if we have an extra line between the two entries,

which is not how it felt or looked while doing the action.

We need to check some of the other property settings for the RTB just to see what is default.

Certainly the Drag Drop works fine. But to be really professional about it we ought to try a third party tool,

so lets use the WordPad applet once more (Windows 7 variety).

Yes, the 'Copy' icon is because I held down 'Ctrl'.

And the overall result is this :-

As you can see there is not another line between them so we must have a sizing effect.

To prove this I have used the designer to change control and window sizes slightly, and now

I will try to copy the WordPad contents into the RTB - here goes :-

And releasing the left mouse button we get this :-

So it would look as if both the Drag Drop functionality is as we would expect when using WordPad,

and also that the apparent extra line in the RTB was a result of sizing and content etc. Nothing to

strange or weird.

One thing to remember when testing with WordPad (and we saw this in the TextBox section previously)

is that it does not remove or delete the source data even if the modifier key position is asking for a 'Move.

This is obviously how the WordPad app is setup to work.

Multiple Instances of Our App. :

Let us use multiple instances of our app as it stands, to test the RTB control, to see that it will behave

in a more 'complete' way than the WordPad control.

Let us see if we can move the complete text string from one RTB instance to the other.

Let start the action :-

The cursor icon would suggest that we will get a move, as no plus sign is shown.

Completing this action we get :-

Yep! we have a 'Move'. So the RTB control acts in a way I would describe as "normally

expected DnD behaviour for Windows".

Finally, to finish this subsection, let me remind you that we have not actually written any code, just

checked out what is there for us to use.

Obviously, if we change the 'IsReadOnly' property then we will change the way in which the RTB

behaves with respect to Drag Drop, but this will simply be in-line with what we saw when we experimented

in the last subsection with this property on a text box.

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

Simply, no matter how well or normal the DnD behaviour is, the R/O behaviour of the control will NOT be

compromised. Its behaviour will be such that the text content is protected.

Before we leave this topic, try dragging an image file into the RTB, or even using copy and paste.

*** note *** in Forms, the RTB control could accept a paste after the screen was captures with Shift + Print Screen.

You may like to try this to surprise yourself ;O))

The XAML script looks likes this after my amendments :-

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

Check this out :-

All I have done is change the properties to allow scroll bars both vertically and horizontally.

Notice how the screen image is chopped at the right hand side.

But NOT in the Forms equivalent! - below I am using the 'ClickPad' applet from the F2D eNotes :-

Notice the position and length of the sliders, we have a full three screen image in the RTB of the

Forms applet. It does not get chopped in any way.

Now lets move on to the next subsection to look at Label and TextBlock controls.

See you there ;-)

PHH - February 2010.


the Label control

The reason we are investigating Drag Drop for both a Label and a TextBlcok control is because in the

PP there is a property named 'AllowDrop'.

If such a property exists then the controls themselves much be DnD aware.

I note however that the default setting for this property, both these controls, is not 'True' as it was

for the previous TextBox and RTB controls. This suggests to me that we may have to do some extra work

in order to get DnD to work for us.

Let us start by creating a new project and doing some testing. We are getting good at testing ;-)

In the same solution as we have been using, select the solution element in the SE with a single left click

of the mouse, and use the menus to add a new project, as shown below :-

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

When the 'Add new Project' dialog appears select the Vulcan.NET project type,

and the WPF Application as a template, as well as supplying the name as shown below :-

If you are successful this should make the SE pane look like this :-

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

Now all we need do is right click in the new project element itself (second line of the SE) and from the

context menu choose to make the new project the 'StartUp Project' as shown below :-

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

After clicking with the mouse in this menu option the bold style in the SE text should move to

the new project as shown below :-

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

Okay then, lets build and run just to see we are getting somewhere. Mine works at this end of

things, hope yours does too.

Let us use the designer to change the Title property of the window from 'Window1', even though the

name of the object will remain unchanged - at least it looks better.

This is what you have before making changes :-

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

Change the title to "Drag Drop : Label & TextBlock : " and save, build and run.

After a slight amount of resizing you will see this below :-

Checking the XAML script the Designer has created, we see there is not a lot there at the moment.

Only the window and an empty grid control as seen below :-

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

Note that although the window has a title, the Designer has not provided it with a default 'Name' property.

The Grid control also has not been provided with a 'Name' property.

To gain some useful experience, let us use the PP to help use give both these objects a name, ones we

could use in our Vulcan code afterwards if we chose to.

It may help us if we open the Document Outline (DO) pane by selecting from the top menu 'View, Other

Windows, Document Outline'. The keyboard shortcut is 'Ctrl+Alt+T'.

Size the DO to suit your screen, and from it select the 'Window' element so that on the Designer

surface we get the window brought into focus. You should see something like this :-

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

Now use the PP to input a name for the window - I have chosen "owinDnDLabelTextBlock" for mine.

See below to find that this appears in the XAML script as soon as enter is pressed, or the focus moves

off the input box in the PP area.

I now have this :-

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

[ *** Notice *** I have tidied the XAML text in the editor by adding a few return characters, so we can

display it better for our purposes of reading these notes. ]

Fine, we have named our window, so why not do something similar for the grid control ?

Naming the Grid :

Go to the DO and select the 'Grid' element with the mouse.

This will bring into focus the correct element on the Designer surface, and even the XAML scrip shows

the focus control by changing the highlighting - see below :-

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

Notice that the name is still missing, or empty, in the PP input box, so let us now provide a

suitable name for the grid.

I suggest "ogrdLnTBK" ... for object grid, Label and Text Block. Well, if you don't like it then

choose your own ;-)

After providing the name and moving the input focus you should see something like that

shown below, although I have move panes and resized things to make a smaller and more

compact image to display in the notes :-

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

Okay, after all this messing in the IDE we better see if we can still build and run the app ;-)

Here goes .....

Yep! working OK and I have also resized it a little to make a suitable image.

Adding Some Controls :

It is now time to add some controls to our WPF window, so lets start with a Label.

From the toolbox select a Label and place it on the Designer surface near the top left as

shown below :-

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

We need to give this control a name right away, as we will probably need to use it in our Vulcan

code when later we add DnD functionality to suit our needs / purposes.

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

Change this to "olbDnDfirst" and after focus change many places will reflect the name change made.

See below for how it looks here :-

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

You will notice that the 'Content' property is still 'Label' and this is what we may wish to change

when we get Drop to work for us.

For now change this to " .. content will change ! .." and then change the background colour to something

easier for us to see against the window, choose 'Beige'.

You can actually just type in the text input 'Beige' in place of selecting from the drop list.

Close any open instances of the app, then build and run.

After a slight resizing I get the following :-

What of Drag Drop Capability ? :

Let us now focus our attention on the XAML script we have created by the Designer, and also what

properties and settings are shown in the PP, these will be mostly default settings as we have changed

very little so far.

Below is what I have in the developing app we are building :-

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

It would appear for the settings I see here, that we have a property called 'AllowDrop' which would

suggest to me that the control is going to be DnD aware, but also that the default is 'False', unlike

the TextBox and RTB controls.

This probably means that not all DnD functionality is in-built as we might like. We will see in time.

The first thing we need to do is check the property and do some tests to see what the default DnD

functionality is.

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

Close all instances of our app, then build and run.

Mine at this end looks like this :-

Ok, you are going to say that it does not look any different to before, well we shall see when

we start to do some tests.

In order to have some text data to drag onto the label control, we need to open WordPad

and type in some suitable text, see below :-

In the above image you will notice that the mouse cursor icon show (correctly) that there

could be a 'Move' DnD action pending (likely to happen).

So what happens when I try to complete the DnD action by releasing the left mouse button ?

Look at the image below to get some clues :-

The image above shows that we had both something and nothing happen as a consequence of

our attempted DnD action.

The fact that the highlighted content was removed from the source suggests that there is 'contact activity'

taking place.

remember when we have a DnD action, we have a source and a target control, and these enter into a

'contract' when DnD is to take place. There is more of this in the ClickStartDnD eNotes if you are interested.

However, from a simple observational point of view, it appears as if the DnD is trying hard and not quite doing

what we want - it looks as if we just need to add a little bit mode functionality by including our own Vulcan code.

Before we do however, lets finish out testing for the present, by .....

Checking the 'Copy' Mode :

We need to find out what happens when we try to 'Copy' some text from the WordPad text area, onto

the Label surface - at the same time as holding the modifier key (Ctrl) down.

here is what happens at the moment :-

It would appear that the DnD contract system knows we wish to have a 'Copy' action carried out,

but what happens when I release the mouse button ?

See below for the results :-

In the above image we see that effectively a 'Move' has been completed at the WordPad end of the contract, and

not a 'Copy' as required. Okay then, we expected nothing to happen visibly with the label, as it do not previously.

This behaviour suggest we need to make something suitable happen at the label end of things - like changing

the 'content' property with the dragged text, as well as then sending the correct message back to the source to

let it know whether to remove the highlighted text or not.

So we have at least two things to do to make things work as we may wish.

Let us start with the first one, placing the dragged text onto the label as content. We can worry about other things

afterwards.

So we need to find one or more events to help us in our quest.

Which Events can help us ? :

Lets keep it simple, and try the Drop event, which can be found in the yellow lightning icon list in PP.

If you did any Forms DnD you will remember this is the event which fires when the user releases a dragged

item over the target control. Obviously the 'AllowDrop' property needs to be set true for any control which

can receive dragged data.

lets look at what we see in the event list when the Label control is in focus on the Designer surface :-

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

[ Make sure the label control is in focus on the Designer surface. ]

If we now double click in the input box on the right of the property name values, we

get a default name as shown :-

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

So the XAML script now looks like this for the Label control :-

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

Oh! I forgot to say that I have made the label wider for testing purposes, it now looks like this :-

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

As a consequence of selecting the default Drop event handler association above, we get some Vulcan code

appearing in the 'Code Behind', as a stub method - see below :-

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

Notice line 28, that was put there by me so that I could write some Vulcancode of my own in my PRG

file I call "WinInFront".

In keeping with earlier section of these notes, and the earlier F2D eNotes on Forms, I keep my developer

code quite separate from the designer code as possible. They have the Code Behind and I have the Code InFront.

So what does the code look like in my manually created method "DoDrop" ?

Lets have a look at what we have as a start :-

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

This is simply adapted from my notes on DnD in ClickStart.

We can use the objects passed as parameters from the stub method in the code behind, to pick

up the data - see line 44 for this.

Now we need to see what happens when we test this added code :-

And now what is the result when we release the mouse button ?

See below to find out :-

Well, first we have some good news, we have transferred the string data from the source onto the target

label as the new 'content' property.

What I think we do need to do, certainly as a start, is make sure we don't remove of delete the source data.

We can actually do this quite easily, and in one line of code.

Below is the new 'DoDrop' method code :-

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

Notice that line 43 has been added and provides the probably more suitable 'Copy' effect back to the

calling object 'e'. This is used by the DnD system in its handling of the contract.

So basically the source control is told to finish the DnD operation, or Drag Drop contract, by performing

a 'Copy' and therefore not removing / deleting the original highlighted data.

Lets give this a go to see it works as expected - even with no modifier key pressed, so the mouse

cursor icon would still have us believe we are to get a 'Move'. Lets see :-

Notice the cursor icon.

And now the results of the action :-

Well, we have been able to control the fact that we wish to leave the source 'untouched' as

it were, and only copy it to the label control.

Finishing the Job - Responding to the Modifier Key :

Now that we are starting to get somewhere with adding our own code, and consequently changing the

behaviour of the DnD actions taking place, we need to see if we can respond correctly to the 'Ctrl' or

modifier key, as it is called (termed).

Below are details of what I have got to work here this morning, and they have just been copied from

my earlier ClickStartDnD notes and altered to take account of the changes Microsoft have made when

implementing the functionality in WPF classes.

First let us see how we need to change the 'DoDrop' method :-

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

In the code shown above we have changed line 43 to call a new method in order to set the 'Effects'

property of the calling DragEventArgs object.

Look at how the single line has been improved to handle a few possible situations. This is very much

like the Forms code, but enumerations have changed a little, but enough to stop the Forms code from

working as it was ;O))

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

The code above deals with the modifier key ('Ctrl' or Control key) being pressed down, or not - as well as

the situation in which the data type available for Drag Drop is not suitable.

By not suitable, in the case in question, we mean that it is not string data, or text. So we need to do nothing

when this is the case.

Obviously, for other controls we may like to change this 'data type test' to check out some other value, we

will see this later in the notes.

In the code above, the DragDropKeyStates and DragDropEffects enumerations are part of dotNet 4.0, and the

following is in the new SDK documentation :-

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

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

Anyone having looked at the Forms notes on DnD will remember that we needed to create

our own 'KeyState' enumeration previously, but this is just another example of how 'things' have been

tidied up in WPF by Microsoft developers. Well done indeed ;-)

I think all that many of you may wonder over, is the meaning of line 54. Well if you don't know anything

much about bit and byte operations, Anding and Oring and the like - just get a feel for what it is doing, and don't

bother too much about how it is doing it.

It reads as this, take the 'bit pattern' supplied by the event args object and 'AND' it with the known bit

pattern for the control key, and if the result is the same as the pattern for the control key, then that pattern

supplied must be a control pattern - so the control key is being pressed.

Enough of bits and bytes, does it work ? ;O))

Lets do some testing :- [ starting a move - see cursor icon ]

And now the result of this move :-

So far so good ;-)

And now lets try a 'Copy' :-

The icon looks promising, what of the result ?

Here we have it :-

Well, all looks good to me.

I will leave it up to you to retry the 'AllowDrop' property set to false, but we might as well

try together the occasion when an incorrect object is dragged.

Let us try and set up a situation in which the third part of our If / Else / Else structure comes into play.

Handling Wrong Data Types :

I have just realised that our adapted code from the Forms DnD does not correctly handle the case in

which we have the wrong data type for dropping on the target control.

Below is what is required, lets see the newer version of 'DoDrop' :-

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

We are now only attempting to change the label content to that from the dragged data, when the

result of the 'SetDropEffect' method is to set something which is NOT 'none'.

If we have a data type mismatch we now change the label content to tell us so - but this would

not be the case in a real live situation I should think ;-)

Here is how it looks in operation, and we will use Windows Explorer (WE) to drag a JPEG file onto

our target label control :-

[ Yes, the eagle eyed amongst you will see that the cursor icon needs to be changed if the ]

[ data type is incorrect - we will deal with this later, in the coming 'ListBox' section. ]

[ Can't do everything at once ! ]

And the result of this attempted 'Move' action is :-

We seem to have come a long way, and need to take a break soon, and think of where we

are headed next.

I may well be that we do the same drop stuff for the TextBlock control.

First we must complete something, see below.

Checking Functionality with 'AllowDrop' set False :

We have forgotten to try and test the case when the 'AllowDrop' property is set to 'false'.

Below is a test using WordPad :-

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

As you can see the visual feed-back information is that no contact is to be made between the chosen

source (WordPad) and the target over which the mouse cursor hovers (the Label control).

When the left mouse button is released nothing happens, the original highlight text in WordPad is simply left highlighted.

Time to move on, lets go and try to make things happen for the TextBlock control.

See you in the next subsection.

PHH - February 2010.


the TextBlock control

PHH - February 2010.


the ListBox control - simple data

Hi guys, so far we are doing OK, nothing too complicated or complex yet - let's keep it that way ;-)

Now let us focus our attention on the ListBox control, and develop our DnD skills to the full.

The ListBox control is a very common control in modern GUIs and so we need to be able to gain

full control over how we code it in Vulcan.

Also we need to be able to show unsuitable data types, for dropping into the list, by changing the

mouse cursor icon when we 'drag enter' the target control.

However, let us make a simple start, using simple data types. Once we start to find our feet we

can develop more comprehensive skills with this control.

Making a Start :

Let us attempt to make a simple list of string data types - we will use person names - and then try

to drag a new name into the list from another source, either external like WordPad, or internal like

a TextBox control.

We will make a list of "Tom, Dick, and Harry" and then to this list we will add "Mary" by dragging a

suitable string from a drag source.

Let us start with a new project.

Right click the mouse while the pointer is in the solution element of the SE pane :-

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

Now in the 'Add New Project' dialog enter the project name as show below :-

Your SE (Solution Explorer) pane should now look like this :-

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

We need to make the new project the startup project, so right click the mouse while the pointer is

in the new project element in the SE - that of "WPF_DnD_ListBox01".

See below for how it should look :-

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

And if you have been successful the text style will go to that of 'Bold'.

See below :-

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

Before we try to build and run the new app, let us add a ListBox control from the toolbox.

My Designer surface looks like this :-

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

We now need to change the 'Title' property of the window to something more suitable than 'Window1',

so use the PP to enter "Drag Drop : ListBox - Simple : ".

[ Make sure the window object is in focus, if in doubt use the Document Outline (DO) pane. ]

Then also, while in the PP, give the window object a name, something like "winDnDListBox01".

Here is what I have in my VS solution / project :-

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

Set the focus back to the ListBox control and provide it with a suitable name also.

See below for the name I chose, as well as the background colour :-

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

My Designer surface now looks like this :-

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

So let us now try our first build and run. I tend to use the 'Ctrl'+F5 option, in place of the Debug one.

After a little bit of resizing here is what we have :-

Good, successful, but not really very useful or exciting ;-)

So lets keep going.

Before we move on, the XAML script which has been created by the Designer should be like this

but after some slight tidying to improve display in these notes ;-)

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

Adding Some Names to the List :

Okay then, lets call on Tom, Dick and Harry.

Close any open instance of the app.

Before we start to add Vulcan code of our own, we need a new PRG file in which to put

our 'InFront' code. Remember, the code made by the Designer is called 'Code Behind' and we

need to keep our code quite separate from this.

Add a new item to our project element in the SE pane as shown below :-

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

Then choose a code file and a suitable name as shown below :-

The result looks like this in the SE pane :-

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

I then copy code lines from the code behind PRG called "Window1.xaml.prg" and edit it to look

something like this below :-

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

Finally, before we move on to more exiting and interesting 'things', remember to ad an extra line in the PRG

file for the code behind, so that our code is used at startup time.

Like so :-

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

In the image above, line 25 is what has been added by me.

This will then run any code we place in our Initialize method at run time, when the window is opened.

Now we can get back to making our short list of names.

For the moment we can do this in our Initialize() method as shown below :-

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

You can see above the results of building and running the app as it now is.

Making the 'ListBox' Control Drag Drop Aware :

In the WPF Designer, make the ListBox control the focus, and then in the PP find the 'AllowDrop'

property input check box.

Set this to 'true' by changing the default setting of 'false'.

Build and run the app.

Open an instance of WordPad and resize to something very small, add "Mary" as a text string to it.

Now highlight the text 'Mary' and drag it into the list box as shown below :-

Notice that our new list box seems to be DnD aware, and as previously, when we complete the DnD

action, the source text disappears and nothing happens in the target control.

We do not yet get a new list item.

It would appear that we have to add the Drop functionality ourselves, in our own code.

So, let us do that now.

Close all open instances of your app.

In order to make our ListBox control respond to dropping dragged data, we need to select and use the 'Drop' event.

So in the Designer, make sure the list box is in focus, and then use the PP to choose the yellow lightning icon list,

and from the list find the 'Drop' event.

You should see something like the image below, once you have double clicked in the input box of the drop list :-

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

Now go to the code behind file and change the stub method created by the Designer to look like that below :-

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

Notice the 'DoDropToList01' method, this has yet to be written. However, we have 'passed through' the parameters

so that we have full use of them in our own new method if we so wish.

Fine, now we come to the easy bit - or VERY easy bit!

All we need do is copy and paste the details from the 'DoDrop' method we developed in the Label section,

into our new code file, and also that for the 'SetDropEffects' method.

These now look like the image below, after making a few minor changes :-

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

Notice that line 44 now outputs to a message box instead of 'writing' to the label content.

Also we have used the code which will handle the wrong data type correctly ;-)

Now lets see what the 'SetDropEffects' method looks like :-

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

Yes, life gets even easier, as this is just exactly the same as we had for the Label control.

So from now on we can use it 'as is', and we do not need to show the coding. The code is independent of the

control being used, it is more to do with the properties of the Drag Drop operation itself.

Okay then lets do some testing on our list box app.

Let us first try to do a 'Copy', see below for the results :-

Part way through the Drag Drop operation, when the source has been identified, and a possible target is being

hovered over (DragOver), the mouse cursor icon seems correct.

Now lets see what we get when the DnD contact is completed, or fulfilled :-

Yep! we have what we require, a new list item called 'Mary' and the source data left as it was - so 'Copy' has

been achieved.

Look at the image below which shows both the result of a 'Move' action, where the source is removed, as well

as the results of my trying to drag a Jpeg file 'format' object using WE :-

As you can see, because the data type of the dragged object was not a simple string type, the appropriate

message is displayed when the 'Effects' property has been set to DragDropEffects.None by the

'SetDropEffects' method.

Where Now ? :

Well, stand back and think.

So far we can 'Drop' but not drag. Also we could possibly try to stop multiple dropping of the same item.

This could prove useful in the future, and stop this happening :-

Finally, for this short subsection, we also need to find out how to Drag Drop complex objects.

Suppose we have a list of 'PersonCS' objects like those used in earlier chapters - the data binding chapter 7.

I suggest we go a small step at a time and now try to add the code and functionality to allow us to drag a list

item from the list to another drop target, like WordPad or indeed an added TextBox control on our WPF window.

Once we can do a drag, and respond to 'Move' and 'Copy', then we can discuss and learn to handle the more

complex type of objects.

Adding the Drag Functionality :

Okay then lets move ahead and add the other part of the DnD functionality to our ListBox control.

We need to provide our own code so that we can drag items to another target, most likely another list box

related to the GUI task in hand.

To get us going, let us look at some success, so we know where we are headed ;-)

Below is our 'improved' app with two instances on the desktop. I am in the middle of a drag from

the bottom instance, into the list box of the top instance, when the screen shot was captured.

Note I have the modifier key pressed to force a 'Copy' :-

And now below is shown the result of the Drag Drop operation, when the drop was completed within

the list box boundary of the top (target) instance.

Notice the information in the title bar of each window :-

In the above image you can see that we now have two items in our list named 'Harry'.

The appropriate title bar shows what has just taken place.

And what do we get if we try a 'Move' ? See below :-

The image above shows the results of me dragging item 'Dick' and dropping it into the

top instance list box.

The title bar information agrees with this assessment, and you can take my word that the

mouse cursor icon showed the correct icon image - that without a plus (+) sign.

Testing with WordPad :

You may also be interested to see what happens when we try to move the list items and the drop target

is WordPad and not another instance of the same ListBox in another app :-

Although the image captured is me copying item 'Dick', you will have to believe me that I started trying

to move the whole list, item by item, just to find that WordPad is not sending a 'Move' effect message back

to the source control - it must only do 'Copy' for any Drag Drop operation.

In fact this has prompted my to try an experiment with simply two instances of WordPad in a test.

Below I am trying to find out what happens when I try to drag and move the highlighted text 'Tom' to the lower

instance :-

Whilst doing this I found out that however I press the modifier key, or not, the mouse cursor icon shows a

positive sign, and so it is not surprising that we always get a copy and NOT a move!

So for our test with the ListBox app we can't empty the list by successive drag operations.

This is VERY useful information to have and know, whilst testing Drag Drop functionality.

Using 'ClickPad' for Testing :

So then if we can't use WordPad (W7 flavour) then I will use my 'ClickPad' applet that was developed for the

F2D notes - while doing Forms research and development.

Below is the same test repeated with ClickPad :-

Note this time I made some space in the ClickPad work area by adding some carriage returns, then

dragged each item from the list box to the work area - without pressing the modifier key.

I was actually quite interested to see what would happen when the list was emptied, and even tried a

further drag when the list was already empty.

As you can see we did not get any runtime errors ;-)

[ I tried the reverse operation ClickPad to our app. just to find that drag does not work with our app, but does to WordPad

so just keep this in mind. It may have to do with how the data is transferred - maybe by the Clipboard, time will tell.

Also more may become obvious when we study our code for the TextBox dragging operation. ]

What Has Been Done in Code ? :

First of all we need to realise that although we only needed to use one event while doing the Drop part

of the DnD functionality, we need three to take care correctly of the Drag part of it.

Below is the code behind, as it now is for the full working applet. Notice the three extra event handlers to

go along with the associations which have been created by the WPF designer.

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

The XAML script for the working Darg Drop app is shown below, note particuarly the lower section (separated by an empty line)

as contains all the attributes / settings for the DnD :-

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

In the image above the four event handler associations are created from the bottom four lines - ignoring just tag lines.

You will see the we have chosen to use 'DragEnter', 'PreviewMouseUp', and 'PreviewMouseDown'.

We shall not get into discussions here as to when to use 'Bubbling' events as opposed to 'Tunnelling' events.

All event names with 'Preview' at the start of the name, are 'Tunnelling' events. See another section on such

matters - in these eNotes.

The Extra Complexity - What is Required ? :

Although this chapter and section is about Drag Drop, we need to handle many other issues at the same time

when we pick a really meaningful example.

The ListBox drag and drop example being studied here has many complexities which must be dealt with. And

quite honestly in my opinion, if you can get your head around DnD for a full list box example, then you are very

likely to be able to handle - and created code for - any other type of DnD situation which arises.

We will deal in this example app, with copying and moving list items from one list to another, as well as allowing or

disallowing dragging within the same control (source and target are the same object).

We will also discuss whether to allow (and how) multiple copies of items entered into the list by a drag.

One possible problem with lists is that we usually expect to be able to select an item with a mouse click, but

also expect to drag with a mouse click and movement too. This just makes life more interesting ;O))

Also, we need to improve our testing abilities to check we don't get unhandled errors when source and target lists

are empty, and other such situations. remember if you can get DnD functionality across app instances, you can

handle it between similar controls in the one app. This is where testing techniques come into play.

Finally, we have the obvious requirement for 'more complex' objects to be included in our lists to

be handled by Drag Drop. Here, in this particular subsection, we build our techniques using the string data type,

but in the next subsection we will take this list box example and get it to perform with much more

complex types.

If we can handle one custom object type, then we can handle anything ;-) So roll up those sleeves !

'Mouse Down' - What can be Involved :

Well, to keep it simple, the pressing of the left mouse button can either be part of selecting an item, or

can be the start of a drag action.

In our code to handle the 'Mouse Down' event, we first need to check to see if an item is already selected.

Study this section of code below :-

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

Line 52 deals with the situation when no item is selected - in which case a drag can't be allowed to take place.

Notice that line 51 is our way of tracking which list box object is involved in the start of the DnD contract, that is

the source. We simply have a class variable to be a 'helper' to us, see below :-

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

Notice we set 'lDragSource' as false in the Initialize method, since a drag operation has not yet begun.

How do we Know the Correct Item is Selected ? :

This is surprising not difficult, as the user can see it their required item is highlighted, if not they reselect. I

am sure you must have done this sort of thing many times using other Windows applications. Starting a DnD

operation before realising a source item does not seem to be selected correctly, and then returning to the GUI

control to re-select etc. etc.

So if we don't need to worry if the 'correct' item is selected before starting a Drag operation, we simply need to

check that at least one is selected.

Okay then, what do we do in the 'Mouse Down' once we know we have an item in focus ?

Let us study the working Vulcan code below :-

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

Don't worry if you think line 61 is a duplication ;-) this comes of copying and adapting existing code from

earlier working apps. ! However, it is useful to be able to adapt code, especially in the new world of dotNet.

Simply, in line 62 we are recording the value of the index of the selected item, just in case after a drop on the

target control, we need to complete a 'Move' effect, in which case we will need to remove the item from the list.

NOTICE >> the most important line is line 67, because this is the one which starts off the Drag Drop operation.

The format of this method is different to that used in Forms, and in my opinion makes life for the DnD developer

much easier indeed.

All we have to supply to this static method is the source object (usually a control), the data object, and the effects

that you in your code wish to support.

The returned effect, that which comes back after the drop is finished in the target control, supplies information on

which the rest of the code needs to act. Obviously, different things need to take place depending on whether a

move or a copy, or nothing, actually took place.

Finally, at the end of the method code we need to clear any previous logical setting for the drag source.

*** NOTE *** there may be better ways for checking whether a source is also the target object, but this way will

do us for the moment. Once we have things all working we may return to this issue to improve things.

So having selected an item, checked that we have, and started a possible 'contact', we can then move on to the

part of the code which handles when we move the drag move pointer into a possible target list box.

Mouse Enter - What Happens at the Target Control :

Yes, I know, we are dealing with source and target code on the same ListBox control, but then that's what is

required most often. We can drag items into a control and also drag them out too.

So lets see what the code is like for when we move the drag mouse pointer into the list box control screen area :-

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

In the code above we are responding to when a drag mouse pointer enters into the screen area of our list box

control.

The first thing we have to do is check to see if a DnD contract can be completed - if this possible drop target

were to actually be the drop target. Things taken into account in the 'SetDropEffects' method can be seen below :-

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

This is VERY similar to what we have seen previously, but we are now also checking to see if the helper

class field is indicating that we have a source-target situation.

Notice that in the code we have at the moment, any source-target situation results in nothing happening. And that

means nothing at both ends of the contract, source or target.

Okay then, supposing we have a possible drop situation, what does our code look like in the method to handle

what is to happen when the left hand mouse button is released ? That is when we actually do the drop.

Let's have a look at it below :-

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

Don't get mixed up between finishing the drop and the 'Mouse Button Up' event - the later happens when no drag drop

action is in progress, the former happens as a result of the mouse button being released - BUT - only after a drop operation.

In the code above, line 89 is some 'belt and braces' code, as it should already have been done previously, but it doesn't hurt ;O))

Then, if we are to do something, other than nothing, we need to add the item into the list.

Note at the moment we are just placing it in the list, later more fancy behaviour requirements may expect us to place it at a

particular place. Anyway, not a worry for the moment.

The 'else' part of the clause handles the cases when the data type is wrong and also any other reason why the effects is set

to 'none' and this includes the case where the source is also the target, because of the first couple of lines in the 'SetDropEffects'

method.

Last, But Not Least :

In the working code that you have seen demonstrated so far, we also have a 'Mouse Up' event handler.

Let us study the event handler method to see if it does anything particular :-

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

Notice that it simply takes account of when an item from the list may have been selected.

If an item has been selected it is simply placed in the suitably named class field variable, and some useful text

added to the window title string - that in the title bar.

So in other words it does not actually take part in the Drag Drop operation itself.

Testing for Source-Target Situation :

I will run just one instance of the app, and try to drag and drop an item within the list box

control. Here are some images of the results :-

And no matter what I do I can't get any drop behaviour to take place, or any strange or

erroneous things to happen at all.

The image below is what was produced when I dropped the dragged 'Harry' item within

the source list box :-

However, when we comment out a few lines we get the following :-

By commenting the few line shown below we have got back the 'intra' drop facility, or

functionality of the list box. 'Intra' meaning within the same 'thing' or object.

Here is the commented code :-

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

We have done enough for one applet, lets us call it a day, and move on to making a list box example which

will handle much more complex objects than simple strings.

See you in the next section.

PHH - February 2010.

*** Post Script ***

Tidying a Loose End :

Earlier in the above section I said we would deal with using a 'smarter' way to tell if the source control

was also the target control - well he we sort out the problem.

Below is the code which handles this eventuality (source-target) much neater indeed.

Study the code :-

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

In the code above, the 'sender' object is checked against the list control itself. No more need

for a 'helper' field or property to hold this information.

In order to get this code to work the sender object has to be passed into the 'SetDropEffects' method

along with the DropEventArgs object 'e'. Changes to a couple of lines in the body of the code in this PRG have

been made to accommodate this change.

PHH - Frebruary 27th. 2010.


Useful Working Code

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

<Window x:Class="WPF_DnD_ListBox01.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Drag Drop : ListBox - Simple : "

Height="300"

Width="300"

Name="winDnDListBox01">

<Grid>

<ListBox Margin="27,25,131,0"

Name="olbxSimple1"

Height="100"

VerticalAlignment="Top"

Background="SeaShell"

AllowDrop="True"

Drop="olbxSimple1_Drop"

DragEnter="olbxSimple1_DragEnter"

PreviewMouseUp="olbxSimple1_PreviewMouseUp"

PreviewMouseDown="olbxSimple1_PreviewMouseDown" />

</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 WPF_DnD_ListBox01

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD olbxSimple1_Drop( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDropToList01(sender,e)

RETURN

PRIVATE METHOD olbxSimple1_DragEnter( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDragEnterList01(sender,e)

RETURN

PRIVATE METHOD olbxSimple1_PreviewMouseDown( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseDown(sender, e)

RETURN

PRIVATE METHOD olbxSimple1_PreviewMouseUp( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseUp(sender,e)

RETURN

END CLASS

END NAMESPACE

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

// winInFront.prg

//----- created February 26th. 2010 -----

//----- for ClickStartWPF and Pearls -----

//----- by PHH .....

#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 WPF_DnD_ListBox01

/// <summary>

/// My InFront code for Window1 - DnD ListBox simple 01 -----

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private cSelected as string

private lDragSource as Boolean

method Initialize() as void

self:cSelected := "..nothing.."

self:lDragSource := false

self:olbxSimple1:Items:Clear()

self:olbxSimple1:Items:Add("Tom")

self:olbxSimple1:Items:Add("Dick")

self:olbxSimple1:Items:Add("Harry")

return

method DoPrevMouseUp(sender AS Object, e AS System.Windows.Input.MouseButtonEventArgs ) as void

if self:olbxSimple1:SelectedItem = NULL

return

else

self:cSelected := (string)self:olbxSimple1:SelectedItem

self:Title := "Drag Drop : ListBox - Simple : "+ self:cSelected

endif

return

method DoPrevMouseDown(sender AS Object, e AS System.Windows.Input.MouseButtonEventArgs ) as void

self:lDragSource := true

if self:olbxSimple1:SelectedItem = NULL

return

else

self:cSelected := (string)self:olbxSimple1:SelectedItem

self:Title := "DnD : ListBox - Simple : will drag >> "+ self:cSelected

endif

LOCAL cText AS STRING

LOCAL nIndex AS INT

cText := (string)self:olbxSimple1:SelectedItem

nIndex := self:olbxSimple1:SelectedIndex

LOCAL DragEffect AS DragDropEffects

LOCAL SupportedEffects AS DragDropEffects

SupportedEffects := DragDropEffects.Copy | DragDropEffects.Move

DragEffect := DragDrop.DoDragDrop(self:olbxSimple1, cText, SupportedEffects)

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

IF (DragEffect == DragDropEffects.Move)

self:olbxSimple1:Items:RemoveAt(nIndex)

self:Title := "DnD : ListBox - Simple : drag Moved >> "+ self:cSelected

ENDIF

IF (DragEffect == DragDropEffects.Copy)

self:Title := "DnD : ListBox - Simple : drag Copied >> "+ self:cSelected

ENDIF

self:lDragSource := false

return

method DoDragEnterList01(sender AS Object, e AS DragEventArgs ) as void

self:SetDropEffects(e)

if !self:lDragSource

LOCAL cDragData AS STRING

cDragData := (STRING)e:Data:GetData(typeof(STRING))

self:Title := "DnD : ListBox - Simple : can drop >> "+cDragData

endif

return

method DoDropToList01(sender AS Object, e AS DragEventArgs ) as void

self:SetDropEffects(e)

if !e:Effects = DragDropEffects.None

LOCAL cDragData AS STRING

cDragData := (STRING)e:Data:GetData(typeof(STRING))

self:olbxSimple1:Items:Add(cDragData)

self:Title := "DnD : ListBox - Simple : have dropped >> "+cDragData

else

if !self:lDragSource

//----- just for testing and demo purposes -----

self:Title := "Drag Drop : ListBox - Simple : "+"[ Wrong Type of Data ! ]"

endif

endif

return

method SetDropEffects( e AS DragEventArgs ) as void

if self:lDragSource //----- disallow drop within SELF -----

e:Effects := DragDropEffects.None

return

endif

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

LOCAL oKS AS DragDropKeyStates

oKS := (DragDropKeyStates)e:KeyStates

IF (e:Data:GetDataPresent(typeof(STRING)))

//------------- 'Ctrl' modifier key indicates a copy -------------

IF ((oKS & DragDropKeyStates.ControlKey) == DragDropKeyStates.ControlKey)

e:Effects := DragDropEffects.Copy

ELSE

//--------- lack of modifier keys indicates a move ---------

e:Effects := DragDropEffects.Move

ENDIF

ELSE

//----- when incorrect data type, do nothing ! -----

e:Effects := DragDropEffects.None

ENDIF

return

END CLASS

END NAMESPACE

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

PHH - February 2010.


the ListBox control - complex data

Hi guys, let us make a start to using complex, or customised objects in the 'ListBox' control that we are going

to set up for Drag Drop operations.

By 'complex' we mean it will be an object with a mixture of standard data types, as well as possibly having itself

other complex class types for the field data.

Let me start by saying that any object which is to by involved in a Drag Drop operation MUST be 'serializable'.

If you have not used such class types previously then refer to the F2D volume of the ClickStart eNotes and

search on 'serializable' :-

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

The image above shows what I get when I search the 'Forms 2.0 designer' eNotes for 'serializable'.

Put simply, if a class is serializable it means an object of that class can be turned into pure XML code.

It is also important for all fields / properties needing to be copied or moved, when serialization takes place, are NOT set

to private. I would suggest 'Public' or Export'. Just keep it simple and straightforward and it will work ;O))

Practical Demonstration :

It would appear that in order to Drag Drop, or indeed use the Clipboard facility, an object needs to be serializable.

I will make a simple demonstration to show this point clearly to you.

From my working code I will comment out the code line which sets this attribute like shown below :-

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

Now when I try to drag an item from one running instance of the app to another, I get the following

error message, or should I say 'exception' :-

Simply changing the commented code to that shown below :-

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

makes all the difference to successful operation of the app and we now get the following result :-

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

As you may be able to tell from studying the above image, Ginny (Chaughey) has been copied from the

top (and behind) app instance, into the lower (and in front) instance of the same app.

We now have one more member of the list in this target of the Drag Drop operation.

Notice that we are adding a suitable message to the title bar of the running app, so we can see more

clearly what is taking place.

Finally, before we move on from this serializable class topic, we also need to know that any class which

is to be serializable, also appears to need an empty 'constructor'. I simply say this from previous

experience, and what I thought I had read in the SDK documentation etc., certainly it does not hurt to

include one in your serializable class.

*** However, I have to come clean, and be honest with you, when I comment the lines 186-188 and rebuild

and run the app, it still seems to work OK. I will leave my empty constructor in place, just for safety ;-)

Examining Our Complex Serializable Class :

Okay then, we have talked and tested enough, let us take a close look at the customized (or complex) class we are

going to use for our ListBox drag and drop operations :-

Take note that this is a simple example of a custom (complex) class of object, but it will need to be handled in exactly

the same way as more complicated ones. So it will do nicely for our coding and testing.

The example class shown below is called 'SimpleGuy' :-

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

Nothing too flash, but we have a few different types of fields, these are just standard data types for now.

Making Our List of Items :

Now that we have decided on a class of custom object, lets us see the Vulcan code to create a few

typical 'SimpleGuy' objects and place them in the list help by the ListBox control.

Here is the code being used at the moment :-

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

In the image above you will notice that we only provide three data field / property values, and the constructor fills the other

two will default values for us - just to simplify things during development.

When we get this app running as we would like we will investigate ways of doing Drag Drop with collections bound

to the ListBox property called 'ItemsSource'.

Anyway, how does our app get setup at time of instantiation ? See below :-

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

After disassociation of the my event handlers (I say 'mine' as the system ones are left associated), we call

the method to created and fill the list, and then make three simple but important settings.

I am still using the logical field 'lDragSource' as a way of tracking whether we have a source-target situation,

most tests have found this approach to be good and robust, so I an putting the 'sender' approach to one side for now.

Line 36 sets the object selected, or in focus, to null, and the list has no selection set in line 37.

So this is what the code seen so far does for us :-

The Source End of Things ! :

Code to Respond to 'DnD' Related Events :

Fine so far, but now lets see what our code looks like which is in the event handlers which are associated with

the events related to the Drag Drop operations.

Let us start with the mouse down event, here is the code which works :-

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

Yes, by now this is starting to get familiar if you have studied the earlier sections in these notes.

Line 110 is the one to focus our attention on particularly.

Since we now have a list of complex objects in place of 'string' data type objects, we have cast accordingly, as

well as placing the result into a variable of suitable type.

Now what about the rest of this mouse down method ? Check the code out below :-

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

Once again, this is not very different from what we may have expected, after studying earlier sections of our notes.

To me, line 124 is the 'great news' about WPF as far as Drag Drop is concerned.

We have the same syntax for simple data types as for complex objects, and we don't need to resort to using the

Clipboard class to make our 'DnD' actions work for us ;-)

In the static method call in line 124 we once again supply the control object at the focus of the drag, the data item

selected in the control, and the effects we wish to be supported in the DnD action.

This is a double edged sword, as although we don't need to use the clipboard for DnD any more, we still need to

know how it works for us to make a context menu for the ListBox control - with Cut, Copy and Paste all available.

We will focus on the context menu aspect of this later.

Finally for the mouse down handler method, we have code to make suitable messages in the title bar, and one of

these (line 131) also has to deal with the removal of the list element since a 'Move' has been signalled through the

system and the Drag Drop 'contract' arrangement.

Lastly, but quite importantly, the three lines we first saw in the Initialize() method are used to clear source and

selection values, so a fresh operation may be started if the user requires it.

So then, with the mouse down we have either made a selection (using a system handler), or carried out a DnD

action, which may or may not have been successful. The success depends on the selected 'target' control being

capable of accepting the data object type on offer in the drag.

*** Note *** If you wish to study general details behind Drag Drop then look at a later section in these notes

called 'some general DnD Principles'.

>> We still need to discuss the 'mouse up' event as far as the source is concerned, but this will come later.

Although it is not complex, it is necessary to get it suitable and correct, for both source and target.

The Target End of Things :

Code to Respond to the 'Drag Enter' Event :

In this working app example I am simply using the 'drag enter' event to access the data and if it is suitable then

display some simple detail in the title bar.

Here is what works :-

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

With Drag Drop there are a few possibilities as to how you code the methods and even which methods to choose.

However, you need to make your choice depending on what your style happens to be, and whatever else you do,

you MUST then test thoroughly to see that your code, and running app, does exactly as you require. if it does, then

you are OK !

When we get a bit more advanced we may wish to look at changing mouse cursor icons when the data type dragged

is unsuitable - but we will leave that for another day. First things first !

Code to Respond to the 'Drop' Event :

Once again we see the benefits of WPF and the dotNet classes, as the code for the 'Drop' event shown below, is

very similar to that we have seen previously, for the simple string data type objects.

View the working code below :-

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

In the image of the code above, it is lines 157 and 159 which has changed significantly since the simpler

example in earlier subsections of these notes.

Obviously, the 'SetDropEffects' method has checked to see if the data available for drop is suitable, so we do

not need to recheck in the drop handler method.

Lines 160 & 161 add the suitable data item to the list box collection, and then clear any selection within the 'ListBox'

control itself.

Once again we make suitable messages available for the user to read in the title bar.

What has Changed in the 'SetDropEffects' method ? :

Firstly, let us study the code which is working in the app :-

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

Looking at the code shown above we can see that basically it is line 80 which has been changed to

handle the more complex data object.

Line 88 just checks that at the target control, there is no item selected. See like a good idea even if it is also

'belt and braces' ;O))

Mouse Up - at Source and Target :

Since we need to have a 'mouse up' event handler, it has to take into account the fact that it will be called into

'play' or action, at both the source and the target end of things.

All the other events related to Drag Drop only take place at either the source or the target.

So let us study the code and see what it means in light of this 'double' requirement.

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

Firstly, at the source end of the 'contract' if the mouse button is being released then no 'drag' is taking place. This

means the logic flag used for tracking source-target situations can be made false.

The other lines are for the benefit of displaying information, and are self explanatory.

What we now need to do is to ask ourselves if the code we see will cause any issue at the target end of things,

when the mouse button is released.

Check this for yourselves, to convince yourself it is OK. However, be careful about wanting to clear list item selection as

this event will trigger (or fire) after selection in the source, and before the drag action starts, so we can't do a clear.

In fact it is a few 'fringe' issues like this which make getting your DnD actions correct, quite difficult. You can have a

few simple code lines missing, extra, or wrong, and the whole DnD operation fails or does weird things. You need to

have some working code which you then take and copy, then adapt.

Be careful, it is easy to mess things up, even though the DnD and contract thing is not difficult to understand.

Before moving on to testing, lets see the even handler association code which I group into a pair of my own methods :-

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

This is inline with what I have developed earlier in these notes, and obviously they are called in the

Initialize() method as per usual.

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

*** Remember *** I like to turn off any associations made by the designer, before I initialize my controls and other

related data structures etc. etc.

This way we don't get any unforeseen and strange behaviour at window opening time. I find this most effective in

both Forms work and WPF stuff.

Now lets move on to see if we have a successfully work app, in all respects.

Testing the App Thoroughly :

The Same App :

First of all let us see if our code as written prevents us from dragging items from the same object - back onto itself.

Study the image below :-

In the image above, having selected 'Ginny' (Caughey - woops! just spotted a spelling error), I am then trying to

drop the data into the same (source) list box control.

The result is shown below :-

Nothing seems to have happened, and the list is as long as it was, with all elements in their original

places.

The title bar message also seems correct.

Now what about 'cross dragging' - that is data from one instance of the app to another instance of the

same app ? How does this work out ?

This time lets try and select Chis (Pyrgas) and drag him down into the second instance, at the same

time as holding down the modifier key ('Ctrl') - this is what we get :-

In the above image we are part way through the DnD process (action) as we have moved the 'drag' mouse

pointer into a suitable target - and not the source.

What is the result ? See below for an answer :-

We now have an extra 'Pyrgas' element in the lower ListBox control, and the title bar entries in both

app instances reflect what has taken place.

So what happens if we empty the one (top) list into the other, can we cause an error ?

Lets see :-

In the image above we are nearly there, the top list is getting a lot shorter, and I am in the process of

moving Ginny (caughey spelled correctly this time) into the lower list.

Notice that the lower list has grown a lot, and that the mouse drag icon shows no positive sign ('+') and

so a 'move' is about to take place.

And this is what we end up with if we keep going :-

Take my word for it, I can't force an error by clicking in the empty list box control in the top instance.

Obviously, I could open a third instance, and then empty the list contents of that into the for ever

growing list in the second instance - but hey! life is short anyhow, so lets move on.

Before we move to third party app testing, just let me tell you that the app as we see it here, has a

good 'feel' to it. It feels as you would expect a working Drag Drop windows situation to 'feel'. Not

sticky or awkward at all.

Which is good news!

This App with Third Party Apps :

I have opened my 'ClickPad' applet from my Forms notes, and am trying a drag of text into the ListBox

control of our new app .

*** Notice, I am simply using as a target the instance of our new app that we have emptied by moving all the

elements from the list to a second instance.

And what shows when I release the drag mouse button ?

Not a lot if you study the code below :-

Because we have not made our list box app aware of anything other types of data, by checking for them,

we get mixed results with third party apps.

My ClickPad app does not (for some reason) even force a suitable title bar messaged. However, when

I try the using WE (Windows Explorer ) (from W7) and use an image file (JPEG) I get this :-

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

This might suggest to me that the DnD functionality of the RichTextBox in Forms uses the Clipboard which is why

we do not get a suitable message.

However, I would suspect that WE in W7 is doing things the modern and accepted way, so we get our message as

expected, in the title bar.

How about try to drag and move a list element into our WE folder ?

Below are the results of the attempt.

First the image of when we 'drag enter' the file pane of WE :-

Notice that WE shows the incompatibility of the data by displaying a suitable icon.

This is the more advanced 'extra' bits I would like to add to our DnD code when I find out how to do this.

It may mean chewing Paul Piko's ears I feel ;-)

However, one thing which does function correctly is the message display, telling us that Ginny (Caughey)

was dragged, but that nothing was done with the list element.

Please be aware that this testing has so many little side to it, that it is easy to miss some strange or unacceptable

behaviour.

Be aware of the biggest problem when developers build functionality into their controls, they only test for the situation

for which they designed it - they forget that DnD is a 'global' Windows feature and that therefore they need to do tests

on a far wider scale than they usually do. I know, I have been there ;O))

I will leave any further testing up to you, and suggest that you read the more general section on Windows Drag and Drop

later in these notes.

Adding a Context Menu :

Well, some good news is that it is easy to code yourself a context menu for the ListBox control.

Also, I think from experimenting with the RC (Release Candidate) copy of Visual Studio 2010 (version 10) it

will become quite possible to create context menus in the designer, as easily as it was for Forms.

For now, let us look at both the Vulcan code as, well as the the result, in the image below :-

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

Fine, at least we have a menu showing when I right click the list box, but we need some event associations

even before we can output and simple messages - like "we are about to do a 'Copy' ...".

One thing I have noticed, and that is you have to get your right mouse button click in the right sequence

as far as the Drag Drop selection is concerned. Not 'sticky' just a complex event situation, and I have seen and

felt others like this before.

Adding the Event Handlers and Associations :

Quite honestly, this was very straightforward (easy?) and only took me about five minutes, even though I had

never made a context menu previously in WPF, and had never added any handler associations either.

However, with experience of adding other general handler associations earlier in these notes, it was quick to

get to what you see below :-

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

You will notice that I have changed slightly the way I have declared and instantiated the menu items.

This may be a little unnecessary to do things like this, but as it is my first time it is good to keep the

Vulcan code so it is very readable. Maybe I could go back to using just 'oMI' for all, but for now we have

the following :-

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

Finishing the Context Menu Item Behaviour :

Copying to the Clipboard :

All we need to do to complete our handler method for the required 'Copy' action, is supply the Vulcan code

which sees if any list item is selected, and if it is, place it in the system clipboard.

Here is the small amount of code needed to get our app to function correctly :-

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

In the code shown in the above image, a check is first made to see if an item in the list has been selected.

Only if selection is in place does the method continue, and the selected item used in the code portion which

checks the clipboard to see what is there.

Lines 113 to 115 operate until the Clipboard has within it a copy of the selected item. The order of the double

condition is important as the second part will cause an error if the Clipboard is empty. However, because of the

first test to see if it is empty, the second condition never gets evaluated until it holds some data.

Only then does it come into play and the do loop operate until the Clipboard holds a copy of the item in focus.

Sections of code like these need to be carefully tested out in as wide a range of situations as possible.

If it works when tested thoroughly, then use it.

Presumably the format of line 113 may be new to many readers, if that is so go to Chapter 13 later in these eNotes

which deals specifically with using the system Clipboard. In many ways it is self explanatory, and not unlike what

we have been using in the 'DragDrop.DoDragDrop()' static method itself.

Otherwise, look at the relevant sections in the 'ClickStartDnD' notes already available on the web site. Actually, I

referred to them myself when doing this section yesterday, just to remind me what the syntax was for getting the

Clipboard to function as required ;-)

See, I told you, I use ClickStart notes for myself!

Pasting from the Clipboard :

This code displays syntax which is very similar to what we have seen already, and is not difficult to understand.

Let us take a look at what works nicely here in testing :-

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

In the Vulcan code show above we first check the Clipboard to see that it contains the correct data object

for the rest of the paste operation. Only when this is found to be the case does it try to retrieve the information.

Line 121 does the checking, and line 123 actually retrieves the data object.

Once we have the data object available it may be used to print a message, and also added to the ListBox

collection.

Finally, the code does some tidying, by making sure the list has no item selected, and also that the Clipboard

is emptied, no data is left behind to cause problems later - or even raise data security issues.

'Cutting' - Removing an Item from the List :

Obviously, in its simplest form this does not require the Clipboard, we would simply remove the item, if any is

selected - or in focus.

However, the Windows convention appears to be that when we do a 'Cut' we also place a copy of what is 'Cut'

in the Clipboard for possible use later.

Lets us take a look at what the Vulcan code looks like :-

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

In the code shown in the image above, the line 87 is included to keep the operation in-line with accepted

Windows practice. A copy to the Clipboard is carried out first.

However, the rest of the code should be familiar since it is used in the Drag Drop methods seen previously,

where a 'Move' operation is to be carried out. Line 98 removes the item from the 'ListBox' collection using

the value of the index of the selected item.

So nothing too difficult to understand in making our first Context Menu.

If you wish to improve both your skills and your knowledge concerning Context Menus in WPF, then refer to

Chapter 12 of these notes, entitled "Context Menus".

See you in the next subsection ;-)

PHH - March 2010.


Useful Working Code

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

<Window x:Class="WPF_DnD_ListBoxComplex.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Drag Drop : List Box : Complex Objects :"

Height="300"

Width="461"

Name="winDnD_LB_Complex">

<Grid>

<ListBox Margin="19,22,0,26"

Name="olbxComplex01"

HorizontalAlignment="Left"

Width="168"

Background="Wheat"

AllowDrop="True"

Drop="olbxComplex01_Drop"

DragEnter="olbxComplex01_DragEnter"

PreviewMouseDown="olbxComplex01_PreviewMouseDown"

PreviewMouseUp="olbxComplex01_PreviewMouseUp" />

</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 WPF_DnD_ListBoxComplex

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD olbxComplex01_Drop( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDropToList01(sender,e)

RETURN

PRIVATE METHOD olbxComplex01_DragEnter( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDragEnterList01(sender,e)

RETURN

PRIVATE METHOD olbxComplex01_PreviewMouseDown( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseDown(sender, e)

RETURN

PRIVATE METHOD olbxComplex01_PreviewMouseUp( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseUp(sender,e)

RETURN

END CLASS

END NAMESPACE

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

// winInFrontComp.prg

//----- created February 28th. 2010 -----

//----- for ClickStart and Pearls -----

//----- PHH .....

#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 WPF_DnD_ListBoxComplex

/// <summary>

/// my In Front code for main Window1

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private oSG as SimpleGuy

private oSGselect as SimpleGuy

private cSelected as string

private lDragSource as Boolean

method Initialize() as void

self:UnsetMyEventHandlers()

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

self:FillList01()

self:lDragSource := false

self:oSGselect := null

self:olbxComplex01:SelectedIndex := -1

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

self:SetMyEventHandlers()

return

method SetMyEventHandlers() as void

self:olbxComplex01:Drop += DragEventHandler{self, @olbxComplex01_Drop()}

self:olbxComplex01:DragEnter += DragEventHandler{self, @olbxComplex01_DragEnter()}

self:olbxComplex01:PreviewMouseDown += MouseButtonEventHandler{self, @olbxComplex01_PreviewMouseDown()}

self:olbxComplex01:PreviewMouseUp += MouseButtonEventHandler{self, @olbxComplex01_PreviewMouseUp()}

return

method UnsetMyEventHandlers() as void

self:olbxComplex01:Drop -= DragEventHandler{self, @olbxComplex01_Drop()}

self:olbxComplex01:DragEnter -= DragEventHandler{self, @olbxComplex01_DragEnter()}

self:olbxComplex01:PreviewMouseDown -= MouseButtonEventHandler{self, @olbxComplex01_PreviewMouseDown()}

self:olbxComplex01:PreviewMouseUp -= MouseButtonEventHandler{self, @olbxComplex01_PreviewMouseUp()}

return

method FillList01() as void

self:oSG := SimpleGuy{"Cricket","Jimmy",DateTime{2001,03,13}}

self:olbxComplex01:Items:Add(self:oSG)

self:olbxComplex01:Items:Add(SimpleGuy{"Johnson","Sally",DateTime{2003,05,21}} )

self:olbxComplex01:Items:Add(SimpleGuy{"Smith","Deliah",DateTime{1997,11,03}} )

self:olbxComplex01:Items:Add(SimpleGuy{"Pyrgas","Chris",DateTime{1987,09,25}} )

self:olbxComplex01:Items:Add(SimpleGuy{"Parker","John",DateTime{1983,07,17}} )

self:olbxComplex01:Items:Add(SimpleGuy{"Chaughey","Ginny",DateTime{1991,08,09}} )

self:olbxComplex01:Items:Add(SimpleGuy{"Feldman","Brian",DateTime{1980,04,21}} )

self:olbxComplex01:DisplayMemberPath := "Surname"

self:olbxComplex01:SelectedIndex := -1

return

method SetDropEffects(sender AS Object, e AS DragEventArgs ) as void

if self:lDragSource //----- disallow drop within SELF -----

e:Effects := DragDropEffects.None

return

endif

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

LOCAL oKS AS DragDropKeyStates

oKS := (DragDropKeyStates)e:KeyStates

IF (e:Data:GetDataPresent(typeof(SimpleGuy)))

//------------- 'Ctrl' modifier key indicates a copy -------------

IF ((oKS & DragDropKeyStates.ControlKey) == DragDropKeyStates.ControlKey)

e:Effects := DragDropEffects.Copy

ELSE

//--------- lack of modifier keys indicates a move ---------

e:Effects := DragDropEffects.Move

ENDIF

self:olbxComplex01:SelectedIndex := -1

ELSE

//----- when incorrect data type, do nothing ! -----

e:Effects := DragDropEffects.None

ENDIF

return

method DoPrevMouseUp(sender AS Object, e AS System.Windows.Input.MouseButtonEventArgs ) as void

self:lDragSource := false

if self:olbxComplex01:SelectedItem = NULL

return

else

self:oSGselect := (SimpleGuy)self:olbxComplex01:SelectedItem

self:cSelected := self:oSGselect:Surname

endif

return

method DoPrevMouseDown(sender AS Object, e AS System.Windows.Input.MouseButtonEventArgs ) as void

if self:olbxComplex01:SelectedItem = NULL

self:lDragSource := false

return

else

self:oSGselect := (SimpleGuy)self:olbxComplex01:SelectedItem

self:cSelected := self:oSGselect:Surname

self:Title := "DnD : ListBox - Complex : will drag >> "+self:cSelected

endif

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

LOCAL nIndex AS INT

nIndex := self:olbxComplex01:SelectedIndex

LOCAL DragEffect AS DragDropEffects

LOCAL SupportedEffects AS DragDropEffects

SupportedEffects := DragDropEffects.Copy | DragDropEffects.Move

self:lDragSource := true

try

DragEffect := DragDrop.DoDragDrop(self:olbxComplex01, self:oSGselect, SupportedEffects)

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

catch oEx as SystemException

MessageBox.Show(oEx:message,"Mouse Down :")

end try

IF (DragEffect == DragDropEffects.Move)

self:olbxComplex01:Items:RemoveAt(nIndex)

self:Title := "DnD : ListBox - Simple : drag Moved >> "+ self:cSelected

ENDIF

IF (DragEffect == DragDropEffects.Copy)

self:Title := "DnD : ListBox - Simple : drag Copied >> "+ self:cSelected

ENDIF

IF (DragEffect == DragDropEffects.None)

self:Title := "DnD : ListBox - Simple : nothing Done .. >> "+ self:cSelected

ENDIF

//----- clear drag % selection details -----

self:lDragSource := false

self:olbxComplex01:SelectedIndex := -1

self:oSGselect := null

return

method DoDragEnterList01(sender AS Object, e AS DragEventArgs ) as void

if !self:lDragSource

local oS as SimpleGuy

oS := (SimpleGuy)e:Data:GetData(typeof(SimpleGuy))

self:Title := "DnD : ListBox - Complex : can drop (enter) >> "+oS:Surname

endif

return

method DoDropToList01(sender AS Object, e AS DragEventArgs ) as void

self:SetDropEffects(sender, e)

if !e:Effects = DragDropEffects.None

local oS as SimpleGuy

oS := (SimpleGuy)e:Data:GetData(typeof(SimpleGuy))

try

self:olbxComplex01:Items:Add(oS)

self:olbxComplex01:SelectedIndex := -1

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

self:Title := "DnD : ListBox - Simple : have dropped >> "+oS:Surname

else

if !self:lDragSource

//----- just for testing and demo purposes -----

self:Title := "Drag Drop : ListBox - Simple : "+"[ Wrong Type of Data ! ]"

endif

endif

return

END CLASS

[Serializable]CLASS SimpleGuy

EXPORT cSurname AS STRING

EXPORT cFirstname AS STRING

export dDob AS DateTime

export nCash as Double

export nAge as int

CONSTRUCTOR()

SUPER()

return

CONSTRUCTOR(cS AS STRING, cF AS STRING)

SUPER()

SELF:Surname := cS:TRIM()

SELF:cFirstname := cF:TRIM()

SELF:dDoB := DateTime.Now

self:nAge := 21

self:nCash := 13.13

RETURN

CONSTRUCTOR(cS AS STRING, cF AS STRING, oDT as DateTime)

SUPER()

SELF:Surname := cS:TRIM()

SELF:cFirstname := cF:TRIM()

SELF:dDoB := oDT

self:nAge := 21

self:nCash := 13.13

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

access DoB as dateTime

return self:dDoB

assign Dob(dD as DateTime) as void

self:dDoB := dD

return

end class

END NAMESPACE

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

method Initialize() as void

self:UnsetMyEventHandlers()

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

//Clipboard.Clear()

self:FillList01()

self:lDragSource := false

self:oSGselect := null

self:olbxComplex01:SelectedIndex := -1

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

self:MakeContextMenu()

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

self:SetMyEventHandlers()

return

method MakeContextMenu() as void

self:oCMenu := ContextMenu{}

self:olbxComplex01:ContextMenu := self:oCMenu

local oMIX, oMIC, oMIP as MenuItem

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

oMIX := MenuItem{}

oMIX:Header := "Cut"

oMIX:Click += RoutedEventHandler{self, @CutClick()}

self:oCMenu:Items:Add(oMIX)

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

oMIC := MenuItem{}

oMIC:Header := "Copy"

oMIC:Click += RoutedEventHandler{self, @CopyClick()}

self:oCMenu:Items:Add(oMIC)

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

oMIP := MenuItem{}

oMIP:Header := "Paste"

oMIP:Click += RoutedEventHandler{self, @PasteClick()}

self:oCMenu:Items:Add(oMIP)

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

return

method CutClick(sender as object, e as RoutedEventArgs) as void

MessageBox.Show("we will do a 'Cut' operation !!")

self:CopyClick(sender, e)

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

LOCAL nIndex AS INT

local cName as string

nIndex := self:olbxComplex01:SelectedIndex

MessageBox.Show("index value is :- "+nIndex:ToString())

if nIndex < 0

MessageBox.Show("no item selected - can't remove !?","CARE :")

else

cName := ((SimpleGuy)self:olbxComplex01:SelectedItem):Surname

try

self:olbxComplex01:Items:RemoveAt(nIndex)

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

MessageBox.Show("item removed is >> "+cName,"Information :")

endif

return

method CopyClick(sender as object, e as RoutedEventArgs) as void

local oSG as SimpleGuy

if !self:olbxComplex01:SelectedItem = null

oSG := (SimpleGuy)self:olbxComplex01:SelectedItem

else

MessageBox.Show("No item selected !?","CARE :")

return

endif

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

do while (!Clipboard.ContainsData("SimpleGuy") .and. !(SimpleGuy)Clipboard.GetData("SimpleGuy") = oSG)

Clipboard.SetData("SimpleGuy",oSG)

enddo

return

method PasteClick(sender as object, e as RoutedEventArgs) as void

MessageBox.Show("we will do a 'Paste' operation !!")

if Clipboard.ContainsData("SimpleGuy")

local oSG as SimpleGuy

oSG := (SimpleGuy)Clipboard.GetData("SimpleGuy")

local cText as string

cText := ""

cText += oSG:Firstname+CRLF

cText += oSG:Surname+CRLF

cText += oSG:DoB:ToShortDateString()+CRLF

MessageBox.Show(cText,"Retrieved :")

self:olbxComplex01:Items:Add(oSG)

self:olbxComplex01:SelectedIndex := -1

self:olbxComplex01:SelectedItem := null

Clipboard.Clear()

else

MessageBox.Show("Can't seem to retrieve object!?")

endif

return

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

PHH - March 2010.


Making a 'DnD_ListBox' Class

Enter topic text here.


the ListView control

Before we see how to write the code to make and display a suitable WPF 'ListView' control, let

us see what we are aiming at producing - see below :-

The image above shows a set of custom objects of the type 'SimpleGuy' displayed in the ListView

control, after it has been set to use the 'GridView' mode.

*** Note *** the 'SimpleGuy' class has been changed and improved (augmented) since it was used

in the earlier ListBox control examples. Below are shown some of the changes :-

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

We now have an overload of the Constructor which allows age and cash properties to be set at instantiation.

Below are a few extra property assignments and access methods :-

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

The extra code in the 'SimpleGuy' class makes it possible to create our collection view as

shown below :-

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

Since code line 63 is using the WPF ListView control object, and setting its 'ItemsSource' property, we

had now better look at the new project we need to create.

[ We have used an array list as this is the object from which will need to add and remove items at the DnD action stage. ]

[ It is much easier to handle this with an array list as opposed to a strongly type array. ]

Follow the instructions in the earlier section on ListBox controls in this DnD chapter, so you set up a new

project within the DragDrop solution we are developing - here is my SE pane after doing so :-

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

Notice that I have already made a code 'InFront' PRG file in which I have started to place my new project

Vulcan code.

Study the image below to see how the XAML script created by the Designer looks after I have used the Properties

Pane (PP) to allow me to add a few property setting for the 'ListView' control :-

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

In the image above I have edited the XAML script, or markup, so that it displays easier to read by the attributes

lining up vertically. (I just added a few carriage returns.)

Also notice in the above image that I have used the PP to provide a name for the window, as well as the ListView control.

Okay then, let us see what our Initialize() method looks like, in our code 'InFront'.

Study the Vulcan code below :-

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

For the code image above, we have already looked at line 36, the way to create the collection view.

Before we look at the code for the set-up of the ListView control itself, don't forget that we need to link

into the code behind a new Vulcan code line which calls our 'Initialize()' method - see below :-

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

Setting Up the 'ListView' for 'GridView' Mode :

Now that we have done a few basic things, many of which we have seen done previously, let us now turn

our attention to setting up the ListView control itself. This is new to us, and much more about this control

may be seen and read in chapter 11 later in these eNotes.

Let us take some time to look at the detail in the method called 'SetUpListViewControl()' which we call from

the Initialize method in our code InFront.

Here is the very first part :-

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

In the code shown above, notice that we are actually just setting a sort condition / description, which is applied

to the 'View' object.

The 'ListView' control setting up starts at line 71 below :-

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

Study the image shown above, notice that we have to create a 'GridView' object which is associated

with the 'View' property of the 'ListView' control in line 108.

Lines 71 to 74 create the 'GridView' and set a couple of properties on it.

Then in lines 76 onwards, we create 'GridViewColumns' which we add to the column collection of the

'GridView' object.

Probably Vulcan code line 79 is the most interesting, where the Surname property of the 'SimpleGuy'

class is bound to the content display for the column within the grid view.

Remember anything bound in this manner has to be a property of the object within the collection view

and not just a data field. So I had to add Age and Cash properties to the 'SimpleGuy' class to make

this example work correctly.

Now we can see the other 'GridViewColumn' creation sections - see below :-

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

In the code shown in the image above, there is nothing new or difficult to understand, just

notice that the column width varies depending on the column contents.

Although the column widths are supplied as a default value, they can be changed at run time

simply because the resize property of the 'GridView' has a default of true it would appear.

However, we don't really wish to see our demo app look like this :-

Focusing Attention on Drag Drop Operations :

Before we get into detail about writing code to allow both Drag and Drop functionality to exist for our

ListView control, we better keep our minds 'wide' for a moment, thinking on the bigger picture !

If we create code to allow 'SimpleGuy' objects to be both dragged and dropped, we could therefore

do one set of test later, where we have a ListBox control as either the source or the target of a DnD

action, with the ListView(Grid mode) control being the second partner in the contract arrangement.

*** REMEMBER *** Drag Drop is a communication arrangement and the data and its type is really

an extra layer over the DnD contract 'thing' itself. So whether the Drag Drop is successful - or not -

depends on the data types expected, once the basic DnD functionality is in place.

So to summarize, if we have any control which can participate in DnD, and it handles data of the type

'SimpleGuy' single objects, then they should work together, however we mix them up. If a label accepts

such a data type object then it can have one dragged from a list box, or a ListView control, or anything

else setup correctly for 'SimpleGuy' single objects.

*** Note *** It would appear that the changes I have had to make to the 'SimpleGuy' class in this example

will not affect the DnD operations with earlier ListBox projects and examples - fingers crossed ;-)

Selecting the Drag Drop Events :

Before we can start developing our DnD functionality it is a good idea to use the WPF Designer to help

us select the necessary events required for our coding which follows.

From our ListBox experience we look as if we need events for Drop, DragEnter, PreviewMouseDown, and

PreviewMouseUp.

*** Remember *** we said we need to look deeper into the Bubbling and Tunnelling types of events, and

which we ought to be using, and why. See later sections on this.

In the WPF designer, set the object in focus to be the ListView control by clicking it with the mouse.

Then use the PP and choose to click the yellow lightning icon to produce the list of available events. Note

sometimes you seem to need to click this twice with the mouse, possibly the first time just to give it focus.

With the mouse pointer in the Input box of the 'Drop' event, double click the mouse button to get this :-

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

Okay, I know, when you double click the designer takes you immediately to the code behind where you see

the following :-

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

I have adjusted the stub code with a little editing so that it is easier to display and read in these notes.

Simply we need to add a method call to this so it does what we wish when the event triggers, or fires.

For now, let us continue using the PP so we can put in place the other three stub methods for handling

the events.

After completing this task, and providing suitable method names for the handlers, I have this code

in the code behind - shown below with inserted handler methods :-

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

Now we need to add some code to create and fill the required handler methods.

Adding the Drag Drop Methods :

Before we get into details on the Drag drop methods let us stand back and look at what may happen

if we don't appreciate just how a 'ListView' control works - yes, it appears to be different to a 'ListBox' in

this respect - more of this later, first the potential results as seen below :-

It would appear that while a 'View' object is associated with the control (or is in use by the control), we

can't carry out edits. The SDK documentation says it is Read Only.

This does not cause us any real problems, we just need to realise that in order to add or remove items

from the list used by the collection view, we need to "disconnect" before making list changes. Then

reconnect afterwards.

Keep this in mind as we do the next part of the notes.

Starting a Drag - Mouse Down :

As we go through the even handler methods you will see that the basic structure is left intact, from previous

examples of DnD we have covered, particularly the 'ListBox' with complex objects.

The parts that change are to do with the data and how to either add or remove items to or from the list structures

or collections involved.

The nice thing about DnD is that once you know how to do it correctly for one scenario, it is virtually the same for

all others.

Let us move on by having a look at the mouse down handling method - the action which takes place at the source

control.

Study the Vulcan code carefully, and see if you can see what is familiar from previous examples, and what you think

is new and to do with the 'ListView' control behaviour itself :-

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

Probably the first thing to notice is how familiar it all looks - which must be good !

Secondly, the use of the index value for pinpointing an item is no good in this case as the value refers to

how it looks in the GridView of the ListView, and not the real position in the array list itself.

Hence the code is built around removing the object itself - that which is the item in the control. One

row is a complete item, and a single object.

Since this is a method to deal with the source, we can only see the new method to remove an item, and

this is called 'RemoveSpecifiedItem()' - there is another used later which 'adds' an item.

Let us study this method below :-

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

In the code shown in the image above, notice we have taken into account the fact that the data source is not

editable when it is use by the ListView control.

Vulcan code line 163 is disconnection as such, 164 is obviously editing, then refreshing in 165 is very important

otherwise you may well see no change on the screen.

Finally, line 166 re-associates the view with the control, so it is once again Read Only in status.

Try commenting line 165 for your own testing, and see how the action seems not to have worked. Then uncomment

once again to see positive behaviour !

Entering a Drop Target - Drag Enter :

As with our previous example this does not do a great deal, just check the data type and post a message.

Here is the code below :-

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

Interestingly enough, only the text of the message has changed in this code.

Dropping on the Target - Drop :

Once again, not many changes, and surprising simple really. Here is the code :-

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

Just a few cosmetic changes, and a call to a new method in line 140, that of 'AddAnotherItem()' which is shown below :-

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

Exactly similar things are taking place to the 'Remove' flavour of this method, only line 156 adds an

item this time around.

Mouse Up :

The final event handler method is related to the mouse up event (the tunnelling variety - hence 'Preview').

Remember this will fire when the 'ListView' control is both source and target.

Here is the code :-

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

Notice in the code in the image above, the only thing which is different to previous examples is the control

referred to in line 230 and 233.

Finally, we need to see the 'SetDropEffects()' method, and this has not changed at all.

See the code below :-

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

The reason this code is unaffected is because the data type of the item being passed in the Drag Drop action is the

same as we used in the ListBox (complex) example.

Some Test Results :

Let us see some test results before we look at the final bits of code related to starting the app correctly.

Here is my first real live test with some useful results :-

In the above image notice that Ginny (Caughey) has been 'Moved' (copied and deleted), such that she

now appears twice in the right hand side instance, and not at all in the left hand instance of the app.

Furthermore, the two items called 'Ginny' are now together, and this was not the case with out ListBox

tests.

The reason for this is that we have a sort on the Birth Date column (actually the DoB property) and so any

items which are similar will be grouped together.

Please note that the highlight in the RHS is not due to selection, but to the mouse hovering over the item row.

Please also notice the title bar messages which agree with what I have just explained.

So our last section of Vulcan code deals with setting up the class as far as the code InFront is concerned.

See below :-

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

Remember the Unset & Set methods are called right at the start and end of the Initialize() method in line 38 - but it is shown collapsed.

And a final look at the XAML script shows this :-

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

The changes (or additions) are simply to associate the events with the stub handlers in the code behind.

These have been rewritten in the Set and Unset methods, so I can turn on/off the associations.

*** Note *** I was hoping to show DnD from a ListView example to a ListBox example - using the same data type,

that is the updated 'SimpleGuy' custom class. Unfortunately, I seem to be getting a problem which I do not understand.

So I will have to leave this topic till I find a solution.

Note on Selection Mode :

The app as developed above seems to work better from a user's point of view if the 'SelectionMode' property is

set to 'Single' in the Properties Pane (PP) - see image below :-

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

When set to single is seems less 'sticky' and a bit more responsive, as far as the DnD operations are concerned.

However, as far as the Context Menu is concerned it would seem to me to be necessary to add some extra functionality

such that a previous selection may be 'cleared' easily, otherwise the context menu does not appear when required, as

the code is handling a part completed Drag Drop operation.

Rather than add another control, like a button, to the window, I have now added another event handler method and

associated this with the pressing, or clicking, of the mouse wheel. I have also added a tooltip to the ListView control

and the visible results are as shown below :-

The required Vulcan code line for the 'Set' MyEventHandlers method is as shown :-

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

Obviously there is a complementary line in the 'Unset' method too.

The association stub made by the Designer, along with the call to my 'InFront' handler method is as follows :-

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

The handler method itself, entitled 'DoClearSelection()' is shown below :-

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

So the result is that clicking the mouse wheel will remove any item selection within the ListView control, and then a

right mouse button click will bring up the context menu and operate on the item under the mouse pointer at the time

of clicking.

Try to add this extra functionality for yourself.

See you in the next subsection.

PHH - February 2010.


Useful Working Code

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

<Window x:Class="WPF_DnD_ListView.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ListView : GridView Mode :"

Height="300"

Width="495"

Name="winListView">

<Grid>

<ListView Margin="12,12,12,55"

Name="olvDnD_01"

AllowDrop="True"

Background="SkyBlue"

AlternationCount="2"

Drop="olvDnD_01_Drop"

DragEnter="olvDnD_01_DragEnter"

PreviewMouseDown="olvDnD_01_PreviewMouseDown"

PreviewMouseUp="olvDnD_01_PreviewMouseUp"

SelectionMode="Single"

PreviewMouseWheel="olvDnD_01_PreviewMouseWheel"

ToolTip="To de-select - press mouse wheel !" />

</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 WPF_DnD_ListView

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD olvDnD_01_Drop( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDropToListView(sender,e)

RETURN

PRIVATE METHOD olvDnD_01_DragEnter( sender AS System.Object, e AS System.Windows.DragEventArgs ) AS System.Void

self:DoDragEnterListView(sender,e)

RETURN

PRIVATE METHOD olvDnD_01_PreviewMouseDown( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseDown(sender, e)

RETURN

PRIVATE METHOD olvDnD_01_PreviewMouseUp( sender AS System.Object, e AS System.Windows.Input.MouseButtonEventArgs ) AS System.Void

self:DoPrevMouseUp(sender,e)

RETURN

PRIVATE METHOD olvDnD_01_PreviewMouseWheel( sender AS System.Object, e AS System.Windows.Input.MouseWheelEventArgs ) AS System.Void

self:DoClearSelection(sender,e)

RETURN

END CLASS

END NAMESPACE

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

// winInFrontMain.prg

//----- created March 2nd. 2010 -----

//----- for ClickStartWPF and Pearls -----

//----- PHH (UK) ............

#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.ComponentModel

#using System.Collections

BEGIN NAMESPACE WPF_DnD_ListView

/// <summary>

/// My code 'In Front' for Window1 .....

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

//private oGuysGroup as List<SimpleGuy>

private oView as ICollectionView

private lDragSource as Boolean

private cSelected as string

private oSGselect as SimpleGuy

private alGuys as ArrayList

private oCMenu as ContextMenu

method Initialize() as void

self:UnsetMyEventHandlers()

self:lDragSource := false

self:MakeCollectionView2()

self:SetUpListViewControl()

self:MakeContextMenu()

self:SetMyEventHandlers()

return

method SetMyEventHandlers() as void

self:olvDnD_01:Drop += DragEventHandler{self, @olvDnD_01_Drop()}

self:olvDnD_01:DragEnter += DragEventHandler{self, @olvDnD_01_DragEnter()}

self:olvDnD_01:PreviewMouseDown += MouseButtonEventHandler{self, @olvDnD_01_PreviewMouseDown()}

self:olvDnD_01:PreviewMouseUp += MouseButtonEventHandler{self, @olvDnD_01_PreviewMouseUp()}

self:olvDnD_01:PreviewMouseWheel += MouseWheelEventHandler{self, @olvDnD_01_PreviewMouseWheel()}

return

method UnsetMyEventHandlers() as void

self:olvDnD_01:Drop -= DragEventHandler{self, @olvDnD_01_Drop()}

self:olvDnD_01:DragEnter -= DragEventHandler{self, @olvDnD_01_DragEnter()}

self:olvDnD_01:PreviewMouseDown -= MouseButtonEventHandler{self, @olvDnD_01_PreviewMouseDown()}

self:olvDnD_01:PreviewMouseUp -= MouseButtonEventHandler{self, @olvDnD_01_PreviewMouseUp()}

self:olvDnD_01:PreviewMouseWheel -= MouseWheelEventHandler{self, @olvDnD_01_PreviewMouseWheel()}

return

method DoClearSelection(sender AS Object, e AS MouseWheelEventArgs) as void

//----- clear drag & selection details -----

self:lDragSource := false

self:olvDnD_01:SelectedIndex := -1

self:oSGselect := null

return

method MakeContextMenu() as void

self:oCMenu := ContextMenu{}

self:olvDnD_01:ContextMenu := self:oCMenu

local oMIX, oMIC, oMIP as MenuItem

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

oMIX := MenuItem{}

oMIX:Header := "Cut"

oMIX:Click += RoutedEventHandler{self, @CutClick()}

self:oCMenu:Items:Add(oMIX)

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

oMIC := MenuItem{}

oMIC:Header := "Copy"

oMIC:Click += RoutedEventHandler{self, @CopyClick()}

self:oCMenu:Items:Add(oMIC)

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

oMIP := MenuItem{}

oMIP:Header := "Paste"

oMIP:Click += RoutedEventHandler{self, @PasteClick()}

self:oCMenu:Items:Add(oMIP)

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

return

method CutClick(sender as object, e as RoutedEventArgs) as void

MessageBox.Show("we will do a 'Cut' operation !!")

local oSG as SimpleGuy

if !self:olvDnD_01:SelectedItem = null

oSG := (SimpleGuy)self:olvDnD_01:SelectedItem

else

MessageBox.Show("No item selected !?","CARE :")

return

endif

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

do while (!Clipboard.ContainsData("SimpleGuy") .and. !(SimpleGuy)Clipboard.GetData("SimpleGuy") = oSG)

Clipboard.SetData("SimpleGuy",oSG)

enddo

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

LOCAL nIndex AS INT

local cName as string

nIndex := self:olvDnD_01:SelectedIndex

MessageBox.Show("index value is :- "+nIndex:ToString())

if nIndex < 0

MessageBox.Show("no item selected - can't remove !?","CARE :")

else

cName := oSG:Surname

try

self:RemoveSpecifiedItem(oSG)

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

MessageBox.Show("item removed is >> "+cName,"Information :")

endif

//----- clear drag & selection details -----

self:lDragSource := false

self:olvDnD_01:SelectedIndex := -1

self:oSGselect := null

return

method CopyClick(sender as object, e as RoutedEventArgs) as void

MessageBox.Show("we will do a 'Copy' operation !!")

local oSG as SimpleGuy

if !self:olvDnD_01:SelectedItem = null

oSG := (SimpleGuy)self:olvDnD_01:SelectedItem

else

MessageBox.Show("No item selected !?","CARE :")

return

endif

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

do while (!Clipboard.ContainsData("SimpleGuy") .and. !(SimpleGuy)Clipboard.GetData("SimpleGuy") = oSG)

Clipboard.SetData("SimpleGuy",oSG)

enddo

//----- clear drag & selection details -----

self:lDragSource := false

self:olvDnD_01:SelectedIndex := -1

self:oSGselect := null

return

method PasteClick(sender as object, e as RoutedEventArgs) as void

MessageBox.Show("we will do a 'Paste' operation !!")

if Clipboard.ContainsData("SimpleGuy")

local oSG as SimpleGuy

oSG := (SimpleGuy)Clipboard.GetData("SimpleGuy")

local cText as string

cText := ""

cText += oSG:Firstname+CRLF

cText += oSG:Surname+CRLF

cText += oSG:DoB:ToShortDateString()+CRLF

MessageBox.Show(cText,"Retrieved :")

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

self:AddAnotherItem(oSG)

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

self:olvDnD_01:SelectedIndex := -1

self:olvDnD_01:SelectedItem := null

Clipboard.Clear()

else

MessageBox.Show("Can't seem to retrieve object!?")

endif

//----- clear drag & selection details -----

self:lDragSource := false

self:olvDnD_01:SelectedIndex := -1

self:oSGselect := null

return

method MakeCollectionView2() as void

alGuys := ArrayList{}

alGuys:Add(SimpleGuy{"Cricket","Jimmy",DateTime{2001,03,13},13,45.23})

alGuys:Add(SimpleGuy{"Johnson","Sally",DateTime{2003,05,21},23,34.78})

alGuys:Add(SimpleGuy{"Smith","Deliah",DateTime{1997,11,03},45,2.19})

alGuys:Add(SimpleGuy{"Pyrgas","Chris",DateTime{1987,09,25},32,56.78})

alGuys:Add(SimpleGuy{"Parker","John",DateTime{1983,07,17},46,78.93})

alGuys:Add(SimpleGuy{"Caughey","Ginny",DateTime{1991,08,09},21,0.69})

alGuys:Add(SimpleGuy{"Feldman","Brian",DateTime{1980,04,21},55,999.01})

alGuys:Add(SimpleGuy{"Piko","Paul",DateTime{1985,06,11},43,119.37})

alGuys:Add(SimpleGuy{"Crispien","Dieter",DateTime{1983,02,19},42,77.66})

self:oView := CollectionViewSource.GetDefaultView(self:alGuys)

self:olvDnD_01:ItemsSource := self:oView

return

method Contains(de as object) as Boolean

local lResult as Boolean

lResult := false

local oPerson as SimpleGuy

oPerson := (SimpleGuy)de

//----- return guys older than 13 ! -----

if oPerson:Age > 13

lResult := true

else

lResult := false

endif

return lResult

method SetUpListViewControl() as void

//self:oView:SortDescriptions:Add(SortDescription{"Surname", ListSortDirection.Ascending})

//self:oView:SortDescriptions:Add(SortDescription{"ShortDoB", ListSortDirection.Descending})

self:oView:SortDescriptions:Add(SortDescription{"DoB", ListSortDirection.Descending})

//self:oView:Filter := Predicate<object>(Contains)

local oGV as GridView

oGV := GridView{}

oGV:AllowsColumnReorder := true

oGV:ColumnHeaderToolTip := "My Simple Guys !"

local gvc1, gvc2, gvc3, gvc4, gvc5 as GridViewColumn

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

gvc1 := GridViewColumn{}

gvc1:DisplayMemberBinding := Binding{"Surname"}

gvc1:Header := "Last Name"

gvc1:Width := 100

oGV:Columns:Add(gvc1)

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

gvc2 := GridViewColumn{}

gvc2:DisplayMemberBinding := Binding{"Firstname"}

gvc2:Header := "First Name"

gvc2:Width := 100

oGV:Columns:Add(gvc2)

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

gvc3 := GridViewColumn{}

gvc3:DisplayMemberBinding := Binding{"ShortDoB"}

gvc3:Header := "Birth Date"

gvc3:Width := 100

oGV:Columns:Add(gvc3)

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

gvc4 := GridViewColumn{}

gvc4:DisplayMemberBinding := Binding{"Age"}

gvc4:Header := "Age"

gvc4:Width := 70

oGV:Columns:Add(gvc4)

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

gvc5 := GridViewColumn{}

gvc5:DisplayMemberBinding := Binding{"Cash"}

gvc5:Header := "Cash Due"

gvc5:Width := 85

oGV:Columns:Add(gvc5)

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

self:olvDnD_01:View := oGV

return

method DoDropToListView(sender AS Object, e AS DragEventArgs ) as void

self:SetDropEffects(sender, e)

if !e:Effects = DragDropEffects.None

local oS as SimpleGuy

oS := (SimpleGuy)e:Data:GetData(typeof(SimpleGuy))

try

self:AddAnotherItem(oS)

catch oEx as SystemException

MessageBox.Show(oEx:message,"Dropping ...")

end try

self:Title := "DnD : ListView - 'SimpleGuy' : have dropped >> "+oS:Surname

else

if !self:lDragSource

//----- just for testing and demo purposes -----

self:Title := "Drag Drop : ListView - 'SimpleGuy' : "+"[ Wrong Type of Data ! ]"

endif

endif

return

method AddAnotherItem(oS as SimpleGuy) as void

self:olvDnD_01:ItemsSource := null

self:alGuys:Add(oS)

self:oView:Refresh()

self:olvDnD_01:ItemsSource := self:oView

//MessageBox.Show(self:alGuys:Count:ToString(),"Length :")

return

method RemoveSpecifiedItem(oS as SimpleGuy) as void

self:olvDnD_01:ItemsSource := null

self:alGuys:Remove(oS)

self:oView:Refresh()

self:olvDnD_01:ItemsSource := self:oView

//MessageBox.Show(self:alGuys:Count:ToString(),"Length :")

return

method RemoveSpecifiedItemAt(nIndex as int) as void

self:olvDnD_01:ItemsSource := null

self:alGuys:RemoveAt(nIndex)

self:oView:Refresh()

self:olvDnD_01:ItemsSource := self:oView

//MessageBox.Show(self:alGuys:Count:ToString(),"Length :")

return

method DoDragEnterListView(sender AS Object, e AS DragEventArgs ) as void

if !self:lDragSource

local oS as SimpleGuy

oS := (SimpleGuy)e:Data:GetData(typeof(SimpleGuy))

self:Title := "DnD : ListView - 'SimpleGuy' : can drop (enter) >> "+oS:Surname

endif

return

method DoPrevMouseDown(sender AS Object, e AS MouseButtonEventArgs ) as void

if self:olvDnD_01:SelectedItem = NULL

self:lDragSource := false

return

else

self:oSGselect := (SimpleGuy)self:olvDnD_01:SelectedItem

self:cSelected := self:oSGselect:Surname

self:Title := "DnD : ListView - 'SimpleGuy' : will drag >> "+self:cSelected

endif

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

/*LOCAL nIndex AS INT

nIndex := self:olvDnD_01:SelectedIndex*/

LOCAL DragEffect AS DragDropEffects

LOCAL SupportedEffects AS DragDropEffects

SupportedEffects := DragDropEffects.Copy | DragDropEffects.Move

self:lDragSource := true

try

DragEffect := DragDrop.DoDragDrop(self:olvDnD_01, self:oSGselect, SupportedEffects)

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

catch oEx as SystemException

MessageBox.Show(oEx:message,"Mouse Down :")

end try

IF (DragEffect == DragDropEffects.Move)

self:RemoveSpecifiedItem(self:oSGselect)

//self:RemoveSpecifiedItemAt(nIndex) //----- Remember the View has sorts !! -----

self:Title := "DnD : ListView - 'SimpleGuy' : drag Moved >> "+ self:cSelected

ENDIF

IF (DragEffect == DragDropEffects.Copy)

self:Title := "DnD : ListView - 'SimpleGuy' : drag Copied >> "+ self:cSelected

ENDIF

IF (DragEffect == DragDropEffects.None)

self:Title := "DnD : ListView - 'SimpleGuy' : nothing Done .. >> "+ self:cSelected

ENDIF

//----- clear drag & selection details -----

self:lDragSource := false

self:olvDnD_01:SelectedIndex := -1

self:oSGselect := null

return

method DoPrevMouseUp(sender AS Object, e AS MouseButtonEventArgs ) as void

self:lDragSource := false

if self:olvDnD_01:SelectedItem = NULL

return

else

self:oSGselect := (SimpleGuy)self:olvDnD_01:SelectedItem

self:cSelected := self:oSGselect:Surname

endif

return

method SetDropEffects(sender AS Object, e AS DragEventArgs ) as void

if self:lDragSource //----- disallow drop within SELF -----

e:Effects := DragDropEffects.None

return

endif

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

LOCAL oKS AS DragDropKeyStates

oKS := (DragDropKeyStates)e:KeyStates

IF (e:Data:GetDataPresent(typeof(SimpleGuy)))

//------------- 'Ctrl' modifier key indicates a copy -------------

IF ((oKS & DragDropKeyStates.ControlKey) == DragDropKeyStates.ControlKey)

e:Effects := DragDropEffects.Copy

ELSE

//--------- lack of modifier keys indicates a move ---------

e:Effects := DragDropEffects.Move

ENDIF

//self:olbxComplex01:SelectedIndex := -1

ELSE

//----- when incorrect data type, do nothing ! -----

e:Effects := DragDropEffects.None

ENDIF

return

END CLASS

[Serializable]CLASS SimpleGuy

EXPORT cSurname AS STRING

EXPORT cFirstname AS STRING

export dDob AS DateTime

export nCash as Double

export nAge as int

CONSTRUCTOR()

SUPER()

return

CONSTRUCTOR(cS AS STRING, cF AS STRING)

SUPER()

SELF:Surname := cS:TRIM()

SELF:cFirstname := cF:TRIM()

SELF:dDoB := DateTime.Now

self:nAge := 21

self:nCash := 13.13

RETURN

CONSTRUCTOR(cS AS STRING, cF AS STRING, oDT as DateTime)

SUPER()

SELF:Surname := cS:TRIM()

SELF:cFirstname := cF:TRIM()

SELF:dDoB := oDT

self:nAge := 21

self:nCash := 13.13

RETURN

CONSTRUCTOR(cS AS STRING, cF AS STRING, oDT as DateTime, nA as int, nC as Double)

SUPER()

SELF:Surname := cS:TRIM()

SELF:cFirstname := cF:TRIM()

SELF:dDoB := oDT

self:nAge := nA

self:nCash := nC

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

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

access DoB as dateTime

return self:dDoB

assign Dob(dD as DateTime) as void

self:dDoB := dD

return

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

access ShortDoB as string

return self:dDoB:ToShortdateString()

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

access Age as int

return self:nAge

assign Age(nA as int) as void

self:nAge := nA

return

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

access Cash as Double

return self:nCash

assign Cash(nC as Double) as void

self:nCash := nC

return

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

end class

END NAMESPACE

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

PHH - March 2010.


some General DnD Principles

PHH - March 2010.


Events - Bubbling and Tunnelling

PHH - March 2010.


Chapter 9 - a Better WPF Application

PHH - January 2010.


Chapter 10 - Interoperability / Working together!

PHH - January 2010.


Forms controls on a WPF window

PHH - January 2010.


WPF controls on a Form

PHH - January 2010.


a Form from a WPF window

PHH - January 2010.


a WPF window from a Form

PHH - January 2010.


Chapter 11 - '..Controls' Namespace

Just like the System.Windows.Forms namespace was used for Forms application development, that

for WPF is called 'System.Windows.Controls'.

This is what the Framework 3.5 SDK doc has to say about it :-

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

When expanded the 'Classes' section starts as shown below :-

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

Using the Red Gate's Reflector software tool, we see this below when we try to view details :-

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

To find out more about using the Microsoft SDK documentation and the Reflector tool, see both the next

subsection and the 'Appendix D' section.

PHH - January 2010.


Using SDK documentation with Reflector

I use the Framework SDK documentation a huge amount for my researching into all things dotNet.

Currently I am using the 3.5 variety of this Microsoft publication, and although helpful, it can be frustrating

at times to find out what you know to be there - somewhere !

In order to easily find the information your require on the WPF controls available to us through dotNet 3.5 it

is suggested that you set the filter to 'no filter', and the look for 'System.Windows.Controls.xxxx' as below :-

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

In the image above you see we get the what used to be termed 'about' section, of previous SDK operations.

If we wish to see all the members of the class in focus (required?) we then simply select 'members' in the navigation / sub-caption bar

and we get that which is shown below :-

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

So using the image above, if we were to click the 'AppendText' method link we would get the following :-

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

PHH - January 2010.


SDK for WPF - .Net 4

SDK for WPF - .Net Framework 4

I used the following link to acquire the necessary CHM file for use to create the demo examples

for these eNotes :-

http://blogs.msdn.com/wpfsdk/archive/2009/05/21/offline-wpf-net-framework-4-documentation.aspx

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

After downloading and placing a shortcut on the desktop, I managed to get the following :-

Obviously, I was searching for the Web Browser control which I had previously failed to

find in the earlier version of the SDK documentation.

This detail should allow me to try and recreate the V3 app in WPF, and call it 'V3X'.

So after selecting the 'members' we get :-

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

I shall now use this while I research and write a lot more of the Chapter 11 subsections on the WPF controls.

When I have anything more positive and helpful to contribute I will add it here.

PHH - January 2010.


Setting up Reflector for '..Controls'

When you have successfully downloaded and installed the Reflector tool from 'Red Gate', you

will be able to open it and initially get something very close to this image shown below :-

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

[ See appendix 'D' to get the plug-in from Fabrice Foray, this will also give us Vulcan.NET in the list. ]

If we choose the PresentationFramework and open it we get this below :-

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

And when we open up the desired 'Controls' namespace we see the following :-

Okay then, things looking good, but I ought to now go off and install Fabrice's plug-in.

Then at least we can see what we may get in code when we burrow a little bit deeper into things.

Yep! I have just spent some time writing Appendix 'D', and downloading and installing the 'plug-in' and it

has worked VERY well ;-)

Please go and visit this appendix to see what you need to do to have Vulcan disassembler available to

your install of reflector.

PHH - January 2010.


TextBox

The TextBox control in the WPF toolbox can contain ONLY unformatted text.

Here is a simple (but real) example :-

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

In the example above I have hand written no code at all, just used the designer and property pane to

position a single control, and provide some values for a couple of properties.

Note that I selected to have 'wrap' set on, and the tooltip also.

I did format the XAML to allow for clearer presentation, by adding some carriage returns at a few

suitable places in the script.

Let us now add some extra properties, also using the PP, and improve what we have so far.

I will remember to make the TextBox the control in focus, by clicking the mouse within its border, as so :-

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

Then using the PP I will select a number of properties so far unused, and give these selected

properties values.

See below for a range of these extra properties :-

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

In the image above, notice that the Spell Checker is not included.

I looked throughout the PP to try and find it, and when the A-Z icon is selected I see the following list :-

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

However, when I manually type the required script for the 'SpellChecker' attribute as shown

below :-

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

And then build and refresh the designer and PP area, we see this :-

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

There is now an entry in the A-Z listing of the properties pane. So it would appear that not all properties

are shown, and that the ones shown can change on circumstances also.

Remember however, that this is VS 2008 as it stands in January 2010, and that what we get in the new

Visual Studio in March coming, could be quite different. I suggest we will see more features (if not all) and

also all of the time.

For the present however, we will have to learn to dig out the XAML script and manually edit it.

Anyway, for now lets see if we can detect a spelling error :-

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

Well, although a simple test, it does tend to indicate that we have a spell checker working.

*** note *** I change a few colour property values to be able to see more clearly when the spell

checker indicates a problem.

Putting Most of this in Vulcan Code :

When a new WPF window is created, we get a bunch of XAML code as expected, but also some Vulcan

code in a PRG file called 'WindowName.xaml.prg' where the "WindowName" text string represents the

actual name of the window itself.

So for example, if we have a new window called 'NationalLottery' then the Vulcan file created by the designer

to go along with the XAML stuff is called :- "NationalLottery.xaml.prg".

Lets look at what we have for our simple demo WPF app so far. Remember, we have not renamed it with

a name any more meaningful than default name of 'Window1' which was supplied by the system.

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

The other tab (the one hidden), is the standard XAML stuff.

So quite simply, if we are to add a pile of our own Vulcan code, to set properties in place of the XAML script we

have at the moment, we need to make a new code file, which is also a partial class, similar to that shown above.

Lets start by adding the new empty code file :-

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

And the dialog screen to supply the file details is as shown below :-

And this results in a clean new file as shown below, as well as the necessary entries in the SE pane:-

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

All we need to do is copy the basic code lines necessary to make our partial class fit in

with the existing system stuff. So this is what I have made :-

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

Obviously, to make this work as shown I have added one Vulcan code line to the system file, to

allow it to link, and this is shown below at line 25 :-

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

Moving Script to Code :

Now that we have a working app, and that our two partial class files work together, we can start

to think of removing XAML script for attribute setting, and creating new and suitable Vulcan code.

Let us start by setting the fore and back colours this way :-

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

And now the partly 'cleaned' or 'tidied' XAML script :-

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

Results are still as when all instructions were in XAML :-

Fine, now lets try and move even more script to code.

We need to take care to get the exact enumerations for FontStyles and TextWrapping.

This is what we have at the moment :-

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

So finally, we have a very small XAML script section like so :-

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

And now a much larger Vulcan code section as displayed below :-

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

To Summarize :

Whatever can be set through the designer and the properties pane, can be written in Vulcan code.

Both ways work, and until we find out which parts MUST be scripted in XAML, it is a matter of personal choice.

I suspect that as we gain more experience, and need to have our GUI physically separated on a client machine, we

can leave the issue as it is - do whatever you like, and make it work ;O)

Obviously, the downside of doing very little in the designer is that the visual image you are working with

becomes rather devoid of details, and to my mind, more difficult to work with. See below for how it now looks

after removing much of the XAML script into the Vulcan code section :-

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

Okay then, enough of all that. Let us move on to making an interactive example of a WPF app using

TextBox and a few other simple controls.

In the next subsection we can try to move some of the property settings to the design surface, so the user

can change then while the app is running.

See you in the next subsection.

PHH - January 2010.


Working Example One

Okay then, I have added some check box controls to our previous simple application.

To these I have also added a couple of events with their associated handlers.

So for the 'Text Wrapping' property, the XAML script looks as that below :-

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

This means we need handlers for both the 'Checked' and 'Unchecked' events.

These are as shown below :-

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

And obviously, in the Initialize() method we need to alter a line of code so that the default setting we require

is set 'on' when the app runs. This now looks like the following :-

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

I am sure there are others ways of doing this, but what is shown appears to work interactively, so

that is enough for me!

On second thoughts, after playing with the code, and doing some tests, i think I will change this back to

that shown below, it makes more coding sense :-

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

Anyway, it builds and runs and looks like that shown below :-

Note, even though a lot of interactivity / responsiveness is still missing, the following is seen when the

'Text Wrap' is unchecked :-

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

And obviously I had to enable the horizontal scroll bars (Auto) on the TextBox control

itself :-

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

Just before I have to go off and make all the boring bits of the check boxes work, lets look at some

results of a slightly more exciting nature.

The image shown below has an extra button which takes the content of the TextBox control

and applies this as the ToolTip content - check this out ;O))

This solution has just been posted on Pearls.

I will also include all the code in a subsection one level below this !

PHH - January 2010.


Useful Working Code

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

// Win1InFront.prg

//----- created January 4th 2010 -----

//----- for ClickStartWPF -----

#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 TextBoxWPF

/// <summary>

/// my code 'InFront'

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

method Initialize() as void

self:otbSimpleDemo:Background := Brushes.PowderBlue

self:otbSimpleDemo:Foreground := Brushes.DarkSlateGray

self:otbSimpleDemo:VerticalAlignment := VerticalAlignment.Top

self:otbSimpleDemo:ToolTip := "Try to edit the content !"

self:otbSimpleDemo:Text := "Hello World! You can edit text in a textbox."

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

self:otbSimpleDemo:SpellCheck:IsEnabled := True

self:ocbSpellCheck:IsChecked := true

self:otbSimpleDemo:AcceptsReturn := True

self:ocbReturns:IsChecked := true

self:otbSimpleDemo:AcceptsTab := True

self:ocbTabs:IsChecked := true

self:otbSimpleDemo:FontSize := 13

self:otbSimpleDemo:FontStyle := FontStyles.Italic

self:otbSimpleDemo:AutoWordSelection := True

self:ocbAutoWord:IsChecked := true

self:otbSimpleDemo:TextWrapping := TextWrapping.Wrap

self:ocbWrap:IsChecked := true

self:otbSimpleDemo:CharacterCasing := CharacterCasing.Normal

self:ocbUpper:IsChecked := false

return

method DoMakeContentsToolTip() as void

local cContents as string

cContents := self:otbSimpleDemo:Text

self:otbSimpleDemo:ToolTip := cContents

return

method DoRemoveAll() as void

self:otbSimpleDemo:TextWrapping := TextWrapping.NoWrap

self:ocbWrap:IsChecked := false

self:otbSimpleDemo:SpellCheck:IsEnabled := false

self:ocbSpellCheck:IsChecked := false

self:otbSimpleDemo:AcceptsReturn := false

self:ocbReturns:IsChecked := false

self:otbSimpleDemo:AcceptsTab := false

self:ocbTabs:IsChecked := false

self:otbSimpleDemo:AutoWordSelection := false

self:ocbAutoWord:IsChecked := false

self:otbSimpleDemo:CharacterCasing := CharacterCasing.Normal

self:ocbUpper:IsChecked := false

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

self:otbSimpleDemo:ToolTip := "Try to edit the content !"

self:otbSimpleDemo:Text := "Hello World! You can edit text in a textbox."

return

method DoWrapCheck() as void

self:otbSimpleDemo:TextWrapping := TextWrapping.Wrap

return

method DoWrapUnCheck() as void

self:otbSimpleDemo:TextWrapping := TextWrapping.NoWrap

return

method DoTabsCheck() as void

self:otbSimpleDemo:AcceptsTab := True

return

method DoTabsUnCheck() as void

self:otbSimpleDemo:AcceptsTab := false

return

method DoReturnsCheck() as void

self:otbSimpleDemo:AcceptsReturn := True

return

method DoReturnsUnCheck() as void

self:otbSimpleDemo:AcceptsReturn := false

return

method DoSpellCheckerCheck() as void

self:otbSimpleDemo:SpellCheck:IsEnabled := True

return

method DoSpellCheckerUnCheck() as void

self:otbSimpleDemo:SpellCheck:IsEnabled := false

return

method DoAutoWordCheck() as void

self:otbSimpleDemo:AutoWordSelection := True

return

method DoAutoWordUnCheck() as void

self:otbSimpleDemo:AutoWordSelection := false

return

method DoUpperCasingCheck() as void

self:otbSimpleDemo:CharacterCasing := CharacterCasing.Upper

return

method DoUpperCasingUnCheck() as void

self:otbSimpleDemo:CharacterCasing := CharacterCasing.Normal

return

END CLASS

END NAMESPACE

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

<Window x:Class="TextBoxWPF.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Simple Demo WPF : TextBox :" Height="300" Width="300">

<Grid Background="Cornsilk">

<TextBox Height="60.471"

Margin="27.5,12,96.25,0"

Name="otbSimpleDemo"

VerticalAlignment="Top"

HorizontalScrollBarVisibility="Auto"

></TextBox>

<CheckBox Height="16"

Margin="27.5,88.298,131.603,0"

Name="ocbWrap"

VerticalAlignment="Top"

Checked="ocbWrap_Checked"

Unchecked="ocbWrap_Unchecked">Text Wrap :</CheckBox>

<CheckBox Margin="27.5,109.956,129.937,0"

Name="ocbTabs"

Height="16.265"

VerticalAlignment="Top"

Checked="ocbTabs_Checked"

Unchecked="ocbTabs_Unchecked">Accepts Tabs :</CheckBox>

<CheckBox Height="16"

Margin="27.5,0,129.937,114.121"

Name="ocbReturns"

VerticalAlignment="Bottom"

Checked="ocbReturns_Checked"

Unchecked="ocbReturns_Unchecked">Accepts Returns :</CheckBox>

<CheckBox Height="16"

Margin="27.5,0,129.937,92.463"

Name="ocbSpellCheck"

VerticalAlignment="Bottom"

Checked="ocbSpellCheck_Checked"

Unchecked="ocbSpellCheck_Unchecked">Check Spelling :</CheckBox>

<CheckBox Height="16"

Margin="27.5,0,105.791,69.972"

Name="ocbAutoWord"

VerticalAlignment="Bottom"

Checked="ocbAutoWord_Checked"

Unchecked="ocbAutoWord_Unchecked">Auto Word Selection :</CheckBox>

<CheckBox Height="16"

Margin="27.5,0,105.791,47.481"

Name="ocbUpper"

VerticalAlignment="Bottom"

Checked="ocbUpper_Checked"

Unchecked="ocbUpper_Unchecked">Force Upper Case :</CheckBox>

<Button Height="23" HorizontalAlignment="Right"

Margin="0,88.298,12,0"

Name="opbRemoveAll"

VerticalAlignment="Top"

Width="75"

Click="opbRemoveAll_Click">Remove All</Button>

<Button Height="23"

Margin="28.322,0,70.805,12.495"

Name="opbMakeContentsTT"

VerticalAlignment="Bottom"

Click="opbMakeContentsTT_Click">Make Contents the ToolTip</Button>

</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 TextBoxWPF

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD opbRemoveAll_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoRemoveAll()

RETURN

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

PRIVATE METHOD ocbWrap_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoWrapCheck()

RETURN

PRIVATE METHOD ocbWrap_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoWrapUnCheck()

RETURN

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

PRIVATE METHOD ocbReturns_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoReturnsCheck()

RETURN

PRIVATE METHOD ocbReturns_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoReturnsUnCheck()

RETURN

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

PRIVATE METHOD ocbTabs_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoTabsCheck()

RETURN

PRIVATE METHOD ocbTabs_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoTabsUnCheck()

RETURN

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

PRIVATE METHOD ocbSpellCheck_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoSpellCheckerCheck()

RETURN

PRIVATE METHOD ocbSpellCheck_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoSpellCheckerUnCheck()

RETURN

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

PRIVATE METHOD ocbAutoWord_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoAutoWordCheck()

RETURN

PRIVATE METHOD ocbAutoWord_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoAutoWordUnCheck()

RETURN

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

PRIVATE METHOD ocbUpper_Checked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoUpperCasingCheck()

RETURN

PRIVATE METHOD ocbUpper_Unchecked( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoUpperCasingUnCheck()

RETURN

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

PRIVATE METHOD opbMakeContentsTT_Click( sender AS System.Object, e AS System.Windows.RoutedEventArgs ) AS System.Void

self:DoMakeContentsToolTip()

RETURN

END CLASS

END NAMESPACE

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

PHH - January 2010.


Coding Event Handler Associations

Anyone reading my Forms eNotes (ClickStartF2D) will realise that I like to have the facility to

be able to turn on/off all my event handling associations.

Or I suppose it would be more correct to say I like to be able to make / break any such

associations.

In order to do this we need to be able to code both the 'make' and the 'break' association

in Vulcan coding.

Good News :

The good news is that we have already seen enough information to do this quite easily.

The information the designer provides in both XAML, and in the association 'stub' it places in the

".xaml.prg" file, is sufficient to hand code the full Vulcan associations.

Example :

To see what I mean, and how it is done, we will take a real example.

Let us take the 'Remove All' push button as an example to follow through :-

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

We wish to create Vulcan code lines to associate the button click event with an appropriate

handler.

What does the designer do for us ? - What code / script does it create for us ?

First the XAML script :-

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

As you can see from the above detail, all that the XAML provides is the name of the event

for the control. So it is the 'Click' event on the 'opbRemoveAll' control.

However, the couple of lines in the '.xaml.prg' file is more helpful, and looks like this :-

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

There is enough information in this code to allow us to find a way to hand-craft our own associations.

All I did to succeed was to look at the format of previous Vulcan code associations and copy what

seemed sensible, in order to get it to compile.

Lets look at what I ended up with, so we can examine its structure and syntax :-

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

It is line 63 which matters, and line 62 just informs me that I removed 'System.Windows.' from the

beginning of the "RoutedEventHandler" since the '#using' had an appropriate line inserted in the namespace

section.

Obviously, once we have a working 'make' association we can easily create a 'break' association ;-)

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

The event is the 'click' event the handler method is 'opbRemoveAll_Click()' and the "RoutedEventHandler"

means that we are using 'RoutedEventArgs' as the parameters.

More Complex Associations :

Note that for more complex situations, it will be necessary to pass the arguments into the handler method

which we create, so they may be used to gain access to details / data of mouse buttons pressed, or cursor

positions etc.

Before we look at such a more complex situation, lets see it working - in action so to speak :-

The large button has the 'fuller' form of the event association syntax and it still builds

and runs OK !

Lets see the code :-

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

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

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

I realise we can simplify this still further by removing some of the long syntax, and leaving the specified

namespaces to do their job, but for an initial explanation it is useful to see what is there, and where it has come from.

Okay, I hear you say that we don't actually use 'sender' and 'e' in the code - the point is it is now available

to be used in any method we require it. And we can leave it out if we so wish.

Other Event Types :

In the simple example we have in front of us, we have only one more type of event, and that is a CheckBox

check/uncheck and these too have the same event arguments :-

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

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

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

That should be enough for now.

We will see just what other types of event arguments are used for other events on other controls, as we

gain more experience.

What I can say in order to summarize, is that we can now 'set' and 'unset' event handler associations

exactly as we did in the Forms eNotes written for the November 2009 VODC event.

See you in the next subsection.

PHH - January 2010.


TextBlock

Before we explore some aspects of the TextBlock control, we ought to read what the SDK

documentation has to say about it.

The TextBlock class provides a lightweight control for displaying small amounts of flow content.

TextBlock is designed to be lightweight, and is geared specifically at integrating small portions of flow

content into a user interface (UI). TextBlock is optimized for single-line display, and provides good

performance for displaying up to a few lines of content.

TextBlock supports the hosting and display of Inline flow content elements. Supported elements include

AnchoredBlock, Bold, Hyperlink, InlineUIContainer, Italic, LineBreak, Run, Span, and Underline.

See "TextBlock Content Model Overview" for more information (SDK docs Framework 3.5).

TextBlock is not optimized for scenarios that need to display more than a few lines of content; for such

scenarios, a FlowDocument (object) coupled with an appropriate viewing control is a better choice than TextBlock,

in terms of performance.

After TextBlock, FlowDocumentScrollViewer is the next lightest-weight control for

displaying flow content, and simply provides a scrolling content area with minimal UI.

FlowDocumentPageViewer is optimized around "page-at-a-time" viewing mode for flow content.

Finally, FlowDocumentReader supports the richest set functionality for viewing flow content, but is

correspondingly heavier-weight.

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

Example 1 :

The SDK documentation for Framework 3.5 shows a simple example which looks like this :-

However, I am sure we can make things slightly better and certainly more interesting - BUT - remember

that Confucius said "Every long journey starts with a single step!", so lets not get ahead of ourselves.

The image above shows two TextBlock objects, each above a push button.

I have simply copied the SDK XAML code into my editor in VS, after opening a new project.

The code builds and runs and shows this :-

Well, for such a small amount of effort, it does look similar to our target app !

But moving borders a little and adding a colour to the background of the first 'block' makes this better :-

This in fact may be a good case of where we need a "stack panel" in which to display the four

elements. The window would then resize much better.

The XAML script which produces this result is as shown below :-

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

Please note that the TextBlock 'Inlines' property can be alternatively set by Vulcan code. We

need to create an InlineCollection object then assign it to this property.

The result of using Vulcan code, and XAML script is exactly the same - and object of the FlowDocument type.

So what we will do for the moment, is place some small amount of text in the XAML, which we will

change and improve when we make our coded object.

Here is what we have at the moment :-

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

PHH - January 2010.


WebBrowser

The SDK documentation has this to say :-

WebBrowser Class :

Hosts and navigates between HTML documents. Enables interoperability between WPF managed code and HTML script.

Well lets start this section by see some colourful success !

Here is a small app which took little effort to create, but it does need much further work however :-

Basically, after making a new partial class file called "WB_WPFinFront.prg" I have added just

a couple of lines to the code, in order to achieve the results you see.

Here are the lines of Vulcan code :-

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

Phase Two :

Okay, fine so far.

Let us now move on a step or two, and provide a set of Uri's in a drop list.

Remember in dotNet we keep well away from placing string values in list controls.

If we are to require Uri objects then we may as well make a strongly typed list of Uri objects.

Below is an image of the designer stuff we have for phase two :-

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

As with Forms design, I find the Document Outline (DO) very useful when selecting elements on the

design surface. It is much easier to tell what is in focus than just interactively clicking the mouse

within the design area.

You will notice that I have chosen an event for the drop list (combo box) and the designer has made

an association with the event handler.

Here is the developing solution in the SE pane :-

The code behind looks like this :-

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

I call this 'code behind' as it is Vulcan code, as opposed to XAML script, and also because it

is made by the designer, to sit behind (or in support of) the design surface.

Now my code 'InFront' looks as follows :-

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

I call this code 'InFront' as it is hand coded by the developer, and goes to support the designer generated

script and code.

When we build and run the app it looks as shown below :-

The drop list is filled as shown below :-

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

So we appear to have a good start to the V3X applet I am to create.

This applet will allow a 'one stop shop' for developers wish to consult any of the ClickStart

web notes volumes. The series has so far grown to about ten in number, and as yet the web

link for the 'ClickStartDocs' has not been included.

Event Handling :

Thinking ahead a little, I have started to create two methods for setting the association between

control events and their handler methods.

See below to see the start of the 'set' and 'unset' MyEventHandler() methods :-

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

As previously with the TextBox control section, the 'System.Windows.' is implied and handled by

the using settings at the start of the PRG file.

As is my usual practice, I have included calls to the 'set' and 'unset' methods at the start and conclusion

of the Initialize() method.

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

Finally, needless to say I tested the code - and it works !

PHH - January 2010.


DataGrid

The DataGrid is a very useful control, and one which we shall use a lot in future I feel.

To get us off to a good start let us look at an image or two of success!

Below are two images of the same small WPF window app., the first with the DataGrid control

filled by using an observable collection of custom objects, and the second with the same grid

populated using a DataTable object.

[ In good time we will get to explaining about observable collections, and much more.]

Here is how the data grid looks when displaying the observable collection :-

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

And secondly, when displaying a 'view' of a standard data table object :-

Notice the column headers - these have been auto filled with values from properties of the underlying data objects.

These may be over-written by the coder / developer, but will often be left 'as-is' since it often relates to field names

in the original data file, or data table.

Once we know what to do, we find we need do very little in fact to get our data source to show within the grid

itself. However, knowing how to set filter conditions and sorting by one or more properties takes a little more

effort and knowledge - BUT - it is still not a difficult task.

*** Note *** [ the data grid control on the Designer surface does not show much - see below ... ]

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

The large grey rectangle is in fact the data grid control. Notice the lighter grey header area.

The XAML script is quite simple for the way I have set this up in the applet. Here we have how it looks :-

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

Notice that the namespace is different to earlier controls, TextBox, TextBlock, and WebBrowser.

If we don't provide a data source object to associate with the 'ItemsSource' property of the grid, we in

fact get a grey box (rectangle) at run time also :-

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

Preparing the Data Sources :

Okay then, having seen a data grid displaying details, let us focus more on the actual data objects which we need to

associate with the 'ItemsSource' property of the grid.

Lets us see which fields are declared in the test app we have been using to display the images :-

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

The crucial ones are on lines 41 - 45, as we shall see.

Line 42 is what I have been led to believe is called an observable collection.

Now lets see the code to make and setup our collection :-

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

So finally we need to see what is done in method 'CreateObjectSTarray()' to make the system array

of 'PersonCS' objects.

Below is the necessary code :-

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

So this shows we have made ten separate objects of type 'PersonCS' and that these are placed into a system array, strongly typed.

[ *** note *** the PersonCS class is shown in the working code subsection, and has to have an interface 'INotifyPropertyChanged' ]

[ if the object class is to be used with data binding. At present this is not available in Vulcan so C# has been used. ]

Once the strongly typed system array has been used to add a range of objects to the observable collection of 'PersonCS' objects,

then the 'view' object is generated from it.

All we now have to do is provide this as a data source for the grid.

Below is the code to both associate the data source with the 'itemsSource' property and also setup a few other

basic settings also :-

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

Note the single lines to auto generate the column headers, and to alternate every second row (or line) so

that we get a light blue colour.

If the line at position 76 is not included we do not seem to get our binding working with other bound controls.

Finally for this small section, because we have associated the 'view' and not the observable collection itself, we

can then apply filtering and sorting conditions criteria at a later stage.

So this code above produces the following DataGrid control output initially :-

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

For your information, the data in the example is editable and can have changes show up in other controls

which are bound to the same data source. More of these details in the Data Binding section of these

eNotes.

Using a DataTable Source :

For this example, I am using an XML file pair which was used with the Forms 2.0 eNotes and

relevant examples in those.

The original file pair (.XML and .XSD) were created by serialising a DataTable object.

All I am doing in this case, so I can populate the grid, is to make a DataTable object by deserialising.

The code below has been included in may places now throughout the ClickStart notes :-

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

So reading the above code, if things all go well and to plan, we will have the oDT object made from

the XML data, and ready for use.

So if we adjust the code at the start of the Initialize() method to this below :-

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

Then we can use the oDT object to associate with the DataGrid control in place of the observable collection.

Here are the changes :-

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

Instead of the 'view' used previously, we are now associating the default view of the oDT object with

the 'ItemsSource' property.

Further more, you may like to see a couple more lines of code which also set sort and filter conditions.

The greyed out lines were tried previously, while developing the applet.

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

So lets see what we get when we build and run the code :-

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

If you study the scroll bars you will be able to work out for yourself that the data lines (rows) are starting at 'S'

items and are in ascending order.

You will have to take it from me that there are not many product items (ITEMNO field) between 'H' and 'S100'.

And what there are get filtered out because of the other two restrictions in the line as shown below :-

self:oDT:DefaultView:RowFilter := "ITEMNO > 'H' AND EAN12 AND GINTOTAL > 2"

So to summarize, a DataGrid control can be probably best used with a 'view' object, and one which has either

been made from an observable collection, or a DataTable object. Views facilitate easy use of Sort and Filter

condition specifications.

Sorting and Filtering - View & the Grid itself :

For a short while longer, lets stick with the example we have been using above. The data table of bar codes

in the DataGrid control on the WPF window.

This time we are seeing that extra drop list controls have been added, one for Sorts and one for Filters.

This is how the applet looks now :-

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

and the sorts are as so :-

If you are sharp eyed you will see that although the options lists have been shown, nothing has been selected since

the rows look exactly the same.

So lets apply one of each and see what we get :-

I have set the code so that tool tips on the combo box drop list show what has been selected.

The sorter sort condition is easy to see in the drop list text area itself.

Note the position of the vertical slider, proving that we have a Z-A (descending) order applied.

If I now move the column headers along the horizontal - i.e. rearrange to show the relevant columns together,

we can see that the filter condition does seem to be applied OK. See below :-

*** Note *** we can only do this if the properties of the DataGrid control are set correctly.

Look at the image below which shows the default settings for the DataGrid :-

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

Of the three checked, the top two are those which I used to make the changes to create

the image displayed above.

Lets see if we can use the third one to sort a chosen column into order. Let us choose the

GINTOTAL column, click in the column header, and we get this result :-

Obviously, I had to scroll after the sort order had been changed.

Thinking ahead to the next section on ComboBox controls, and how they may be used in conjunction with the DataGrid control

to allow 'view' filter and sort conditions to be changed, it may NOT be a good idea to allow both column header 'sorting' as

well as application of sort order using the data view object.

However, in the end the choice is yours.

Looking at the XAML :

The way the XAML markup looks at the moment we see only this script below :-

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

However, if we use the properties pane to uncheck the three properties shown above, we get this in the XAML :-

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

The results of these changes are almost as we may expect. We can no longer move the actual columns

about (change their order), or indeed resize them by moving the extreme Left or Right margins.

However, there is an issue with the sorting, I still seem to be able to sort the row order. So I need to look into this.

What I will do, is put the checks back in the properties pane, and see what happens when I place the un-checking

into Vulcan code at initialize time.

I have now removed the extra XAML line of script and it looks once again like this :-

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

The Vulcan code has been changed to as follows :-

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

The app seems to work OK as far as not allowing the user to reorder or resize at run time, but the sorting still happens,

and the properties pane now shows all three properties as checked. Which is correct at GUI building time, until

our Vulcan code over-rides these.

So for now, remaining we have the sorting issue - we can't appear to turn off sorting.

Lets move on however, but remember we have an issue to solve, or put right.

Let us now move on to the next subsection where we use ComboBox controls.

PHH - February 14th. 2010


ComboBox

Let us now focus on how we can use the ComboBox control in WPF to enhance our GUI, and also

our interactivity as far as the user experience is concerned.

Lets us look first at a few instances within a working app :-

Above we have a ComboBox control which has its 'ItemsSource' property associated with a data source

which is actually a Data Columns Collection object.

Since we wish the drop list shown to be related to the DataTable object which is represented in the DataGrid control

beneath the ComboBox, it seems a good idea to get the collection of data columns in the table and use this directly.

Examine the Vulcan code to see just how easy this is to do :-

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

The first few lines shown above just make the data table itself.

It is line 326 which creates the data column collection object, and as a useful cross check, the

value of the number of columns in the collection is displayed at startup.

So there is little coding to do to complete our task ;-)

The data column collection is associated with the 'ItemsSource' property in line 329, and then we need

to specify which property is to be displayed in the list to be shown. Notice I have experimented with

the 'Caption' property and that seems to work fine also.

Lastly, the Tooltip text has been set to show a default setting for the control.

Since we will eventually use this drop list control to allow users to remove columns from the data grid, we need to

inform them as the the detail of the column last removed - see below for a live example of this :-

Please notice that because I let the headers in the data grid be specified by the automatic column creation process,

the property values displayed in the drop list match those text entries in the column headers of the data grid.

Example 2 :

Okay then, now let us look at a second example of using a ComboBox control.

This time we will fill the drop list with simpler objects, these are just text strings, which

we will use for filtering conditions, see below :-

The idea for the GUI interactivity is that when a text string is selected, it is then applied to the

view object associated with the 'ItemsSource' property of the data grid. Doing this will then

cause a different set of rows to be seen, as long as the selection is different to that chosen

previously ;-)

Let us take a look at the Vulcan code to do this :-

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

As you can see in the above image, it is simply a case of adding each string object, one at a time.

If in another situation you had the string items in a system array, then the whole set could be added

in one go by using the AddRange() method.

To reinforce this second example, here is a sample of the sort drop list, which also has a simple

set of text string objects in its collection. See below :-

You may have noticed that the data grid row colouring has been changing, we this has to do with

what we will discover in example 3 below.

Example 3 :

Having associated the 'ItemsSource' property of one ComboBox with a data column collection, and another

where we filled the control object collection with simple text objects, we can now look at the case where

the objects are 'ComboBoxItem's - made from Brush objects.

First lets see how the drop list appears in use :-

You will now probably realise that this is how we can colour the alternate rows of the data grid.

Because each item in the list is a 'ComboBoxItem', we can use properties of this class of object

to improve the visual effect when the list is displayed.

In order to achieve the visual effect above, I have used the following code to generate the objects :-

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

And this short method is used in conjunction with the simple code shown below - note many

of the brushes have been left out to shorten the detailed list.

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

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

Once again, the Tooltip is just to allow the user to see which list item was used previously to colour the data grid.

One criticism of the visual effect of the drop list was that the hex values were not necessary - this may be the case,

or indeed not. However, we can look at doing something to improve the user experience.

Changing the content of the ComboBoxItem to the 'ToString()' value of the brush yields the same hex output,

which is not surprising to any experienced dotNet user. So I have changed the code to produce the following :-

As you can see above, the user at least gets to know what the English term for the 'color' of the brush object.

So in the image the 'Yellow' brush must have been selected previously - see below :-

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

Well we have had to do a bit of a fiddle with the code to achieve all this, but at least it works.

Out of interest, lets start with the last bit of the Vulcan code used in the process of applying the new colours

to the data grid - check out below :-

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

In the code shown above, we now hold the brush 'in' the background property, and the 'content' property of

the ComboBoxItem is a simple string which describes the colour.

Lets see this second approach to making the oCBI (ComboBoxItem) :-

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

So we need to pass into the method as a second parameter, the text value of the color as a string value.

The newly adapted method looks like this :-

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

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

There may even be a smarter way to do the same thing, but at least we have got rid of the hex values which are

meaningless to the casual user, and got readable and more meaningful detail to most users !

So the last thing to say about this third method, is that we need to call the newly named method in

our Initialize() method as so :-

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

Now we need to move on to events, for ComboBoxes, and responding to events to make interesting things happen ;-)

Events, and How to Respond :

To get a good idea of how to set up and use an event for a ComboBox control, let us use

the Filter condition drop list in the example app we have been following so far in this section of these notes.

Let us look at what the Designer has done for us when we placed the control on the design surface, and then when

we chose an event to associate with an event handler.

Below is both the XAML markup and the relevant section of the properties pane (PP) :-

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

So by me selecting the default event for this control ('SelectionChanged') the Designer has created the

code shown in the left hand portion of the above image.

Now let us see what the Designer has done to create a 'stub' in the code behind the XAML markup :-

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

All I have done is add line 29 which tells the 'system' to run my method called 'DoChangeFilterCondition'.

This means that at build and run time the event association is already 'hooked up' and will therefore 'fire' when

I do anything to fill and pre-select an item in the control. I often do this sort of setup procedure, and don't wish

the event to trigger any action while I am in the process of setting up the GUI.

For this reason, I create a pair of methods called SetMyEventHandlers and UnsetMyEventHandlers. These

are seen below for this application :-

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

Obviously, these methods need to be called just going into the Initialize() method, and then just before leaving it.

This is how it looks :-

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

This now prevents any action taking place during the initialization process.

It also means that if the code needs to do anything serious with a collection in a drop list as part

of the on-going runtime processing, say like clearing and replacement with a new of fresh collection,

then the two methods may be conveniently called Unset first - then the big changes - and then Set once

more. This stops any spurious output, and can even prevent unwanted error messages too, when

an action is attempted on an empty list, and where no list item has been selected etc. etc.

Very handy, so remember the technique.

Finally, for this 'Filter' example, lets see what the method looks like which responds to the selection :-

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

This is a simple example, but useful. It shows that line 337 is required to extract the text string

from the list collection item selected, and allow this to be used to set the row filter property, as

well as change and set the Tooltip string.

Before we move on to another good ComboBox eample, let us now look at the more complex case of the Column

drop list control, where as part of the action the selected item is removed from the list, or in fact the collection of

objects which the ComboBox is maintaining, or is associated with.

First lets start with the completed stub code :-

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

And now we need to see what the 'DoRemoveColumn' method looks like :-

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

Before we go into detail about what is been done in the code, lets us see where I started out a little while back

while developing the app.

Before I added the correct 'handling' code in Vulcan, this is what I saw when I was part way through removing a

column :-

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

Then after adding a try / catch / end try code structure I got this :-

Slightly better in some ways, but still not what I am after ;O)

If you now look at the offending section of code producing this output you will see this :-

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

Yes, simply because our code to handle drop list item selection expects an item to

be present, and while clearing the collection and refreshing it with another and new

collection we need to have events switched off, so here is the fully working code section

once more :-

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

There may be other arrangements of the code which also work, but this seems to handle most

eventualities. One thing we have to avoid is no handlers being on after some sort of issue, as well as

there being two sets of the same handler associations being made.

Yes, if the 'Set' method is used more than once we could have problems. 'Unset' is different, it will not

do anything, or cause problems, if it is used multiple times and there are no associations to remove - then

nothing happens.

Detailed Explanation of the Column of Removal Process :

Referring to the code above (in the 'DoRemoveColumn' method), line 302 accesses the selected column item.

Then two data structure changes need to be made - removing a column from the data table, and remaking a

new data columns collection from the 'chopped down' table.

The disassociation of data sources, and the fresh associations, have to be done with care and have plenty of

testing done on the code produced.

Line 306 is simple enough to read, the selected column is removed from the DataTable column collection.

However, nothing will show in the data grid unless we set the ItemsSource property to NULL (line 308), before

re-association with the new table object on line 310.

Then after turning off any event handler associations, the data column collections is effectively 'disconnected'

and after a new data column collection is made, it is reconnected to the combo box, as its ItemsSource.

The event handling is only turned off for one line of operation - that on which the data source is effectively removed.

I am sure this sort off procedure will be repeated many times in similar situations and circumstances over the

months and years to come. So study it carefully.

Final Useful Example of a ComboBox Control in use :

Although the following example is short, it is extremely useful indeed.

Quite often on a WPF window you will need to supply a drop list if available printers.

Also it is good to pre-select the one which is the current default printer.

View the image below to see such a list in a WPF combo box :-

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

Although there are many similarities in the code to find the set of available printers, one thing which

does differ in the code shown below, is the way the list of string objects are provided to the control :-

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

Notice that the 'ItemsSource' property is the one we associate with the system array of string objects.

I have left line 97 in place to remind folk like myself, that things have changed from the old Forms

way of doing things.

We also need to make a special note to include the following namespace at the head of the PRG file :-

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

As well as include a reference in the list of assemblies to "System.Drawing".

Obviously, we access the selected printer in the usual way, since it is still a string object.

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

I will leave you to do more with events and handling etc. ;O)

See you in the next subsection.

PHH - February 15th. 2010


ListView

A 'ListView' control can have more than one viewing 'modes'.

A commonly used mode is the 'GridView', and an example is shown below - it was taken from the beta2

SDK documentation for .NET 4.0 and is a useful place to start.

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

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

PHH - February 2010.


Image

In WPF we have an Image control, and not one called "PictureBox", as in Forms.

The SDK documentation from Microsoft has these 'remarks' to make about the Image class :-

The Image class enables you to load the following image types: .bmp, .gif, .ico, .jpg, .png, .wdp, and .tiff.

When displaying a multiframe image, only the first frame is displayed. The animation of multiframe images

is not supported by the Image control.

Until the image content is loaded, the ActualWidth and ActualHeight of the control will report as zero, because

the image content is used to determine the final size and location of the control.

For a fixed size control, the Width and/or Height properties can be set. However, to preserve the media's

aspect ratio, set the Width or Height properties but not both.

In order to get us going, let us see a working example. here is on below :-

Let us see what is required to get this 'picture' to show up in and 'Image' control.

First of all here is the XAML script used to make our nice sports car picture :-

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

From the image above you can see that the name of the 'Image' control is "oimgMine", yes, a little naff but

it will do for now.

So in the Vulcan code shown below we see how to this control is supplied with a suitable set of picture data :-

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

In the above image of the VN code we see we need to first create a 'BitmapImage' object. Once we have a suitable

such object we can the associate it with the 'Source' property of the 'Image' object.

The first thing to notice is the action of Vulcan code line 35, which sets the 'Stretch' property. If we comment this

line we get the image below - we will only examine the top right corner :-

Compare this with the full picture displayed previously, and you will clearly see that some of the original jpeg information

is missing. A good chunk of Alwyn is missing. Maybe another comparative image set is helpful here.

Look closely at these :-

Now let us move on to see what the 'DecodePixelWidth' and the similar for 'height' do to the BitmapImage' which is then

applied to 'Image' control as its 'Source'.

Before we move on to see in depth what happens when changing these, we need to look at the information provided

in the 'MessageBox' by lines 38 to 42 of our code :-

Once we change our VN code to be as follows :-

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

this output changes to the following :-

which is not too surprising, but lets see what has changed in the picture :-

If we stay with the top right of the larger picture, we can see a definite 'fuzziness' which was not

there previously.

You will probably realise that if we changed the commented lines so that we had the pixel height

set to 190 (as in line 32) we would get a result which was a little sharper (old height was 134).

It is not the proportions (or perspective) which change at all by doing this, just the resolutions.

Early Summary :

We need to take into account the members of both 'BitmapImage' as we as the 'Image' control itself

when we display picture information on a WPF window.

There are many properties and method for each of these classes and it will take time to get more familiar

with them. So we need to experiment and write some demo apps too.

Universal Resource Identifier :

Look at the following code to see how we can change the VN to do the same image operation a slightly

different way :-

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

This can prove useful when we wish to reuse the same source image to populate a number of different

'Image' controls on our WPF window.

We will see how this is done to make the 'Puzzle Game' where we need fifteen (15) such 'Image' controls,

each with a different clipped version of the original picture.

Attempting a Jigsaw Puzzle :

Okay you guys, now we are starting to get somewhere ;-)

If we apply some of the ideas we have seen here so far, we can make ourselves a nice puzzle game.

Let us see a start to such a game below :-

And what of the VN code which generated this ? Well see below to get an idea :-

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

The code shown above produces one of the four pieces of the puzzle picture.

In the test example shown in the previous image, there are four similar such code 'chunks'

or sections, here is another :-

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

Notice that the same 'BitmapImage' object is being used for all the pieces. All that is happening is

that the image is being clipped before it is being associated with its 'Image' control object.

So effectively we have four image controls showing through one another, as the bits missing are taken

as transparent.

The Puzzle Game :

And now we have done enough with the 'Image' control to start setting up the board for a new puzzle game.

I got the idea after some holiday reading of a book called "Introducing Silverlight 2" by Laurence Moroney. I

strongly recommend you getting hold of a copy and put aside a few hours to gain some insight into where we

may be headed.

Below is the 'picture' of the app when it is launched at the moment, see below :-

You may realise why I called it a 'picture' and not an image, since we actually have fifteen 'Image'

controls, showing different portions of the same image.

You may have 'twigged' by now that when we scramble, or randomise the pieces, the game is one

of those where you move the empty 'slot' around until the picture is all sorted out.

Let us see some of the Vulcan code to display the picture correctly.

[ Unfortunately for Alwyn he does not feature in the moment of glory, as he has been cropped ! ]

Let us start be viewing the very start of the code 'In Front' :-

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

We are going to make strongly typed arrays of image and canvas objects.

Vulcan code lines 40 to 43 do all the setting up required to start the game. So let us start

by looking at the 'CreatePicture' method below :-

*** Note *** in order for us to find it easier to translate Laurence Moroney's C# code I have set

the compiler switch for zero based arrays, see below :-

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

To get the properties option, right click the project element in the SE pane, as seen above.

In the properties tag select the side tab for Language as below :-

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

So the Vulcan code shown below uses zero based arrays in order to 'do its stuff' !

Let us now examine in detail the code to set up the game's board :-

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

The clipped images are added to each canvas before adding the first 15 to the 'GameContainer' canvas.

If you wish to know more about clipping an the likes, then consult the SDK documentation and also get

yourself a copy of the Silverlight book to read the example through in detail.

At the moment, so that I can check that all individual images are created correctly, the 'Shuffle' method is incomplete.

Look at how it looks below, before the randomising process is added :-

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

We will revisit this once we have seen how the board is drawn each time the game situation requires it.

Here is the code for the method to draw the board :-

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

In the code shown above the canvas objects holding the separate image fragments (those clipped) are placed in

suitable positions on the window, within the 'GameContainer' canvas object.

So basically, the game code does not swap images around, but alters the positions at which the containing canvases

are displayed within the larger and containing canvas. You need to study the code detail very closely to appreciate

exactly what is going on.

Getting the Shuffle Right :

Okay then, we seem to have some suitable working code and this is one result of running it :-

Obviously, there is also a chance that some pieces come together and produce a larger coherent 'mini'

picture, but that is chance for you. I had to run the app about four or five times to get a complete visual

mix-up.

So what does the new 'Shuffle' code look like ? - see below for the answer :-

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

Lines 100 and 101 were used in testing, to seed the random object differently,

For instance, using the '13' line as a seed we get the same initial pattern each time we run the

app, and this is shown below :-

This sort of functionality could be useful if you wished to do some type of practice or training

in working out a solution to the same initial conditions.

For now I will return the code to producing a different initial patter each time.

Responding to a Mouse Click :

So we are now going to add the last required part of our game playing code.

We need to be able to click any of the picture squares, and if they are next to the 'empty' square

location, have the clicked image swap places with the empty square.

You will remember, from the earlier code to create the separate canvas objects, in the 'SetUpBoard'

method, we have already created the event handler associations.

So all we need to do now is to provide the code for the single method "Page_MouseLeftButtonDown()"

as well as a small method for checking to see if after a move the result is a full correct picture - that is

we have a winner !

More of winning and that later ;-)

Here is the main 'square movement' method required :-

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

In the Vulcan code above we can see that only after a swap over of squares has been made do we then

check to see if the resulting arrangement is fully correct, and "a winner is born" ;-)

Checking for a Winner ! :

This is not as complex as you may think. Study the code below to see how its done :-

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

And now for a confession, I am not very good at games like this and so my best efforts so far are shown below.

Yes, some may say pathetic - well let them try - I will place this in Pearls to see how any other colleagues do!!!

My efforts are shown below :-

Obviously, extra code can be added to allow numbers of clicks to be counted, as well as best performances

of game players to be recorded for future comparisons, and one other feature I would certainly appreciate,

a cheat feature - where an offending square can be dragged onto the desired position and the swap to take

place even though the empty square is not a nearest neighbour ;O))

I like that idea, so I will use as an excuse the Drag Drop chapter of these eNotes. Go to the section in

chapter eight and see how we can add DnD functionality to our game - a nice cheat level, just for me !!!

What may be Achieved by Cheating :

I have added the code to the solution for the Puzzle Game and below is what may be achieved using the

new Drag Drop functionality :-

Go to the relevant subsection of the DnD chapter to see the details of what has been done.

Lets move on, see you in the next section.

PHH - March 2010.


Useful Working Code

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

<Window x:Class="WPF_PuzzleGame.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Height="438"

Width="416" Name="winPuzzleGame" Title="WPF : Puzzle Game :">

<Canvas x:Name="LayoutRoot" >

<!--<Canvas x:Name="GameContainer" />-->

</Canvas>

</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 WPF_PuzzleGame

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

END CLASS

END NAMESPACE

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

// PuzzleInFront.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

BEGIN NAMESPACE WPF_PuzzleGame

/// <summary>

/// My code In Front for Window1

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private obiTest as BitmapImage

private oU as Uri

private i as Image[]

private cI as Canvas[]

private board as int[]

private GameContainer as Canvas

method Initialize() as void

self:GameContainer := Canvas{}

self:LayoutRoot:Children:Add(self:GameContainer)

i :=Image[]{16}

cI := Canvas[]{16}

board := int[]{16}

try

self:CreatePicture()

self:SetUpBoard()

self:Shuffle()

self:DrawBoard()

catch oEx as SystemException

MessageBox.Show(oEx:message)

end try

return

method CreatePicture() as void

self:oU := Uri{"C:\Temp\CarAJB.jpg"}

self:obiTest := BitmapImage{}

self:obiTest:BeginInit()

self:obiTest:UriSource := self:oU

self:obiTest:EndInit()

return

method SetUpBoard() as void

local r as RectangleGeometry

local oI as Image

local oC as Canvas

local nx, ix, iy as int

nx := 0

for ix := 0 upto 3

for iY := 0 upto 3

nx := (ix*4)+iy

oI := Image{}

oI:Height := 400

oI:Width := 400

oI:Stretch := Stretch.UniformToFill

r := RectangleGeometry{}

r:Rect := Rect{(ix*100),(iy*100),100,100}

oI:Clip := r

oI:Source := self:obiTest

oI:SetValue(Canvas.TopProperty, Convert.ToDouble(iy*100*-1))

oI:SetValue(Canvas.LeftProperty, Convert.ToDouble(ix*100*-1))

i[nx] := oI

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

oC := Canvas{}

oC:Width := 100

oC:Height := 100

oC:Children:Add(i[nx])

oC:SetValue(Canvas.NameProperty, "C"+nx:ToString())

oC:MouseLeftButtonDown += MouseButtonEventHandler{self, ;

@Page_MouseLeftButtonDown()}

cI[nx] := oC

if nx < 15

self:GameContainer:Children:Add(cI[nx])

endif

next

next

return

method Shuffle() as void

local i as int

for i := 0 upto 15

board[i] := i

next

//--------- now randomize ---------

local rand as Random

//rand := Random{}

//rand := Random{13}

local oDT as DateTime

oDT := DateTime.Now

rand := Random{oDT:Second}

local n, n1, n2, swap as int

for n := 0 to 100

n1 := rand:@@Next(15)

n2 := rand:@@Next(15)

if n1 != n2

swap := board[n1]

board[n1] := board[n2]

board[n2] := swap

endif

next

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

board[15] := -1

return

method DrawBoard() as void

local n, nx, ny as int

nx := 0

ny := 0

for n := 0 upto 15

nx := n/4

ny := n%4

if board[n] >=0

cI[board[n]]:SetValue(Canvas.TopProperty, Convert.ToDouble(ny*100))

cI[board[n]]:SetValue(Canvas.LeftProperty, Convert.ToDouble(nx*100))

endif

next

return

method Page_MouseLeftButtonDown(sender as object, e as MouseButtonEventArgs) as void

//MessageBox.Show("mouse has been clicked ...")

//----- find canvas which raised the event -----

local oC as Canvas

oC := (canvas)sender

local nCanvasID, nBoardLoc, nEmptyLoc, i as int

nCanvasID := -1

nBoardLoc := -1

nEmptyLoc := -1

for i := 0 upto 15

if oC == cI[i]

nCanvasID := i

exit

endif

next

for i := 0 upto 15

if board[i] == nCanvasID

nBoardLoc := i

else

if board[i] == -1

nEmptyLoc := i

endif

endif

next

//----- check if a move can be made -----

local lResult as Boolean

lResult := (nBoardLoc == nEmptyLoc+1)||(nBoardLoc == nEmptyLoc-1)||;

(nBoardLoc == nEmptyLoc+4)||(nBoardLoc == nEmptyLoc-4)

local nx, ny as int

if lResult

nx := nEmptyLoc/4

ny := nEmptyLoc%4

cI[nCanvasID]:SetValue(Canvas.TopProperty, Convert.ToDouble(ny*100))

cI[nCanvasID]:SetValue(Canvas.LeftProperty, Convert.ToDouble(nx*100))

board[nEmptyLoc] := nCanvasID

board[nBoardLoc] := -1

self:CheckWinner()

else

//----- do nothing -----

endif

return

method CheckWinner() as void

//MessageBox.Show("... will see if there is a winner ...","Check :")

local bCompleted as Boolean

bCompleted := true

local n as int

for n := 0 upto 14

if n != board[n]

bCompleted := false

exit

endif

next

if bCompleted

MessageBox.Show("Congratulate yourself ... well done indeed !!!","WINNER :")

endif

return

END CLASS

// method TestDraw2() as void

/*local r as RectangleGeometry

local oI1 as Image

oI1 := Image{}

oI1:Height := 400

oI1:Width := 400

oI1:Stretch := Stretch.Fill

r := RectangleGeometry{}

r:Rect := Rect{100,200,100,100}

oI1:Clip := r

oI1:Source := self:obiTest

self:GameContainer:Children:Add(oI1)

local oI2 as Image

oI2 := Image{}

oI2:Height := 400

oI2:Width := 400

oI2:Stretch := Stretch.Fill

r := RectangleGeometry{}

r:Rect := Rect{300,200,100,100}

oI2:Clip := r

oI2:Source := self:obiTest

self:GameContainer:Children:Add(oI2)

local oI3 as Image

oI3 := Image{}

oI3:Height := 400

oI3:Width := 400

oI3:Stretch := Stretch.Fill

r := RectangleGeometry{}

r:Rect := Rect{0,300,100,100}

oI3:Clip := r

oI3:Source := self:obiTest

self:GameContainer:Children:Add(oI3)

local oI4 as Image

oI4 := Image{}

oI4:Height := 400

oI4:Width := 400

oI4:Stretch := Stretch.Fill

r := RectangleGeometry{}

r:Rect := Rect{0,0,100,100}

oI4:Clip := r

oI4:Source := self:obiTest

self:GameContainer:Children:Add(oI4)*/

//return

END NAMESPACE

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

PHH - March 2010.


Chapter 12 - Context Menus

PHH - March 2010.


Chapter 13 - Using the System Clipboard

Enter topic text here.


Chapter 14 - Collections and Enumerators

The main essence of this chapter can be put quite simply ....

"To get the most from WPF and it underlying technologies, we must get a firm understanding of

Collections and Views."

Binding of data is very often based on an underlying collection, or indeed it 'view'.

A 'View' object is basically a wrapper which goes around a collection.

We can have one underlying collection of objects which has many views. the same collection but

a number of ways of looking at it.

So we may have UI elements bound to views showing sorted, grouped or filtered 'views' of the same

fundamental collection.

Apparently, when we bind a collection directly to a UI element - like a ListBox - in XAML script, we

get a default view bound for us, without asking.

I suggest that we all get used to using Views and not the Collections directly.

Enumerators are simply objects which allow us quick and easy access to the set of objects 'held' by

a collection or view.

An example of this above, may be that we have a collection of 'Person' objects, and a handful of views

bound to UI elements like List and Combo boxes. We may have reduced sets of persons, 'viewed' by

gender (men and women) and possibly sets like 'Older' and 'Younger'.

The sets for Men, Women, Old and Young would be filtered views. That is four separate view objects based

on the same underlying collection, which have filter conditions applied. This means that each view would

see a smaller set of objects than the one on which it is based, and that generally each set would have a

different size, or count.

If we then create our enumerator object from these views, then the enumerator will allows us to access the

'Person' objects in these different, and very specific, subsets.

Enough of the chat, and setting out the 'mental model', let us move on to seeing some of this stuff in action.

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

PHH - 17th. April 2011.


Collections - types and Viewing

If we search the WPF 4 documentation for the SDK for the word 'collection' we get the following

as a 'found set' :-

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

The 'CollectionView' class is a base class and is not to be used by us directly.

Things will become clearer when we read the following SDK documented explanation :-

When you bind to a data collection, you may want to sort, filter, or group the data.

To do that, you use collection views.

You can think of a CollectionView as the layer on top of the binding source collection that allows you to navigate

and display the source collection based on sort, filter, and group queries, all without having to manipulate the

underlying source collection itself.

If the source collection implements the INotifyCollectionChanged interface, the changes raised by the CollectionChanged

event are propagated to the views.

All collections have a default CollectionView.

You should not create objects of this class in your code.

To create a collection view for a collection that only implements IEnumerable, create a CollectionViewSource object,

add your collection to the Source property, and get the collection view from the View property.

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

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

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

BindingListCollectionView, ListCollectionView, and ItemCollection

PHH - 17th. April 2011.


Enumerators - examples

To aid the coder / developer, the Collection classes are provided with a means of getting at a suitable

enumerator, so we can easily cycle through the collection of objects.

So ICollectionView, ObservableCollection, and PagedCollectionView classes all have a 'GetEnumerator' method, as

do a few others.

Before we get into any more detail, see the following image where we have some working code to study :-

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

In fact if your search the WPF4 SDK for 'GetEnumerator' we get 68 entries in the left hand side pane !

So we obviously need to know what these are, and how to use them to our best advantage.

Let us look at the ninth one in this long list to see what it has to tell us about an Enumerator, and how best

to use one - checkout the details shown below :-

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

It is VERY important that we ready and fully understand the SDK details shown below.

Study these carefully before you move on :-

Remarks :

Enumerators only allow reading the data in the collection.

Enumerators cannot be used to modify the underlying collection.

Initially, the enumerator is positioned before the first item in the collection.

Reset also brings the enumerator back to this position.

At this position, calling Current throws an exception.

Therefore, you must call MoveNext to advance the enumerator to the first item of the collection before reading the

value of Current.

Current returns the same object until either MoveNext or Reset is called.

MoveNext sets Current to the next item.

After the end of the collection is passed, the enumerator is positioned after the last item in the collection, and calling

MoveNext returns false.

If the last call to MoveNext returned false, calling Current throws an exception.

To set Current to the first item of the collection again, you can call Reset followed by MoveNext.

An enumerator remains valid as long as the collection remains unchanged.

If changes are made to the collection, such as adding, modifying or deleting items, the enumerator is irrecoverably invalidated

and the next call to MoveNext or Reset throws an InvalidOperationException.

If the collection is modified between MoveNext and Current, Current will return the item that it is set to,

even if the enumerator is already invalidated.

The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is intrinsically

not a thread-safe procedure.

Even when a collection is synchronized, other threads could still modify the collection, which causes the enumerator to throw

an exception.

To guarantee thread safety during enumeration, you can either lock the collection during the entire enumeration or catch the

exceptions resulting from changes made by other threads

Taking note of these details, and heeding the warnings, we can improve our previous code by trying to catch any exceptions

which may be thrown.

Study the working code below :-

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

Also be aware that the order of the items in the collection can't be guaranteed to be always the same each

time an enumerator is instantiated on the same collection or view.

I seem to have rad somewhere in the SDK documentation about such matters, so be warned.

I suggest you look at this a bit like the returned data sets from SQL queries - more of a 'set based' situation than

the old 'array' of fixed elements we came from in VO.

Trying a Simple Experiment :

In running the 'WorldTime4' app, of which this code is a part, I tried to change a property which was responding in

real time to editing changes I made through the GUI - the property was set as shown below (see line 70) :-

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

However, no matter how hard I tried I could not get the Try/Catch to throw me an error screen.

So it would appear that it may take a removal or insertion of an item from/in the collection to cause such an error to be thrown.

*** Just be aware of the situation ***

The Process in Hand :

In our example above, the process which is being carried out, I call the 'process in hand' or the 'task in focus', is one

which is going through the set of 'GlobalPlaceTime' objects and for each item (or object) we are updating the DateTime

for the time zone for that particular person, or item.

This task is best done using an enumerator on the underlying collection, rather than with one on the View on which we

could have a filter, and do have a sort. It is the filter which may cause a problem, and exclude the items the filter does

not make visible. Once again be aware - however, this may work in your favour once you do know, as it could be an easy

way to get to the sub-set of objects you wish to process in some way or another.

PHH - 17th. April 2011.


working script and code

PHH - 17th. April 2011.


Chapter 15 - 'WorldTime4' sample

PHH - 20th. April 2011.


working script and code

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

//

// Class1.prg

//

//----- created April 16th. 20011 -----

//----- for ClickStartXAML / WPF -----

#using System.Windows

#using System

#using System.Windows.Controls

#using System.Windows.Media.Imaging

BEGIN NAMESPACE GlobalPlaceTime4

CLASS GlobalPlaceTime Inherit DependencyObject

static public ZoneProperty := DependencyProperty.Register("Zone", typeof(string), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{""}) as DependencyProperty

static public NameProperty := DependencyProperty.Register("Name", typeof(string), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{""}) as DependencyProperty

static public CityProperty := DependencyProperty.Register("City", typeof(string), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{""}) as DependencyProperty

static public LocalDateTimeProperty := DependencyProperty.Register("LocalDateTime", typeof(DateTime), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{}) as DependencyProperty

static public TimeDiffProperty := DependencyProperty.Register("TimeDiff", typeof(TimeSpan), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{TimeSpan.FromHours(0)}) as DependencyProperty

static public FaceImageProperty := DependencyProperty.Register("FaceImage", typeof(BitmapImage), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{}) as DependencyProperty

static public HourOfDayProperty := DependencyProperty.Register("HourOfDay", typeof(int), typeof(GlobalPlaceTime4.GlobalPlaceTime), PropertyMetadata{-1}) as DependencyProperty

CONSTRUCTOR()

SUPER()

self:Initialize()

RETURN

CONSTRUCTOR(n as string, c as string, z as string)

SUPER()

self:Name := n

self:City := c

self:Zone := z

self:Initialize()

RETURN

method Initialize() as void

self:LocalDateTime := DateTime.Now

return

method SetZoneTime(oDT as DateTime) as void

local pZone as TimeZoneInfo

pZone := TimeZoneInfo.FindSystemTimeZoneById(self:Zone:Trim())

self:LocalDateTime := TimeZoneInfo.ConvertTime(oDT, TimeZoneInfo.Local, pZone)

return

method ToString() as string

return self:City+" | "+self:Name+" | "+self:Zone+" | "+self:LocalDateTime:ToString()

//return (string)self:GetValue(CityProperty)+" | "+(string)self:GetValue(NameProperty)+" | "+(string)self:GetValue(ZoneProperty)+" | "+((DateTime)self:GetValue(LocalDateTimeProperty)):ToString()

Access City as string

return (string)self:GetValue(CityProperty)

Assign City(cText as string) as void

self:SetValue(CityProperty, cText)

return

Access Name as string

return (string)self:GetValue(NameProperty)

Assign Name(cText as string) as void

self:SetValue(NameProperty, cText)

return

Access Zone as string

return (string)self:GetValue(ZoneProperty)

Assign Zone(cText as string) as void

self:SetValue(ZoneProperty, cText)

return

Access LocalDateTime as DateTime

return (DateTime)self:GetValue(LocalDateTimeProperty)

Assign LocalDateTime(oDT as DateTime) as void

self:SetValue(LocalDateTimeProperty, oDT)

return

Access TimeDiff as TimeSpan

return (TimeSpan)self:GetValue(TimeDiffProperty)

Assign TimeDiff(oTS as TimeSpan) as void

self:SetValue(TimeDiffProperty, oTS)

return

Access HourOfDay as int

return (int)self:GetValue(HourOfDayProperty)

Assign HourOfDay(nI as int)

self:SetValue(HourOfDayProperty, nI)

return

Access FaceImage as BitmapImage

return (BitmapImage)self:GetValue(FaceImageProperty)

Assign FaceImage(oBI as BitmapImage) as void

self:SetValue(FaceImageProperty, oBI)

return

END CLASS

END NAMESPACE // GlobalPlaceTime4

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

#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 GlobalPlaceTime4

BEGIN NAMESPACE WorldTime4

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

RETURN

PRIVATE METHOD cbSort_SelectionChanged(sender AS Object, e AS SelectionChangedEventArgs ) AS Void

if self:cbSort:SelectedIndex > -1

local cWhich as string

cWhich := (string)((ComboBoxItem)self:cbSort:SelectedItem):Content

do case

case cWhich = "Name"

self:GeneralSortApply(cWhich)

case cWhich = "City"

self:GeneralSortApply(cWhich)

case cWhich = "Zone"

self:GeneralSortApply(cWhich)

case cWhich = "Diff"

self:GeneralSortApply("TimeDiff")

case cWhich = "LDT"

self:GeneralSortApply("LocalDateTime")

otherwise

self:oView:SortDescriptions:Clear()

self:comboBox1:SelectedIndex := 0

MessageBox.Show("Can't apply sort !?")

endcase

endif

RETURN

END CLASS

END NAMESPACE

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

<Window x:Class="WorldTime4.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="World Time 4 :" Height="460" Width="490">

<Grid Background="Bisque">

<ComboBox Height="89" Margin="12,15,12,0" Name="comboBox1" VerticalAlignment="Top"

ItemsSource="{Binding}" Background="CadetBlue">

<ComboBox.ItemTemplate>

<DataTemplate>

<Border BorderBrush="Chartreuse" BorderThickness="3" Width="300">

<StackPanel Name="spMain" Orientation="Horizontal">

<StackPanel Name="spItem" Orientation="Vertical" Width="180">

<TextBlock Name="tbName" Text="{Binding Path=Name}" Background="White"

Foreground="Red" FontFamily="Century Schoolbook" FontSize="14"

FontStyle="Italic" FontWeight="Bold"/>

<TextBlock Name="tbCity" Text="{Binding Path=City}"/>

<TextBlock Name="tbZone" Text="{Binding Path=Zone}"/>

<TextBlock Name="tbLocalTime" Text="{Binding Path=LocalDateTime}" FontSize="12" FontWeight="Bold"/>

</StackPanel>

<StackPanel Height="80" Width="190" Orientation="Horizontal" HorizontalAlignment="Right">

<TextBlock Width="40" Height="80"/>

<Image Source="{Binding Path=FaceImage}" Stretch="Fill" Height="80" Width="80" HorizontalAlignment="Right"/>

</StackPanel>

</StackPanel>

</Border>

<!--*********************************************************************************************-->

<DataTemplate.Triggers>

<DataTrigger Binding="{Binding Path=City}" Value="London">

<Setter TargetName="tbCity" Property="Background" Value="Brown"/>

<Setter TargetName="tbCity" Property="Foreground" Value="Yellow"/>

<Setter TargetName="tbCity" Property="FontSize" Value="16"/>

<Setter TargetName="tbCity" Property="FontStyle" Value="Italic"/>

<Setter TargetName="tbCity" Property="FontWeight" Value="Bold"/>

</DataTrigger>

<Trigger Property="IsMouseOver" Value="true">

<Setter TargetName="tbName" Property="Background" Value="Black"/>

<Setter TargetName="tbName" Property="Foreground" Value="White"/>

<Setter TargetName="tbName" Property="FontSize" Value="22"/>

<Setter TargetName="tbName" Property="FontStyle" Value="Normal"/>

</Trigger>

<!--=============================================================================-->

<MultiDataTrigger >

<MultiDataTrigger.Conditions>

<Condition Binding="{Binding Path=Zone}" Value="AUS Eastern Standard Time"/>

<Condition Binding="{Binding Path=City}" Value="Melbourne"/>

</MultiDataTrigger.Conditions>

<!--=========================================================================-->

<Setter TargetName="tbZone" Property="Background" Value="Thistle"/>

<Setter TargetName="tbZone" Property="Foreground" Value="BlueViolet"/>

</MultiDataTrigger>

<MultiDataTrigger >

<MultiDataTrigger.Conditions>

<Condition Binding="{Binding Path=Zone}" Value="W. Europe Standard Time"/>

<Condition Binding="{Binding Path=City}" Value="Amsterdam"/>

</MultiDataTrigger.Conditions>

<!--=========================================================================-->

<Setter TargetName="tbZone" Property="Background" Value="Green"/>

<Setter TargetName="tbZone" Property="Foreground" Value="Beige"/>

</MultiDataTrigger>

</DataTemplate.Triggers>

</DataTemplate>

</ComboBox.ItemTemplate>

</ComboBox>

<!--=====================================================================================================-->

<TextBox Height="23" HorizontalAlignment="Left" Margin="120,118,0,0" Name="textBox1" VerticalAlignment="Top"

Width="170" Text="{Binding ElementName=comboBox1, Path=SelectedItem.Name, Mode=TwoWay,

UpdateSourceTrigger=PropertyChanged}" Background="#FFEAFAE3" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="120,158,0,0" Name="textBox2" VerticalAlignment="Top"

Width="131" Text="{Binding ElementName=comboBox1, Path=SelectedItem.City, Mode=TwoWay}"

Background="#FFEAFAE3" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="120,200,0,0" Name="textBox3" VerticalAlignment="Top"

Width="170" Text="{Binding ElementName=comboBox1, Path=SelectedItem.Zone, Mode=TwoWay}"

Background="#FFEAFAE3" />

<Image Height="139" HorizontalAlignment="Left" Margin="313,161,0,0" Name="image1" Stretch="Fill"

VerticalAlignment="Top" Width="135" Source="{Binding ElementName=comboBox1, Path=SelectedItem.FaceImage, Mode=TwoWay}"

/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="121,242,0,0" Name="textBox4" VerticalAlignment="Top"

Width="169" Text="{Binding ElementName=comboBox1, Path=SelectedItem.LocalDateTime, Mode=TwoWay}"

Background="#FFEAFAE3" />

<TextBox Height="23" HorizontalAlignment="Left" Margin="120,279,0,0" Name="textBox5" VerticalAlignment="Top"

Width="120" Text="{Binding ElementName=comboBox1, Path=SelectedItem.TimeDiff, Mode=TwoWay}"

Background="#FFEAFAE3" />

<Label Content="Name :" Height="28" HorizontalAlignment="Left" Margin="12,113,0,0" Name="label1"

VerticalAlignment="Top" Width="80" Foreground="Maroon" FontWeight="Bold" />

<Label Content="City :" Height="28" HorizontalAlignment="Left" Margin="12,153,0,0" Name="label2"

VerticalAlignment="Top" Width="80" Foreground="Maroon" FontWeight="Bold" />

<Label Content="Time Zone :" Height="28" HorizontalAlignment="Left" Margin="12,195,0,0" Name="label3"

VerticalAlignment="Top" Width="80" Foreground="Maroon" FontWeight="Bold" />

<Label Content="Local Time :" Height="28" HorizontalAlignment="Left" Margin="12,237,0,0" Name="label4"

VerticalAlignment="Top" Width="80" Foreground="Maroon" FontWeight="Bold" />

<Label Content="Hours Diff. :

" Height="28" HorizontalAlignment="Left" Margin="12,274,0,0" Name="label5"

VerticalAlignment="Top" Width="80" Foreground="Maroon" FontWeight="Bold" />

<Border BorderBrush="Red" BorderThickness="3" Height="113" VerticalAlignment="Bottom">

<StackPanel Orientation="Vertical" Height="103" VerticalAlignment="Bottom" >

<Grid Height="100">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

<ColumnDefinition Width="1*"/>

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="1*"/>

<RowDefinition Height="1*"/>

</Grid.RowDefinitions>

<Slider Height="23" Name="slider1" Width="466" Value="{Binding ElementName=comboBox1, Path=SelectedItem.HourOfDay}"

Minimum="0" Maximum="24" Interval="1"

Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="9"/>

<Border BorderBrush="Black" BorderThickness="1" Grid.Column="0" Grid.Row="0">

<Image Grid.Column="0" Grid.Row="0" Source="Images/moonNight1.jpg"

ToolTip="Everyone in bed by now!?" Stretch="Fill"/>

</Border>

<Border BorderBrush="Black" BorderThickness="1" Grid.Column="1" Grid.Row="0">

<Image Grid.Column="1" Grid.Row="0" Source="Images/starsNight1.jpg"

ToolTip="Deep sleep" Stretch="Fill"/>

</Border>

<Border BorderBrush="Black" BorderThickness="1" Grid.Column="2" Grid.Row="0">

<Image Grid.Column="2" Grid.Row="0" Source="Images/CockMorning1.jpg"

ToolTip="Dawn, the cockerel awakens all ...." Stretch="Fill"/>

</Border>

<Border BorderBrush="Black" BorderThickness="1" Grid.Column="4" Grid.Row="0">

<Image Grid.Column="4" Grid.Row="0" Source="Images/sunNoon.jpg"

ToolTip="Noon - middle of the day." Stretch="Fill"/>

</Border>

<Border BorderBrush="Black" BorderThickness="1" Grid.Column="6" Grid.Row="0">

<Image Grid.Column="6" Grid.Row="0" Source="Images/teaTime1.jpg"

ToolTip="Tea to be taken ..." Stretch="Fill" />

</Border>

<Border Grid.Column="7" Grid.Row="0" BorderBrush="Black" BorderThickness="1">

<Image Source="Images/sunSet1.jpg" ToolTip="Evening starts at sunset." Stretch="Fill"/>

</Border>

<Border Grid.Column="8" Grid.Row="0" BorderBrush="Black" BorderThickness="1">

<Image Source="Images/startsNight1.jpg" ToolTip="Bed is beckoning!" Stretch="Fill" />

</Border>

</Grid>

</StackPanel>

</Border>

<ComboBox Height="23" HorizontalAlignment="Left" Margin="312,118,0,0" Name="cbSort" VerticalAlignment="Top"

Width="63" SelectionChanged="cbSort_SelectionChanged">

<ComboBoxItem Content="Name" />

<ComboBoxItem Content="City" />

<ComboBoxItem Content="Zone" />

<ComboBoxItem Content="Diff" />

<ComboBoxItem Content="LDT" />

</ComboBox>

<Label Content="Sort by :" Height="22" HorizontalAlignment="Left" Margin="314,141,0,0" Name="label6"

VerticalAlignment="Top" Width="61" FontSize="10"/>

</Grid>

</Window>

<!---->

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

// MyPartWindow1.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.ObjectModel

#using GlobalPlaceTime4

#using System.Collections

#using System.ComponentModel

#USING System.Windows.Threading

BEGIN NAMESPACE WorldTime4

/// <summary>

/// PHH - my Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private oGPT as GlobalPlaceTime

private oGL as GlobalList

private cAppdir, cImageDir as string

PRIVATE oView AS ICollectionView

private UpdateDateTime as DateTime

private oTimer AS DispatcherTimer

private nDelay as int

method Initialize() as void

self:nDelay := 5

self:cAppDir := System.IO.Directory.GetCurrentDirectory() +"\"

self:cImageDir := self:cAppDir+"..\Images\"

try

self:oGL := GlobalList{self:cImageDir}

self:oView := CollectionViewSource.GetDefaultView(self:oGL)

self:comboBox1:DataContext := self:oView

catch oEX as System.Exception

MessageBox.Show(oEX:message)

end try

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

self:oView:SortDescriptions:Clear()

self:oView:Refresh()

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

self:oGL:AddNewItem()

self:comboBox1:SelectedIndex := 0

self:UpdateDateTime := DateTime.Now

self:SetTimesCycle() //----- first time setup of regional times -----

self:GeneralSortApply("Name")

self:cbSort:SelectedIndex := 0

self:RunUpdatingLocalDT()

return

method GeneralSortApply(cWhich as string) as void

local oSD as SortDescription

oSD := SortDescription{cWhich, ListSortDirection.Ascending}

self:oView:SortDescriptions:Clear()

self:oView:SortDescriptions:Add(oSD)

self:oView:Refresh()

self:comboBox1:SelectedIndex := 0

return

method RunUpdatingLocalDT() as void

//----- build & run our timer -----

self:oTimer := DispatcherTimer{}

self:oTimer:Tick += System.EventHandler{ SELF, @timer1_Tick() }

self:oTimer:Interval := TimeSpan{0, 0, self:nDelay}

self:oTimer:Start()

RETURN

PRIVATE METHOD timer1_Tick( sender AS System.Object, e AS System.EventArgs ) AS System.Void

try

self:UpdateDateTime := DateTime.Now

//----- used so all 'batch' times are similar / comparable! -----

SELF:SetTimesCycle()

catch oEx as System.Exception

MessageBox.Show(oEx:message,"timer1 Error :")

end try

RETURN

method SetTimesCycle() as void

local nCount as int

nCount := self:oGL:Count

local oE as IEnumerator

//oE := self:oView:GetEnumerator()

//----- need to use underlying collection in case of a filter ! -----

oE := self:oGL:GetEnumerator()

local LocalDateTime as DateTime

local oG as GlobalPlaceTime

try

oE:Reset()

do while oE:MoveNext()

oG := (GlobalPlaceTime)oE:Current

self:GetSetLocalDateTime(oG)

enddo

catch oEX as System.Exception

MessageBox.Show(oEX:Message,"ERROR :")

end try

return

method GetSetLocalDateTime(oG as GlobalPlaceTime) as void

local oDT := self:UpdateDateTime as DateTime

local pZone as TimeZoneInfo

pZone := TimeZoneInfo.FindSystemTimeZoneById(oG:Zone:Trim())

local LocalDateTime as DateTime

LocalDateTime := TimeZoneInfo.ConvertTime(oDT, TimeZoneInfo.Local, pZone)

oG:LocalDateTime := LocalDateTime

oG:TimeDiff := LocalDateTime - self:UpdateDateTime

oG:HourOfDay := LocalDateTime:Hour

if LocalDateTime:Minute > 30

oG:HourOfDay +=1

endif

return

END CLASS

CLASS GlobalList INHERIT System.Collections.ObjectModel.ObservableCollection<GlobalPlaceTime>

// ---- thanks to Chris Pyrgas for his help with syntax etc. -----

private cImageDir as string

CONSTRUCTOR(cID as string)

local oG as GlobalPlaceTime

self:cImageDir := cID

SUPER()

oG := GlobalPlaceTime{"Paul Piko", "Melbourne", "AUS Eastern Standard Time"}

self:AddGuysImage(oG, "PaulPiko")

SELF:Add(oG)

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

oG := GlobalPlaceTime{"Willie Moore", "Birmingham", "Central Standard Time"}

self:AddGuysImage(oG, "WillieMoore")

SELF:Add(oG)

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

oG := GlobalPlaceTime{"Chris Pyrgas", "Athens", "E. Europe Standard Time"}

self:AddGuysImage(oG, "ChrisPyrgas")

SELF:Add(oG)

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

oG := GlobalPlaceTime{"Mike Pitcher", "London", "GMT Standard Time"}

self:AddGuysImage(oG, "MikePitcher2")

SELF:Add(oG)

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

oG := GlobalPlaceTime{"Robert vd Hulst", "Amsterdam", "W. Europe Standard Time" }

self:AddGuysImage(oG, "Robert_vd_Hulst")

SELF:Add(oG)

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

oG := GlobalPlaceTime{"Brian Feldman", "Miami", "Eastern Standard Time"}

self:AddGuysImage(oG, "BrianFeldman")

SELF:Add(oG)

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

RETURN

method AddNewItem() as void

local oGPT as GlobalPlaceTime

oGPT := GlobalPlaceTime{"Dan Logan", "Bucharest", "E. Europe Standard Time"}

self:AddGuysImage(oGPT, "DanLogan")

SELF:Add(oGPT)

return

method AddGuysImage(oG as GlobalPlaceTime, ImageFile as string) as void

local oBI as BitmapImage

oBI := BitmapImage{}

oBI:BeginInit()

oBI:UriSource := Uri{self:cImageDir+ImageFile+".jpg"}

oBI:EndInit()

oG:FaceImage := oBI

return

/*method DeleteOldItem(nS as int) as void

SELF:RemoveAt(nS)

return*/

END CLASS

END NAMESPACE

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

<Application x:Class="WorldTime4.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>

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

PHH - 20th. April 2011.


Chapter 16 - using 'real' XML data - sample

We are lucky to be able to follow through a prototype application I have recently done for a couple of my

'real world' clients - yes, real stuff, not demo and learning material !

Probably like many of you I have steered away from XML - certainly in the early days of .NET - but have

gradually come around to realising that it is important, and something which can't be swept to one side.

In fact, once we get into seeing how useful XML is under the hood for many things, we start to see that

XML can actually be our friend ;-)

In the image shown below we have a copy of part of my most recent 'Prescription' from the doctor. It is

a 'script' which belongs to me and so no confidentiality is being breached by showing it here.

Note we in Wales (UK) call these 'scripts', although the correct term/word is 'Prescription'.

Study the details below, and in particular the blackish square near the bottom right of image :-

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

Note that this blackish square has been printed on the script along with the text of the items required from the

Pharmacy, and the other details of Doctor, surgery, and also my name and address which are actually missing

off the top of this cropped image.

This printed 'square' is actually a 2D barcode, and contains all the details (or data) previously mention above.

With a suitable 2D bar 'wand' this barcode can be read into any UI element of the application in focus. The easiest

way to test the scanning device is in 'Notepad' - see below :-

If you are not aware, the scanner, or barcode 'wand', acts just similar to a keyboard (input) device. However, just

like a keyboard, the correct driver must be selected, with suitable settings.

It is enough to say here that I had to find the manufacturer's website and get the barcodes (single variety) to

programme the device so that I got UK character mapping and not the default US ones. Yes, it was even more

unreadable to the human eye before this ;0))

Now look at the prototype app of mine, and the TextBox element which is receiving the scanned data directly.

Look at the image below to see what the pure XML data looks like - study carefully, and checkout both the start

and the end of the text string, to see the 'tags' :-

If the barcode reader see what it can decode, we get a nice audible 'beep' and the characters appear as if typed by

a quick and proficient typist - they do NOT come onto the screen immediately.

Note the scanner tends to work in such a way that it either does not scan (and beep), or it produces a whole and

complete 'object'. This is denoted by the start and end tags of .... <xml> </xml> .

You will see later that I use these as a way of knowing it the "scanning / reading / decoding" process is complete.

In my prototype app form I have added the push buttons for the single steps of the full process, and this was done as

part of the development and testing phase. In fact, now that it runs OK I have rolled all these together into an

automatic cycle - which will be described below in detail.

Basically, by having the default setting as 'Automate' the relevant button can have its 'IsDefault' property set to TRUE,

and the whole thing simply does it all for the user. All the user has to do is scan in one script after another - BUT - at the

moment it is best to make sure that the decoding is complete and that an end tag is visible to match the starting tag.

In practice it works quite easily as the time to move on physically to a new script, hold it appropriately, then scan it,

takes longer than the 5 seconds which it takes to finish inputting the longest script.

In the image shown below, checkout the script line 22 which has the attribute setting at the right hand side :-

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

Now then, once we have a full and complete XML data object decoded and as a text string in our TextBox UI element, we need to

save this to file (a safety precaution at the moment) and then deserialize it into a .NET object in memory.

The national authority which deals with Public Health - we call the NHS (National health Service) for Wales, has issued to those

who have need of it, as document which explains the schema - or structure of the 'script object'.

The following image is part of this document - check it out now :-

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

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

In the image shown above you will have noticed that some fields are mandatory, and therefore need to be supplied

for the script to be a valid one.

The yellow for the 'Age' field means that for Wales it is different to England, and the integer field is mandatory. This means

that for any eagle eyed guys they can work out me age - further more they can then send me a card at my next birthday ;-)

However, these small details are all mine, and also are all publicly available as well.

Let us now turn our attention to how we deserialize the XML data. How do we make a class in code which is a true representation

of the XSD schema file that the NHS Wales provides developers ?

Here Microsoft comes to the rescue, and provides an app called ".NET Frameworks Xml Schema Tool" and which is 'xsd.exe' in reality.

In the following image we see one version of this 'tool' from Microsoft, BUT, please be aware that there seems to be more than one on

any given PC system. I tried to find the most appropriate for the latest version of the .NET Framework I had installed - I don't think that

the image displayed below is that one, although it does work satisfactorily.

You will need to search your own PC for the latest - meanwhile study the details below :-

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

More Recent Version :

While you were studying the above image, I went to look again, and found this below :-

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

This seems more up to date, as we now get to March of last year ;-)

Before we get to trying out this MS tool, let us take time to see what the MSDN entry has to say on the subject :-

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

We need to run the applet from the DOS prompt, and so we will need to do a couple of things to our desktop.

First, let us read on, and see how to get HELP, and possibly run the tool to make a class from the XSD file supplied by NHS Wales.

Study the image details below :-

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

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

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

As we can see from the three previous images there is a clear way for us to create a 'class' to use in our dotNet code.

Once we can make a class file, we can then from VS10 make a 'Class Library' - and since the default option is C# we can

then use the DLL produced, from within our Vulcan coded project. Yes, we will need to add an entry to the 'References' section.

Running the XSD.exe applet from the Desktop :

As is the following image get yourself an icon which can run the command prompt - DOS shell - or whatever you prefer to call it!

It also helps if you copy the exact version of the XSD.exe file you require, and place it in a folder nearer the root - I have done so

into a folder called "C:\XmlXsd\" .... this makes for less typing and hence errors, at the prompt itself.

After launching the DOS prompt environment and typing a couple of this I get the following on my development PC :-

As you can see in the image above, we are quite close to being able to use the 'c' switch to create a class file on disk.

First it helps to copy the XSD file into the same directory as the tool - so I have done as shown below :-

Now we can get to the DOS prompt and see if we can make a class file in C# code.

In the following image I have used the simplest of switch options to create a suitable code file - check it out :-

And reading the screen prompts it would appear that we have not had any errors, and therefore must have some useful

result to examine.

Let us see the target folder using WE once again :-

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

And judging from the file size (17 KB) we ought to have quite an amount of code to look at ;-)

The following image shows the start of the C# file generated for us - study the details :-

And now the very end of the same CS code file, having missed out a large chunk in the middle :-

Before we move on, notice that we had this success with an input at the DOS prompt of :-

"xsd.exe ScriptNHSW.xsd /c"

Deserializing to make an Object in Memory :

Before we look in detail at the few lines of code which do the deserialization of the XML script from the file stored

on disk. Let us take a few moments to see how we can use the object once it is in memory, and available to us to

use throughout our application coding.

In the working code shown below we have an object variable named 'self:res' and of type 'xml'. You may also notice

that I have added suitable comments to the code lines so that they relate directly back to the schema and hence the

barcode itself.

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

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

Why are the Properties Named with so Short Variable Names ? :

The thing I noticed immediately, when coding the prototype originally, was the very short variable names.

Having being used to dotNet and the helpful long names we all seem to use - to make our code VERY readable -

I found this difficult. However, there is a good reason why they are so short in this particular case.

The actual 'content' of the 2D bar code is limited to a set number of characters. Looking at the XML data above that

came directly into the Notepad test, we see that a certain amount of the data is made up of the variable names

themselves. So the longer the names the less the content of the barcode, and hence the smaller the 'script'

actually is.

Or to put it another way, if the variable names representing the properties / fields are short we can have more script

items entered on the one script, and reduce the need for multiple scripts for a patient requirement.

Out of interest, you may like to know that as resolutions of printing devices and scanners improves, the content

of the barcode increases. And already they are putting a higher limit on the size of the XML data string which can

be included on one script.

Let us see what the document of a deserialized script looks like - or at least part of it :-

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

Well, as you can see, a lot more understandable than the original scanned XML.

Reading and Validating, then Deserializing - the Details :

Now that we all have a good general idea of what is taking place (before we store data into SQL tables), we can

now more on to see the necessary details of how it is all done.

Saving the XML data to File :

Basically, the process of saving XML text strings to file is simple enough - BUT - we also need to check a few

things first ! We need to know that the XML string is a complete 'object' and also not with parts of previously scanned

XML strings either before or after the complete entity.

Let us leave these double checking issues for now, and look at the more general process of saving to file.

Study carefully the following image, to see what needs to be done :-

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

The method will only return TRUE if the saving to disk goes without a problem.

Obviously, we must have some detailed and complex rules in the method named "XmlComplete()" - we will see these

later when we know what is going on overall.

As you can see above, it is pretty standard coding for taking a string of characters and placing then in an external disk file.

At this point you may like to know that 'ShowDetails' is really just a MessageBox system, but one which may be toggled

on/off by the user - at the UI with the check box.

So basically, everything goes OK unless we have a text string which is other than one single XML object.

Checking Out the 'Complete' Tests :

Let us first look at the checking method (named 'XmlComplete' ) as it is partially collapsed, just to get an overview first :-

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

As you can see from the image we are checking to see the string is not empty.

That it has at least one start and one end tag.

That there is not more than one end or start tag.

And in fact we ought also to check that he start tag is before the end tag, and that the length of the xml string

has a meaningful length.

I will add these extra checks later.

Details of one of the expanded checks is shown below - note most of the others are very similar.

Study the details carefully :-

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

The checks are simple enough but the recording of the error details in an error log to both text log and SQL

table rows is more verbose, and bulks out the code somewhat.

Note the same error logging system is used throughout the app, not just in this section.

Now let us move on to see how to read the XML data and convert it to an object in memory.

Reading XML data and Converting it :

At the moment, with the state of the Prototype app as it stands, it seems like a good idea to be able read directly

from the XML string and create an object in memory.

This way I can also read any XML script stored in an SQL table, as a 'Master Archive' of the original script. The

current system of making a photo/scanned copy of each incoming script will be replaced with one where the XML

script string is saved in an SQL table.

So it seems like a good idea to be able to start with either a disk file, or an XML string from a table, and be able to

create the 'script' object as a dotNet object.

Below we will examine the method which does all of what we have just discussed -

If the file can be found on disk we then move on to the 'ValidateScript' method. At the moment with the current state

of the development of this prototype I wish to still continue even if the script seems to be non valid. We will still have

logged the issue in the error log and table.

Study carefully the detailed code shown below :-

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

What Does the Validation Code Look Like ? :

Validating XML data is a whole new world to me, and what follows is what I could glean from the internet, and also test

and get working for me.

The following image shows that he process may be new to us, but is not in itself difficult to appreciate - see below :-

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

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

Currently I only provide the one schema to be used for validation checks, and that is the same one as I used to create the

class previously.

Because the NHS Wales 2Dsx system is still being finalised, it could be that we need to read scripts which although are

not valid for some reason, do actually deserialize to make an in-memory object.

After deserialization (lines 464 to 470 above) we can use the object 'self:res' directly in our code. Below we are starting

to access the in-memory object data to create new paragraphs which we then add to the Document object which is to

be displayed in the Scroll Viewer - see below :-

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

In the following image we will see how to gradually build up a paragraph from the 'res' object.

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

When we add lines to our current (and building) paragraph, we use the methods shown below.

I have made two overloads so that a font style may also be provided as a third parameter.

The default is 'normal' - check this out below :-

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

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

At the end of building each paragraph we add this to the document, and the (as shown below) after the end of the last

paragraph itself, we associate the newly made document with the document property of the Scroll Viewer UI element.

Checkout some of these points and ideas with the last section of the document creation code - see below :-

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

The line 603 refers to adding suitable information to the log when a successful deserialization and document display have

occurred.

The method 'AddNewScript2Log' selects only header type information for future reference - see below :-

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

While we are on the subject of logging, and errors etc., here is the 'AddObject2Log' we saw much earlier in the code

shown here - check this out :-

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

We are getting there, do not panic ;-))

Now for the Automated part of all this.

Automation of the Batch Scanning of Scripts :

Getting the basic parts of a prototype to function is one thing, but making a User-Proof working system is quite another ;0))

We need to automate the scanning process to make it as quick and as easy as possible, at the same time as making sure

that as few things can go wrong with what we put in place for the user.

SO one thing you will certainly notice in the following image, is that we are checking to see that the script being scanned

and subsequently processed, is not already in the system. We don't want one script in more than once - as this would lead

to multiple orders being created.

Let us have a look at what the automated code looks like - checkout below :-

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

Line 184 in the image shown above is what will query the SQL table to see if the header table already exists for the

script with the given identity (unique reference number).

Anyone having done some SQL work with ADO.NET in dotNet will recognise the following code structure.

Study the details carefully, and see that it is only a few lines of code which do all the real work - see below :-

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

Obviously, a return value of zero is what we are after, as this means the script has not previously been entered and stored.

On the other hand, if we do get a result great than zero, then we need to record in our log file and SQL table that this attempt

to multiple scan has taken place.

PHH - 21st. April 2011.


Binding to UI elements on Tabs

We are now going to look at the other side of the Script Scanning applet.

Behind the click of the 'System' button we have a second WPF form which uses much more WPF oriented

technology. We have a lot of binding of custom data to list and grid UI elements.

First lets us look at part of the XAML designer - checkout below :-

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

Notice in the image shown above that the tab under our attention is the "Error Log" tab, and this appears

like so at run time :-

By studying the XAML script you will see that the UI element displaying the error log data is a DataGrid element.

In this particular case we have turned off the auto generation of the columns, and have specified both those we wish

to have on display, and also the custom object properties which are to be bound to each column.

However, when we have a small number of properties in our custom object - and hence columns to fill - it is just

as easy to use auto generation, as show in the "All Codes" tab below :-

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

This XAML code produces the following runtime display - see below :-

Simple enough, I think you will agree.

At this point, let us break from looking at the tabs and their look / feel at runtime, and see how the data return set

from SQL query statements may be bound to the UI elements.

In the following image see how the DataSet parameters are passed into the 'SystemWin' class constructor, and then

the first DataTable associated with the 'DataContext' property of the various UI elements - ListBox, dataGrid, or ComboBox

elements.

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

I have to say, it seems remarkably simple to bind our returned data in this way ;0))

Notice in line numbered 41 we are using the archive DataSet object to associate with the DataContext property.

Let us now see how we create this data set from a method named 'GetXML_Archive()', which is shown in detail below :-

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

Having set the SQL query string it is lines 276 and 278 which actually do the business.

We have a similar method to find return data sets for each of the tab controls - or should I say the UI elements

showing on those controls. Checkout the collapsed code shown below :-

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

So there is only one more part of this jigsaw to fit in place, and that is how to launch the tab window itself.

The following code shows how we open the 'System' form - study it carefully :-

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

Notice that we are retrieving each of the SQL data sets before we launch and show the 'SystemWin' form itself.

We could design things differently in an app which is not a prototype, and also where the data may take longer to retrieve.

If this was the case then we would need to provide one data set for the default or opening tab, and then respond to the

tab click event for the other tabs - those not open / visible at launch time.

So line 263 matches up with line 30 as far as our understanding is concerned.

In fact if we experiment with trying to add an event to an existing tab item we see that the most likely event to choose for

the above change in operation is the 'GotFocus' event, as seen in the image shown below :-

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

*** Side Note *** you may wonder at my choice of name for the grid - BUT - 'Rosetta Stone' is used to describe that situation

where I found recently a table which related one set of product item codes with a hitherto completely separate set of different

codes for the same product items. Now I can use this VERY useful lookup table to unlock the mysteries !!

Archive Tab :

Let us now return to the tab controls, and the grid / list UI elements display thereupon.

If we examine the look and behaviour of 'XML Archive' data grid we see we also have some editable text box elements too,

as well as three read only TextBlock elements - check the background colour.

Let us first of all study the look of the form when this tab is selected - see below :-

Studying the form shown in the image above, we see that the two TextBox elements are capable of being edited.

The other three text block elements are not as white in colour - in fact they are beige.

Also notice that the changes are NOT shown immediately (character by character) in the selected row of the grid,

when the first (top) text box is edited, but after the focus has been lost by this text box element.

Unfortunately this is difficult to show in eNotes and you will just have to believe me ;0)

However, the second (lower) text box element does have "instant editing" show in the selected grid row - let us now

checkout our XAML script to see why this may be - see below :-

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

Script line 31 shows we have the attribute 'UpdateSourceTrigger' set to a value of "LostFocus", where as line 35 shows

that the same attribute for the lower TextBox element is set to a value of "PropertyChanged".

It would appear that the default setting is the latter, and it proved to be the case until I added the extra script for this property / attribute.

Study in detail line 14 through 20, and see how to both create a column with a specified header, and also how to bind the newly

declared column to a chosen data property, or in this case a column in a DataTable.

Please also be aware that the coder can make errors and have the "Scan date" header column actually bound to the 'username'

property - this is totally in the hands of the developer ;O))

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

PHH - 22nd. April 2011.


WinForm Display of Headers & Items

PHH - 22nd. April 2011.


working script and code

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

#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.IO

#using WelshScriptsXSD

#using System.Xml.Serialization

#using System.Xml

#using System.Xml.Schema

#using System.Data.Common

#using System.Data

#using System.Data.SqlTypes

BEGIN NAMESPACE WPFscripts2D

/// <summary>

/// Interaction logic for Window1.xaml

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private str as StreamReader

private xSerializer as XmlSerializer

private res as xml

private oSB as StringBuilder

private fdWelshTwo as FlowDocument

private cAppDir, cSchema, cLogFile, cScriptIn as string

private oDTH, oDTL as DataTable

private oEL as ErrorLog

private cXML_master as string

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:Initialize()

self:InitConnection()

RETURN

method Initialize() as void

//------- pathname (includes filename) ---------

//MessageBox.Show(System.Reflection.Assembly.GetExecutingAssembly():Location )

//MessageBox.Show(System.Environment.GetCommandLineArgs()[1])

//-------- folder name only ---------

//MessageBox.Show(System.IO.Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess():MainModule:FileName))

self:cAppDir := System.IO.Directory.GetCurrentDirectory() +"\"

//MessageBox.Show(self:cAppDir)

self:cLogFile := self:cAppDir+"BarCodeScanLog.txt"

self:cSchema := self:cAppDir+"BrianH.xsd"

self:cScriptIn := self:cAppDir+"scriptin.xml"

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

self:cXML_master := ""

self:DoClear()

self:cbDetails:IsChecked := false

return

method XmlComplete() as Boolean

local lResult, lComplete as Boolean

lResult := true

lComplete := false

local cText as string

cText := self:tbScanIn:Text:Trim()

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

if string.IsNullOrEmpty(cText) //----- test for No XML string -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "checking TextBox string"

self:oEL:cDescGen := "data string content is NULL or empty"

self:oEL:cGenTask := "checking scanned XML input"

self:oEL:cComment1 := ""

self:oEL:cComment2 := ""

self:oEL:cScriptID := "unknown"

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

self:CommitError2SQL(self:oEL)

lResult := false

return lResult //----- { input string Empty or NULL } -----

//----- no point in testing any further -----

endif

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

local nSFirst, nSLast as int //----- two start tag indexes -----

nSFirst := cText:IndexOf("<xml>")

nSLast := cText:LastIndexOf("<xml>")

if nSFirst < 0 //----- test for a Start Tag -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "checking XML start tag"

self:oEL:cDescGen := "finding if one exists in a real string"

self:oEL:cGenTask := "checking scanned XML input"

self:oEL:cComment1 := "none found"

self:oEL:cComment2 := "string NOT empty or NULL"

self:oEL:cScriptID := "unknown"

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

self:CommitError2SQL(self:oEL)

lResult := false //----- { NO - no existing 'start tag' } -----

endif

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

local nEFirst, nELast as int //----- two end tag indexes -----

nEFirst := cText:IndexOf("</xml>")

nELast := cText:LastIndexOf("</xml>")

if nELast < 0 //----- test for an End Tag -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "checking XML end tag"

self:oEL:cDescGen := "finding if one exists"

self:oEL:cGenTask := "checking scanned XML input"

self:oEL:cComment1 := "none found"

self:oEL:cComment2 := ""

self:oEL:cScriptID := "unknown"

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

self:CommitError2SQL(self:oEL) //----- { NOT complete - no matching 'end tag' } -----

endif

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

if nELast > nEFirst //----- test for multiple entry (or part) -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "checking for XML end tags"

self:oEL:cDescGen := "multiple entry, more than one end tag"

self:oEL:cGenTask := "checking scanned XML input"

//self:oEL:cComment1 := "Number found is "+nStart:ToString()

self:oEL:cComment1 := ""

self:oEL:cComment2 := ""

self:oEL:cScriptID := "unknown"

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

self:CommitError2SQL(self:oEL)

lResult := false //----- { multiple attempts to 'scan Xml' } -----

endif

if nSLast > nSFirst //----- test for multiple entry (or part) -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "checking for XML start tags"

self:oEL:cDescGen := "multiple entry, more than one start tag"

self:oEL:cGenTask := "checking scanned XML input"

//self:oEL:cComment1 := "Number found is "+nStart:ToString()

self:oEL:cComment1 := ""

self:oEL:cComment2 := ""

self:oEL:cScriptID := "unknown"

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

self:CommitError2SQL(self:oEL)

lResult := false //----- { multiple attempts to 'scan Xml' } -----

endif

return lResult

method myGetBatch_Click(sender as object, e as RoutedEventArgs) as void

self:GetBatchHeader()

self:GetBatchLine()

local oFGB as GetBatchForm

oFGB := GetBatchForm{self:oDTH, self:oDTL}

oFGB:Show()

return

method myAutomate_Click(sender as object, e as RoutedEventArgs) as void

try

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

if self:Do_mySave2File_Click() //----- XML data input, is sent to file -----

if self:Do_myTurn2Object_Click() //----- XMl is deserialized to a custom 'Script' object -----

local cSID as string

cSID := self:res:sc:id

if self:CountRowsInScriptHeader(cSID) = 0 //----- check for script already stored -----

self:Do_myObj2SQL_Click() //----- save new script to SQL tables -----

else

//----- record error of 'scan again' ! -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "finding any rows with same 'sid'"

self:oEL:cDescGen := "counting existing rows"

self:oEL:cGenTask := "checking for already scanned"

self:oEL:cScriptID := cSID

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

self:CommitError2SQL(self:oEL)

endif

endif

//self:DoClear()

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

else

MessageBox.Show("Error or issue somewhere in input routine .... ","ERROR :")

endif

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

catch oEX as System.Exception

MessageBox.Show("Something wrong in Automating process !? .... ","Gen. ERROR :")

//----- record error to SQL log table -----

end try

self:tbScanIn:Focus()

return

method myFormLoaded(sender as object, e as RoutedEventArgs) as void

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

return

method ArchiveXML(cXML as string, script_id as string) as void

//MessageBox.Show(cXML,"Archiving :")

if self:CountRowsInArchive(script_id) = 0

//----- make an Archive object -----

local oXA as XmlArchive

oXA := XmlArchive{}

oXA:dtWhenScan := DateTime.Now

oXA:cScriptID := script_id

oXA:cXML_body := cXML

oXA:cUserID := "134903PHH"

oXA:cUserName := "PhilOfBESTcon"

//MessageBox.Show(oXA:cXML_body,"Archiving :")

//----- then store it in an SQL table -----

self:XmlArchive2SQL(oXA)

else

//----- Archive record already exists! -----

//----- send message to SQL ErrorLog ------

self:oEL := ErrorLog{}

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

self:oEL:cFocusTask := "finding any XML rows with same 'sid'"

self:oEL:cDescGen := "counting existing rows"

self:oEL:cGenTask := "checking for previous Archiving"

/*self:oEL:cComment1 := ""

self:oEL:cComment2 := ""*/

self:oEL:cScriptID := script_id

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

self:CommitError2SQL(self:oEL)

endif

self:tbScanIn:Focus()

return

method mySystem_Click(sender as object, e as RoutedEventArgs) as void

self:Do_mySystem_Click()

return

method Do_mySystem_Click() as void

//MessageBox.Show("... ready to show System form ...","System :")

try

self:GetXML_Archive()

self:GetSQL_errors()

self:GetSQL_CodesAll()

self:GetSQL_CodesDT()

self:GetSQL_CodesDC()

//MessageBox.Show(self:oDS_arch:DataSetName,"Name before :")

local oSysW as SystemWin

oSysW := SystemWin{self:oDS_arch, self:oDS_errors, self:oDS_codes, self:oDS_DTcodes, self:oDS_DrugCodes}

oSysW:ShowDialog()

catch oEX as System.Exception

MessageBox.Show(oEX:message,"Error :")

end try

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

self:tbScanIn:Focus()

return

method myClear_Click(sender as object, e as RoutedEventArgs) as void

self:DoClear()

return

method DoClear() as void

self:tbScanIn:Text := ""

self:tbScanIn:Focus()

return

method myObj2SQL_Click(sender as object, e as RoutedEventArgs) as void

self:Do_myObj2SQL_Click()

self:tbScanIn:Focus()

return

method Do_myObj2SQL_Click() as Boolean

local lResult as Boolean

lResult := false

if self:res != NULL

//MessageBox.Show("Will make rows / records in table ...","SQL:")

self:ShowDetails("Will make rows / records in table ...","SQL:")

else

MessageBox.Show("Script object is NULL !? ...","NO SQL:")

endif

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

if self:SaveScriptRes()

if self:SaveLineItemsRes()

lResult := true

endif

endif

return lResult

method myTurn2Object_Click(sender as object, e as RoutedEventArgs) as void

self:Do_myTurn2Object_Click()

self:tbScanIn:Focus()

return

method Do_myTurn2Object_Click() as Boolean

local lResult as Boolean

lResult := false

try

self:ReadXMLdata()

//self:AddToLog("Script XML read and validated.")

self:DoClear()

lResult := true

catch oEX as System.Exception

MessageBox.Show(oEX:message,"Error :")

self:AddToLog("Errror : "+oEX:message)

end try

return lResult

method mySave2File_Click(sender as object, e as RoutedEventArgs) as void

self:Do_mySave2File_Click()

self:tbScanIn:Focus()

return

method Do_mySave2File_Click() as Boolean

local lResult as Boolean

lResult := false

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

if !self:XmlComplete() //----- check for state of Xml input string -----

self:ShowDetails("Not of correct / commplete format ... ?!","Check Scan Input :")

return lResult

endif

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

local oSB as StringBuilder

oSB := StringBuilder{}

oSB:Append(self:tbScanIn:Text)

local oSW as StreamWriter

oSW := StreamWriter{self:cScriptIn}

oSW:Write(oSB:ToString())

oSW:Close()

lResult := true

self:ShowDetails("File created ... ?!","Disk Write :")

self:AddToLog("File created ... ?!")

return lResult

method Add2ParaAsInline(oP as Paragraph, cText as string) as void

oP:Inlines:Add(Run{cText+CRLF})

return

method Add2ParaAsInline(oP as Paragraph, cText as string, oE as A2Ptype) as void

do case

case oE = A2Ptype.normal

oP:Inlines:Add(Run{cText+CRLF})

case oE = A2Ptype.bold

oP:Inlines:Add(Bold{Run{cText+CRLF}})

case oE = A2Ptype.italic

oP:Inlines:Add(Italic{Run{cText+CRLF}})

case oE = A2Ptype.bolditalic

oP:Inlines:Add(Bold{Italic{Run{cText+CRLF}}})

endcase

return

method A2P(oP as Paragraph, cText as string, oE as A2Ptype) as void

self:Add2ParaAsInline(oP, cText, oE)

return

method A2P(oP as Paragraph, cText as string) as void

self:Add2ParaAsInline(oP, cText)

return

method DateNumber2DT(cDTnum as string) as DateTime

local dtResult as DateTime

local nC, nM, nD as int

dtResult := DateTime{1900,01,01}

if cDTnum:Length < 8

MessageBox.Show("date too short ....!?","WARNING :")

return dtResult

endif

if cDTnum:Length > 8

MessageBox.Show("date too long ....!?","WARNING :")

return dtResult

endif

nC := Convert.ToInt32(cDTnum:SubString(0,4))

nM := Convert.ToInt32(cDTnum:SubString(4,2))

nD := Convert.ToInt32(cDTnum:SubString(6,2))

dtResult := DateTime{nC,nM,nD}

return dtResult

method ValidateScript() as Boolean

local lResult as Boolean

lResult := false

//----- Create the XmlSchemaSet class -----

local sc as XmlSchemaSet

sc := XmlSchemaSet{}

try

//----- Add the schema to the collection -----

sc:Add("PharmaSys-schema", self:cSchema) //----- relative filepath to .XSD -----

//----- Set the validation settings -----

local settings as XmlReaderSettings

settings := XmlReaderSettings{}

settings:ValidationType := ValidationType.Schema

settings:Schemas := sc //----- as in my schema 'set' -----

//----- make an association to a handler which will be called when Validation fails -----

settings:ValidationEventHandler += ValidationEventHandler{self, @ValidationCallBack()}

//----- Create the XmlReader object -----

local reader as XmlReader

reader := XmlReader.Create(self:cAppDir+"scriptin.xml", settings)

//----- Parse the file -----

local nParse as int

nParse := 0

do while reader:Read()

nParse +=1

enddo

lResult := true

self:ShowDetails("Passes while parsing ..."+nParse:ToString(),"Parse :")

self:AddToLog("Valid - Passes while parsing ..."+nParse:ToString())

catch oEX as System.Exception

MessageBox.Show(oEX:message,"Error :")

lResult := false

self:AddToLog("NOT Valid - Error !?")

end try

return lResult

//----- Display any validation errors -----

method ValidationCallBack(sender as object , e as ValidationEventArgs) as void

MessageBox.Show("Validation Error: {0}", e:Message)

self:AddToLog("Validation Error: {0} "+e:Message)

return

method ReadXMLdata() as void

self:fdWelshTwo := FlowDocument{}

self:fdsvWelshTwo:Document := self:fdWelshTwo

local cFile as string

cFile := self:cAppDir+"scriptin.xml"

if !File.Exists(cFile)

MessageBox.Show(e"File not found ... \n\""+cFile+e"\"","WARNING :")

self:AddToLog(e"File not found ... \n\""+cFile+e"\"")

return

endif

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

local lTryRead as Boolean

lTryRead := self:ValidateScript()

if !lTryRead

MessageBox.Show("Result of validation is ..."+lTryRead:ToString(),"Validation :")

endif

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

self:str := StreamReader{cFile}

//----- use real scanned data items for patient information -----

self:xSerializer := XmlSerializer{typeof(xml)}

self:res := (xml)self:xSerializer:Deserialize(self:str)

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

self:str:Close()

self:str := StreamReader{cFile}

//----- use real scanned data as Master Archive !! -----

self:cXML_master := self:str:ReadToEnd():Trim()

self:ArchiveXML(self:cXML_master, res:sc:id)

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

local oP as Paragraph

oP := Paragraph{}

//----- Comments taken directly from gary Jones's 2007 Document !! -----

//----- [pa, pb & sc >> (ScriptsXML) ] -----

if self:res:pa != null

self:A2P(oP, "[ Fictitious Patient details : ]", A2Ptype.bold)

self:A2P(oP, res:pa:l, A2Ptype.bolditalic) //----- Last name

self:A2P(oP, res:pa:m, A2Ptype.italic) //----- Middle name(s)

self:A2P(oP, res:pa:f, A2Ptype.italic) //----- First name

self:A2P(oP, res:pa:s) //----- Title / salutation

self:A2P(oP, res:pa:b) //----- Date of birth produced in CCYYMMDD format

//self:DateNumber2DT(res:pa:b)

self:A2P(oP, self:DateNumber2DT(res:pa:b):ToLongDateString() + " [ date of birth ]")

self:A2P(oP, res:pa:h + " [ NHS no. ]") //----- Health care number / NHS number – new style

self:A2P(oP, res:pa:o + " [ age ]") //----- Age

self:A2P(oP, res:pa:a) //----- Includes 5 lines of address (comma delimited)

self:A2P(oP, res:pa:pc) //----- Post code without spaces

self:A2P(oP, "")

endif

self:fdWelshTwo:Blocks:Add(oP)

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

oP := Paragraph{}

if self:res:pb != null

self:A2P(oP, "[ GP / Doctor details : ]", A2Ptype.bold)

self:A2P(oP, res:pb:i + " [ prescriber id ]") //----- Prescriber id

self:A2P(oP, res:pb:d + " [ prescriber name ]") //----- Prescriber name

self:A2P(oP, res:pb:n + " [ practice name ]") //----- Practice name

self:A2P(oP, res:pb:pi + " [ practice id ]") //----- Practice id

self:A2P(oP, res:pb:a) //----- Includes 5 lines of address (comma delimited)

self:A2P(oP, res:pb:pc) //----- Post code without spaces

self:A2P(oP, "")

endif

self:fdWelshTwo:Blocks:Add(oP)

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

if self:res:sc != null

self:A2P(oP, "[ Script details : ]", A2Ptype.bold)

//----- 'sc.id' is the unique identifier for all SQL tables -----

self:A2P(oP, res:sc:id) //----- Prescription id

//----- Prescription Form Type depends on the type of Prescriber at the GP Practice:

self:A2P(oP, res:sc:ft:ToString() + " [ form type ]")

self:A2P(oP, res:sc:t) //----- Date prescription produced in CCYYMMDD format

self:A2P(oP, self:DateNumber2DT(res:sc:t):ToLongDateString() + " [ script date ]")

local nD, i as int

nD := Convert.ToInt32(res:sc:dn)

self:A2P(oP, res:sc:dn + " [ no. of items ]") //----- Number of Drugs on Prescription

self:A2P(oP, res:sc:r) //----- Repeat info (e.g. “1 of 4”) – link to Repeat Master ID

self:A2P(oP, res:sc:rm) //----- Repeat Master Script ID – Identifier Code for Master Script

self:A2P(oP, "")

self:fdWelshTwo:Blocks:Add(oP)

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

//self:A2P(oP, "")

self:A2P(oP, "======================================")

self:A2P(oP, "")

//----- Drug Item element (DrugItems) -----

nD := self:res:dd:Length

for i := 1 upto nD

oP := Paragraph{}

self:A2P(oP,"[ Item line >> " + i:ToString() + " : ]", A2Ptype.bold)

self:A2P(oP, res:dd[i]:d + " [ original item ]") //----- Original drug description; e.g. “Aspirin 50mg Tablets”

self:A2P(oP, res:dd[i]:q + " [ original quantity ]") //----- Original drug quantity; e.g. “32 Tablets”

self:A2P(oP, res:dd[i]:sq) //----- Quantity when mapped to SNOMED coded UOM unit, when available

self:A2P(oP, res:dd[i]:@@do) //----- Dosage, as shown; e.g. “TAKE ONCE BEFORE MEALS”

self:A2P(oP, res:dd[i]:dm + " [ DM&D code ]") //----- DM&D code

self:A2P(oP, res:dd[i]:u) //----- Unit, or SNOMED coded UOM unit, when available

self:A2P(oP, "")

self:fdWelshTwo:Blocks:Add(oP)

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

next

endif

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

//----- Registered GP elements (RegGP) ------

local nRC as int

oP := Paragraph{}

if self:res:rc != null

nRC := self:res:rc:Length

if nRC > 0

for i := 1 upto nRC

self:A2P(oP, res:rc[i]:a) //----- 5 x address lines, comma-delimited

self:A2P(oP, res:rc[i]:d) //----- Doctor name

self:A2P(oP, res:rc[i]:i) //----- Doctor id

self:A2P(oP, res:rc[i]:n) //----- Practice name

self:A2P(oP, res:rc[i]:pc) //----- Post code without spaces

self:A2P(oP, res:rc[i]:pi) //----- Practice id

self:A2P(oP, "")

next

endif

endif

self:fdWelshTwo:Blocks:Add(oP)

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

//----- Responsible GP elements (RespGP) -----

local nGP as int

oP := Paragraph{}

if self:res:gp != null

nGP := self:res:rc:Length

if nGP > 0

for i := 1 upto nGP

self:A2P(oP, res:gp[i]:a) //----- 5 x address lines, comma-delimited

self:A2P(oP, res:gp[i]:d) //----- Doctor name

self:A2P(oP, res:gp[i]:i) //----- Doctor id

self:A2P(oP, res:gp[i]:n) //----- Practice name

self:A2P(oP, res:gp[i]:pc) //----- Post code without spaces

self:A2P(oP, res:gp[i]:pi) //----- Practice id

self:A2P(oP, "")

next

endif

endif

self:fdWelshTwo:Blocks:Add(oP)

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

self:fdsvWelshTwo:Document := self:fdWelshTwo

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

self:str:Close()

self:AddNewScript2Log(self:res)

return

END CLASS

ENUM A2Ptype //----- Add to Paragraph < type > -----

bold

bolditalic

italic

normal

end enum

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

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

#using System.Data

BEGIN NAMESPACE WPFscripts2D

/// <summary>

/// Interaction logic for SystemWin.xaml

/// </summary>

PARTIAL CLASS SystemWin INHERIT Window

private oDSA, oDSE, oDSRS, oDSDT, oDSDC as DataSet

CONSTRUCTOR()

SUPER()

InitializeComponent()

self:oDSA := DataSet{"Archives"}

RETURN

CONSTRUCTOR(oDS1 as DataSet, oDS2 as DataSet, oDS3 as DataSet, oDS4 as DataSet, oDS5 as DataSet)

SUPER()

self:oDSA := oDS1

self:oDSE := oDS2

self:oDSRS := oDS3

self:oDSDT := oDS4

self:oDSDC := oDS5

InitializeComponent()

RETURN

PRIVATE METHOD mySystemFm_Loaded( sender AS System.Object, e AS RoutedEventArgs ) AS Void

self:myArchiveDG:DataContext := self:oDSA:Tables[0]

self:myErrorDG:DataContext := self:oDSE:Tables[0]

self:myRosettaStoneDG:DataContext := self:oDSRS:Tables[0]

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

self:myLBcodesDT:DataContext := self:oDSDT:Tables[0]

//MessageBox.Show(self:oDSDT:Tables[0]:Rows:Count:ToString(),"Details :")

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

self:myCB_drug_codes:DataContext := self:oDSDC:Tables[0]

RETURN

END CLASS

END NAMESPACE

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

// LogPart.prg

//----- added March 9th. 2011 -----

#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.IO

#using WelshScriptsXSD

#using System.Xml.Serialization

#using System.Xml

#using System.Xml.Schema

BEGIN NAMESPACE WPFscripts2D

/// <summary>

/// Interaction logic for LogPart.prg

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

private oSWlog as StreamWriter

method AddObjectToLog(oEL as ErrorLog) as void

local cText as string

cText := ""

cText += oEL:dtOccur:ToLongDateString()+CRLF

cText += oEL:dtOccur:ToLongTimeString()+CRLF

cText += oEL:cUserName+CRLF

cText += oEL:cUserID+CRLF

cText += oEL:cScriptID+CRLF

cText += oEL:cGenTask+CRLF

cText += oEL:cFocusTask+CRLF

cText += oEL:cDescGen+CRLF

if !string.IsNullOrEmpty(oEL:cComment1)

cText += oEL:cComment1+CRLF

endif

if !string.IsNullOrEmpty(oEL:cComment2)

cText += oEL:cComment2+CRLF

endif

cText += "--------------------------------------"+CRLF

self:AddToLog(cText)

return

method AddToLog(cText as string) as void

//self:oSWlog := StreamWriter{Application.StartupPath+"/TransformLog.txt", true} //----- will append to -----

//self:oSWlog := StreamWriter{"C:/Temp/BarCodeScanLog.txt", true} //----- will append to -----

self:oSWlog := StreamWriter{self:cLogFile, true} //----- will append to -----

self:oSWlog:WriteLine(cText)

self:oSWlog:Flush()

self:oSWlog:Close()

return

method AddNewScript2Log(oR as xml) as void

self:AddToLog("[ scanned on & at ]:- "+DateTime.Now:ToString())

self:AddToLog(oR:sc:id+" [ prescription id ]")

self:AddToLog(self:DateNumber2DT(oR:sc:t):ToLongDateString() + " [ script date ]")

self:AddToLog(oR:pb:i + " [ prescriber id ]")

self:AddToLog(oR:pb:pi + " [ practice id ]")

self:AddToLog(oR:pa:h + " [ NHS no. ]")

self:AddToLog(self:DateNumber2DT(oR:pa:b):ToLongDateString() + " [ date of birth ]")

self:AddToLog(oR:sc:dn + " [ no. of items ]")

self:AddToLog("------------------------------------")

return

END CLASS

END NAMESPACE

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

// PartConnection.prg

//----- added March 10th. 2011 -----

#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.IO

#using WelshScriptsXSD

#using System.Xml.Serialization

#using System.Xml

#using System.Xml.Schema

#using System.Data.Common

#using System.Data.SqlClient

#using System.Data

#using Microsoft.VisualBasic

#using System.Collections

#using System.Xml.Serialization

#using System.Diagnostics

#using System.Xml

//#using System.Windows.Forms

BEGIN NAMESPACE WPFscripts2D

/// <summary>

/// Interaction logic for LogPart.prg

/// </summary>

PARTIAL CLASS Window1 INHERIT Window

PROTECT cFile, cPath AS STRING

PROTECT cFilePath AS STRING

EXPORT cStrConn AS STRING

EXPORT oConnDetails AS ConnectionDetails

EXPORT oDFactory AS DbProviderFactory

EXPORT oDConn AS DbConnection

EXPORT lConnectionTestedOK AS Boolean

export oDS_arch, oDS_errors, oDS_codes, oDS_DTcodes, oDS_DrugCodes as DataSet

method InitConnection() as void

SELF:DoForceDisconnect()

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

SELF:oConnDetails := ConnectionDetails{}

IF SELF:ReadSerialXmlFile()

SELF:DoSetConnectionString()

IF SELF:DoTryToConnect()

self:ShowDetails("Connection String - GOOD !!")

ELSE

MessageBox.Show("CARE - you need to set your connection string !!!")

ENDIF

ENDIF

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

return

method ShowDetails(cText as string) as void

if (Boolean)self:cbDetails:IsChecked

MessageBox.Show(self,cText)

endif

return

method ShowDetails(cBody, cHead as string) as void

if (Boolean)self:cbDetails:IsChecked

MessageBox.Show(self,cBody, cHead)

endif

return

method GetSQL_CodesDC() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "select * FROM [dbo].[UniqueNHSdrugCodes]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

self:oDS_DrugCodes := DataSet{"MyDrugCodes"}

//----- execute the select statement -----

oDA:Fill(self:oDS_DrugCodes)

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method GetSQL_CodesDT() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "select * FROM [dbo].[UniqueProdCodesNSS]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

self:oDS_DTcodes := DataSet{"MyDTcodes"}

//----- execute the select statement -----

oDA:Fill(self:oDS_DTcodes)

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method GetSQL_CodesAll() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "select * FROM [dbo].[_phh_DistinctDMnDs]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

self:oDS_codes := DataSet{"MyCodes"}

//----- execute the select statement -----

oDA:Fill(self:oDS_codes)

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method GetSQL_errors() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "select * FROM [dbo].[SS_O_ErrorLog]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

self:oDS_errors := DataSet{"MyErrors"}

//----- execute the select statement -----

oDA:Fill(self:oDS_errors)

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method GetXML_Archive() as Boolean

local lResult as Boolean

lResult := false

LOCAL cText AS STRING

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

cText := "select * FROM [dbo].[SS_O_XmlArchive]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

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

self:oDS_arch := DataSet{"MyArchives"}

//----- execute the select statement -----

oDA:Fill(self:oDS_arch)

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

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method CountRowsInScriptHeader(cSID as string) as int

local nResult as int

nResult := -1

if cSID:Trim() == ""

//----- add to error log -----

return nResult

endif

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

LOCAL cText AS STRING

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

cText := " SELECT count(*) FROM [dbo].[SS_O_ScriptHeader] where prescription_id = "+cSID

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN nResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

local nSelect as int

nSelect := (int)oCommand:ExecuteScalar()

nResult := nSelect

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

//----- add to log file -----

END TRY

SELF:DoForceDisconnect()

return nResult

method CountRowsInArchive(cSID as string) as int

local nResult as int

nResult := -1

if cSID:Trim() == ""

//----- add to error log -----

return nResult

endif

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

LOCAL cText AS STRING

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

cText := " SELECT count(*) FROM [dbo].[SS_O_XmlArchive] where prescription_id = "+cSID

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN nResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

local nSelect as int

nSelect := (int)oCommand:ExecuteScalar()

//MessageBox.Show(self,nSelect:ToString(),"SQL Count:")

nResult := nSelect

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

//----- add to log file -----

END TRY

SELF:DoForceDisconnect()

return nResult

method XmlArchive2SQL(oXA as XmlArchive) as void

LOCAL cText AS STRING

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

cText := "INSERT INTO [SS_O_XmlArchive] ([prescription_id], [scan_datetime], [XML_fullbody], [user_id], [username])"

cText += "VALUES (@sid, @sdt, @xbdy, @uid, @un);"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

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

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

LOCAL oParam AS DbParameter

local i as int

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@sid"

oParam:Value := oXA:cScriptID

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@sdt"

oParam:Value := oXA:dtWhenScan

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@xbdy"

oParam:Value := oXA:cXML_body

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@uid"

oParam:Value := oXA:cUserID

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@un"

oParam:Value := oXA:cUserName

oCommand:Parameters:Add(oParam)

TRY

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

oCommand:ExecuteNonQuery()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

//----- add to log file -----

END TRY

SELF:DoForceDisconnect()

return

method CommitError2SQL(oEL as ErrorLog) as void

self:AddObjectToLog(oEL)

LOCAL cText AS STRING

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

cText := "INSERT INTO [SS_O_ErrorLog] ([Date_and_Time],[Description_General],[Focus_Task],[General_Task],"

cText += "[Comment_1],[Comment_2],[Script_ID],[User_ID] ,[User_Name])"

cText += "VALUES ( @dat, @dg, @ft, @gt, @c1, @c2, @sid, @uid, @un );"

//MessageBox.Show(self,cText,"SQL statement :")

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN

ENDIF

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

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

LOCAL oParam AS DbParameter

local i as int

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@dat"

oParam:Value := oEL:dtOccur

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@dg"

oParam:Value := oEL:cDescGen

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@ft"

oParam:Value := oEL:cFocusTask

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@gt"

oParam:Value := oEL:cGenTask

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@c1"

oParam:Value := oEL:cComment1

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@c2"

oParam:Value := oEL:cComment2

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@sid"

oParam:Value := oEL:cScriptID

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@uid"

oParam:Value := oEL:cUserID

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@un"

oParam:Value := oEL:cUserName

oCommand:Parameters:Add(oParam)

TRY

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

oCommand:ExecuteNonQuery()

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

//----- add to log file -----

END TRY

SELF:DoForceDisconnect()

return

method SaveLineItemsRes() as Boolean

local lResult as Boolean

lResult := false

local nD as int

nD := self:res:dd:Length

if nD < 1

MessageBox.Show(self,"Drug item list empty !? ...","Missing :")

//----- include message in log file -----

return lResult

endif

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

LOCAL cText AS STRING

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

cText := "INSERT INTO [SS_O_ScriptItemLine] ([prescription_id] ,[one_item_of] ,[item_line_num] , "

cText += "[orig_drug_description] ,[orig_drug_quantity] ,[snomed_quantity] ,[dosage] ,[DMnD_code] ,[unit_or_UOMunit], [scan_when])"

cText += "VALUES ( @ptnID, @OneOf, @itmln, @od_desc, @od_q, @sq, @dos, @Dcod, @unit, @scw );"

//MessageBox.Show(self,cText,"SQL statement :")

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

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

LOCAL oParam AS DbParameter

local i as int

LOCAL oCommand AS DbCommand

for i := 1 upto nD

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@ptnID"

oParam:Value := self:res:sc:id

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@OneOf"

oParam:Value := nD

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@itmln"

oParam:Value := i

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@od_desc"

oParam:Value := self:res:dd[i]:d

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@od_q"

oParam:Value := self:res:dd[i]:q

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@sq"

oParam:Value := ""

if self:res:dd[i]:sq != NULL

oParam:Value := self:res:dd[i]:sq

endif

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@dos"

oParam:Value := ""

if self:res:dd[i]:@@do != NULL

oParam:Value := self:res:dd[i]:@@do

endif

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@Dcod"

oParam:Value := self:res:dd[i]:dm

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@unit"

oParam:Value := ""

if self:res:dd[i]:u != NULL

oParam:Value := self:res:dd[i]:u

endif

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@scw"

oParam:Value := DateTime.Now

oCommand:Parameters:Add(oParam)

TRY

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

oCommand:ExecuteNonQuery()

lResult := true

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

//----- add to log file -----

END TRY

next

SELF:DoForceDisconnect()

return lResult

method GetBatchHeader() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "select * FROM [dbo].[SS_O_ScriptHeader]"

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS, "Scripts")

self:oDTH := oDS:Tables["Scripts"]

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method GetBatchLine() as Boolean

LOCAL cText AS STRING

local lResult as Boolean

lResult := false

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

cText := "SELECT * FROM [dbo].[SS_O_ScriptItemLine]"

//MessageBox.Show(self,cText,"SQL statement :")

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

TRY

LOCAL oDA AS DbDataAdapter

oDA := SELF:oDFactory:CreateDataAdapter()

oDA:SelectCommand := oCommand

LOCAL oDS AS DataSet

oDS := DataSet{}

//----- execute the select statement -----

oDA:Fill(oDS, "LineItems")

self:oDTL := oDS:Tables["LineItems"]

lResult := true

CATCH DbEx AS DbException

lResult := FALSE

oCommand:CommandText := "SELECT @@Error;"

LOCAL nError AS INT

nError := (INT)oCommand:ExecuteScalar()

MessageBox.Show(nError:ToString(),"Error number :")

END TRY

SELF:DoForceDisconnect()

return lResult

method SaveScriptRes() as Boolean

local lResult as Boolean

lResult := false

LOCAL cText AS STRING

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

if (Boolean)self:cbDetails:IsChecked

LOCAL oMBB AS MessageBoxButton

oMBB := MessageBoxButton.YesNo

LOCAL oIcon AS MessageBoxImage

oIcon := MessageBoxImage.Question

LOCAL cMess, cHead AS STRING

cMess := "Update existing record in the SQL table ?"

cHead := "CONFIRM - changes!?"

LOCAL oDR AS MessageBoxResult

oDR := MessageBox.Show(SELF, cMess, cHead, oMBB, oIcon, MessageBoxResult.Yes)

IF oDR = MessageBoxResult.No

MessageBox.Show(self,"Will abort any updating attempts .....","No Change :")

RETURN lResult

ENDIF

endif

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

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

cText := "INSERT INTO [SS_O_ScriptHeader] ([Title],[FirstName],[Middlename],[LastName],[dob],[NHSnumber],[Age],"

cText += "[pat_5xAddress],[pat_postcode],[prescriber_id], [prescriber_name], [practice_name], [practice_id], [pract_5xAddress], [pract_postcode],"

cText += "[prescription_id], [form_type], [script_date], [num_of_items], [scan_when], [user_id], [username] )"

cText += "VALUES (@tl, @fn, @mn, @ln, @dob, @Nnum, @ag, @patAdd5, @patPC, @pbID, @pbN,"

cText += " @prcN, @prcID, @prcAdd, @prcPC, @ptnID, @scFT, @scd, @noi, @scw, @uid, @un);"

//MessageBox.Show(self,cText,"SQL statement :")

self:ShowDetails(cText, "SQL statement :")

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

SELF:DoSetConnectionString()

IF !SELF:DoTryToConnect()

RETURN lResult

ENDIF

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

LOCAL oCommand AS DbCommand

oCommand := SELF:oDFactory:CreateCommand()

oCommand:CommandText := cText

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

oCommand:CommandType := CommandType.Text

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

oCommand:Connection := SELF:oDConn

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

LOCAL oParam AS DbParameter

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@scw"

oParam:Value := DateTime.Now

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@tl"

oParam:Value := self:res:pa:s

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@fn"

oParam:Value := self:res:pa:f

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@mn"

oParam:Value := ""

if self:res:pa:m != NULL

oParam:Value := self:res:pa:m

endif

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@ln"

oParam:Value := self:res:pa:l

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@dob"

oParam:Value := self:DateNumber2DT(res:pa:b)

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@Nnum"

oParam:Value := self:res:pa:h

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.Int32

oParam:ParameterName := "@ag"

oParam:Value := Convert.ToInt32(self:res:pa:o)

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@patAdd5"

oParam:Value := self:res:pa:a

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@patPC"

oParam:Value := self:res:pa:pc

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@pbID"

oParam:Value := self:res:pb:pi

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@pbN"

oParam:Value := self:res:pb:d

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@prcN"

oParam:Value := self:res:pb:n

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@prcID"

oParam:Value := self:res:pb:i

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@prcAdd"

oParam:Value := self:res:pb:a

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@prcPC"

oParam:Value := self:res:pb:pc

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:DbType := DbType.String

oParam:ParameterName := "@ptnID"

oParam:Value := self:res:sc:id

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := true

oParam:DbType := DbType.String

oParam:ParameterName := "@scFT"

oParam:Value := self:res:sc:ft:ToString()

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := false

oParam:DbType := DbType.DateTime

oParam:ParameterName := "@scd"

oParam:Value := self:DateNumber2DT(res:sc:t)

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := false

oParam:DbType := DbType.Int32

oParam:ParameterName := "@noi"

oParam:Value := Convert.ToInt32(res:sc:dn)

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := false

oParam:DbType := DbType.String

oParam:ParameterName := "@uid"

oParam:Value := "134903PHH"

oCommand:Parameters:Add(oParam)

oParam := SELF:oDFactory:CreateParameter()

oParam:IsNullable := false

oParam:DbType := DbType.String

oParam:ParameterName := "@un"

oParam:Value := "PhilOfBESTcon"

oCommand:Parameters:Add(oParam)

TRY

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

oCommand:ExecuteNonQuery()

lResult := true

/*//----- refill the list box-----

SELF:DoPopulateListView()

SELF:FillCustomerList()

SELF:DoClearFields()*/

CATCH DbEx AS DbException

MessageBox.Show(DbEx:Message)

END TRY

SELF:DoForceDisconnect()

return lResult

METHOD ReadSerialXmlFile() AS LOGIC

LOCAL serial AS XmlSerializer

LOCAL oSR AS StreamReader

LOCAL cFile, cText, cHead AS STRING

LOCAL lResult AS Boolean

lResult := FALSE

lConnectionTestedOK := FALSE

//cFile := Application.StartupPath+"\ConnectionDetails.xml"

cFile := self:cAppDir+"\ConnectionDetails.xml"

IF File.Exists(cFile)

cText := "Existing file will be read!"

cHead := "XML file :"

//MessageBox.Show(SELF,cText,cHead)

//----- read object from XML file -----

serial := XmlSerializer{typeof(ConnectionDetails)}

oSR := StreamReader{cFile}

SELF:oConnDetails := (ConnectionDetails)serial:Deserialize(oSR)

oSR:Close()

oSR := NULL_OBJECT

serial := NULL_OBJECT

lResult := TRUE

lConnectionTestedOK := TRUE

//MessageBox.Show(SELF,cText,cHead)

self:ShowDetails(cText, cHead)

ELSE

cHead := "XML file :"

cText := "File can't be found !?"+CRLF

cText := "Go to Connection and create a string."+CRLF

MessageBox.Show(SELF,cText,cHead)

ENDIF

RETURN lResult

METHOD DoSetConnectionString() AS VOID

IF String.IsNullOrEmpty(SELF:oConnDetails:cProvider)

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

RETURN

ENDIF

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

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

SELF:oConnDetails:cUserName := ""

ENDIF

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

SELF:oConnDetails:cPassWord := ""

ENDIF

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

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

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

oDConn := oDFactory:CreateConnection()

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

SELF:cStrConn := ""

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

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

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

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

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

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

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

//cInst := ""

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

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

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

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

cInst := ""

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

oDConn:ConnectionString := cStrConn

local cText as string

cText := oDConn:ConnectionString:ToString()

//MessageBox.Show(cText)

self:ShowDetails(cText, "Connection :")

self:oConnDetails:cConnectionString := cStrConn

RETURN

METHOD DoForceDisconnect() AS string

LOCAL cText AS STRING

IF ! oDConn = NULL_OBJECT

oDConn:Close()

cText := oDConn:ConnectionString+" - State = "+ oDConn:State:ToString()

//SELF:Text := "Vulcan.NET : BEST company : << Disconnected! >>"

oDConn := NULL_OBJECT

ENDIF

RETURN cText

METHOD DoTryToConnect() AS LOGIC

LOCAL lResult AS Boolean

lResult := FALSE

TRY

oDConn:Open()

//SELF:Text := "Vulcan.NET : BEST company : << Can Connect! >>"

//SELF:Text := "Vulcan.NET : BEST company : << Connected! >>"

lResult := TRUE

CATCH sqlEx AS DbException

MessageBox.Show(sqlEx:Message)

//SELF:Text := "Vulcan.NET : BEST company : << NOT Connected! >>"

END TRY

RETURN lResult

END CLASS

[Serializable]CLASS ConnectionDetails

EXPORT cFile AS STRING

EXPORT cFolder AS STRING

EXPORT cFilePath AS STRING

EXPORT cProvider AS STRING

EXPORT cServerName AS STRING

EXPORT cSecurity AS STRING

EXPORT nTimeOut AS INT

EXPORT cUserName AS STRING

EXPORT cPassWord AS STRING

EXPORT lWindowsUserAccess AS Boolean

EXPORT cInstance AS STRING

EXPORT cConnectionString AS STRING

CONSTRUCTOR()

SUPER()

cFile := ""

cFolder := ""

cFilePath := ""

cProvider := ""

cServerName := ""

cSecurity := ""

nTimeOut := 0

cUserName := ""

cPassWord := ""

lWindowsUserAccess := TRUE

cInstance := ""

cConnectionString := ""

RETURN

END CLASS

CLASS ErrorLog

EXPORT dtOccur AS DateTime

EXPORT cDescGen AS STRING

EXPORT cFocusTask AS STRING

EXPORT cGenTask AS STRING

EXPORT cComment1 AS STRING

EXPORT cComment2 AS STRING

EXPORT cScriptID AS STRING

EXPORT cUserID AS STRING

EXPORT cUserName AS STRING

CONSTRUCTOR()

SUPER()

dtOccur := DateTime.Now

cDescGen := "..."

cFocusTask := "..."

cGenTask := "..."

cComment1 := "..."

cComment2 := "..."

cScriptID := "..."

cUserID := "..."

cUserName := "..."

RETURN

END CLASS

CLASS XmlArchive

EXPORT dtWhenScan AS DateTime

EXPORT cScriptID AS STRING

EXPORT cUserID AS STRING

EXPORT cUserName AS STRING

Export cXML_body as string

CONSTRUCTOR()

SUPER()

dtWhenScan := DateTime.Now

cScriptID := "..."

cUserID := "..."

cUserName := "..."

cXML_body := "..."

RETURN

END CLASS

END NAMESPACE

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

// PartGetBatchForm.prg

//----- created March 22nd. 2011 -----

//----- for Prototype of Scripts for GB -----

#using System.Data.Common

#using System.Data

#using System.Data.SqlTypes

partial CLASS GetBatchForm INHERIT System.Windows.Forms.Form

private oDTH, oDTL as DataTable

CONSTRUCTOR(oDTHin, oDTLin as DataTable)

SUPER()

SELF:InitializeComponent()

self:oDTH := oDTHin

self:oDTL := oDTLin

self:Initialize()

RETURN

method Initialize() as void

SELF:dataGridView1:DataSource := self:oDTH

self:dataGridView2:DataSource := self:oDTL

return

end class

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

<Window x:Class="WPFscripts2D.Window1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Welsh Scripts XSD :" Height="377" Width="600" Loaded="myFormLoaded">

<Grid Name="GridWelsh" Background="BlanchedAlmond">

<FlowDocumentScrollViewer Name="fdsvWelshTwo" Background="AliceBlue" BorderBrush="Purple"

BorderThickness="3" Margin="0,0,0,167">

</FlowDocumentScrollViewer>

<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Height="27" VerticalAlignment="Bottom">

<Button Content="Clear" Height="23" HorizontalAlignment="Left"

Name="pbClear" VerticalAlignment="Top" Width="50" Click="myClear_Click"/>

<Button Content="To File" Height="23" HorizontalAlignment="Left" Name="pbFile"

VerticalAlignment="Top" Width="55" Click="mySave2File_Click"/>

<Button Content="To Object" Height="23" HorizontalAlignment="Left" Name="pbObject"

VerticalAlignment="Top" Width="70" Click="myTurn2Object_Click"/>

<Button Content="Obj to SQL" Height="23" HorizontalAlignment="Left" Name="pbObj2SQL"

VerticalAlignment="Top" Width="75" Click="myObj2SQL_Click"/>

<Button Content="Get Batch" Height="23" HorizontalAlignment="Left" Name="pbGetBatch"

VerticalAlignment="Top" Width="70" Click="myGetBatch_Click"/>

<CheckBox Name="cbDetails" Content="See Detail" Width="75" IsChecked="True"/>

<Button Content="Auomate" Height="23" HorizontalAlignment="Left" Name="pbAutomate" IsDefault="False"

VerticalAlignment="Top" Width="75" Click="myAutomate_Click"/>

<Button Content="System" Height="23" HorizontalAlignment="Left" Name="pbSystem"

VerticalAlignment="Top" Width="70" Click="mySystem_Click"/>

</StackPanel>

<TextBox Margin="2,0,0,38" Name="tbScanIn" Background="AliceBlue"

BorderBrush="Red" BorderThickness="3" TextWrapping="Wrap" Height="123" VerticalAlignment="Bottom" />

</Grid>

</Window>

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

<Window x:Class="WPFscripts2D.SystemWin"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="2Drx : System - Functions and Data Views :" Height="600" Width="670" Background="#FFE7EAFF"

Loaded="mySystemFm_Loaded" Name="SystemWPF">

<Grid>

<TabControl Background="Thistle">

<TabItem Header="XML Archive" Background="BlanchedAlmond">

<Grid>

<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="false" IsReadOnly="True"

Height="275" VerticalAlignment="Top" Name="myArchiveDG" AlternatingRowBackground="Beige"

AlternationCount="2"

Margin="0,0,0,0" Width="645">

<DataGrid.Columns>

<DataGridTextColumn Header="Script ID" Binding="{Binding prescription_id}"/>

<DataGridTextColumn Header="Scan date" Binding="{Binding scan_datetime}"/>

<DataGridTextColumn Header="User ID" Binding="{Binding user_id}"/>

<DataGridTextColumn Header="Username" Binding="{Binding username}"/>

<DataGridTextColumn Header="XML master text ..." Binding="{Binding XML_fullbody}"/>

</DataGrid.Columns>

</DataGrid>

<TextBlock Height="23" HorizontalAlignment="Left" Margin="102,284,0,0" Background="Beige"

Name="myTBdgXML_pid" VerticalAlignment="Top" Width="298"

Text="{Binding ElementName=myArchiveDG, Path=SelectedItem.prescription_id}"/>

<TextBlock Height="23" HorizontalAlignment="Left" Margin="102,324,0,0" Background="Beige"

Name="myTBdgXML_sdt" VerticalAlignment="Top" Width="298"

Text="{Binding ElementName=myArchiveDG, Path=SelectedItem.scan_datetime}"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="102,363,0,0"

Name="myTBdgXML_uid" VerticalAlignment="Top" Width="298"

Text="{Binding ElementName=myArchiveDG, Path=SelectedItem.user_id,

UpdateSourceTrigger=LostFocus}"/>

<TextBox Height="23" HorizontalAlignment="Left" Margin="102,402,0,0"

Name="myTBdgXML_un" VerticalAlignment="Top" Width="298"

Text="{Binding ElementName=myArchiveDG, Path=SelectedItem.username,

UpdateSourceTrigger=PropertyChanged}"/>

<TextBlock Margin="102,442,6,6" Name="myTBdgXML_fullbody" Background="Beige"

Text="{Binding ElementName=myArchiveDG, Path=SelectedItem.XML_fullbody}"

TextWrapping="WrapWithOverflow"/>

</Grid>

</TabItem>

<TabItem Header="Error Log" Background="Red">

<DataGrid AutoGenerateColumns="False" Height="481" Margin="0,0,0,0" ItemsSource="{Binding}"

Name="myErrorDG" VerticalAlignment="Top" IsReadOnly="True" AlternatingRowBackground="Wheat"

AlternationCount="2" Width="643">

<DataGrid.Columns>

<DataGridTextColumn Header="Date-Time made" Binding="{Binding Date_and_Time}"/>

<DataGridTextColumn Header="General Description" Binding="{Binding Description_General}"/>

<DataGridTextColumn Header="Focus Task" Binding="{Binding Focus_Task}"/>

<DataGridTextColumn Header="Gen. Task" Binding="{Binding General_Task}"/>

<DataGridTextColumn Header="Script ID" Binding="{Binding Script_ID}"/>

<DataGridTextColumn Header="User ID" Binding="{Binding User_ID}"/>

</DataGrid.Columns>

</DataGrid>

</TabItem>

<TabItem Header="All Codes" Background="CornflowerBlue">

<DataGrid AutoGenerateColumns="True" Height="482" ItemsSource="{Binding}"

Name="myRosettaStoneDG" VerticalAlignment="Top" IsReadOnly="True"

AlternatingRowBackground="Thistle" AlternationCount="2">

</DataGrid>

</TabItem>

<TabItem Header="DT codes" Background="Chartreuse">

<ListBox Height="444" Name="myLBcodesDT" Width="575" VerticalAlignment="Top"

HorizontalAlignment="Left" ItemsSource="{Binding}"

ItemTemplate="{DynamicResource myDTcodesTemplate}"

AlternationCount="2" FontSize="13" Background="Thistle" />

</TabItem>

<TabItem Header="Drug codes" Background="LightGoldenrodYellow">

<Grid>

<ComboBox Name="myCB_drug_codes" VerticalAlignment="Top" ItemsSource="{Binding}"

AlternationCount="2" FontSize="13" Background="AliceBlue" Height="39"

ItemTemplate="{DynamicResource myDrugCodesTemplate}" Margin="6,0">

<TextBlock.FontSize>26</TextBlock.FontSize>

<TextBlock.FontFamily>Comic Sans MS</TextBlock.FontFamily>

<TextBlock.Foreground>Orange</TextBlock.Foreground>

</ComboBox>

<TextBox Name="myTB1" VerticalAlignment="Top" Height="31" Margin="113,65,6,0"

Text="{Binding ElementName=myCB_drug_codes, Path=SelectedItem.drug_code,

UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />

<TextBox Name="myTB32" VerticalAlignment="Top" Height="31" Margin="113,114,6,0"

Text="{Binding ElementName=myCB_drug_codes, Path=SelectedItem.description, Mode=TwoWay,

UpdateSourceTrigger=PropertyChanged}"/>

<Label Content="Drug Code :" Height="28" HorizontalAlignment="Left" Margin="6,68,0,0"

Name="myLab1" VerticalAlignment="Top" Width="101" />

<Label Content="Description :" Height="28" HorizontalAlignment="Left" Margin="6,117,0,0"

Name="myLab2" VerticalAlignment="Top" Width="101" />

</Grid>

</TabItem>

</TabControl>

<Grid.Resources>

<DataTemplate x:Key="myDrugCodesTemplate" >

<StackPanel Orientation="Horizontal">

<TextBlock Text="[ " />

<TextBlock Text="{Binding Path=drug_code}" FontWeight="Bold" FontStyle="Italic"

Foreground="Teal" Width="149"/>

<TextBlock Text=" ] >>-- " />

<TextBlock Text="{Binding Path=description}" />

</StackPanel>

</DataTemplate>

<DataTemplate x:Key="myDTcodesTemplate" >

<StackPanel Orientation="Horizontal">

<TextBlock Text="[ " />

<TextBlock Text="{Binding Path=Code}" FontWeight="Bold" Width="126"/>

<TextBlock Text=" ] >>-- " />

<TextBlock Text="{Binding Path=Name}" />

</StackPanel>

</DataTemplate>

</Grid.Resources>

</Grid>

</Window>

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

<Application x:Class="WPFscripts2D.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>

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

PHH - 21st. April 2011.


Chapter 17 - making 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 our 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 its 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 - 26th. March 2012.


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 fulfil 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 - 26th. March 2012.


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 - 26th. March 2012.


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 <<<

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

PHH - 26th. March 2012.


our Visual Tree STATIC class

Okay you guys, what is this all about ? What is a Visual Tree, and why do we wish to access it, as well

as how do we use it once we can access it ?

Let us start out very simple. Let us look at an image of a typical WPF form. Well, it is sort of typical,

except I have added a further couple of levels of Grids within another Grid etc..

Study the image shown below :-

You can see in the above illustration that basically we have a standard type (or sort) of data dialog form.

WPF 'TextBox' controls (GUI elements) allow users to input and edit the fields data as they wish.

In the Visual Studio 'shell' the DO (document outline) pane looks like that shown below :-

As you can see in the above image there are lots of elements making up this Form.

Moving on, we can see that we also have a Form which is 'aware' of some of the data values supplied

to the 'TextBox' input elements.

Checkout carefully the next illustration below :-

It is very obvious that four of the controls have red borders and as such we assume that something is 'wrong' !

If we study the following illustrations we see XAML script which applies a dotNet coded validation rule to the

'Height' related 'TextBox'. Checkout the details below :-

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

Notice that the prefix 'pearls' is required for us to instantiate an object of the .NET class 'MyIntegerVR', simply

because the namespace declaration at the head of the XAML file looks like this shown below :-

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

And I suppose we are all keen to see what the dotNet class called 'MyIntegerVR' looks like !

Well, wait no longer, here it is below, a little 'collapsed', but only in regions where there is nothing new, or extra, to

be shown. Checkout the details thoroughly :-

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

Notice that it is within the Validation Rule that we can make all our 'custom' comparisons - or put

another way, basically do what we want to.

However, by writing it as we have, we can at least set the lower and upper comparison limits on the one single VR class.

So at least it is 'general' in some ways.

Now lets take on overall 'glance' at what the STATIC class looks like, at the stage of development it has reached at this moment.

I must stress that this is very much a 'work in progress' project, so there will be much to be changed and added too.

For now just get an overall 'look / feel' of its :-

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

As you can see in the image above, we have STATIC methods, some STATIC fields (a mix of public and private), one property

(with more to follow) and also a STATIC constructor.

So if one of the public fields, properties or methods gets called during execution, then the 'Constructor' is run just once, for the

first call to one of these.

Note, as you would expect, only public fields are visible for use outside of the class itself.

Now lets move on a step or two, the following illustration shows a list of GUI elements, and this has come

from our new STATIC class.

Check them out, and compare the names with the image of the application form above - at run time.

We can see in the above image list that the Grids and TextBoxes of the nested elements have clearly

been found by our code.

However, at this point we are not really interested in any element which can't be edited by the user.

Moving on a little, we see in the following image that we have reduced our list to only eleven ( 11 ) elements

in total. These are in fact the seven on the main dialog form itself, and the two nested, and two more

nested even deeper.

Study the details shown in the following illustration :-

What we really need is to show the names of the 'TextBox' elements - this will come shortly.

First, we need to see that we can in fact quite easily identify those which have invalid contents - see below for details :-

And tidying up the identified - invalid - list we see the following :-

Ahaaaar ! That looks better.

So lets track the code to see what is going the 'important' bits.

Behind the push button click event we have this code :-

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

As you can see line 183 is a call to a method in out STATIC class, called 'GetMyVisualElements'. To those newish

to Vulcan (from VO ?) the dot gives us the clue.

For any object that we instantiate in VN we have to use a colon ( : ) for our 'action' symbol.

Let us see what this STATIC method looks like - checkout below :-

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

Line 84 in the above image points us to where the important 'bit' of the work is done.

We appear to have another method which selects from a found list of elements, only those which are of type 'TextBox'.

Study carefully the details in the following code :-

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

Once again we have a single line which seems to be the 'heart' of the matter - here it is line 123.

We appear to be using in line 123, a System STATIC class called 'Validation' and it is useful to see the SDK documentation

which is shown below :-

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

There is not a public constructor to be seen !

However, we do have a couple of Properties and an event - see below :-

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

It would appear that as long as we supply a genuine 'FrameworkElement' object to the 'GetHasError' method we get back

a logical (Boolean) value.

Yes, it is easy to use, AND, it works too ;-0)

So far we have not looked in detail as to how we find the VisualTree elements - lets do that now.

Finding the elements of the 'VisualTree' :

To put things simply, 'Visual' elements in the tree describing the WPF form, are created and placed in the Tree

when the form has been displayed at least once.

These elements are all of those possible from the elements placed on the Form, as well as all those that may also

arise from applying 'Templates'. At this moment in time I am not sure if 'Styles' can also do similar.

Anyway, we get many more 'Visual' elements than we often assume, by us focusing on just those GUI controls we

manually placed on the WPF designer surface.

Keeping this in mind we can now look at the small amount of code we need to make a 'Recursive' method for

our STATIC class.

Checkout below the 'FindVisualTree' method code - note commented lines are due to the project being not completed

as yet :-

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

In the above code line 164 was commented because it cause duplicate entries in the final long list. This was not at

first obvious because of the overall complexity.

Line 169 is the one doing the clever stuff - it uses yet another (System) STATIC class, and that called 'VisualTreeHelper'.

The SDK documentation is shown below - and once again no 'Constructor' is obvious to us at a 'Public' level.

The following illustration shows just methods, there are no properties in this class :-

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

In fact for our own STATIC class we are only going to use two of the methods - those shown above in red.

These have been used in lines 165 and 168 in our code shown in the image two above.

In fact line 165 finishes the recursion, as when the list of children to an element is zero, or empty, then no further

calls to 'GetChild' take place. The 'chain' is then completed because an 'end' has been found - the code can then

work back up the chain to the top, where the first recursive call was made.

A recursive method can always be spotted by the fact that somewhere in it, it calls a method of the same name as itself.

If you are even cleverer, you can spot a bit of code which terminates recursion, and this has to come in the method

before any subsequent calls to the same method. Ours is shown below :-

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

Once we have an empty list, that is, no more children of the Tree, then we do not execute the 'GetChild' code any

more times.

So what of the other members of the class ? - let's move on.

What of the Other Bits 'n' Bobs ? :

Well, it is quite simple to see that the purpose of the whole class revolves around the ability to take a 'FrameworkElement',

or indeed a GUI control - as we usually think of these - and find out if there are any children further down the Tree.

As we can now see with the aid of intellisense we do ahave quite a few supporting bits and pieces - see below :-

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

Any other properties and methods (and fields) are supplied / provided in the class to support the processing of the Visual

Tree. So we see facilities to include certain elements, and exclude others as well.

Obviously, we see methods related to selecting ONLY 'TextBox' elements, and finding if there are any Invalid cases, and if

so how many and also a list of them.

Depending on your requirements we could add other members to the class, like stuff to do with 'levels'. As yet the level of

what I have found has not been of particular interest to me, so there are no members for that present.

No doubt the STATIC class will grow in time as I find more needs for it.

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

PHH - 26th. March 2012.


Supporting members - 'VT' Static Class

Like with many classes, STATIC and non-static, as well as the few 'main' methods to do the job in hand,

there seems to be a lot of others needed to 'support' those main methods.

We can see this in the 'Visual Tree' STATIC class we have so far been building up. If we are to allow certain

GUI elements to be added to the lists for 'Including' and 'Excluding', then we need methods to allow us to be

able to do this.

We also need methods to tidy the list for 'Include / Exclude', as well as re-setting the default lists.

In fact, by allowing the 'Include / Exclude' lists to be changed we add quite a few methods to our STATIC class.

To some of you who have not gotten their head into this class as much as me, you may think this all a bit

'over the top', or unnecessary, but you will find it does need to be done like this.

If you are NOT convinced then take this as an opportunity to get more familiar with the methods and properties

of the 'List<T>' ( or in C# syntax - List(T) ) class.

The strongly typed List class is in the System.Collections.Generic namespace. Take a look at the SDK

documentation details - some of which are shown below :-

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

Within this subsection we will take a chance to look into using some of these methods, some which carry out really important

and useful functions.

Those like 'Add', 'Remove', 'Contains', 'ForEach', and a few more as well.

Let us start to see how to use strongly typed lists to help us with our STATIC class supporting methods. It is a good

idea to start out with the 'AddTypeForInclude' method.

The 'AddTypeForInclude()' method :

Let us first see what our code does in the STATIC constructor, and what the default Types are - see below :-

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

So the very first time a STATIC method or property (or public field) is used from this class, the 'Constructor' is called

and run. This means the strongly typed list for Include and Exclude will be set.

Lines 38 through 45 show just which WPF GUI element types these are. Note line 44 is commented and only there

to help do the necessary testing, to show all is working OK - more on testing later.

So we can see that our 'Include' list has three types of element by default - TextBox, ComboBox and ListBox.

What if in the course of running our application we need to add a further GUI element ?

Study the code shown below :-

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

*** Note *** line 106 is crucial to get right, as we need to be comparing the correct types of object. I had a bit

of a struggle with this line - to get it right !

As you can see from the above image we only add the supplied Type if it is an object, and also not already in the

list we have.

As this code stands we have not done any checks to see that it is a suitable Framework Element, but first things first!

Let us now see how to use this STATIC method in our application code - see below :-

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

When we run the few lines of code shown above, we get the following screen results, although I have used a little

bit of 'screen capture' magic to show it all in one go !

Checkout the following :-

So it would appear from the above 'composite' image that the 'Border' Type has now been added to our list.

The following code is that run by line 187 in the 'above but one' image - study this carefully below :-

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

So we see that we now have four list items and the fourth one is 'Border'.

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

Notice that our test shows that we do not add multiple 'similar' items to our list, just the one. This is as it should be.

Moving on, what if we now wish to remove an item ?

The 'RemoveTypeForInclude()' method :

We shall see in the following code for the 'Remove' form of our method, that we use 'Contains' and 'Remove' form the

available List<T> class methods. 'Contains' is particularly useful.

Checkout the following image details :-

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

You need to realise that in the above code (as with other existing methods at the moment) I have included

some lines of test code, for printing useful messages and the likes. These will be got rid of in the final version

of the STATIC class.

Let us now run a couple of lines to test out the 'Remove' form of our method - see below :-

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

In the image above we are attempting to remove the 'ComboBox' object 'Type' from our default list of three items.

Note that the 'ListBox' and 'TextBox' remain after the running of the method code.

PHH - 28th. March 2012.


our Logical Tree STATIC class

Enter topic text here.


Globally available data objects

*** brief notes to myself *** [ before expanding and tidying ]

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 ?


Problems and Gotcha's - Current

PHH - January 2010.


Appendix A -

PHH - January 2010.


Appendix B -

PHH - January 2010.


Appendix C -

Put simply, Silverlight is a subset of WPF.

It is that reduced set of classes from WPF which is run in the browser on the client computer.

[ The machine at a distance from the web server computer. ]

Look at the image below which serves as a good example.

Focus particularly on the GUI stuff below the live TV picture of the snow scene :-

Above is a real (and live) example of the 'Sky Player' facility I have available to me on my desktop machine.

I have right clicked over the 'controls area' to show the Silverlight info tip.

It would appear as though the whole GUI section, going from '501 Sky News' in the top left to 'TV Guide' and

the full/part screen icons bottom right, is a WPF window created using the Silverlight subset of controls, or

tags.

Remember, every tag in XAML as a corresponding class in Vulcan, and this is in the System.Windows.Controls

namespace.

The attributes used in the XAML script running on my 'client' PC also have a 100% correspondence with properties

found in the classes within the namespace.

PHH - January 2010.


Appendix D -

PHH - January 2010.


Downloading the 'Plug-In' from Fabrice

 Top Previous

I have just used Google to find this link :-

http://www.fabtoys.net/index.php?searchword=plug-in&ordering=&searchphrase=all&Itemid=1&option=com_search

All I did was search on FabToys as shown below :-

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

... and then when I got to the 'fabtoys.net' site search on 'plug-in' as once again shown below :-

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

Now download the latest version of the plug-in, I am doing this for '018' as shown below :-

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

Once the files from Fabrice have been downloaded and expanded into a chosen folder (I chose "C:\Reflector2"),

the 'View' menu is accessed like so :-

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

After choosing this option we get an easy way to navigate to the required DLL files as shown below :-

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

If all goes well you should get something like this, that is, one or two listed and available Add-Ins :-

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

After closing this dialog, and going back to the main Reflector screen, we see an extra option in the

drop list - see below :-

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

So if I now move on to highlight and select the 'TextBox' control, from the list, we get the following :-

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

Now the interesting and exciting part ;-) If I double click on the Clear() method in the tree view, I get

the following results in Reflector :-

Yes !!, I hear you cry, Vulcan at last instead of C# all the time ;O))

Well done Fabrice, what a good man you are.

Oh, I should have said, you can have multiple instances of Reflector, and with different or similar

target DLLs and the like. Quite useful at times, I should think.

Enough of all this, time to move on.

Just remember to make good use of Reflector, and the FF plugin.

See you in the next appendix.

PHH - January 2010.