Managing Complexity in Windows Forms

  • Thread starter Thread starter RL Stevenson
  • Start date Start date
R

RL Stevenson

What is a reasonable way to manage a complex form with 5 or so tabs with 100
or more controls bound to 5-10 tables in a database?

Pasting all those controls, datasets, data adapters directly onto the form
resulted in an unwieldy file of nearly 10,000 lines of code.

Seems like there must be a better way.
 
Normally you do not want to put code logic directly into the GUI, this makes
testing difficult and like you say, you get unwieldy code.

Possibly try to extract all the code into a couple of classes, then your GUI
simply calls methods on your class library objects, making the GUI code very
lightweight, yes we have really just moved the code from one place to another
but, hopefully with a good design there will be plenty of places where you
will stop redundancy and be able to reduce code complexity and size.

Also moving all your code out of the GUI will allow you to easily test your
code with something like NUnit which would be difficult otherwise if it was
embedded inside the GUI code.
 
If you check out VS 2005 Beta 2, I belive they will automatically put
the UI codes outside of the class.

So, a lot of people are also awaring your problems.

JW
 
Thanks, you are quite right about keeping the application logic out of the
GUI.

In the case I described, the unwieldy code seems to be all GUI.
For example, the designer-generated code is 4500 lines.
The declarations for the controls add another several hundred lines.
There are 100 lines of :

binding=this.control.DataBindings["Text"];
binding.Format+=new ConvertEventHandler(this.control.Control_Format);
binding.Parse+=new ConvertEventHandler(this.control.Control_Parse);
There are 8 datasets and 8 data adapters and the code to take care of adding
rows and inserting them into the database.

There are 18 combo boxes spread over the 10 tabs on the form, and each one
is initialized by calling a stored procedure in the database and using a
data reader to create a DataSource for the control.

Seems like the controls need to be in the same class as the datasets in
order to do binding. One might hope that the tab pages could be decomposed
into separate units, but one of the datasets is bound to controls on each of
the tabs on the form.

The form doesn't seem to have many opportunities for partitioning, but then
maybe there is something quite elementary that I have overlooked due to my
inexperience with Windows Forms.

Any suggestions?
 
Hi,

I recently investigated the same problem, and have come up with what I think
is a standard solution.

My last app was a GUI with just one main form, and many controls. It "grew"
as I added features, and at no point could I see a way to partition the code.
I ended up with 3000 lines, of which 50% were in the main form.

For my next GUI, which I started recently, I anticipated this problem, and
did a bit of research before starting, and came up with two parts of a
solution.

1. Use c# UserControls to carve up the GUI code into smaller classes.
2. Use the Mediator design pattern to do manage the interraction of the
various UserControls.

I wish I could say that this has worked, but I can't be sure yet, as I am
yet to implement the mediator part of the code, but I will be doing it in the
next day or two, so I'll get back here, and tell you how it went.

WRT Design Patterns, I personally find it difficult to read them, to
understand how they apply to my problems, and to code them. However, my
understandign of the Mediator pattern is that is used to partition complex
GUI's.

Regards,

Stephen

RL Stevenson said:
Thanks, you are quite right about keeping the application logic out of the
GUI.

In the case I described, the unwieldy code seems to be all GUI.
For example, the designer-generated code is 4500 lines.
The declarations for the controls add another several hundred lines.
There are 100 lines of :

binding=this.control.DataBindings["Text"];
binding.Format+=new ConvertEventHandler(this.control.Control_Format);
binding.Parse+=new ConvertEventHandler(this.control.Control_Parse);
There are 8 datasets and 8 data adapters and the code to take care of adding
rows and inserting them into the database.

There are 18 combo boxes spread over the 10 tabs on the form, and each one
is initialized by calling a stored procedure in the database and using a
data reader to create a DataSource for the control.

Seems like the controls need to be in the same class as the datasets in
order to do binding. One might hope that the tab pages could be decomposed
into separate units, but one of the datasets is bound to controls on each of
the tabs on the form.

The form doesn't seem to have many opportunities for partitioning, but then
maybe there is something quite elementary that I have overlooked due to my
inexperience with Windows Forms.

Any suggestions?



Normally you do not want to put code logic directly into the GUI, this
makes
testing difficult and like you say, you get unwieldy code.

