I'm doing a personnel absence registration application and the problem arose that I need to indicate to a form, where a certain entity is listed, the next form that should be opened after having been chosen.
My question is, more than anything, about implementation and design since I have an idea of how to apply it but I have my doubts about whether it is maintainable. I have the ability to use an enum to indicate the next form or pass the form type or form instance per constructor. There may be another, more elegant way to do it, but I don't know of it.
I didn't choose to instantiate the forms serially for performance, maintainability, and extensibility issues.
To guide you a little more, workflow would be as follows:
frmMain.cs :
It's the main screen to boot up and you have these 2 options.
private void tsmiDatosPersonal_Click(object sender, EventArgs e)
{
frmPersonalListado frm = new frmPersonalListado();
frm.ShowDialog(this);
}
private void tsmiInasistenciasPersonal_Click(object sender, EventArgs e)
{
frmPersonalListado frm = new frmPersonalListado();
frm.ShowDialog(this);
}
frmPersonalListing.cs :
It is where the staff of the establishment is listed and then I select one from the list and open the following form.
public frmPersonalListado()
{
InitializeComponent();
_personalNegocio = new PersonalNegocio();
_inasistenciaNegocio = new Lazy<InasistenciaNegocio>();
}
public frmPersonalListado(Form formularioObjetivo)
: this()
{
_formularioObjetivo = formularioObjetivo;
}
private void tsbVerPersonal_Click(object sender, EventArgs e)
{
Aplicacion.Entidades.Personal personalSeleccionado = lvPersonal.SelectedItems[0].Tag as Aplicacion.Entidades.Personal;
frmPersonalCRUD frm = new frmPersonalCRUD(ModoFormulario.Ver, personalSeleccionado); // <<< Aca deberia instanciar el formulario correcto elegido desde el menú.
frm.ShowDialog(this);
}
The flow is similar to that of a workflow with which there is a behavior that is defined at the beginning: If X or Y final form is opened.
Edit 1:
As it is difficult to interpret the idea, I leave you with an image that shows the workflow that the forms I work with follow.
The real problem is what is the correct design and implementation to make the initial form tell the next form what behavior it should have so that it opens one of the last forms.
Edit 2:
The following code commit demonstrates the code smell I mentioned earlier.
My idea is that, every time I instantiate frmPersonalListado
, I pass through the constructor the behavior that it has to have when I select a staff from the list. This behavior is to instantiate the following form, passing the staff chosen from the list as a parameter to the constructor.
private void tsbPersonalInasistencias(object sender, EventArgs event)
{
var frm = new frmPersonalListado(x => {
var frm2 = new frmCrudPersonal(); // <<< Como inyecto el personal aca?
frm2.ShowDialog(this) // <<< Ese this podria ser frm?
});
frm.ShowDialog(this);
}
public frmPersonalListado(Func<Personal, void> nextForm)
{
_func = nextForm;
}
private void btnSeleccionar_Click(object sender, EventArgs event)
{
var personal = ObtenerPersonalSeleccionado();
_func(personal); // Magicamente, deberia instanciar el form con el personal seleccionado.
}
But the code above is an idea that doesn't compile.
edited
If you want to go through callbacks, so that the code you have put compiles for you, you must do something similar to this:
In the form that calls it:
In frmPersonalListing:
That said, this option is neither better nor easier to maintain than the other. All you're doing is delegating the code to a callback, but in the end you write the same code as if you did it by hand.
As I say, there are a thousand ways to do it: you could pass the type and use the
Activator
to create the instance, or directly pass the following type in a form generic (although you would lose the WinForms designer unless you use some tricks). Your target forms could also implement an interface that had some functionEligePersonal
or something similar, and pass the interface type to the staff selector (such as dependency injection, but not automatic, since it is variable according to parameters).In any case, I think you are starting the house from the roof: if your idea is to have maintainable and manageable code, start by stopping writing business logic in the form and start using some design pattern (the one that is most comfortable for you). , although MVP is the one that is usually applied to WinForms, you can also use MVVM or MVC)... and if you don't want to use any of these, you can make your own, but write the logic of what the functions do in the events themselves (apart from modifying the view itself) is already unmaintainable, and you should start there.
What you want to do you can move around, but in the end, you have to somehow write the code (or the type of form at least) that you need. Either in
Actions
, either through interfaces, or by passing types and creating them with theActivator
, or by injecting dependencies.THAT is not the maintainability issue.
And the most logical thing in your case (be it a form, or 100, it doesn't matter), is that if a form needs a selected person, it has two options: you call it with that person (through a function, in the constructor, wherever you want) , or else you call him without that person: in that case, that form (and not the "caller") should be the one in charge of taking you out of the personnel selection window.
That is what is "conceptually" logical, and not the other.
old question
No longer applicable after question edit
Man, I don't really understand the question very well, but if the idea is (if I haven't misunderstood it), that when you select an option, a form first opens letting you choose the staff (this is common regardless of the option you choose ), and then the form opens with the data of the person you have chosen...
Well, there are a thousand ways to implement it, but seeing that you are using it
ShowDialog
for everything and that the application follows a fixed flow blocking the rest...So I would reverse the order of actions, and have a "staff" selector in a helper class.
Following your example:
frmPersonalListado.cs
And in the main form, where before you did the
ShowDialog
defrmPersonalListado
, now you do:This would be the simplest and "healthiest" way according to what I have understood from the specifications.
If the recruiter is going to be called from other places, it might be worth putting it in each of those forms (in the event
Load
or similar).From there, and in terms of maintainability, you can mess with whatever you want... from injecting (making an interface and using DI) the personnel selection form to the target form, to whatever you want. This solution that I give you is very basic but so does the code that you show us.
Disclaimer: I have written the code quickly and without checking it, there are probably syntax errors
If you don't want your project to turn into unmaintainable chaos as soon as it gets a little bigger, you should separate logic from presentation. A good way to achieve this in Windows Forms is to use the Model-View-Presenter pattern . Copied from that link:
Here's an example solution on GitHub built from the example with three forms you show in your question. Below I explain the most significant points of the project.
We also include a repository for data, with its corresponding interface:
We also create a base interface with common functionality:
Notice how we have two types of interactions: towards the view (we establish the data to be displayed) and from the view (events generated, in this case, only by the user).
Also in this case we create a base class that will implement the common interface:
This would be the factory interface, the example project implementation uses
Activator.CreateInstance
but that is now irrelevant:A continuación un fragmento del código del presentador. ¡Fíjate que no tiene ninguna dependencia con Windows Forms!
Como guinda, así es como se inicia la aplicación. En una aplicación real se usaría algún contenedor de inyección de dependencias:
Puntos interesantes
And basically that's it. I hope this ramble is helpful to you.
I still don't quite understand what you want, but if you want to have a "creator" of forms, it would be good if you implemented a design pattern of the creation category, to separate the form creation logic from the logic that chooses the form that should go on, and in the form creation logic you handle instantiation and stuff. I recommend you read the Factory Method Design pattern , this to ensure code maintainability.