Using form controls from other classes.

P

Phil

I've done a decent amount of searching but I really can't even figure
out what it is I should even be trying to do. I tried to make
everything as clear as possible, which unfortunately also made this
post more lengthy than most people would like to read.

Diagram of my setup (not UML but good enough to explain):
http://i35.tinypic.com/5bz9fn.png
- Program.cs was auto-generated when I made a Windows Form
Application. It just creates a new Form and runs the application.
- Form has an instance of A.
- A has multiple instances of B.
- A has an instance of C.
- D and E are threads in A.
- X is a static class intended to log information to a control in
Form.
- Everything is in the same namespace.

My requirements...
1. Form basically just contains the click events for my buttons at the
moment, which control what thread B does.
2. A, B, C, D and E need to use X to log information to a control in
Form.
3. C needs to disable a button in Form.
4. D needs to update some status bar text in Form.
5. D needs to update a log in Form. (Different than the log that X
updates.)

Now my question: What is it that I need to meet my requirements? When
I ask 'what ' it is that I need to meet my requirements, I am really
just trying to ask what I should be searching for. I do not understand
the correct approach to implement this. Requirement 1 is solved.
Requirements 3, 4 and 5 would presumably be solved using the same
approach. I would guess that requirement 2 would be solved a bit
differently than the others.

One solution that worked for requirements 3, 4 and 5 were to pass the
Form's 'this' instance into A, and then continue to pass the Form
instance into C through the constructors and had public methods in
Form that modified the controls as necessary. I read online that said
solution is considered improper.

Any help would be great.
 
P

Peter Duniho

[...]
My requirements...
1. Form basically just contains the click events for my buttons at the
moment, which control what thread B does.
2. A, B, C, D and E need to use X to log information to a control in
Form.
3. C needs to disable a button in Form.
4. D needs to update some status bar text in Form.
5. D needs to update a log in Form. (Different than the log that X
updates.)

Now my question: What is it that I need to meet my requirements? When
I ask 'what ' it is that I need to meet my requirements, I am really
just trying to ask what I should be searching for. I do not understand
the correct approach to implement this. [...]

Part of the problem is that in truth, there is no one right way. .NET is
a complex environment, Forms more complexity on top of that, and the
environment offers a wide variety of possible routes to working code. So
you have the difficult task of selecting one useful, efficient, and
maintainable solution from all the possibilities that exist.

That said, it seems to me that for numbers 2 through 5, you should
consider using the same approach used to deal with #1: create events on
the classes, and respond to them appropriately.

