What is the best practice for communicating forms? - c#

When I need to pass some information from a form to another I usually do the following:
Form2 form = new Form2(this);
form.ShowDialog();
And inside Form2.cs, I use a constructor like:
private Form1 parent;
public Form2(Form1 form)
{
...
parent = form;
}
This way I can get a information from a textbox doing parent.textbox1.Text only if textbox1 is not a private member from Form1. Ok, a lot of time I need to get information about controls in Form1, should I make the setters and getters for each attribute of a control needed in Form2? For example: I need to know the values of Text, ReadOnly and Location. Should I make the setters and getters for each one of these attributes? Is the use of internal modifier a bad practice?

The correct way to do it is with delegates. They are really pretty simple but it takes awhile to get your head around them. Here is a great example of what I think you're looking for: http://samgaut.blogspot.com/2007/11/use-delegates-to-pass-data-between.html

Since I am not allowed to add comments to answers I'm going to add this.
The linked blog post from the accepted answer does not make sense to me (could just be my lack of thorough understanding of delegates).
If the next-in-line form frmDestination has a publicly accessible setter method (SetCustomerID(string strCustID)), then why do you need to pass that into a delegate when you can just pass customerID directly to the setter?
I noticed he mentioned that
Basically, the member variable that is set within the delegate method will be populated before the Form_Load event is executed. If you notice the delegate call is executed before the frmDestination.Show() call is made. This way, you have that variable available to execute in your Form_Load processing.
Would just calling dest.SetCustomerID(customerID) before dest.Show() not do the same thing?

Seeing as this is not a reusable framework from what I can tell, I wouldn't create wrapper properties around the control properties.
If there was something that needed to be flexible about this parent form then the proper course might be to use an interface that specifies the particular controls exist or a specific base form class.

Related

How come I can't access public properties or user set controls in C# WinForms?

I grew up on VB.Net. From there, in a routine, I could instantiate a form, and access controls that I had put on that form, like a textbox. Further, after hiding the form, I could read public properties that got set on the form. I am trying to do this in C# and according to the intellisense, neither is accessible.
so, in Form 1, for example I say
Form f = new MyOtherForm();
from there, f only returns things like Text, or Tag.....I can't see any controls on f to set them.
Further, MyOtherForm has a public property called MyResult. After a show dialog, in VB.Net, I could, from the calling form, access
f.MyResult
In C#, I cannot.
Can someone help me understand how it works in C#?
If you want to see the properties of MyOtherForm, you should declare the form as MyOtherForm:
MyOtherForm f = new MyOtherForm();
Beside that, you should also check if the property you are looking for is set as public.
To make your life easier, learn and love the var keyword.
The implicit typing that this introduces allows for you to not have to worry about getting the correct type when declaring a variable. In your case, you know exactly what you're setting it to, so it isn't a huge deal. However, when you're getting results from methods, or LINQ calls, this provides a huge benefit.
Bear in mind, this isn't like the var keyword in JavaScript. Your variable still has the correct type, only you don't have to set it explicitly.

Access NSTextBox from another class

I have my WinForm (Form1) and another class (Class1). Form1 has a textbox with the name of TextBox and I'm wanting to change the text inside TextBox within Class1. How can I do this? I've already checked everywhere and tried all the methods on Stackoverflow and other sources but they all seem to not be working.
EDIT:
I've tried making the TextBox public, but that didn't work.
Then I tried using the following code:
private readonly Form1 form;
public Class1(Form1 form) {
this.fom = form;
}
private void EditText() {
form.TextBox.Text = .... (TextBox seems to not be an object, even though it is)
}
Which also ended up not working.
Finally once I set the TextBox component modifiers to public inside the visual studio editor it gave me no errors until I ran the program in which it said:
Error 1 Inconsistent accessibility: field type 'NSTextBox' is less accessible than field 'OverloadBot_Panel.Form1.ConsoleTextBox' D:\Users\goff-laptop\Desktop\OverloadBotNet\OverloadBot_Panel\OverloadBot_Panel\Form1.Designer.cs 364 26 OverloadBot_Panel
I am using a Theme if that matters at all.
Basically, you need to do this the right way. Or at least a better way than exposing the control directly.
In order to keep tight encapsulation, as well as separation of concerns, you shouldn't be exposing your controls to the outside world anyways. Instead, define a method on your form class that sets the text box for you:
public void UpdateText(string text)
{
myTextBox.Text = text;
}
And in the caller:
private void DoStuff()
{
form.UpdateText("Test");
}
Now the caller doesn't need to know about the internal structure of the form. In fact, if you went through an interface instead, it wouldn't care if it was event talking to a form. The class on the other side would simply react to the "UpdateText" message in whatever way it needed to. This design is much better in addition to fixing your problem.

