When I first looked at Win32::GUI for the first time many many years ago (4? ok, not so many) I found it kind of confusing how the objects and events worked.

Coming from an OO background and with experience from a few other window toolkits (VB and Borland TurboVision among others) I would have expected to subclass the Window class, create an object, add controls to the object, and then I would defined methods in my subclass to be called when events fired. Kind of like this:


package DemoWindow;
#My DemoWindow is-a Window...
use base qw(Win32::GUI::Window);

#...but with event handlers for events that may fire
sub btnOk_Click { my $self = shift;  #the Window object
        #Change text of the clicked button
        $self->btnOk->Text( "Ok - " . int(rand(100)) );
        return(1);
}


But that's not how it works. Instead, event handlers based on the -name of the windows and controls created. They are sub routines called in a procedural fashion which requires me as an application programmer to keep track of which window the event belongs to. This is not very convenient.

I have not yet seen any "best practice" emerge for dealing with this. Either the window variable needs to be a global or a singleton (which in this case is the same thing, but without the stigma of globals, and people don't argue back when you bring up the havey artillery of design patterns), or it has to be connected to a global or singleton which is your "application" object or whaterver. It's nothing that can't be solved, but it's either inconvenient or not very maintainable.

One other drawback with the event-handler-as-procedure approach is that there can only be one instance of the window, because if you create a new window with the exact same names the event handler sub has no way to determine which window fired the event. This can be coded around at great inconvenience (with Perl, few things are impossible).

My point is: If only the Window object (or the window object of the control) firing off the event was passed as the first parameter to the event handler everything would be so much easier to live with. Finding the event handler sub in the correct package (OO style) would be a boon.


Actually, the result of my holiday hacking today is a module that pretty much does this. Win32::GUI::Window::Object
http://www.bahnhof.se/~johanl/perl/Win32GUI/Window/

The synopsis script (below) creates three distinct windows of the same class DemoWindow. The event handlers are methods on a subclass of Win32::GUI::Window, so the context ($self) for all events is the current Window::GUI::Window object.

Consider this a proof-of-concept. A test of whether it could be done or not. I've had the idea for a long time, but never had the free time and motivation to actually do it. I have obviously not done anything real with this yet :)


- Is this something that seems useful to proceed with? At least the concepts, if not the code?

- Is this something that can easily be solved from within Win32::GUI XS code, passing the window object as the first variable ot the event handlers? Perhaps using Perl's OO mechanism so that the correct package is used? Doing it the real way is definitely more stable than my hack.

- The subclassing isn't necessary, but could be useful. At the moment, with the objects as hash keys in the object, there is a high risk of clobbering them unintentionally, should people start subclassing Win32::GUI::Window en masse. Perhaps they could be moved to a "_controls" hash ref property of the window or something? Would that be difficult to use from XS code?



So finally, an example program using Yet Another Event Model:
http://www.bahnhof.se/~johanl/perl/Win32GUI/Window/synopsis.pl.txt

#!/usr/local/bin/perl -w

use strict;
use Data::Dumper;
use Win32::GUI;
use lib ("../lib");
use Win32::GUI::Window::Object;



for (1..3) {
        my $id = int(rand(100));
        my $win = DemoWindow->winCreate($id);
        $win->Show();
        }

Win32::GUI::Dialog();





package DemoWindow;

#Inherit from the new Window-as-object class
use base qw(Win32::GUI::Window::Object);



#A demo property to give it some identity
use Class::MethodMaker get_set => [ "no" ];



#Class method to create/build a window of this class. It
#could eventually be a window built by TGL.
#
#This could be in the new() method as well, calling the
#SUPER::new() to create the object, then setting the no()
#property.
#
sub winCreate { my $pkg = shift;
        my ($no) = @_;

        my $self = $pkg->new(
              -left   => 100 + int(rand(400)),
              -top    => 50 + int(rand(200)),
              -width  => 300,
              -height => 100,
              -name   => "winMain",
              -text   => "Window $no",
              );

        $self->no($no);              #Set the demo property

        my $btnHelloWorld = $self->AddButton(
                        -name => "btnHelloWorld",
                        -text => "Hello world!",
                        -left => 10,
                        -top => 10,
                        -height => 20,
                        -width => 100,
                        );

        return($self);
        }



#Event handlers are ordinary methods, with $self as
#the first parameter as usual. This gives context to the
#event, making it possible to have many instances of the
#same window class.

sub winMain_Terminate { my $self = shift;
        print "winMain_Terminate from DemoWindow with no (" . $self->no . ")\n";
        return(-1);
        }



sub btnHelloWorld_Click { my $self = shift;
        my $no = $self->no();
        print "btnHelloWorld_Click in DemoWindow no == $no\n";
        $self->Text("Window $no - " . int(rand(100)) );

        return(1);
        }



__END__


/J
-------- ------ ---- --- -- --  --  -    -     -      -         -
Johan Lindström    Sourcerer @ Boss Casinos     [EMAIL PROTECTED]

Latest bookmark: "ACM Queue - Code Spelunking Exploring Cavernous..."
<http://www.acmqueue.com/modules.php?name=Content&pa=showpage&pid=67&page=7>
dmoz (1 of 4): /Arts/Music/Bands_and_Artists/T/ 20


Reply via email to