It's not clear from your post what "information" is being generated by
classes A through E. However, assuming they can be represented as an
event, raised whenever the information needs to be presented to something
(e.g. a logging class), then you can just subscribe to the events in each
class and log them as you see fit (or not at all, if you reuse the class
in a scenario where logging isn't needed).

Of course, even having decided to use events, there is still the question
of how to expose them. My own initial preference would be to extend the
event subscription to follow the class containment hierarchy. That is,
your Form sub-class would subscribe only to an event or events in class
A. Class A would in turn subscribe to events in classes B and C, and then
feed the information from those events back to the event or events it
exposes.

Again, without specifics, I can't give a specific suggestion. But for
example, you might be logging progress data being emitted from each class
and thread. If so, class A could expose a single event through which all
progress data is exposed, or it could expose separate events for each of
classes A, B, and C, and possibly the threads D and E (which in your
question, I would have called T1 and T2, to make more clear the
distinction between the threads and the classes in the discussion).

If class A exposes just a single event, then the event could provide some
way to indicate the source of the event, so that the consumer of the event
could tell where the event came from. This could as simple as an event
that emits a simple string, and to which class A prepends something like
"B: " or "C: ", etc. to indicate the source, or the event could emit a
more complex event data type as one of its arguments in which the sender
is indicated (either via reference or some other description).

If it turns out that class A never will have any need whatsoever to
monitor events in classes B and C, and if classes B and C are exposed to
your Form sub-class (for example, the Form sub-class actually creates
those objects and then passes them to class A for use), then it could
actually make some sense to not chain the events, and just have your Form
sub-class subscribe directly to the events in all three classes.

As you can see, even using an event here, there are lots of design choices
to be made. Not only do you have to figure out the above, there is also
the question of who is actually subscribing to the event, and how that
subscription is done.

One example might be that your class X subscribes to the event(s), and is
somehow given a reference to the control where it's supposed to log the
information. Or, class X could itself be some kind of event aggregator,
where you have code in your Form sub-class that subscribes to an event in
class X, and in the handler you update the control in question. Or you
could just skip having class X altogether, and just have your Form
sub-class code subscribe to the logging-related events in classes A, B,
and C, and do the appropriate thing in the handler.

Requirements 3 through 5 can be solved similarly. In those cases, the
event would be exposed in class A, just as the more general event
described above would be. Then you can have code in your Form sub-class
that handles the appropriate behavior.

Class A need not, nor should not, really know anything about the form.
Instead, for example, for requirement #3, class A can expose an event that
indicates whatever state change is related to the button being enabled or
disabled. In other words, class A doesn't enable or disable the button;
it simply provides a way for other code that is more directly tied to the
button to observe when the button should be enabled or disabled.

Note that from class A's point of view, this event may have absolutely
nothing to do with "enable" or "disable", in the sense that the Form
sub-class understands it. You should name the event so that it describes
its relationship to class A, and not to how it expects clients to use it.

So, for example, suppose the button is supposed to be disabled when some
thread in class A starts up. You might name the event
"WorkerRunningChanged" and expose a property named "WorkerRunning" that
can be examined when the event is raised, or just have two events, named
"WorkerStarted" and "WorkerStopped".

Either way, you leave it up to the subscriber to decide what the
appropriate response is. The Form sub-class may want to disable/enable a
button, but a logging class might just write out information about the
state-change, a status indicator might just change color rather than
becoming disabled/enabled, some other class might start or stop processing
based on the change, etc. No matter what the need of the client code, the
event in the class works fine, because it's declared relative to its
relationship with the class that exposes the event, rather than assuming
some specific usage on the part of the client of that class.

Requirements 4 and 5 sound a lot like the logging requirement in #2, so I
think most if not all of the discussion above with respect to #2 should
apply to numbers 4 and 5 as well.

Of course, to further complicate things, you could in fact embed the
logging code in each class. This is often done with debugging-only type
code. If the events don't represent any specifically useful aspect of the
public API you intend for each class to expose, a static class called at
appropriate places in each of the other classes could in fact be just the
right approach, rather than exposing events on each class.

In other words, I prefer events as a general rule. But if you can't
justify the event as an integral part of a class's public API, a different
approach is warranted.
[...]
One solution that worked for requirements 3, 4 and 5 were to pass the
Form's 'this' instance into A, and then continue to pass the Form
instance into C through the constructors and had public methods in
Form that modified the controls as necessary. I read online that said
solution is considered improper.

"Improper" can be taken strongly. It's not an entirely unreasonable
solution. But it does cause the classes to be tightly integrated when
there's no clear need to write them that way. The less integration
between the classes, the better IMHO, because it allows you to maintain
one class without having to worry too much about how changes in that class
will affect other classes.

Instead, with an event-driven approach, you define a clear public API that
is not dependent on the specific classes themselves. That way, you can
easily make changes in one area without being affected by what's going on
in the other classes.

Finally, note that events are not the only way to allow classes to be
connected loosely with each other in this way. But in .NET, it's probably
the most common technique you'll find. Even in other languages, the basic
idea is the same, even if the specifics of the implementation are not
(i.e. in general, this sort of event-driven processing can add a lot of
flexibility, and even in a language without function pointers or events,
like Java, you still see the same basic concept being used).

Pete
 
M

Mr. Arnold

Any help would be great.

It looks like you should be using a Model View Presenter design pattern,
whereas, the presenter controls the form, the forms are dumb forms
controlled by its Presenter. Controls, threads, ect, ect can be instantiated
in a form's Presenter and pass to and/from form through its View interface.
The Presenter calls logic within the Presenter or calls the BLL to do the
complicated stuff and heavy lifting, keeping the UI as dumb as possible.

What is Model -View- Presenter?

MVP is a software pattern considered a derivative of the
Model-view-controller.

http://en.wikipedia.org/wiki/Model_View_Presenter


The tutorial is about Web and Windows UI(s) and the MVP pattern.

MODEL-VIEW-PRESENTER

http://www.polymorphicpodcast.com/

click 'Shows' and located at page 6

click 'Design Patterns Bootcamp: Model View * Patterns*

view parts 1-5



__________ Information from ESET NOD32 Antivirus, version of virus signature database 4478 (20091003) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com
 
P

Phil

@ Pete: Wow, thanks a lot. My project is kind of a one time thing that
won't need to be reused, but I still like to follow good design
practices. I can also see problems arising with the current solution I
mentioned. I am going to give these events a try, and probably
eliminate class X. You gave some excellent tips and so much for me to
think about. At least now I have something more specific to think
about rather than just "what do I do!?". :)