Multiple Opening Paths

I have an application that can launch/new up a form(lets call it QuickNoteForm) from many different actions. It can launch the form from many different tabs and mostly are launched thru buttons all over my application.
I basically want to track where it was launched from, ie I need to track its Launch Path.
What would be a good approach to implement this. I was thinking to enclose this as a property that gets set via the constructor of the QuickNoteForm. I want to track from which action this form got launched from.
This is a windows forms application and not a asp.net app.
thanks.
Create an enum that would list all possible paths (or a static class with constants if you worry about maintainability, enums don't work well when compiled and then modified). Add a custom constructor to your form that would accept this enum as a parameter. When you instantiate a form, use that constructor. Basically replace all occurrences of New QuickNoteForm() with New QuickNoteForm(yourEnumValue). For compatibility, add an Unknown = 0 value to the enum, this way calling form's default constructor will work too, just not as useful.
If this approach is not practical (please provide more details on your application), you can also provide a context Control as parameter in your form's constructor. Then have code like If typeOf ctl Is Button AndAlso DirectCast(ctl, Button).Text = "Something" Then and all sorts of crazy stuff. This promotes separation of concerns, i.e. the calling code does not need to know how to call and only pass itself as a parameter, but also makes your code harder to maintain, because you may end up with one giant know-it-all method which would connect all pieces together.

Passing Data Between 3 Separate WinForms [duplicate]

This question already has answers here:
Communicate between two windows forms in C#
(12 answers)
Closed 3 years ago.
Throughout my time coding in the realm of C# WinForms, I have had many instances in which I have come across different methods of passing data between forms. I work on a large codebase -- some of these methods were written by others, which I subsequently extended, and others were written by myself. It seems there are two main paradigms, both of which I have coded rather comfortably.
1.) Pass the parent form to the child when instantiating or showing the child form. For example:
ChildForm.Instance = new ChildForm(this);
--Or--
ChildForm.Instance = new ChildForm();
ChildForm.Instance.Show(this.TopLevelControl);
This allows the child to pull information from the parent, as well as allows the parent to call methods on the child. Bear with me for a moment -- I do realize that this breaks so many paradigms, and is "bad" practice -- remember, I'm encountering much of this during maintenance of a larger codebase to which I am required to make incremental adjustments without doing a complete refactoring.
2.) Use an event delegate to allow for data to be transferred between parent and child forms. To the best of my knowledge, this still requires that the parent form establish this event when spawning the child. For example:
Within parent:
childForm = new ChildForm(this);
DataRead += new DataReadEventHandler(childForm.ChildForm_DataRead);
Within child:
public void ChildForm_DataRead(Data data)
{
if (InvokeRequired)
{
Invoke(new MethodInvoker(delegate() { ChildForm_DataRead(data); }));
}
else
//do something
}
Something of this nature. Now, I'm still not a strong coder in C# WinForms, but I do realize that the event/messaging approach is probably "better" from a design perspective.
Now, here is my question.
I have a main form, for naming's sake: ParentForm. ParentForm currently utilizes the latter form (har har!) of messaging to pass data to, let's say, FirstChildForm. Essentially, once ParentForm acquires data, it triggers the DataReadEventHandler, and data is passed from ParentForm to FirstChildForm.
No problem.
Now, I /also/ have a form spawned from ParentForm, called SecondChildForm. NB: this is not another instance of ChildForm... it's an entirely different form. Here's the catch -- when data updates on SecondChildForm, I want to have this data passed to FirstChildForm. It seems like such a simple idea, although I'm having some difficulty wrapping my head around how to implement it. All I can think of is setting up unique event handlers from ParentForm for each child, and having the event handler from SecondChildFrom then trigger ParentForm's event handler for FirstChildForm... this sounds terribly convoluted, as the data (of non-trivial size, I might add), must be first passed from SecondChildForm to ParentForm, and subsequently from ParentForm to FirstChildForm.
Is there a better way of doing this?
Also, I'd really prefer not to say this, but, to be perfectly honest, in this highly closed application, I'm OK with breaking paradigm for simplicity if the proper method is highly convoluted (would nevertheless allocate time in the future for proper refactoring -- yes, I actually /am/ able to do this!).
Cheers!
-Kadaj
So the data is first generated on the second child, so on that form we'll want an event that can be triggered that can provide that data:
public class SecondChildForm : Form
{
public event Action<MyData> SomethingHappened;
//Other code, including code that fires the event at some point
}
Then we have the first child which has some method that needs to be called passing in that data:
public class FirstChildForm : Form
{
public void WhenSomethingHappens(MyData data)
{
//Do stuff with data
}
}
Finally we have the main form that creates both of the forms and wires up the appropriate event handlers:
public class ParentForm : Form
{
public ParentForm()
{
FirstChildForm firstChild = new FirstChildForm();
SecondChildForm secondChild = new SecondChildForm();
secondChild.SomethingHappened += firstChild.WhenSomethingHappens;
//show forms and do other stuff
}
}
Voila.
Note that using this pattern each child doesn't know anything about their parent. They expose information needed by the parent through events, and they allow the parent to affect it through public methods, but they don't know or care which class(es) are using them. The parent does know about it's child type; it's appropriate for it to have an instance of the specific child type and to manipulate it's public members (but not its inner controls, which shouldn't be public) directly.