Possibly try to extract all the code into a couple of classes, then your
GUI
simply calls methods on your class library objects, making the GUI code
very
lightweight, yes we have really just moved the code from one place to
another
but, hopefully with a good design there will be plenty of places where you
will stop redundancy and be able to reduce code complexity and size.

Also moving all your code out of the GUI will allow you to easily test
your
code with something like NUnit which would be difficult otherwise if it
was
embedded inside the GUI code.
 
Thanks. I too thought that user control might be the answer.

I found that the user controls also grow to huge size.

If you have many (a dozen or so) controls grouped as a user control, and the
controls are bound to a database, do you include the data adapter and
dataset in the user control? If not, how did you bind to the individual
controls from outside the user control?

How do you debug a complex user control? I developed mine in a separate
Solution file to avoid issues with the form designer. My recompile-retest
cycle required building the control in one solution, switching to the other
Solution, removing the old user control from the client form, remving the
old control from the toolkit, adding the new control to the toolkit and
adding the new control to the client form. This can't be the only way!

Javaman59 said:
Hi,

I recently investigated the same problem, and have come up with what I
think
is a standard solution.

My last app was a GUI with just one main form, and many controls. It
"grew"
as I added features, and at no point could I see a way to partition the
code.
I ended up with 3000 lines, of which 50% were in the main form.

For my next GUI, which I started recently, I anticipated this problem, and
did a bit of research before starting, and came up with two parts of a
solution.

1. Use c# UserControls to carve up the GUI code into smaller classes.
2. Use the Mediator design pattern to do manage the interraction of the
various UserControls.

I wish I could say that this has worked, but I can't be sure yet, as I am
yet to implement the mediator part of the code, but I will be doing it in
the
next day or two, so I'll get back here, and tell you how it went.

WRT Design Patterns, I personally find it difficult to read them, to
understand how they apply to my problems, and to code them. However, my
understandign of the Mediator pattern is that is used to partition complex
GUI's.

Regards,

Stephen

RL Stevenson said:
Thanks, you are quite right about keeping the application logic out of
the
GUI.

In the case I described, the unwieldy code seems to be all GUI.
For example, the designer-generated code is 4500 lines.
The declarations for the controls add another several hundred lines.
There are 100 lines of :

binding=this.control.DataBindings["Text"];
binding.Format+=new ConvertEventHandler(this.control.Control_Format);
binding.Parse+=new ConvertEventHandler(this.control.Control_Parse);
There are 8 datasets and 8 data adapters and the code to take care of
adding
rows and inserting them into the database.

There are 18 combo boxes spread over the 10 tabs on the form, and each
one
is initialized by calling a stored procedure in the database and using a
data reader to create a DataSource for the control.

Seems like the controls need to be in the same class as the datasets in
order to do binding. One might hope that the tab pages could be
decomposed
into separate units, but one of the datasets is bound to controls on each
of
the tabs on the form.

The form doesn't seem to have many opportunities for partitioning, but
then
maybe there is something quite elementary that I have overlooked due to
my
inexperience with Windows Forms.

Any suggestions?



Normally you do not want to put code logic directly into the GUI, this
makes
testing difficult and like you say, you get unwieldy code.

Possibly try to extract all the code into a couple of classes, then
your
GUI
simply calls methods on your class library objects, making the GUI code
very
lightweight, yes we have really just moved the code from one place to
another
but, hopefully with a good design there will be plenty of places where
you
will stop redundancy and be able to reduce code complexity and size.

Also moving all your code out of the GUI will allow you to easily test
your
code with something like NUnit which would be difficult otherwise if it
was
embedded inside the GUI code.



:

What is a reasonable way to manage a complex form with 5 or so tabs
with
100
or more controls bound to 5-10 tables in a database?

Pasting all those controls, datasets, data adapters directly onto the
form
resulted in an unwieldy file of nearly 10,000 lines of code.

Seems like there must be a better way.
 
Hi, RL,

Sorry, I can't answer any of those questions. You've already gone further
with C# controls than I have.

Good luck with it. As promised, I'll let you know how the mediator design
pattern turns out,

Stephen

RL Stevenson said:
Thanks. I too thought that user control might be the answer.

I found that the user controls also grow to huge size.

If you have many (a dozen or so) controls grouped as a user control, and the
controls are bound to a database, do you include the data adapter and
dataset in the user control? If not, how did you bind to the individual
controls from outside the user control?

