I am really confused and hope somebody will be able to help me with the issue I'm having. I want to use the GET command to obtain a value from a new Form, but my code is overwriting the parameters I pass in the constructor and I am not sure why. Not very familiar with C#.
Here the script I use when I click on a specific button. It is a new Form where I pass into parameters a list of interfaces (the parameters will be modified and I do not want to):
private void btn_t1_Click(object sender, EventArgs e) {
InterfaceT1 Formulaire_T1 = new InterfaceT1(**this.Liste_T1**);
if (Formulaire_T1.ShowDialog() == DialogResult.OK) {
//I WOULD WANT TO USE THE GET COMMAND HERE ONLY IF I CLICK 'OK' ON THE FORM
}
Formulaire_T1.Dispose();
}
Here is the constructor of my Form Formulaire_T1 for reference:
public InterfaceT1(List<T1> Liste_T1) {
InitializeComponent();
this.Liste_T1s = new List<T1>(Liste_T1); //suggested, does not change anything
UpdateView(0);
}
The methods I use in Interface_T1 modify the Liste_T1s but why it is also changing Liste_T1 in the main function? I do not return any value. It seems those value are now linked? I know it must be a simple thing but can't figure it out.
The reason your method in InterfaceT1 modifies the list that you pass in is that the constructor stores the reference to the list, rather than copying it. Change the constructor as follows to fix this:
public InterfaceT1(List<T1> Liste_T1) {
InitializeComponent();
this.Liste_T1s = new List<T1>(Liste_T1); // Make a copy
UpdateView(0);
}
Note that this change would make a copy of the list itself, but not its elements: they would remain the same. Adding / deleting elements from the copied list will be OK, but modifying individual elements will be visible in the original. To overcome this problem, change the code as follows:
public InterfaceT1(List<T1> Liste_T1) {
InitializeComponent();
this.Liste_T1s = Liste_T1.Select(t => new T1(t)).ToList();
UpdateView(0);
}
In the code above I am assuming that a new instance of T1 can be created by calling a "copy" constructor that takes another instance of T1, and copies its fields.
List is a reference type. That means that if you pass it as a parameter into another method, you're not passing a copy of the list, you're passing a reference that points back to the original list.
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'm really confused by now.
I've got a WinForm which holds a large Array
int[,] map = new int[1000, 1000];
I've also got a class containing a method "Draw".
The draw method now needs to get the value of a position in the array in the form. I tried doing the following thing:
In my Form class, I added
public int mapContentAtXY(Point mapPosition)
{
return map[mapPosition.X, mapPosition.Y];
}
Now if I try to perform
myInt = Ingame.mapContentAtXY(myPoint);
//Note: Ingame is the name of my Form
It says
Error 2 'Neu.Ingame' does not contain a definition for 'mapAtPositionXY'
This is really confusing, I just added that definition, it's also set as public. So why the hell doesn't it work?
You will need to pass the instance of the Form to the draw-class by using this when you create or call that instance of the draw-class. Then you can access that method through that form instance.
OR
Store the map not in the form, but in that draw-class. Move the mapContentAtXY method to the draw-class. Use the one instance of the draw-class in your form to update that map.
Your method mapContentAtXY is an instance method.
Change Ingame to this, if you're calling it from within your form instance.
myInt = this.mapContentAtXY(myPoint);
Otherwise use the form instance
myInt = frmInGameInstance.mapContentAtXY(myPoint);
Basically, I'm passing a model object to a method and in that method, assigning the proper object from the database. Since this is a reference, I assumed it would persist throughout the rest of the method in which it it was called/passed. I know this has something to do with the proxy of entity framework but can't figure out how to fix it. Here is a fragment of the code:
[HttpPost]
public ActionResult Create(NewFormViewModel nfvm)
{
db = new dbconnection(connStr);
Track track= new Track();
Track parentTrack = new Track();
this.Create_SetTrack(nfvm, track, parentTrack);
...
and then in Create_SetTrack:
private void Create_SetTrack(NewFormViewModel nfvm, Track track, Track parentTrack)
{
track = db.Tracks.FirstOrDefault();
parentTrack = db.Tracks.Where(i=>i.ParentID==track.ID).FirstOrDefault();
}
The track loads in Create_SetTrack but then, after the code after the '...' continues in Create, track is back to it's null values.
Note that a method parameter is a new variable. So you assign track (the variable) to track (the parameter). In the method body, the parameter is overwritten by a new reference, but the original track (the variable) has nothing to do with that.
You are probably confused by the fact that changes you make to the same reference object will be visible outside the method body. If you'd only set a property of a new Track() object, you'd see this value after the Create_SetTrack call.
So I would make a method that returns the tracks, so you can assign then to the original variables. If this is an internal method, you could return a Tuple (using Tuples in API methods is discouraged, because the ItemX properties are too nondescript).
As an alternative, you could assign the tracks to another object (the view model?) that is not overwritten in the method body.
I'd prefer the first alternative though. I don't like methods that create side-effects.
That won't work unless you use a ref parameter. Otherwise, the track parameter reference will only exist in the scope of the method
private void Create_SetTrack(NewFormViewModel nfvm, ref Track track)
{
track = db.Tracks.FirstOrDefault();
}
I would advise against this, though, since it makes the code more complicated that it needs to be. A better solution is to simply return a value from the method and assign that to your variable:
private Track Create_SetTrack(NewFormViewModel nfvm)
{
return db.Tracks.FirstOrDefault();
}
[HttpPost]
public ActionResult Create(NewFormViewModel nfvm)
{
db = new dbconnection(connStr);
Track track= Create_SetTrack(nfvm);
....
I have created a form that is used for both adding and editing a custom object. Which mode the form takes is provided by an enum value passed from the calling code. I also pass in an object of the custom type. All of my controls at data bound to the specific properties of the custom object. When the form is in Add mode, this works great as when the controls are updated with data, the underlying object is as well. However, in Edit mode, I keep two variables of the custom object supplied by the calling code, the original, and a temporary one made through deep copying. The controls are then bound to the temporary copy, this makes it easy to discard the changes if the user clicks the Cancel button. What I want to know is how to persist those changes back to the original object if the user clicks the OK button, since there is now a disconnect because of the deep copying. I am trying to avoid implementing a internal property on the Add/Edit form if I can. Below is an example of my code:
public AddEditCustomerDialog(Customer customer, DialogMode mode)
{
InitializeComponent();
InitializeCustomer(customer, mode);
}
private void InitializeCustomer(Customer customer, DialogMode mode)
{
this.customer = customer;
if (mode == DialogMode.Edit)
{
this.Text = "Edit Customer";
this.tempCustomer = ObjectCopyHelper.DeepCopy(this.customer);
this.customerListBindingSource.DataSource = this.tempCustomer;
this.phoneListBindingSource.DataSource = this.tempCustomer.PhoneList;
}
else
{
this.customerListBindingSource.DataSource = this.customer;
this.phoneListBindingSource.DataSource = this.customer.PhoneList;
}
}
You could always add a function to your object (Customer), either as "Copy(Customer cust)" or "Update(Customer cust)" and consume the changes that way.
The other way would be to have a wrapper class around Customer EditableCustomer, which takes a customer object in its constructor EditableCustomer(Customer root) and uses that to hold the changes. In the final event just call a function like "UpdateRoot()" and populate the changes back to the original customer, failing to call this will be the same as a discard.
You wont be able to use deep copies directly but this will allow you to control this type of situation, and actually allow you to control both edits and undo's dynamically.
I'm not good at C# at all, I just don't get the logics. But VB I seem to understand alot better since it seems much more logical. Atleast to me.
So I'm run into something which isn't a problem at all in VB, accessing controls on a different form then the one you're currently in.
In VB, if I want to set the state of a button say, in Form2. I just type the following:
Form2.Button1.Text = "Text"
In C# I cannot seem to do this. Why? There must be a pretty good reason for this right?
Edit: So if I have this code, what would it look like to be able to access controls on the other form?
if (!AsioOut.isSupported())
{
SoundProperties.radioButtonAsio.Enabled = false;
SoundProperties.buttonControlPanel.Enabled = false;
SoundProperties.comboBoxAsioDriver.Enabled = false;
}
else
{
// Just fill the comboBox AsioDriver with available driver names
String[] asioDriverNames = AsioOut.GetDriverNames();
foreach (string driverName in asioDriverNames)
{
SoundProperties.comboBoxAsioDriver.Items.Add(driverName);
}
SoundProperties.comboBoxAsioDriver.SelectedIndex = 0;
}
Just tried to add this "SoundProperties SoundProperties = new SoundProperties();
And I do get access to the controls. But do I need to add this bit of code in both parts of this IF-statement? Seems like I do, but still, adding that line to the last part of this code doesn't do anything ang gives me the error message:
"A local variable named 'SoundProperties' cannot be declared in this scope because it would give a different meaning to 'SoundProperties', which is already used in a 'child' scope to denote something else"
Removing the line gives me the following error:
"An object reference is required for the non-static field, method, or property 'NAudio.SoundProperties.comboBoxAsioDriver'"
Here's the code after adding these lines in two places:
if (!AsioOut.isSupported())
{
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.radioButtonAsio.Enabled = false;
SoundProperties.buttonControlPanel.Enabled = false;
SoundProperties.comboBoxAsioDriver.Enabled = false;
}
else
{
// Just fill the comboBox AsioDriver with available driver names
String[] asioDriverNames = AsioOut.GetDriverNames();
foreach (string driverName in asioDriverNames)
{
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.comboBoxAsioDriver.Items.Add(driverName);
}
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.comboBoxAsioDriver.SelectedIndex = 0;
}
Please don't hate me for saying this - but I think this is an issue that I've seen a lot of VB coders run into.
VB allows you to not deal with classes if you don't want to. When in C# you are adding a form to your project - visual studio is just making a class file for you that inherits from "Form".
In C# you have to actually instantiate this into an object and then work with that object. VB allows you to just access the class as if it was already instantiated - but it is actually just making a new "Form2" for you.
In vb if you want to have more than 1 actual "Form2" you would say something like this...
Dim window1 as new Form2()
Dim window2 as new Form2()
window1.Show()
window2.Show()
Now you will have two copies of "Form2" on your screen when you run this.
The difference between VB and C# is you also need to actually make(instantiate) your first copy of Form2 - C# will not do it for you.
Now to answer your question:
Once you have an actual object that has been instantiated - you need to actually make "Button1" public instead of private.
To do this - on Form2 - select Button1 and look at the properties...
Find the "Modifiers" property and set this to public.
You will now be able to see "Button1" on both window1 and window2.
Hope that helped.
You can access another form in c# too.
But you need a reference to the Form instance you want to interact with.
So you have to hold the variable of the 2nd Form instance and access it via this.
E.g.:
From the code of the first form call:
Form2 my2ndForm = new Form2();
my2ndForm.Button1.Text = "Text";
Be sure to set the access modifier of the Button1 to public or internal.