Is it bad design to pass a form as an argument to a class just to access some of its variables or methods?

I found that I can either pass 8 arguments to a class constructor or just pass the form variable instead.
However, since I am not using everything on the form it seems like it may be bad design?
Also, the objects I do access I would need to provide accessors for.
Does it violate the principles of OOP?
It depends - if you're using the form as that specific type of form, and "logically" it makes sense that you're working with the form, then by all means, pass a reference to the form.
It's just like any other class - If I was going to be accessing elements of an "employee", I'd write:
void DoSomething(Employee employee) { ...
Instead of:
void DoSomething(string firstName, string lastName, DateTime hireDate...) { ...
The first is very clean and obvious.
However, if the data you're using is unrelated to the form, it'd be better to encapsulate it into its own class usable by both the form and your class.
Also, the objects I do access I would need to provide accessors for.
If this is the case, I suspect that having a class encapsulating the data is likely a better design... The form could expose a property or method that returns an instance of that class, and pass it into your second class.
Passing a gui form to either other gui components or even worse, a model/library that does work does break encapsulation and creates a tight coupling.
The form should abstract the data and the model below. Other model or library classes should be passed model objects. A typical pattern is to "bind" the gui layer to the model.
Instead of passing 8 variables, do the 8 variables logically break into different objects? Ideally, you would pass an object or set of objects which may collectively contain 8 member variables. Then you can simply pass references to objects that are contained in the same model that your gui is abstracting and bound to.
Without seeing the class, I can almost guarantee the class taking 8 arguments is violating the Single Responsibility Principle. It could be a class generated to represent a table in a database (or something to that effect) in which case you should encapsulate it in its own class as pass it around instead of the form.
Something else to consider is that the form you're reviewing is also violating SRP since it's both displaying data and being used as backing for another form.
It typically is, because typically people are lazy or don't understand how to use events, so they write code like this:
class MainForm : Form
{
// stuff
}
class ChildForm : Form
{
private MainForm _mainFrm;
public ChildForm( MainForm frm )
{
_mainFrm = frm;
}
private void someButton_Click( ... )
{
_mainFrm.UpdateSomeText();
}
}
That code creates a terrible coupling between two different UI classes. Now, in a simple, internal, maybe throwaway project it is probably fine and you can write it once and move on. In general it means that you very well may need to change your ChildForm class in response to changes in your MainForm class, which is undesirable and can be avoided via weak coupling mechanisms like events.
On the other hand, there are valid cases to pass in a form to a method or constructor, though these situations are less common in practice. It all boils down to what you code is doing and if it is optimally designed. There is no rulebook for this, it takes years of practice and requires that you make many mistakes first so that you know what to avoid before writing any code at all.
I can imagine a class that controls the formatting of a form (font, size, color etc) that could take a variable of type Form as an argument. I could argue that THAT structure wouldn't violate OO principles. Anything short of that probably not.
Even if you need customer details in the new class and you're tempted to pass the CustomerForm, which contains all the details you need, DON'T DO IT. Create a customer class, provide an instance of that class from the form and pass that instance to the new class instead. If you ever change the UI, or if you ever need to automate part of the workflow that used to be manual you'll be glad you did.

Categories

Resources