sexy sells.

yep, again i bother you with a whitepaper, a weird name, by the way, for a piece of paper, covered with black spots called letters, what is so white on these kind of papers? wink [;)]

each and every salesman can tell you that sexy sells.
coming from the battleship gray colors of windows 95 with dull icons we came into the colorful world of xp and vista (the latter for those who like pain).

the old way of creating applications will not do you any good in terms of sales these days.

of course, good functionality has the highest priority in software development. it does not really matter how sexy your application looks, if the functionality is not good then your customers won’t be happy either.

personal confession.

i have to confess that the way my apps looked could not bother me for a minute in the past, it  just was not important in my book. that attitude went on until i read the book “mastering the requirements process” by suzanne and james roberts (isbn: 0-201-36046-2).

in chapter 7 of that must read book they write:
“you might think of non functional requirements as those that give character to the work”.
things like speed, security et cetera are non functional, but they add to the user’s acceptance of your application. let’s face it, if you application is as slow as a drunken snail in thick mud, as stable as quick sand and shows c5 errors every hour or so your users would not be very happy.

another big point they make is the look and feel of a product. (page 115 to 118)
in this era of attractive looking applications you should also take into consideration the audience of your applications. maybe not so much when you work as an in-house developer, although your users may like the work you do (take a look for some cool samples on bernard bout’s weblog (
http://weblogs.foxite.com/bernardbout), but even more so when you develop applications for commercial sales. i talk your money here. 

the aforementioned book, along with some conversations i had with (potential) customers, showed me that giving this some attentention could very well help me sell more of my applications in the markets.

the target of this whitepaper

this whitepaper covers only a part of making your applications look better.
i show here a way of adding skins to your applications.

in this article i will show some forms with a skin, no doubt that some of you will recognize the skins, as they are taken from the samples that were once available on vfpskin.com.ar. unfortunately that site is under construction for quite a while and it is still unclear when they’ll be back. hence the motivation for my project.

i do not claim it is perfect and complete, far from that, it is just a way to get things started from what i hope may be a way to give the vfp community some extra zest to their applications. i know that there are some great graphical artists out there, so maybe they too can give us some nifty skins to add to our apps.

graig boyd put it so well during one of his sessions at the southwest fox 2008 conference, “creating modern looking applications is a must these days if you want to sell them. (and along with that, your services as a developer.)”

the source of all evil.

for those of you who never visited http://www.news2news.com/vfp/index.php,, you have been missing quite something. the site owner, anatoliy mogylevets, maintains a lot of code and samples, showing what cool things you can do with the windows api. some code is only accessible if you take a subscription to that site, but that still leaves many samples accessible. speaking for myself i can say that taking a year subscription to this site was and is worth each and every €uro i paid for it.
if you even dare to tell that you never heard of that site then that makes me wonder on what planet you have been living.

based on the sample on http://www.news2news.com/vfp/?function=-1&example=499, “how to draw custom window caption on foxpro form” i created a class that handles most of the skinning itself.

i’d like to share this code with you all so you can take advantage of this. (i hope).
for my own usage i did make some changes to the code. the main advantage as i see it is that the way i do things now makes skiining the fox very open to new input so the fox can cunningly disguise himself as a gentleman.

this is a project under construction, so if you have any ideas and improvements, let me know, i am open to suggestions. before sending me suggestions read also the todo list at the end of this article, i can be found at: b.lutgerink@betuwe.net

so what is skinning anyway?

the whole idea is very simple.
put some pictures at the very edges of a form, cut off the titlebar and borders of that form and voila, you have a new border around your forms.
sure enough that does take some real estate off the form, but the gain is a better looking form.
the other gain is that you can create your own borderstyles.
so if you are a graphical artist (besides being a helluvadatabasegeekwhoprogramscirclesaroundthecompetition) go as you please and share your art with us all.
 

in the above picture i stretched the form a bit here so you can see the effect of a new border. normally the picture will show at the very edges of the form. as shown in this article.

there are 8 pictures involved in this, starting at the top left going clockwise you see:

·         topleft.gif;

·         topbar.bmp;

·         topright.gif;

·         rightbar.bmp;

·         rightbottom.gif;

·         bottombar.bmp;

·         leftbottom.gif;

·         leftbar.bmp

notice that for the corners i use gif files, the reason for that is that since they fill in a corner i don’t want to see the inner and outer parts of the picture, so i made those parts of the pictures transparent. gif is supporting transparancy.

 vs

the topbar, rightbar, bottombar and leftbar are bmp files, the reason for that is that when these files get stretched (image property stretch set to 2) they don’t fade, as some other graphical filetypes do.

on the above picture you see the individual parts of the border on the form.
before going really technical i show you what the form looks like when it runs:

i did not add any code to adjust the backcolor of the form, that is all in the class i created.

it reads an ini file and there are the settings for these types of data.

since ini files are very easy to change this gives you a chance to create and add your own skins to your applications. very open source and extensible to your own liking and taste.

now, are you ready to roll? here comes the technique:

teach me the magic

some  assumptions first. so that there will no misunderstanding of how i work.

you might need to adjust the code if you do things differently.

·         when starting my applications i alway create a public application object that handles most of the behavior of the application.
that is also the place where i put the name of the skin i want to see. this makes it very easy to simply drop one object on the form where i want the skin to appear. so my application class has one property named “skin”.

·         the value of this property is also the name of a folder under the application folder where all pictures reside. as well a a skin.ini file. more on that later.

·         besides the property “skin” my application object also has a method setskin.
this method reads my application ini file, locates the skin section and reads the skin key.

using an ini file makes it easy for you to change the behavior of an application

without having to compile the project again.

i use simon arnold’s initoobject class for this, in the original the classname in the

prg is clsinitoobject, i seem to have sticky fingers so i often made the typo

clsinitobject (one o), therefore i changed the classname to clsini2object.

(look at download id 106 on the download page of foxite.

in the setskin method of the application object, the ini file is read and if the skin

section and the skin key are there the value of skin is put in the skin property of

the application object. here is the code:

local loini as clsini2object of ini2obj.prg

 

loini = newobject("clsini2object","ini2obj.prg","",this.cinifile)

 

lcskin = loini.readvalue("skin", "skin")

 

if not isnull( lcskin)

   this.skin = lcskin

endif

 

 

for the remainder of this article i will assume that you drop the formskinning object on the form itself.
i adjusted the code a bit so you can use it by dropping the object on your form.

so here’s the piece of code you need in the init of the form:

if pemstatus( goapp, "skin", 5) ;

   and not empty( goapp.skin) ;

   and directory( goapp.skin)

  

   for each locontrol in this.controls

      if upper(locontrol.class) == "formskinning"

         locontrol.forminit()

         exit

      endif

   endfor

endif

first the existence of the skin property is checked, if that is not empty and there is a folder with the name of that skin only then the remainder of the code will run.

in the for … endfor loop the controls on the form are checked, if one of them is of the formskinning class the forminit method of that class will run.

here’s the forminit:

local lccolor as string

 

local loini as clsini2object of initoobj.prg

 

loini = newobject("clsini2object","initoobj.prg","", goapp.cinifile)

 

this.declare()

with thisform

   .addproperty("proxyborderstyle",3)

   .addproperty("startx",0)

   .addproperty("starty",0)

endwith

 

with this  

   .addimages()

   .activateimages()

   .setmousepointer()

   .positionimages()

   .addbuttons()

   .positionbuttons()

   .adjustproxycaption()

endwith

bindevent( thisform, "resize", this, "cutframe")

bindevent( thisform, "resize", this, "positionbuttons")

thisform.resize()

·         first the declare method is called, here the api functions needed for the skinning process are called.

·         it adds three properties to the form

·         then it adds the pictures, activates the images, this means that there is a bindevent for each of the images 1 to 6;

·         it sets the mousepointers for each of the images 4 to 6,

·         in the next step the close, max and min button are added.

·         the buttons are positioned.
mind you, since the position of the “buttons” (just shapes actually) are determined on the position of the righttop image (img3) the images need to be positioned first.
if you forget about this order things turn out to be so messy, and for sure, the fox doesn’t like messy workers.

·         the next step is to adjust the captioncontrol, this is just a label that:

ø  takes over the caption of the form and;

ø  mimics the dragging of the form once you click with the mouse on that and drag the form.

·         in the last steps there is a bindevent between the form’s resize event and the skincontrol cutframe method, as well as the forms resize event and the positioning of the buttons.

·         then the form is resized to cut away the frame.
this cutting away of the frame means that the borders and the titlebar are actually cut from the form. this is not the same as setting titlebar and borderstyle to 0 (zero), property settings that switch off the titlebar and borders.

all the pictures are subclasses of the skinimage class. the name of the skin is passed as parameter, in the init of that class, the imagename is determined, based on the name of the class, and the correct image is added to the imagecontrol.

lparameters tcskinname

with this

   .top = 0

   .left = 0

   .backstyle= 0

 

   do case

      case upper(.name) = "img1"

         lcimage = "lefttop.gif"

        

      case upper(.name)= "img2"

         lcimage = "topbar.bmp"

     

      case upper(.name)= "img3"

         lcimage = "righttop.gif"

     

      case upper(.name)= "img4"

         lcimage = "rightbar.bmp"

     

      case upper(.name)= "img5"

         lcimage = "rightbottom.gif"

     

      case upper(.name)= "img6"

         lcimage = "bottombar.bmp"

     

      case upper(.name)= "img7"

         lcimage = "leftbottom.gif"

     

      case upper(.name) = "img8"

         lcimage = "leftbar.bmp"

 

   endcase

 

   .picture = tcskinname+"\"+lcimage

   .visible = .t.  

next, a caption is added, it  takes the text from the form caption.

in the activateimages method the images are activated, basically this means that they will respond to mouse actions, here’s the code:

with thisform

 

* pics 1 to 3 are the top 3 img's and the caption control.
* they will respond to the mousedown.

   * basically this means they will make dragging of the form possible.

   *

 

   bindevent( .img1,         "mousedown", this, "onmousedownmove")

   bindevent( .img2,         "mousedown", this, "onmousedownmove")

   bindevent( .img3,         "mousedown", this, "onmousedownmove")

   bindevent( .proxycaption, "mousedown", this, "onmousedownmove")

 

   * images 4, 5 and 6 are the right side, bottomright and the bottombar.

   * they make resizing of the control possible.

   *

   bindevent( .img4,         "mousedown", this, "beforemousedownresize")

   bindevent( .img4,         "mousemove", this, "aftermousedownresize")

   bindevent( .img5,         "mousedown", this, "beforemousedownresize")

   bindevent( .img5,         "mousemove", this, "aftermousedownresize")

   bindevent( .img6,         "mousedown", this, "beforemousedownresize")

   bindevent( .img6,         "mousemove", this, "aftermousedownresize")  

  

this is the code of the onmousedownmove method:

lparameters tnbutton, tnshift, tnxcoord, tnycoord

* emulate the ability to drag the form using mouse

 

#define wm_syscommand 0x112

#define wm_lbuttonup 0x202

#define mouse_move 0xf012

 

if tnbutton = 1

   releasecapture()

   sendmessage(thisform.hwnd, wm_syscommand, mouse_move, 0)

   sendmessage(thisform.hwnd, wm_lbuttonup, 0, 0)

endif


here is the code for the beforemousedownresize method:
lparameters tnbutton, tnshift, tnxcoord, tnycoord

 

if tnbutton=1

   thisform.startx = tnxcoord

   thisform.starty = tnycoord

endif


all it does is adding the coordinates to the startx and starty properties of the form.

the aftermousedownresize:

lparameters tnbutton, tnshift, tnxcoord, tnycoord

 

with thisform

   if tnbutton=1 and .proxyborderstyle=3

      .width  = .width  + tnxcoord - .startx

      .height = .height + tnycoord - .starty

      .startx = tnxcoord

      .starty = tnycoord

   endif

endwith
the form is resized and startx and starty are refreshed.

then the mousepointer is set for the images on the form:

* mousepointer with two arrowheads, pointing left and right

#define mp_we 9 

* mousepointer with two arrowheads, pointing left-up and right-down

#define mp_nw_se 8

* mousepointer with two arrowheads, pointing up and down

#define mp_ns 7 

 

with thisform

   .lockscreen = .t.

   .img4.mousepointer = 0

   .img5.mousepointer = 0

   .img6.mousepointer=  0

 

   if .borderstyle == 3

      * only if the form is resizable.

      *

      .img4.mousepointer = mp_we

      .img5.mousepointer = mp_nw_se

      .img6.mousepointer=  mp_ns

   endif

   .lockscreen = .f.

endwith

when the pictures are positioned this means that clockwise, the topleft, topbar, righttop, rightbottom, bottombar, leftbottom and leftbar are put in position.

the img1 doesn’t need positioning. when adding an object to a form its top left is always 0 (zero), there for we start with img2, being the topbar

    with thisform.img2  && top bar

        .left = thisform.img1.width

        .width = thisform.width - thisform.img1.width - thisform.img3.width

        .anchor = 11

    endwith

 

    with thisform.img3  && top right corner

        .left = thisform.width - .width

        .anchor = 9

    endwith

 

    with thisform.img4  && right side bar

        .left = thisform.width - .width 

        .top = thisform.img3.height

        .height = thisform.height - thisform.img3.height - thisform.img5.height

        .anchor=13

    endwith

 

    with thisform.img5  && right bottom corner

        .left = thisform.width - .width

        .top = thisform.height - .height

        .anchor = 12

    endwith

    with thisform.img6  && bottom bar

        .left=thisform.img7.width

        .width = thisform.width - thisform.img5.width - thisform.img7.width

        .top = thisform.height - .height

        .anchor = 14

    endwith

 

    with thisform.img7  && left bottom corner

        .top = thisform.height - .height 

        .anchor = 6

    endwith

 

    with thisform.

Leave a Reply

Your email address will not be published. Required fields are marked *