How do you debug a complex user control? I developed mine in a separate
Solution file to avoid issues with the form designer. My recompile-retest
cycle required building the control in one solution, switching to the other
Solution, removing the old user control from the client form, remving the
old control from the toolkit, adding the new control to the toolkit and
adding the new control to the client form. This can't be the only way!

Javaman59 said:
Hi,

I recently investigated the same problem, and have come up with what I
think
is a standard solution.

My last app was a GUI with just one main form, and many controls. It
"grew"
as I added features, and at no point could I see a way to partition the
code.
I ended up with 3000 lines, of which 50% were in the main form.

For my next GUI, which I started recently, I anticipated this problem, and
did a bit of research before starting, and came up with two parts of a
solution.

1. Use c# UserControls to carve up the GUI code into smaller classes.
2. Use the Mediator design pattern to do manage the interraction of the
various UserControls.

I wish I could say that this has worked, but I can't be sure yet, as I am
yet to implement the mediator part of the code, but I will be doing it in
the
next day or two, so I'll get back here, and tell you how it went.

WRT Design Patterns, I personally find it difficult to read them, to
understand how they apply to my problems, and to code them. However, my
understandign of the Mediator pattern is that is used to partition complex
GUI's.

Regards,

Stephen

RL Stevenson said:
Thanks, you are quite right about keeping the application logic out of
the
GUI.

In the case I described, the unwieldy code seems to be all GUI.
For example, the designer-generated code is 4500 lines.
The declarations for the controls add another several hundred lines.
There are 100 lines of :

binding=this.control.DataBindings["Text"];
binding.Format+=new ConvertEventHandler(this.control.Control_Format);
binding.Parse+=new ConvertEventHandler(this.control.Control_Parse);
There are 8 datasets and 8 data adapters and the code to take care of
adding
rows and inserting them into the database.

There are 18 combo boxes spread over the 10 tabs on the form, and each
one
is initialized by calling a stored procedure in the database and using a
data reader to create a DataSource for the control.

Seems like the controls need to be in the same class as the datasets in
order to do binding. One might hope that the tab pages could be
decomposed
into separate units, but one of the datasets is bound to controls on each
of
the tabs on the form.

The form doesn't seem to have many opportunities for partitioning, but
then
maybe there is something quite elementary that I have overlooked due to
my
inexperience with Windows Forms.

Any suggestions?




Normally you do not want to put code logic directly into the GUI, this
makes
testing difficult and like you say, you get unwieldy code.

Possibly try to extract all the code into a couple of classes, then
your
GUI
simply calls methods on your class library objects, making the GUI code
very
lightweight, yes we have really just moved the code from one place to
another
but, hopefully with a good design there will be plenty of places where
you
will stop redundancy and be able to reduce code complexity and size.

Also moving all your code out of the GUI will allow you to easily test
your
code with something like NUnit which would be difficult otherwise if it
was
embedded inside the GUI code.



:

What is a reasonable way to manage a complex form with 5 or so tabs
with
100
or more controls bound to 5-10 tables in a database?

Pasting all those controls, datasets, data adapters directly onto the
form
resulted in an unwieldy file of nearly 10,000 lines of code.

Seems like there must be a better way.
 
RL said:
I found that the user controls also grow to huge size.

Let me say this about the original question: I have never separated
parts of a unit (be it a form or a user control) from the rest just
because it was growing large. The only thing that's really growing large
here is the code that's generated automatically by the IDE, I don't see
what's particularly bad about that. In VS 2005, using partial classes,
that code will be in a different file, so you don't see it at all. In VS
2003, it's usually hidden in a region, so you don't normally see it
either. This code has to go *somewhere* - if you create a lot of UI
elements, you'll get a lot of instructions for it.

Of course, user controls are a good mechanism to cut down on the size of
a single unit, but they also produce overhead. For every control you
create, you'll have to define an interface and you'll have to define
exactly how this control will work with others. Using the Mediator
pattern, you don't get rid of these decisions or this work, you only
push them to a different place.