@ Mr. Arnold: Thanks for your reply as well. I've used MVC for some
web design a while back and I didn't really think of anything like
that for Windows Forms. I'll see how my current approach takes me. I
will likely give the MVP approach a try as well after just to learn
and compare.
 
P

Phil

Potential problems include...
1. Form's constructor creates A, whose constructor in turn creates C.
As mentioned in my additional information #2, I want to disable a
button on Form when C catches the exception. It won't use my exception
handler because I add the handler after creating the objects. To solve
the problem, I moved A and C's constructors into initialization
methods and subscribed before calling the initialization method. I
feel like there would be a better solution. I can't really complain
though. It works and didn't require exposing my forms elements. Maybe
given the recent additional information I have provided below, you
might suggest a different approach.
2. My Form contains the handlers for all events including logging. To
log from within the Form itself, I've just been calling my handler.
I'm not really sure if that is the proper way, but it works.

I figured I would add a bit more information since I'm still stuck
trying to figure out the best way to approach this. I have solved all
of these problems using your initial preference of what I interpreted
as having all the handlers that actually do stuff to the form within
the Form itself, and have the events follow the class hierarchy so
they end up trickling down to the Form.
1. When A is in a specific state, its thread D should update the
Form's status bar text indicating the duration of time spent in said
specific state using the Form's stopwatch which is controlled by the
buttons on the Form.
2. C is created from A when the Form first loads. In fact, all classes
and threads mentioned are created when the Form first loads. If C
catches an exception, I just wanted to disable the button on the Form
so the application is basically rendered useless but at least it
doesn't crash.
3. A's threads D and E need to log to two list views on the Form. Both
threads need to log to both list views. The list views have multiple
columns and it is all text.
4. A creates multiple B's. B needs to log to only one of the List
Views on the Form, but really the B's can just return an enumeration
back to the caller (thread D of A) do the logging which might be
cleaner anyways.

I am happy with my solution. Thank you for your help. Of course I
would still accept feedback and make appropriate changes as necessary.
 
P

Peter Duniho

Potential problems include...
1. Form's constructor creates A, whose constructor in turn creates C.
As mentioned in my additional information #2, I want to disable a
button on Form when C catches the exception. It won't use my exception
handler because I add the handler after creating the objects. To solve
the problem, I moved A and C's constructors into initialization
methods and subscribed before calling the initialization method. I
feel like there would be a better solution. [...]

Given the information you've posted, it seems to me that the solution
you've implemented is fine, if perhaps a bit convoluted. If an exception
is thrown and caught during initialization (whether part of construction
or separate), and the fact of that exception is important to the clients
of class C, why is that fact not exposed via a property? I.e. why is the
occurrence of that exception something you feel needs to be reported
in-line with the execution of the initialization code, as opposed to
simply being something inspected after initialization?

