The variable asynchExecutions does get changed, but it doesn't change the reference variable.Simple question, why isn't this ref parameter in this constructor changing the original value passed in?
public partial class ThreadForm : Form
{
int asynchExecutions1 = 1;
public ThreadForm(out int asynchExecutions)
{
asynchExecutions = this.asynchExecutions1;
InitializeComponent();
}
private void start_Button_Click(object sender, EventArgs e)
{
int.TryParse(asynchExecution_txtbx.Text, out asynchExecutions1);
this.Dispose();
}
}
How do you know that asynchExecutions is not changing? Can you show your testcase code that proves this?
It appears that on constructing ThreadForm asynchExecutions will be set to 1. However when you call start_Button_Click, you set asyncExecutions1 to the value in the text box.
This WILL NOT set asyncExecutions to the value in the text box, because these are value types. You are not setting a pointer in the constructor.
It seems to me that you are confused between the behavior of value types versus reference types.
If you need to share state between two components, consider using a static state container, or passing in a shared state container to the constructor of ThreadForm. For example:
public class StateContainer
{
public int AsyncExecutions { get; set;}
}
public class ThreadForm : Form
{
private StateContainer _state;
public ThreadForm (StateContainer state)
{
_state = state;
_state.AsyncExecutions = 1;
}
private void start_Button_Click(object sender, EventArgs e)
{
Int.TryParse(TextBox.Text, out _state.AsyncExecutions);
}
}
The out parameter is only good for the method call, you can't "save" it to update later.
So in your start_Button_Click, you can't change the original parameter passed to your form constructor.
You could do something like:
public class MyType {
public int AsynchExecutions { get; set; }
}
public partial class ThreadForm : Form
{
private MyType type;
public ThreadForm(MyType t)
{
this.type = t;
this.type.AsynchExecutions = 1;
InitializeComponent();
}
private void start_Button_Click(object sender, EventArgs e)
{
int a;
if (int.TryParse(asynchExecution_txtbx.Text, out a))
this.type.AsynchExecutions = a;
this.Dispose();
}
}
That will update the AsynchExecutions property of the instance of MyType.
Related
How to pass a reference to a form object (i.e. TextBox) to a class, so I could specify what text box this instance needs to be working with when creating this instance?
Specific example:
I have a class that processes some text strings. I have few instances of this class.
I also have few text boxes on my form. I have a method in a class that shows some text in a text box. What do I need to do to tell to a specific instance of my class what text box to use when creating this instance? Should be in constructor, something like:
public MyClass (string textString, /ref to a text box/)
This is my class:
public class LogClass
{
private readonly TextBox _textBox;
private string logText;
public string LogText
{
get
{
return logText;
}
set
{
logText = value;
}
}
public void AddToLog(string textString)
{
try
{
if (string.IsNullOrEmpty(textString))
{
throw new ArgumentException("message", nameof(textString));
}
logText = logText+ "\n" + textString;
_textBox.Text = logText;
}
catch (Exception)
{
throw;
}
}
public LogClass(string initialText, TextBox textBox)
{
logText = initialText;
_textBox = textBox;
_textBox.Text = logText;
}
}
And this is my form:
public partial class LogWindow : Form
{
LogClass myLog = new LogClass("this is initial string", logOutputBox);
public LogWindow()
{
InitializeComponent();
}
public string LogTextToPass {
get { return logOutputBox.Text; }
set { logOutputBox.Text = value; }
}
private void buttonWriteLog_Click(object sender, EventArgs e)
{
myLog.AddToLog(inputText.Text);
}
private void logOutputBox_TextChanged(object sender, EventArgs e)
{
}
}
Error CS0236 is on this line:
LogClass myLog = new LogClass("this is initial string", logOutputBox);
Error CS0236 A field initializer cannot reference the non-static field, method, or property 'LogWindow.logOutputBox'
logOutputBox is highlighted
You can pass it in any way you want. A constructor argument seems sensible, if the class isn't useable without a TextBox. In the constructor you can subscribe to the TextBox's events.
class TextBoxHandler
{
private readonly TextBox _textbox;
public TextBoxHandler(TextBox textbox)
{
_textbox = textbox;
_textbox.Click += HandleClick;
}
public void HandleClick(object sender, EventArgs e)
{
//Do something
}
}
It should be as easy as, passing a reference to it in the Constructor
Whenever a class or struct is created, its constructor is called. A
class or struct may have multiple constructors that take different
arguments. Constructors enable the programmer to set default values,
limit instantiation, and write code that is flexible and easy to read.
public class MyLovelyHorse
{
// private field of TextBox to play with internally
private readonly TextBox _textbox;
// constructor
public MyLovelyHorse(TextBox textbox)
{
_textbox = textbox;
}
// some awesome method that does stuff
public void SomeMethodThatDoesStuff()
{
_textbox.Text = "rah";
}
}
Usage
var myLovelyHorse = new MyLovelyHorse(MyTextBox);
// do stuff
myLovelyHorse.SomeMethodThatDoesStuff();
I have a problem with arrays which are made from class; actually I can make an array from a class and in the first form I set the data in may array, but when I switch to my second form and create an object from my class, I find my array empty so I can't use the information witch I had entered into my array.
public partial class Form1 : Form
{
string _name;
string _department;
int _id;
int _count;
int counter = 0;
Mediator m = new Mediator();
Employee ee = new Employee();
public Form1()
{
InitializeComponent();
}
private void Add_to_Array_Click(object sender, EventArgs e)
{
_name = txtName.Text;
_department = txtDepartment.Text;
_id = int.Parse(txtID.Text);
_count = counter;
m.Set_Value(_name, _department, _id,_count);
counter++;
Exchange();
//-----------------------------------
txtDepartment.Text = "";
txtID.Text = "";
txtName.Text = "";
}
private void Search_Click(object sender, EventArgs e)
{
listBox1.Items.Add(m.array[0].E_Name);
listBox1.Items.Add(m.array[0].E_Department);
listBox1.Items.Add(m.array[0].E_ID.ToString());
//---------------------------------------------------
listBox1.Items.Add(m.array[1].E_Name);
listBox1.Items.Add(m.array[1].E_Department);
listBox1.Items.Add(m.array[1].E_ID.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.Show();
}
public partial class Form2 : Form
{
Mediator M;
public Form2()
{
InitializeComponent();
}
private void Show2_Click(object sender, EventArgs e)
{
listBox1.Items.Add(M.array[0].E_Name);
listBox1.Items.Add(M.array[0].E_Department);
listBox1.Items.Add(M.array[0].E_ID.ToString());
//---------------------------------------------------
listBox1.Items.Add(M.array[1].E_Name);
listBox1.Items.Add(M.array[1].E_Department);
listBox1.Items.Add(M.array[1].E_ID.ToString());
}
class Employee
{
string Name;
string Department;
int ID;
//**************************
public string E_Name
{
get { return Name; }
set { Name = value; }
}
public string E_Department
{
get { return Department; }
set { Department = value; }
}
public int E_ID
{
get { return ID; }
set { ID = value; }
}
}
class Mediator
{
public Employee[] array = new Employee[5];
public void Set_Value(string name,string department,int id,int count)
{
array[count] = new Employee();
array[count].E_Name = name;
array[count].E_Department = department;
array[count].E_ID = id;
}
}
I would strongly suggest that you don't make Mediator m static.
IMHO this is a hack and you don't want to get in the habit of effectively making global variables everywhere, it will come back to bite you as you progress through your career.
The better solution is to pass Form2 only the data it needs to work on.
I'm guessing Form2 only needs to display the list of Employees, so you should create a Property in Form2 as follows:
public Employee[] Employees {get; set;}
[If you need more than just the list of employees then you should rather have a property for the Mediator - public Mediator Mediator {get; set;} ]
You then need to send this data to the second form just before you show it:
Form2 f2 = new Form2();
f2.Employees = m.array;
f2.Show();
Because the data is sent by reference any changes you make in form2 will reflect in the objects stored in form1.
Another option is to structure your program using the MVC pattern. It's a little bit more advanced, but it's a great pattern to keep your winForms apps neat and tidy. If you are building a wizard style app then I would strongly suggest this approach.
As an aside note you can also clean up the code quite a bit by using a list instead of an array to store the employee data - this will also make the code more adaptable to larger numbers of employees in the future.
You can also use C#s automatic properties to reduce the amount of code you need to write. So your code can look like this this instead:
public partial class Form1 : Form
{
private Mediator _mediator = new Mediator();
public Form1()
{
InitializeComponent();
}
private void Add_to_Array_Click(object sender, EventArgs e)
{
var newEmployee = new Employee
{
Name = txtName.Text,
Department = txtDepartment.Text,
ID = int.Parse(txtID.Text)
};
_mediator.Employees.Add(newEmployee);
Exchange();
//-----------------------------------
txtDepartment.Text = "";
txtID.Text = "";
txtName.Text = "";
}
private void Search_Click(object sender, EventArgs e)
{
foreach (var employee in _mediator.Employees)
{
listBox1.Items.Add(employee.Name);
listBox1.Items.Add(employee.Department);
listBox1.Items.Add(employee.ID.ToString());
}
}
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.Employees = _mediator.Employees;
f2.Show();
}
}
public partial class Form2 : Form
{
public List<Employee> Employees { get; set; }
public Form2()
{
InitializeComponent();
}
private void Show2_Click(object sender, EventArgs e)
{
foreach (var employee in Employees)
{
listBox1.Items.Add(employee.Name);
listBox1.Items.Add(employee.Department);
listBox1.Items.Add(employee.ID.ToString());
}
}
}
class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public int ID { get; set; }
}
class Mediator
{
public List<Employee> Employees { get; private set; }
public Mediator()
{
Employees = new List<Employee>();
}
}
You need an instance of Form1 in Form2 to use the array you've created.
You have two options;
First one, you can store an instance of Form1 in a common class so you can reach it from Form2.
Second option; you need to pass reference of Form1 to your Form2 instance.
A quick fix for your problem would be to add the static keyword in your array definition. this will make it stateless and therefor there won't be a new (instance of the) array being created each time you access it.
You should also consider placing this array in some kind of a static class that will be in charge of handling this kind of global variables.
As for your edits, you have at least 2 options here: the first one is making the Mediator class and the array inside static; or create a property in your second form like public string [] SomeArray {get; set;} and pass the array to it after declaring the ... = new Form2(); and before calling the .Open()
I have the a class in my application. It has been bound to winform textbox controls. But the textbox which is bound to BookingNo property, always shows zero (0). But i want the textbox keep empty. Is there any way to do it? Here is my code snippet.
public class Booking
{
private int pBookingNo;
private string pCustomerName;
private string pAddress;
public int BookingNo
{
get { return pBookingNo; }
set
{
if (!value.Equals(pBookingNo))
{
pBookingNo = value;
}
}
}
public string CustomerName
{
get { return pCustomerName; }
set
{
if (!value.Equals(pCustomerName))
{
pCustomerName = value;
}
}
}
public Booking() { }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
AddDataBindings();
}
private void AddDataBindings()
{
bsBooking.DataSource = typeof(Booking);
txtBookingNo.DataBindings.Add("Text", bsBooking, "BookingNo", true, DataSourceUpdateMode.OnPropertyChanged, null, "G", GlobalVariables.CurrentCultureInfo);
txtCustomerName.DataBindings.Add("Text", bsBooking, "CustomerName");
}
}
The default value of an Integer is 0, so you have to wrap it into some other object, which supports values other than 0, like
public int? BookingNo { get; set; }
You can use Nullable Type
public int? pBookingNo
{
get;
set;
}
Link : http://msdn.microsoft.com/fr-fr/library/1t3y8s4s(v=vs.80).aspx
You could use custom formatting for the binding by adding a handler to the Format event (http://msdn.microsoft.com/en-us/library/system.windows.forms.binding.format.aspx) and return an empty string when the value is zero. But you wouldn't be able to tell whether the value is actually zero or it just hasn't been set already, in which case using the int? approach suggested by #Grumbler85 is better.
what´s about:
textBox1.BindingContextChanged += new EventHandler(BindingContext_Changed);
private void BindingContext_Changed(object sender, EventArgs e)
{
TextBox txtBox = (TextBox)sender;
if (txtBox.Text == "0"){
txtBox.Text = "";
}
}
don´t know if it works, just an idea.
Ok so I am trying to pass a boolean from my Login form to my Home form, normally this would be fine for me and I would just use a property. However I thought I could use a similar method this time but I am implementing the singleton factory on the forms.
Here is the Login code relevant to this:
The AdminAccess property gets set fine and I have checked the value is correct.
private bool adminAccess;
public bool AdminAccess
{
get { return adminAccess; }
private set { adminAccess = value; }
}
private void btnLogin_Click(object sender, EventArgs e)
{
//Some Code Does Stuff
OpenHome();
}
private void OpenHome()
{
HomeForm CreateHomeForm = HomeForm.HomeUI;
CreateHomeForm.StartupHome = this;
//Trying to set the property.
CreateHomeForm.AdminPermissions= this.AdminAccess;
CreateHomeForm.Show();
this.Hide();
}
Here is the relevant code from the Home form:
public HomeForm()
{
InitializeComponent();
//just to check what is in the property quickly
textBox1.Text = AdminPermissions.ToString();
}
private bool adminPermissions;
public bool AdminPermissions
{
private get { return adminPermissions; }
set { adminPermissions = value; }
}
public Form StartupHome
{
set;
get;
}
private static HomeForm homeUI;
public static HomeForm HomeUI
{
get
{
if (homeUI == null || homeUI.IsDisposed)
{
homeUI = new HomeForm();
}
return homeUI;
}
}
The value gets reset when the HomeUI if loop runs as a new instance of the form is created. I can't seem to think how to modify this to get a working solution. As you can tell I am fairly amateur so I'm just looking for a quick and clean solution to this :)
Thank you very much for your time in advance!
You assign the value in the constructor, BEFORE the AdminPermissions property is actually set. Change your code like this
public class HomeForm
{
public HomeForm()
{
InitializeComponent();
}
private bool adminPermissions;
public bool AdminPermissions
{
get { return adminPermissions; }
set {
adminPermissions = value;
textBox1.Text = value.ToString();
}
}
...
}
Try setting the textBox1.Text value in one of the Form events. Try Loaded first, then Activated. You're resetting it to false every time in your constructor!
How to bind a TextBox to an integer? For example, binding unit to textBox1.
public partial class Form1 : Form
{
int unit;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", unit, "???");
}
It would need to be a public property of an instance; in this case, the "this" would suffice:
public int Unit {get;set;}
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", this, "Unit");
}
For two-way notification, you'll need either UnitChanged or INotifyPropertyChanged:
private int unit;
public event EventHandler UnitChanged; // or via the "Events" list
public int Unit {
get {return unit;}
set {
if(value!=unit) {
unit = value;
EventHandler handler = UnitChanged;
if(handler!=null) handler(this,EventArgs.Empty);
}
}
}
If you don't want it on the public API, you could wrap it in a hidden type somewhere:
class UnitWrapper {
public int Unit {get;set;}
}
private UnitWrapper unit = new UnitWrapper();
private void Form1_Load(object sender, EventArgs e)
{
textBox1.DataBindings.Add("Text", unit, "Unit");
}
For info, the "events list" stuff goes something like:
private static readonly object UnitChangedKey = new object();
public event EventHandler UnitChanged
{
add {Events.AddHandler(UnitChangedKey, value);}
remove {Events.AddHandler(UnitChangedKey, value);}
}
...
EventHandler handler = (EventHandler)Events[UnitChangedKey];
if (handler != null) handler(this, EventArgs.Empty);
You can use a binding source (see comment). The simplest change is:
public partial class Form1 : Form
{
public int Unit { get; set; }
BindingSource form1BindingSource;
private void Form1_Load (...)
{
form1BindingSource.DataSource = this;
textBox1.DataBindings.Add ("Text", form1BindingSource, "Unit");
}
}
However, you'll gain some conceptual clarity if you separate out the data a bit:
public partial class Form1 : Form
{
class MyData {
public int Unit { get; set; }
}
MyData form1Data;
BindingSource form1BindingSource;
private void Form1_Load (...)
{
form1BindingSource.DataSource = form1Data;
textBox1.DataBindings.Add ("Text", form1BindingSource, "Unit");
}
}
HTH. Note access modifiers omitted.
One of the things I like to do is to create "presentation" layer for the form. It is in this layer that I declare the properties that are bound to the controls on the form. In this case, the control is a text box.
In this example I have a form with a textbox to display an IP Address
We now create the binding source through the textbox properties. Select DataBindings->Text. Click the down arrow; select 'Add Project Data Source'.
This starts up that Data Source wizard. Select Object. Hit 'Next'.
Now select the class that has the property that will be bounded to the text box. In this example, I chose PNetworkOptions. Select Finish to end the wizard. The BindingSource will not be created.
The next step is to select the actual property from the bound class. From DataBindings->Text, select the downarrow and select the property name that will be bound to the textbox.
In the class that has your property, INotifyPropertyChanged must implemented for 2-way communication for IP Address field
public class PNetworkOptions : IBaseInterface, INotifyPropertyChanged
{
private string _IPAddress;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public string IPAddress
{
get { return _IPAddress; }
set
{
if (value != null && value != _IPAddress)
{
_IPAddress = value;
NotifyPropertyChanged("IPAddress");
}
}
}
}
In the form constructor, we have to specifically define the binding
Binding IPAddressbinding = mskTxtIPAddress.DataBindings.Add("Text", _NetOptions, "IPAddress",true,DataSourceUpdateMode.OnPropertyChanged);