Please, I'm not saying it's bad to do this. I'm only saying that the
decision for each single user control should IMHO be made when there's a
clear purpose to that control - usually that it's possible to reuse that
control elsewhere - and when the additional effort is justified. Using
user controls, the number of code lines in your whole project will not
get smaller. On the contrary, it will grow, and you get to write a lot
of the additional code manually. So, don't do it just because the
InitializeComponent method is getting long.
If you have many (a dozen or so) controls grouped as a user control, and the
controls are bound to a database, do you include the data adapter and
dataset in the user control? If not, how did you bind to the individual
controls from outside the user control?

Sure, that's what you do. Generally, you can create "proxy properties"
on the user control that simply read and set their values to/from
any/all of the "inner controls". Or your user control can have its very
own properties - if you're mainly creating the control as a reusable
component in its own right, you'll probably encounter more of the latter.
How do you debug a complex user control? I developed mine in a separate
Solution file to avoid issues with the form designer. My recompile-retest
cycle required building the control in one solution, switching to the other
Solution, removing the old user control from the client form, remving the
old control from the toolkit, adding the new control to the toolkit and
adding the new control to the client form. This can't be the only way!

I have user controls in the same project as a form and in a different
project from the form, neither of which is a problem. If these projects
are parts of the same solution, it's easy to develop and debug the user
controls. I'm not aware of any specific "issues with the form designer",
though - it takes some getting used to (make sure to keep your projects
compiled, and there's this bug in VS 2005 beta 2 if you have your form
open while running the debugger), but it works for me.


Oliver Sturm
 
Thanks for your responses.

The difficulty I had with user controls is similar to the issue discussed in
KnowledgeBase article 842706. When one of my user controls was in the same
project as the form, all instances of the control would occasionally
disappear from the form. The frequency increased as the form got larger and
began to take longer and longer to regenerate the designer section of the
code. Moving the control to a separate project has prevented a recurrence
of the problem.

Please elaborate on "If these projects are parts of the same solution, it's
easy to develop and debug the user controls." Don't you still have to
delete the old version of the user control from the form, delete the old
version from the User Controls toolbar, add the new version to the User
Controls toolbar, then add the new version to the form? Repeating these
steps for each recompile-retest cycle seems very tedious. Is there a
shortcut I have missed?

Thanks.
 
RL said:
The difficulty I had with user controls is similar to the issue discussed in
KnowledgeBase article 842706. When one of my user controls was in the same
project as the form, all instances of the control would occasionally
disappear from the form. The frequency increased as the form got larger and
began to take longer and longer to regenerate the designer section of the
code. Moving the control to a separate project has prevented a recurrence
of the problem.

Ah, but in your previous post you said you were using a separate
solution (not a separate project), which is a very different thing.

I've never encountered that problem described in the kb article, but
it's been quite a while since I've really used VS 2003, so maybe I
forgot it by now :-)
Please elaborate on "If these projects are parts of the same solution, it's
easy to develop and debug the user controls." Don't you still have to
delete the old version of the user control from the form, delete the old
version from the User Controls toolbar, add the new version to the User
Controls toolbar, then add the new version to the form? Repeating these
steps for each recompile-retest cycle seems very tedious. Is there a
shortcut I have missed?

This is completely unnecessary, and I'm quite sure it was already
unnecessary in VS 2003. The only reason to have a control in the toolbox
at all is to put it on a form, initially. Once it's on the form, you
don't need it in the toolbox at all, and it doesn't matter whether the
reference in the toolbox is current or not.

(I don't know if it might be a good idea to make the reference current
before adding another instance of the control, but I don't usually care
and I haven't had any problems.)

When compiling the project with the user control, a form that's open in
the designer and which contains this user control will be refreshed with
the updated control once the compilation has finished. There's
absolutely no need to delete the control from the form, put a new one on
or anything like that.

Disclaimer: there may be certain situations, like when debugging
property initialization/persistence code in the user control, when
removing/re-adding it can be a good idea.


Oliver Sturm
 
Thanks, your note is quite helpful, especially the part about "When
compiling the project with the user control, a form that's open in the
designer and which contains this user control will be refreshed with the
updated control once the compilation has finished."

I expected the designer to show me the updated control as soon as I opened
the client form after updating the user control, and when it did not, I
assumed I had to get rid of the old user control and paste on the new one
myself. So I had to update the toolbox to do that.

I didn't mean to be inconsistent about " ... in your previous post you said
you were using a separate solution (not a separate project), which is a very
different thing." My earlier post is correct. The later post should have
said "separate project in a separate solution" or something equivalent.
 

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

Back
Top