I have a main form which opens a second form as a dialog.
On the dialog form close, I would like to edit an external struct so all the form settings are captured on close.
I pass a struct into the dialog form constructor, but it seems it is copied when I save it to a private property inside the class.
How can I maintain a link to the external struct?
Below is the relevant code for the dialog form.
public partial class MASettingsForm : Form {
private MovingAverageSettings _settings;
public MASettingsForm(ref MovingAverageSettings settings) {
InitializeComponent();
_settings = settings;
SetUI(_settings);
}
public void SetUI(MovingAverageSettings settings) {
maTypeComboBox.SelectedIndex = (int)settings.type;
maCalcMethodComboBox.SelectedIndex = (int)settings.calcMethod;
maCalcValueUpDown.Value = settings.calcPeriod;
maOffsetUpDown.Value = settings.offset;
applyToHeikenAshicheckBox.Checked = settings.useHeikenAshi;
OnTypeChange();
}
// User clicks ok to apply the dialog form settings
private void okBtn_Click(object sender, EventArgs e) {
DialogResult = DialogResult.OK;
_settings.type = (enum_Ma_Type)maTypeComboBox.SelectedIndex;
_settings.calcMethod = (enum_Calculation_Method)maCalcMethodComboBox.SelectedIndex;
_settings.calcPeriod = (int) maCalcValueUpDown.Value;
_settings.offset = (int)maOffsetUpDown.Value;
SetExtendedValues(_settings);
Close();
}
The best you can do is is to introduce a wrapper around the struct:
public class Ref<T>
{
public T Value { get; set; }
public Ref(T value) => Value = value;
}
And instead of trying to pass a ref to the class constructor - pass a Ref<MovingAverageSettings> object.
That way your struct can be immutable, and you would just replace the entire struct when it needs to change. You can also add an event that is raised when the value is set, to let any form know if it needs to update itself to show the updated value.
But I would change your struct to a class, there is a lot of limitations and pitfalls with structs in c#, you can get some information in the article from MS: https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct?redirectedfrom=MSDN
Related
I have a class which is a UserControl:
public partial class MyObjectView : System.Windows.Forms.UserControl
This interface has various components for user input. To show the issue I'm having, only one is needed to show, so, in MyObjectView.Designer.cs:
internal System.Windows.Forms.TextBox txtMyNumber;
In the MyObjectView constructor, I have:
this.Validating += new System.ComponentModel.CancelEventHandler(this_Validating);
and then:
private void this_Validating(object sender, System.ComponentModel.CancelEventArgs cancelEventArgs)
{
// MyObject here already contains the data entered in the control by the user
}
So the user enters data and clicks the close X button in the upper right hand corner. I want to see the difference between what MyObject originally contained (when the UserControl was first displayed) and what the user entered on the form. However, in this_Validating, MyObject already has been updated to what the user entered, so I no longer have the 'before'
How do I do this?
Why don't you clone the original object in your constructor so you have a copy of it's initial state:
class MyClass
{
public string Name { get; set; }
public MyClass ShallowCopy()
{
return (MyClass)this.MemberwiseClone();
}
}
//Copy of original state of object
private MyClass _orig;
public Form1()
{
InitializeComponent();
//In your case this comes in via the constructor?
MyClass o = new MyClass();
o.Name = "hi";
_orig = o.ShallowCopy();
...
Currently I have multiple classes, one of which is called the 'Variable class' where I
{get;set;}
my values obtained from other classes. Accessing these values in voids is simply:
Private void (Variables vb)
{
}
However in the 'Load' part of Winforms,
private void Form1_Load(object sender, EventArgs e)
{
}
From the Variables Class:
public class Variables
{
public int Node { get; set; }
}
The object sender, EventArgs e part is occupying the space where I place the arguments. Is there any way that I could obtain Node from the class Variableson the winform?
Your method Form1_Load is an event handler (because it usually gets called as a result of some event occurring). The "Load" event is defined by WinForms, so you cannot change the fact that the arguments are object sender and EventArgs e.
WinForms creates one instance of your Form1 class before it displays your form. Whenever an event happens on your form, the event handler on that same object is called.
So, you can store values in fields and properties of your Form1 class:
public class Form1 : Form
{
Variables _myVariables;
public Form1()
{
InitializeComponent();
_myVariables = new Variables() { Node = 10 }
}
private void Form1_Load(object sender, EventArgs e)
{
MessageBox.Show("The current value of _myVariables.Node is: " + _myVariables.Node);
}
}
If your Variables object is created outside your form, then you can pass it into your Form1 constructor:
public class Form1 : Form
{
Variables _myVariables;
public Form1(Variables variables)
{
InitializeComponent();
_myVariables = variables;
}
// ...
}
// Then, somewhere else:
var variables = new Variables() { Node = 10 };
var myForm = new Form1(variables);
myForm.Show();
// or: Application.Run(myForm);
I'm not 100% sure if this is what you are looking for, but I think I can help.
namespace Example
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//class which can be used anywhere in the namespace
public class exampleClass
{
public const string SETSTATE = "0";
public const string GETSTATE = "1";
public const string SETVALUE = "2";
public const string GETVALUE = "3";
public const string SENDFILE = "4";
public const string STATE_RP = "129";
public const string VALUE_RP = "131";
public const string STATUS_RP = "128";
}
}
}
you can use exampleClass, and any of its enclosed members, anywhere within Form1 here. You do not need to pass it anywhere within the form to use it. You could add a function later that uses it directly like:
void exampleF()
{
//note that when you are accessing properties of UI elements from
//a non-UI thread, you must use a background worker or delegate
//to ensure you are doing so in a threadsafe way. thats a different problem though.
this.txtYourTextBox.txt = exampleClass.GETSTATE;
}
Maybe your attempt is it actually to use MVP-Pattern in WinForms. Very good idea.
Then you can use DataBinding to Bind your Forms-Controls to your "Variables classes" properties. Your variable class takes the presenter role, your form is the view, and your model(s) are the sources of your variable classes data.
Unfortunately, the pattern uses some advanced mechanisms you have to deal with.
For more information, you might take a first look here:
Databinding in MVP winforms
For example after creating a new Windows Form project I have my class called Form1.cs and from that form I can simply start typing the name of a form control and it will auto populate the form control variable names and I am able to use them in the class. However I have other classes that need to be able to access these form control variables as well, but they are not accessible.
Make them public if they are going to be used in another assembly, or internal if they are going to be used in the same project. Making them static means you don't have to pass your Form1 into the other classes.
Example... Say your Form1 has a string that contains the text you display in the title bar. Making it internal static, like this:
internal static readonly string MsgBox_Title = " Best Application Evar!";
lets you access it from other classes like this:
Form1.MsgBox_Title
It doesn't have to be readonly; that's just an example I pulled from an old app...
If you don't want static variables, you'll have to pass in an instance of Form1.
public class SomeClass
{
private Form1 m_Form1;
public SomeClass(Form1 form1)
{
m_Form1 = form1;
}
private void someMethod()
{
string localValue = m_Form1.SomeMemberStringVariable;
}
}
It's a very contrived example, but hopefully you get the idea.
If you want to call the Refresh method from a class instantiated from Form1, you could use an event in the child class to notify Form1.
Example:
This Form1 has a button that I use to show a secondary form.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnShowPopup_Click(object sender, EventArgs e)
{
PopupForm f = new PopupForm();
f.CallRefreshHandler += PopupForm_CallRefreshHandler;
f.ShowDialog();
}
private void PopupForm_CallRefreshHandler(object sender, EventArgs e)
{
Refresh();
}
}
The secondary form, "PopupForm", has a button that I use to raise an event that the Form1 is subscribed to, and lets Form1 know to call Refresh.
public partial class PopupForm : Form
{
public event EventHandler CallRefreshHandler;
public PopupForm()
{
InitializeComponent();
}
private void btnRaiseEvent_Click(object sender, EventArgs e)
{
EventHandler handler = CallRefreshHandler;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Hope this helps.
Create an object of that class & start using those variables like this
Form1 fm = new Form1();
string abc = fm.VAR;
Define a public property in your form.
public string MyProp { get; set; }
Form1 frm = new Form1();
frm.MyProp = "Value";
Or define the property as static to avoid having to instantiate Form1:
public static string MyProp { get; set; }
Form1.MyProp = "Value";
I ran into this issue recently. I was keeping some methods in a separate class. Maybe not a good design decision in my case, I'm not sure yet. And these methods sometimes needed to communicate with controls in the main Form1. For example, to write to textBox1.
Turns out easy enough. Just write your method signature to include a TextBox instance. For example you pass textBox1 in and inside the method you refer to it as tb. Then when you call that method (even though it is in another class) you set the tb.Text property to whatever you like and it will show on textBox1.
This makes sense when you consider that control is just a special kind of object, graphically represented in the Form. When you pass it as an argument to a method in another class or the same class, you are actually passing the reference. So writing text to it in the method call will write text to the original control.
This should be quite simple really - not sure what the problem is.
I have a C# Class (Public.cs) and a windows form (Form1.cs). Through a function in Public.cs, I want to get the value of a control on Form1 (without having to use object parameters).
// This code appears in Public.cs
public string MyFunction(int num_val)
{
if (chk_num.checked == true)
{
// Something here...
}
}
The issue is that my class cannot find the control on my form. Is there some way that I must reference it in C#?
Thank you.
I would strongly suggest exposing the Checked property via a specific property on Form1 (perhaps with a more meaningful name). This will help to hide the implementation details (i.e. control structure) of the Form1 from it's caller and instead expose only the logic that is required for other consumers to do their job
For example:
public bool IsNumberRequested
{
get { return chk_num.Checked; }
}
Or alternatively, if you still really want to access the control directly, from the designer you can select the control and change it's Modifier property to public (or something else) enabling you to access the control object using the code you originally wrote above.
EDIT: (Response based on comment)
Public.cs will still need a reference to Form1 and then will call the IsNumberRequested property of that object.
// Public.cs
public class Public
{
private Form1 _ui;
public Public(Form1 ui) { _ui = ui };
public string MyFunction(int num_val)
{
if (_ui.IsNumberRequested)
{
// Stuff
}
// Else, default Stuff
}
}
Alternatively, you could pass the form as a parameter to the MyFunction too rather than using it as an instance variable.
I would have the set up the other way around
public class Public
{
public bool CheckNumber {get;set;}
public string MyFunction(int val)
{
if(CheckNumber)
{
//do that thing
}
return ...
}
}
public partial class Form1 : Form
{
Public myinstance = new Public();
public Form1()
{
InitializeComponent();
}
private void CheckBoxChanged(object sender, EventArgs e)
{
myinstance.CheckNumber = chk_num.checked;
}
}
You'll need to assign CheckBoxChanged to the OnChanged event handler for your check box (which I'm assuming is chk_num.
This way your class Public doesn't rely on a form, which it shouldn't.
As Reddog says, use better names, although I half suspect you've just given example names in your question.
In a C# Winforms (3.5) application I have added a class that contains many properties (get/set) used to stored values from a 12 form long process.
After form 12 I would like wipe out all the property values in the storing class so that the next user won't accidentally record values when starting the process at form 1.
Is it possible to erase/destroy/dispose of all property values in a class?
My class looks like this:
private static int battery;
public int Battery
{
get { return storeInspectionValues.battery; }
set { storeInspectionValues.battery = value; }
}
Can you create a new instance of the class instead? You will end up with exactly the same object.
Edit in response to comments:
Let's say this is your class:
public class Foo
{
private int _battery;
private string _someOtherValue;
public int Battery
{
get { return _battery; }
set { _battery = value; }
}
public string SomeOtherValue
{
get { return _someOtherValue; }
set { _someOtherValue = value; }
}
}
You say you want to "erase/destroy/dispose of all property values in a class". I assume this means you would like to reset all properties to their default values. That implies something like this:
foo.Battery = 0;
foo.SomeOtherValue = null;
The same can be accomplished by doing this:
foo = new Foo();
Now foo is an instance whose properties all have their default values. Does that solve your problem?
If creating a new instance is not a good choice for you, you can implement a method which assign a default value to value parameters and new instances to reference parameters.
If you absolutely MUST use static, which means that creating a new object will not erase the properties (because they are static) you may consider:
Using a singleton pattern in order to "simulate" the static behaviour.
You may consider creating application domains. When a new application domain is created, static classes (with their properties and fields and everything else) are recreated and static constructors are invoked again.
Hope this helps
There is no built-in mechanism to "reset" the values of all properties of a class. While there are ways that you could accomplish this, both straight-forward (create a method that explicitly resets all property values) and not straight-forward (using Reflection to find all properties and set their values), I would not recommend any of those approaches for what it sounds like you're trying to accomplish.
If you have a user interface that is capturing data, submitting that data somewhere, and then discarding it, then most likely you will want to just create a new instance of your object instead of attempting to clear it out.
I notice in your example that your property has a backing variable that is static. Unless you have a particular reason for doing that, you should probably make your variables non-static, otherwise creating a new instance of your object won't really have the effect you desire (read up on the difference between static and non-static variables if that doesn't make sense to you).
Added the following code example in response to comments:
You could pass your data object between forms as a constructor argument or as a public property on each form. Your code, for instance, could look something like the following, where each form has a "Next" button that, when clicked, closes the current form and opens the next form using the same data object. The MyDataClass object is passed to each form as a constructor argument. The last form does not have a "Next" button but instead has a "Save" button that will of course save the data:
public partial class Form1
{
private MyDataClass _Data;
public Form1(MyDataClass data)
{
InitializeComponent();
this._Data = data;
// TODO: initialize fields with values from this._Data
}
protected void btnNext_Click(object sender, EventArgs e)
{
// TODO: store field values to this._Data
// close this form
this.Close();
// show the next form and pass the data object along to the next form
Form2 form = new Form2(this._Data);
form.Show();
}
}
public partial class Form2
{
private MyDataClass _Data;
public Form2(MyDataClass data)
{
InitializeComponent();
this._Data = data;
// TODO: initialize fields with values from this._Data
}
protected void btnNext_Click(object sender, EventArgs e)
{
// TODO: store field values to this._Data
// close this form
this.Close();
// show the next form and pass the data object along to the next form
Form2 form = new Form2(this._Data);
form.Show();
}
}
// ...
public partial class Form12
{
private MyDataClass _Data;
public Form12(MyDataClass data)
{
InitializeComponent();
this._Data = data;
// TODO: initialize fields with values from this._Data
}
protected void btnSave_Click(object sender, EventArgs e)
{
// TODO: store field values to this._Data
// TODO: save the data stored in this._Data, since this is the last form
// close this form
this.Close();
}
}
You can use a using statement in a loop to limit the scope of your object.
while (i_still_have_more_users_to_process)
{
using (MyObject myObject = new MyObject())
{
// Do stuff with forms and myObject
}
}