If you implement it as the latter, then including the initialization as
part of construction isn't a problem at all. You just catch the exception
which, given it should result in the disabling of some control in the
form, necessarily must have some long-term effect on some state within
class A or C. Then, that state that's a consequence of the exception can
be exposed via a property, and then the client code in the Form that is
creating class A can, after class A is created, inspect that property, at
which point it enables or disables the control as appropriate. (If class
C is not exposed to the Form directly, then you'll want to proxy the
property via a property in class A that is visible to the Form class).
[...]
2. My Form contains the handlers for all events including logging. To
log from within the Form itself, I've just been calling my handler.
I'm not really sure if that is the proper way, but it works.

It seems to me that the alternative would be to add events to the Form
itself, subscribe similar handlers (or the same ones, I suppose...depends
on your implementation) to those events, and then raise those events from
within the Form when you want the logging to occur. That alternative
seems overly complex if there is no client code that would otherwise need
access to those events. On the assumption that the logging is handled
directly by the Form, for the Form's own purposes, it's fine for you to
just call the logging code directly from within the form.

I'm not sure I'd make that the handler itself. Structurally, it might
make more sense to have a common method that does the logging itself,
which you call from the event handlers subscribed to the other objects'
events, and also from places in the Form that need to log. That way, your
actual internal logging API (i.e. the method signature of the method
actually doing the logging) isn't in any way tied to the external event
API (i.e. the method signature for the events). Either can change as
needed without interfering with the other.

But other than that, the direct-class approach seems fine to me.
[...]
1. When A is in a specific state, its thread D should update the
Form's status bar text indicating the duration of time spent in said
specific state using the Form's stopwatch which is controlled by the
buttons on the Form.

Does "When A is in a specific state" mean "for the duration of time that A
is in a specific state", or does it mean "when A enters a specific
state"? If the latter, that seems fine and easy to deal with. Events are
very good at dealing with that sort of "edge-trigger" paradigm, but less
facile in "level-trigger" situations.
2. C is created from A when the Form first loads. In fact, all classes
and threads mentioned are created when the Form first loads. If C
catches an exception, I just wanted to disable the button on the Form
so the application is basically rendered useless but at least it
doesn't crash.

If the exception renders classes A and C useless, why catch it at all?
Especially if it happens during construction, seems like it'd be cleaner
to just let those classes fail to be constructed altogether (backing out
any already-performed initialization, if necessary).

Obviously, this has implications to the #1 point in your first half of
your post.
3. A's threads D and E need to log to two list views on the Form. Both
threads need to log to both list views. The list views have multiple
columns and it is all text.

Restate that requirement not in terms of where the logging needs to be
displayed, but rather in terms of how the information that eventually
finds its way into the log relates to the operation of threads D and E.
This should inform your design of the events.

Presumably you log to two different ListView controls because there are
two different classes of information being logged. So at a minimum,
you'll have two events on class A that represent those two classes. If
for some reason you also need to distinguish between the two threads at
the event level (rather than simply being part of the arguments in the
event itself), then you'll need four events, two for each thread.

In any case, none of the events should be tied conceptually to the
ListView controls involved. Generalize the events to represent what the
_information_ is, not how they will be displayed to the user.
4. A creates multiple B's. B needs to log to only one of the List
Views on the Form, but really the B's can just return an enumeration
back to the caller (thread D of A) do the logging which might be
cleaner anyways.

Again, IMHO you should not think of the logging as "class Foo needs to log
to...". Instead, think of the logging as the consumer of some abstract
information exposed by "class Foo". Let the consumer of the event worry
about how the logging occurs; don't let that show up in the design of the
event itself.

As far as whether the event is on class B, or class A, or both, that
depends on the access your Form sub-class has to the class B instances,
and how you want to handle information flow from class B to its client.
If you can return the information from class B to the class A code
handling the thread D, then it may well make sense to just have an event
on class A that exposes the results, and have thread D handle the raising
of that event in response to the enumeration returned by class B.

