Environment: Visual Studio 2008, C#, SQL Server, Windows development, Multiuser, New Developer
I have a method in form A that I use to display textbox information there.
Also I have a button that displays another form when clicked (form B).
Form B is created with frm.ShowDialog();
Form B has the same textboxes on it with the same names, they are identical (copied and pasted from form A)
I want to use the method in Form A to display the information in Form B, rather than copying and pasting the method from form A into form B
I made the method in form A public and called it from from B but nothing displays in form B's textboxes. Why? I don't get any errors.
Should I keep it simple and put a copy of form A's method in Form B instead of calling the method in form A from form B? That's the only way I can get it to work.
Form A is for displaying customer information, Form B is for editing customer information.
I'm passing and returning information between the forms and that's all working.
The forms are not identical, i.e., form B doesn't have textbox for the customer's notes, they are edited in a separate form.
Thank you for your input.
you should use an info class and retrieve data from it both in form A and form B
don't use duplicate code, just use another class for logic \ info.
You can read more about it here
Make a model class whose members represent the data to be shown in both forms. In visual studio 2008 I believe there is support to create a project data source based on an object. Create the data source from your model class. Then, in both forms, add a binding source. Set the data type for the source to be the project data source. Then, the designer will let you select bindings for each control from the binding source. Finally, in the form's constructor, accept a model class instance, and set the binding source's data source to be that instance.
Even if you have the same textboxes with same names when you call formA you are just changing the instance of formA which means only the textboxes in formA are changed.
You have to understand that every class has its own instance separate from the other.
That does not mean though that you cannot access formB textboxes in formA. I can see that you want to consolidate; if I'm not mistaken what you have in formA method is something like:
public void methodA(Customer customer)
{
textboxName = customer.Name;
textboxAddress = customer.Address;
}
If you dont want to duplicate this then the other approach that I can think of is:
1.) Create an interface for all the duplicate properties.
public interface ICustomerForm
{
string Name {get; set;}
string Address {get; set;}
}
2.) Have the 2 forms implement the interface.
public class FormA : Form, ICustomerForm
{
public string Name
{
get
{
return _textBoxName.Text;
}
set
{
_textBoxName.Text = value;
}
}
}
Do the same for formB
3.) Create a static helper class where you just pass the customer class. e.g:
public static class FormHelper
{
public static UpdateCustomerInfo(ICustomerForm form, Customer customer)
{
form.Name = customer.Name;
form.Address = customer.Address;
}
}
4.) In your two forms just call the helper class and pass itself as the first parameter and the customer class as the second parameter.
//Method in formA
public void formA()
{
FormHelper.UpdateCustomerInfo(this, customer);
}
//Method in formB
public void formB()
{
FormHelper.UpdateCustomerInfo(this, customer);
}
This way you just have one line of code in your forms and the setting of info is on the consolidated helper class.
Related
I'm creating an winforms application, that has the user make inputs in different panels. I already wrote a method to traverse through the panel and get the inputs from the different Controls. Now I need to find a way to serialize these inputs and deserialize them later on, so that all inputs are again in the right Controls (e.g. "Jack" is again in the TextBox "tbName").
I thought of multiple solutions, e.g. creating a list for each panel, which serializes to a .txt with a structure similiar to "tbName=Jack" and so on. But I don't really know how I would deserialize that, without traversing both my panel controls and the list again. Or can I possibly serialize the whole Panel object together with the Child-Controls?
//This is the method I use to gather the inputs from the panels.
public IEnumerable<Control> GetControls(Control parentControl)
{
foreach (Control child in parentControl.Controls)
{
yield return child;
foreach (Control controlChild in GetControls(child))
{
yield return controlChild;
}
}
}
It's not advised to serialize the whole form, as it has a lot of information you don't need (and t hat may affect performance). Instead, create a separate class, make it [Serializable()], make all the variables you need to store your information, and serialize that class.
EDIT:
Say you have the following form:
namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// here, you create the serializing and deserializing methods
public void SerializingInfo()
{
// done however you see fit
}
public StorageClass DeserializingInfo()
{
// also done however you see fit
}
}
}
Then, add another class to your project, which in my example, is named StorageClass.
This will look like:
namespace Test
{
[Serializable()]
public class StorageClass
{
// has all your properties
}
}
Then, whatever you need to storage, you can do so by setting/getting the properties in the Form1. When you serialize it, all the properties are serialized together, and you can retrieve it by accessing their getter method in DeserializeInfo().
For a LIMITED number of controls, you could simply create Settings in your Project --> Properties for each one:
Then, in the ApplicationSettings property for your control, click the three dots to the right of PropertyBinding...
...and select the setting for the Text entry:
You'll now have this:
Finally, in the FormClosing() event of the form, save the settings:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.Save();
}
Thanks for the answers, both are correct and working, but in the end I figured out my own solution to the problem:
I made a seperate class with all the attributes I needed, just like #krobelusmeetsyndra suggested, and made a generic List of the class object i just made. Then I traversed through the controls (with the method from my question), put the data in the List and serialized that data with the XmlSerializer.
Same with deserializing: I made a List of my own object type, then loaded the data from the XML in that list, and then assigned it to the right controls.
Hope that helps everyone with the same question!
I have a question about how to setup components in a winforms application so they can interact with each other. But I want to use the visual designer to set this up.
What I have is a component called myDataBase and a component called myDataTable.
Now the component myDataTable has a property of type myDataBase.
So in code I can do
myDataBase db = new myDataBase();
myDataTable dt = new myDataTable();
dt.DataBase = db;
The property DataBase in component myDataTable is public, so I can also use the visual designer to assign the DataBase property.
Now for my problem.
I have many many forms that have one or more components of myDataTable on it.
I only want one instance for myDataBase.
What I do now is I create a component myDataBase dbMain = new myDataBase() on the mainform.
On every form I have to set the property for all myDataTable components to this dbMain.
I have to do this in code because the visual designer cannot see the dbMain component on the mainform.
So the question is, can I create one instance of component myDataBase that is visible to the visual designer on all forms, so I can use the visual designer to set the property of myDataTable components ?
For those that now Delphi, I want something like the DataModule in Delphi.
You can't without some code.
The easiest thing you can do, as far as I am concerned, it to create a base form, deriving from Form, and in that form, you make a property pointing to a singleton instance of your database object. You can bind to that property, and still keep it as simple as possible.
You just need to make your form derive from this one:
public class DatasourceForm : Form
{
public myDataBase DataBase
{
get
{
return myDataBaseFactory.Current;
}
}
}
And the factory in charge of creating the singleton database instance:
public class myDataBaseFactory
{
private static readonly Lazy<myDataBase> lazy =
new Lazy<myDataBase>(() => new myDataBase());
public static myDataBase Current { get { return lazy.Value; } }
}
(Singleton implementation from here)
This question already has answers here:
Communicate between two windows forms in C#
(12 answers)
Closed 3 years ago.
I have a table named questions with a field name qcategory. In WFA I have a ToolStripMenu where I have a category named Simulation and a sub-category named B. So, I want to create a mysql SELECT where to select only rows which values are equal with sub-category value. (Columns qcategory from table has value B). This is the string:
static string dataA = "SELECT DISTINCT * FROM questions order by rand() limit 1";
The only problem is that I have 2 forms. One with menu and one where I want to make that select.
You should try to split your UI-code and your database code. This is called Layering (MVVM, MVC, MVP,...) but you don't have to!
There are several ways:
1) Make a class that both forms can reference and execute your database logic there. (that would be the cleanest way for now)
2) Create an Event in your Form with the menu and react on it in the other Form
3) Your menu Form holds a reference to the other Form and executes a Method on it passing the selected subitem.
In code
1
public static class SqlClass
{
public static void ExecuteQuery(string menuItem)
{
//execute query
}
}
Form 1
//menu changed...
SqlClass.ExecuteQuery(menuItem)
2
Form1:
public event EventHandler<string> MenuItemChanged;
//menu changed...
if(this.MenuItemChanged != null)
this.MenuItemChanged(this, menuitem)
Form2:
public Form2(Form1 otherForm)
{
InitializeComponent();
_otherForm.MenuItemChange += //... handle your sql code
}
3
private readonly Form2 _otherForm;
public Form1(Form2 otherForm)
{
InitializeComponent();
_otherForm = otherForm;
}
//menu changed...
otherForm.ExecuteQuery(menuitem);
For the examples, Form 2 is the form where you want to execute your query because there is the Method/Event-Handler defined that will interact with your database.
To understand the solution, you need a more high level perspective - you get an information in code behind of a Form (a Class) and you want to consume that information somewhere else.
In general you need a reference to the Form that holds the information you are interested in and it tells you the information when changed (Event) OR
the information source tells every interested destination (calls a Method). Then the Information source hold the references to the consumers.
Both concepts are the same just the direction of the communication (and reference) changes.
The alternative (Option 1) is that you move your information destination somewhere else (e.g. in a static class) and consume it there. The Mechanism of passing the information is pretty much the same (via a parameterized Method call) but it encapsulates the UI-colde (Form) from the Database code (SQL query execution)
I have this class:
public class MyProps
{
public MyProps()
{
}
protected string myVar;
public string MyProperty
{
get { return myVar; }
set { myVar = value; }
}
protected int myOtherVar;
public int MyOtherProperty
{
get { return myOtherVar; }
set { myOtherVar = value; }
}
}
That I want to add to my Form, so when I inherit from it I will be able to fill the properties in the MyPropsX property.
I have this code in my form:
protected MyProps propsX = new MyProps();
[TypeConverter(typeof(ExpandableObjectConverter))]
public MyProps MyPropsX
{
get
{
return propsX;
}
set
{
propsX = value;
}
}
Now, the properties MyProperty and MyOtherProperty are nicely shown in the Properties Window, and I can set their values directly there.
But when I close my form and I open it again, all my changes are lost, the properties being reset to show zero and an empty string.
What am I missing?
Should I inherit my MyProps class from certain special class or interfase?
Or some special attribute?
This is a little bit much for a comment and maybe your solution, so i'm answering to your comment with an answer instead with another comment:
With does not happen when I put properties directly on a form you mean, you are using the designer to set some property of the form. These will be written into the MyForm.designer.cs file. When you go into the code of your class you'll find within the constructor a method InitializeComponent(). Set the cursor on it an press F12. Here you can see what the designer has written into all the properties. You should respect the comment above the mentioned method and not start to modify the code with the code editor unless you really have understand how and when the designer will read and write code here (which is another chapter i can explain if needed). Otherwise it will happen that trying to opening your form with the designer after the code change will lead to an error message or code loss.
If you like to set some default value also, you should go back into the constructor and add the needed initialization code below the InitializeComponent() function and everything should work as expected.
Update
As you wrote in your comment you already know how the Designer interacts with the *.designer.cs file. So i really can't understand your concrete problem but maybe one of these articles can give you a more insight about how Microsoft wrote their components:
Make Your Components Really RAD with Visual Studio .NET Property Browser
Components in Visual Studio
This is very normal, since each time you are closing the form and opening it again you are having a new instance from the form MyPropsX, so the best way would be to save your properties in any kind of a database (sql, access, textfiles,...)
I'm trying to pass values between a few winforms, I've got a total of 6 winforms, that the user will cycle through. I'm passing values between the forms using TextBox and Label Controls.
When I open the Primary winform, then click a button to load the second winform, everything works fine (I can pass values to the First Form). My problem is that once I direct the user to another form and this.Hide(); the current (2nd Winform) then try to use the Third form to pass values to the first, I get the following error:
Object reference not set to an instance of an object.
I'm confused because the control that the should be passing the value is passing the value to the first Form isn't NULL
I'm using the same code to connect all the forms together.
public MainForm MainForm;
Then I'm trying to pass the values like so:
MainForm.textBox1.Text = txt_FileName.Text;
Note: All the TextBox and Label controls that are passing values between the forms are public
Anyone run into this? Or any Ideas?
.
You need to make sure that all your forms are instantiated (through new MyForm1()...). Just declaring a variable of type MainForm won't create a form instance - you'll have to do it. My guess is that one of your forms is not created yet when you try to access a control.
This is yet another reason to not to use public controls (see my comment too), since the lifetime of your controls are tied to the lifetime of your form. It's better to hide controls from public access and send data to the form through data objects - the form will set all those values to its own controls. This also makes validation a lot easier, since a control's value can only be set to values allowed by the form. If you set control values from the outside, you'll have a tough time validating them in all scenarios.
I assume you're trying to use modal forms that work similar to a wizard where users go from one form to the next, following a clear path. If so, you can do something like this:
// Data class to set data in Form2
internal class Form2Data
{
public string Name;
...
}
...
internal class Form2 : Form
{
public static DialogResult ShowDlg ( Form2Data oData )
{
Form2 oFrm = new Form2 ();
oFrm.SetData ( oData );
DialogResult nResult = oFrm.ShowDialog ();
if ( nResult == DialogResult.Ok )
oFrm.GetData ( oData );
return ( nResult );
}
private void SetData ( Form2Data oData )
{
// Set control values here
}
private void GetData ( Form2Data oData )
{
// Read control values here
}
}
...
// You call this as such:
Form2Data oData = new Form2Data ();
oData.Name = "...";
DialogResult nResult = Form2.ShowDlg ( oData );
// after the call, oData should have updated values from Form2
if ( nResult == DialogResult.Ok )
{
// show your next form in a similar pattern - set up data
// call form's static method to pass it and then wait for
// the form to finish and return with updated data.
}
You'd have to use a similar pattern in your other forms, too. This does require more work since you need to set up a different data object for all the forms but this way you can easily do validation before and after the form is shown (in SetData and GetData). It also encapsulates your program better, since controls are not accessible from the outside.
.Net 2.0 and later has a feature for windows forms called the "default instance", where it gives you an instance with the same name as the type. The purpose of this feature is for compatibility with code migrated from old vb6 apps. If you're not migrating from an old vb app, it's usually better to avoid the default instances. They will get you in trouble, such as you have now. Instead, create a variable to hold form instances you construct yourself.
You should pass the value by using the instance value of the form.
for example:
SecondForm secForm2 = new SecondForm();
secForm2.textBox1.Text = txt_FileName.Text
so if you pass the value from SecondForm to ThirdForm:
ThirdForm thiForm = new ThirdForm();
thiForm.textBox1.Text = textBox1.Text