Hope that helps.

Pete
 
P

Phil

Brilliant. Again, thanks a lot.

Most of your response has just given me a lot to think about, where I
don't have any further questions at this point. I did have a few
things to follow up with.

With regards to "when A is in a specific state", I do mean "for the
duration of time that A is in a specific state". I am just keeping an
'Uptime' dispalyed in my status bar. I made use of a Stopwatch so that
the 'Uptime' count. It is the least important part of my application
though. What I have does work, but what it does is raise the event to
update the timer. I'm starting to think this should probably be in a
timer rather than that thread. I haven't read much on the differences
between these though.

Thread D is a thread that has a while(true) loop so it is constantly
running. I had to set the IsBackground property to true so that it
would close when the form does.
Thread E is really a listening thread (while true loop as well) that
is the server of a named pipe. I have almost no knowledge of named
pipes so I am not even sure if this would still be done this way or
not; I'm still doing a lot of reading.

I also come from C++ where I've used threads exclusively to do
whatever background task I need to do. In .NET there appears to be
timers, background workers, thread pools, threads, and possibly more.
I'm still doing readings to figure out what it is that I should be
using.

How the information that eventually finds its way to the logs relates
to D and E...
- Log 1 is populated by button clicks (not related to D or E though).
- Log 1 is populated with information about successful or failed
actions in D. What happens in D is that there is a check made to see
if something needs to happen. It is rarely the case that something
does need to happen, but it needs to be checked every second or so.
When something does happen, it logs it.
- Log 1 is populated with information the named pipe server receives.
- Log 2 is populated with different information the named pipe server
receives.

That being said, I'm going to try taking another look at my code and
events and try to follow your thinking for the logging. For some
reason this is rather difficult for me to think of it from that
perspective.

Thanks again for your time and help.
 
P

Peter Duniho

Brilliant. Again, thanks a lot.

Most of your response has just given me a lot to think about, where I
don't have any further questions at this point. I did have a few
things to follow up with.

With regards to "when A is in a specific state", I do mean "for the
duration of time that A is in a specific state". I am just keeping an
'Uptime' dispalyed in my status bar. I made use of a Stopwatch so that
the 'Uptime' count. It is the least important part of my application
though. What I have does work, but what it does is raise the event to
update the timer. I'm starting to think this should probably be in a
timer rather than that thread. I haven't read much on the differences
between these though.

From your description, it seems to me that the most efficient approach is
probably for class A to simply keep track of its own state and provide a
"Timespan Uptime" property it can return when you want to display that.
Inside class A, just use the Stopwatch to monitor the length of time it's
been in a current state (retrieving the property while class A is
currently "up" would provide the immediate value, otherwise it would be
the total uptime as of the last moment class A was up). Then you can poll
using a System.Windows.Forms.Timer, set to 500 or 1000 ms (I wouldn't poll
much more often than that).

For example, in class A:

class A
{
private DateTime _dtUpStart;
private Timespan _tsUptime;
private bool _fUp;

public Timespan Uptime
{
get
{
Timespan tsRet = _tsUptime;

if (_fUp)
{
tsRet += DateTime.Now - _dtUpStart;
}

return tsRet;
}
}

// The getter could be public, if you expect clients to need
access to
// that information. Alternatively, you might find you don't need
the
// getter at all, if there's never any need to know the "up" state
even
// from within class A
private bool _Up
{
get { return _fUp; }
set
{
if (value != _fUp)
{
if (value)
{
_dtUpStart = DateTime.Now;
}
else
{
_tsUpdate += DateTime.Now - _dtUpStart;
}

_fUp = value;
}
}
}

public void SomeMethodThatCausesAToBeUp()
{
_Up = true;
}

public void SomeMethodThatCausesANotToBeUp()
{
_Up = false;
}
}

As an alternative, if you would prefer to track the uptime outside of
class A (for example, maybe that's just not a feature that is part of
class A's core functionality), you can implement similar code to the
above, but in the client of class A (e.g. your Form sub-class). Then, in
class A, instead of setting the "_Up" property as the "up" state changes,
you'd raise an event that signals the change of the "up" state. The
client would subscribe to that event, and use that to control the tracking
of the uptime (it might even have its own private "_Up" property
implemented in just the way it would have been in class A, set to "true"
or "false" depending on the event being raised from class A).

I do think that in either case, you want the UI updating to be some kind
of very-low-frequency polling done by the UI code. It would be possible
to implement a kind of regularly occurred "class A is up" event in class
A, but to my way of thinking that ties class A much too closely to your
UI. It doesn't seem to me that class A should care at all how the UI
presents the uptime, and in fact some presentations might not need a
regularly occurring notification at all (e.g. the "uptime" value might
simply be presented to the user as part of a snapshot of operation, such
as a logged message or a dialog box that appears).
[...]
I also come from C++ where I've used threads exclusively to do
whatever background task I need to do. In .NET there appears to be
timers, background workers, thread pools, threads, and possibly more.
I'm still doing readings to figure out what it is that I should be
using.

For what it's worth, all those things are available using C++ on Windows
too, even using the unmanaged API. It's just a matter of what tools in
the various libraries that are available one is using.
[...]
That being said, I'm going to try taking another look at my code and
events and try to follow your thinking for the logging. For some
reason this is rather difficult for me to think of it from that
perspective.

For what it's worth, for me it's mostly about separation of duties and
generalizing observable properties. The less a given class can know about
clients of that class, the better. Events allow you to provide a
generalized API for observable properties and, well, events that occur
without tying a class to some specific way of handling changes to those
properties or events that occur.

I agree that there's a conceptual shift that has to occur in order to use
events effectively. And it's also true that events are not actually
always the right way to implement things (for example, the polling logic
discussed above with respect to the display of the uptime). But often,
the "push" model of events works better than the "pull" model of polling,
and events offers a convenient flexible way to implement a "push" model
(while they are really just a fancy wrapper around callback delegates,
syntactically they simplify things well enough to make them pleasurable to
use).

For me, the logging example is a perfect example of where events and
generalization can be useful. Logging is very much consumer-specific in
terms of how a message is logged. But the underlying data is very much
producer-specific. In other words, there's an infinite number of ways
some specific information being emitted can be presented to the user (e.g.
items in a list-based control, text in a text box, message in a dialog
box, lines in a file, text sent out over the network or a serial port,
etc.) but of course the information itself is precisely only that
information exposed by the class reporting it (the producer).

By creating an event that represents that producer-specific information,
and then requiring the clients of the class -- the consumers -- to
interpret and present that information to the user as appropriate to that
consumer, you allow for maximum flexibility (even to the extent of having
multiple consumers...you can present the message in the UI in a list-based
control, _and_ write it to a file, _and_ send it out over the network, all
simply by adding additional handlers to the event).
Thanks again for your time and help.

Glad you find the discussion useful!

Pete
 
P

Phil

I just wanted to conclude this by saying thanks one last time. I
didn't realize so much thought could go into a silly home project of
mine. I've taken your suggestions and still have more playing around
to do but I think I am on the right track now. The great thing is,
this new outlook will help me for future projects as well. You've been
great help.
 
P

Peter Duniho

I just wanted to conclude this by saying thanks one last time. I
didn't realize so much thought could go into a silly home project of
mine. I've taken your suggestions and still have more playing around
to do but I think I am on the right track now. The great thing is,
this new outlook will help me for future projects as well. You've been
great help.

Happy I could. Those "silly home projects" are often the best way to
learn new things, because it's easier to justify using some random piece
of technology, or some new programming technique you otherwise wouldn't
have tried. For real production work, too often the need to actually get
to the finish line prevents that sort of fiddling around.

Have fun!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top