Let's say I have 2 forms. In Form1 I have a timer, which counts (count++) and in Form2 I have a text box (textBox1) which shows how much the timer has counted. Now I want to know how would I show the integer 'count' in 'textBox1' without any user interference (clicking buttons) or in other words, how would I make the data in the text box auto refresh (without using Form2 form = new Form2(); form.Show();). I want 2 separate windows, in one a running timer and in the other a textbox displaying how much the timer has counted and constantly updating (with the help of the timer, I presume).
One way is by creating a public event and registering for that event from the other form.
Simplest way: Expose a public property on Form2. In the setter for the property, set the value of the textbox. I believe the timer event fires on the UI thread, so you shouldn't have any thread safety issues. If you do, you'll have to go back to the public event approach that Brian mentioned above.
Keep in mind that you may also have to do a DoEvents() to get the UI to actually update to the user. Also keep in mind that this kind of update inherently slows down the processing of your application.
public int TimerValue
{
set
{
this.txtTextBox.Text = string.Format("{0:0000}", value);
}
}
You could have a singleton/static class that holds all data that are relevant to all forms, exposed as properties. All forms could write and read these properties. Additionally it fires Events that the forms can subscribe to when the properties change (in case you need live updates).
Just make your timer a public event so it can be referenced on another form.
Implement INotifyPropertyChanged on the timer form, and define a public property representing the count:
private int count;
public int Count
{
get { return count; }
set
{
if (count != value)
{
count = value;
OnPropertyChanged("Count");
}
}
}
Then, on the textbox form, databind your textbox Text value to the Count property of the timer form.
textbox.DataBindings.Add("Text", timerForm, "Count");
Related
What I Have
I have a form with 3 UserControls that contain source fields to be used to calculate another field in a target UserControl.
UserControl1 (uc1)
StartDate DatePicker (dpStart)
EndDate DatePicker (dpEnd)
UserControl2 (uc2)
Rate TextBox (txtRate)
UserControl3 (uc3)
Amount TextBox (txtAmount)
UserControl 4 (uc4)
CalculatedValue TextBox (txtCalc)
Formula to calculate txtCalc is below
txtCalc.Text = (dpEnd.Value - dpStart.Value).TotalDays * txtRate.Text * txtAmount.Text
What I Want To Achieve
Whenever I change a value in any of the first 3 UserControls, the Text in txtCalc should update with calculated value.
What I Have Tried
I have tried DataBindings but it appears to be only for a single field.
e.g.
uc4.txtAmount.DataBindings.Add("Text", uc2.txtRate, "Text", true, DataSourceUpdateMode.Never);
Would really appreciate your inputs.
There are a few things you may want to consider to bind the date pickers and the text boxes as you want. In addition there are a couple of ways you can do this.
One possible problem is that each of the UI “controls” (date pickers and text boxes) are spread out across four (4) different UserControls. This is not a big problem and about the only thing you need to make sure is that each of the “controls” (DTP and TextBoxes) in each of the UserControls is “exposed” to the Main Form.
In other words, if you simply place a TextBox in a UserControl and then place that UserControl onto the Main Form… then the Main Form will NOT be able to access that TextBox … UNLESS it is “exposed.” This can be remedied by simply setting the TextBoxes Modifier property to Public. Then the Main Form WILL have access to the text box in the UserControl.
Therefore, step one is make sure each UI “control” in each of the UserControls that we want to access in the Main Form has its Modifier property set to Public. The reason for this, is that you want to be able to access the data in those controls from the Main Form. In addition to the fact that we want to subscribe to those controls “Events” FROM the Main Form.
What we will do inside the Main Form after the UserControl is placed onto the form is subscribe to the UI Control’s (DTP or TextBox) TextChanged event. Then we can “capture” when one of the controls text changes… and we can capture this change INSIDE the Main Form.
Another possible issue is how the code is calculating the “total days” amount from the two DateTimePickers. The current code looks something like…
(dpEnd.Value - dpStart.Value).TotalDays
This will work, however, if you debug the code and look closely at the result of the calculation you may note that if date 1 is 1/25/2021 and date 2 is 1/26/2021… then you apply the code above… there is a good chance that you may get a result like 0.933 … which will obviously become a 0 when converted to an int and this is not what we would expect. The reason for this is because when you add or subtract two DateTime objects… the calculation is INCLUDING the Time portion of the DateTime object.
Therefore, to get the correct int value… you need to “qualify” that you only want the “Date” difference between the two dates. Fortunately the DateTime object has a Date property that we can use to ignore the Time portion. Therefore only a small change is needed to fix this and may look something like…
(dpEnd.Value.Date - dpStart.Value.Date).TotalDays
As suggested in the comments, using Events is probably the easiest to implement and it is not difficult to understand. Basically, we would subscribe (wire-up) each of the controls in each of the UserControls to the SAME event. Inside that event we would “update” the calculated value.
Typically, you would wire up each control to its own event, however, since you want to simply “update” a single text box when ANY of the control’s changes we can simplify this and create a single method to “update” the calculated text box when ANY of the other controls changes. I hope that makes sense.
To help, and I highly recommend you also (in the near future) do the same… is properly NAME your variables. Naming the controls uc1, uc2, uc3 and uc4 is well … not a good idea. You can do it, but it makes it difficult to tell “what” the control is. Looking at the names… without more research, I have no idea “which” control has the “Rate” text box. Name your variables to something meaningful to avoid any ambiguity. In the example below, for the UserControls I named them like… UC_StartEndDate, UC_Rate etc…
Another possible issue is that since you are wanting to perform a “calculation,” you will need to parse the string values in the TextBoxes to int values. In other words… the code…
txtRate.Text * txtAmount.Text
May well work without an error, however I am confident it will not give you the result you want since both sides of the “*” multiplier are “TEXT/string” values and we will need to parse those string values to int values to do any mathematical calculations.
NOTE the int.TryParse(UC_Rate.txtRate.Text, out int rate); line of code would typically be wrapped in an if statement since it will return true if the parse succeeded and false if it fails. If the parse fails, then the out variable rate will be set to zero (0) and that is ok with me… if it fails then use a zero (0) as the value. You may want to do something different.
private void UpdateUC_Total() {
int tdays = (int)(UC_StartEndDate.dpEnd.Value.Date - UC_StartEndDate.dpStart.Value.Date).TotalDays;
int.TryParse(UC_Rate.txtRate.Text, out int rate);
int.TryParse(UC_Amount.txtAmount.Text, out int amt);
int total = tdays * rate * amt;
UC_Calculated.txtCalc.Text = total.ToString();
}
Now all we have to do is subscribe (wire-up) to the individual UI controls TextChanged event. As already mentioned, since we want ALL the controls to do the same calculation, this simplifies things and we can have ALL the controls subtribe to the SAME event. This one event would simply call our method above and may look something like…
private void UC_ValueChanged(object sender, EventArgs e) {
UpdateUC_Total();
}
In the forms Load event, we could subscribe the controls in each of the UserControls to our event above and it may look something like…
private void Form1_Load(object sender, EventArgs e) {
UC_StartEndDate.dpStart.TextChanged += UC_ValueChanged;
UC_StartEndDate.dpEnd.TextChanged += UC_ValueChanged;
UC_Rate.txtRate.TextChanged += UC_ValueChanged;
UC_Amount.txtAmount.TextChanged += UC_ValueChanged;
}
That is pretty much it. If any of the date pickers or rate or amount text boxes change, then the calculated text box text will “update” automatically. You may need to “leave” the control to see the updated value.
Another approach is to use each control’s DataBindings property to “Bind” each control to something. Currently you have the code…
uc4.txtAmount.DataBindings.Add("Text", uc2.txtRate, "Text", true, DataSourceUpdateMode.Never);
The problem here is that the data bindings is looking for a DataSource of some type like a DataTable or a List<T> or in the example below a Class object. The code “appears” to be using uc2.txtRate as a DataSource and this probably will not work as txtRate is a TextBox and is not necessarily a DataSource.
This can get tricky, and to keep things simple, I will often simply “create” a DataSource to make it easier to set the controls DataBindings to work as I want. The code below shows how you could do this “DataBinding” using your current example. Bear in mind this will work, however, it may create a little more work for you.
So step 1 is to “create” a simple DataSource we can use for ALL the controls (DTP and TextBoxes). In this case I will implement a single Class with the properties we need. Then, we would instantiate only ONE of these objects and then use that object as a DataSource when setting each controls DataBindings property. This UC_Helper class may look something like…
public class UC_Helper {
public DateTime DP_Start { get; set; }
public DateTime DP_End { get; set; }
public string Rate { get; set; }
public string Amount { get; set; }
public int CalcAmount {
get {
int tdays = (int)(DP_End.Date - DP_Start.Date).TotalDays;
int.TryParse(Rate, out int rate);
int.TryParse(Amount, out int amt);
return tdays * rate * amt;
}
}
}
We will be able to instantiate a single UC_Helper object with the values from our user control “controls” and then simply set each control’s DataBinding property to “point” to the proper property in the instantiated UC_Helper object.
Therefore in the form load event, you would instantiate a new UC_Helper object and then “bind” each of the controls in each of the UserControls to one of the properties in our UC_Helper object. This code in the forms Load event may look something like…
UC_Helper ControlHelper;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
ControlHelper = new UC_Helper {
DP_Start = UC_StartEndDate.dpStart.Value.Date,
DP_End = UC_StartEndDate.dpEnd.Value.Date,
Rate = UC_Rate.txtRate.Text,
Amount = UC_Amount.txtAmount.Text
};
UC_Amount.txtAmount.DataBindings.Add("Text", ControlHelper, "Amount");
UC_Rate.txtRate.DataBindings.Add("Text", ControlHelper, "Rate");
UC_Calculated.txtCalc.DataBindings.Add("Text", ControlHelper, "CalcAmount");
UC_StartEndDate.dpStart.DataBindings.Add("Text", ControlHelper, "DP_Start");
UC_StartEndDate.dpEnd.DataBindings.Add("Text", ControlHelper, "DP_End");
}
Sorry for the long post. I hope this helps and I suggest you pick you own poison as to which approach to use. I tend to favor the event approach, however if there is a good DataSource available I may go with the data binding. Good Luck.
I have a Winforms app where a particular textbox field ("Phone Number") gets updated entirely programmatically, as the result of a user search on another form or as queries to a database for the overall main form's (saved) data.
We'd like the textbox to display with a red background whenever the data fits certain situations (blank is one of them, but there's also another string that can show up that we need to treat as "blank"). So I rigged this up on a TextChanged event handler.
However, sometimes the user will press a "Clear" button to blank out the Person data/fields on this form, including this Phone Number textbox. And in that case, we don't want a blank to show up red. So I adjusted the TextChanged event handler to account for this. OK, so far, so good.
Yet if they have done a Clear and now another search takes place, dumping its results back into the field, if the updated data is empty string or null... well... the TextChanged event won't fire because the VALUE is not changing. It already was Null/Empty. Yet in this situation, we'd WANT what I've got in the TextChanged event handler to fire.
I can't use the Validating Event Handler, because that only engages when a USER provides the input (I think?)
So far, my work-around has been to FORCE the event handler to fire after we're basically updating that field at the end of a search (possibly updating Null/Empty with Null/Empty). And this works. But it seems like there ought to be a better way. ??
I didn't see another event handler on that control that seemed to do what I'm looking for, but I thought I'd ask the crowd.
Thanks!
Apparently you want a special kind of TextBox, one that fires the event whenever property Text is set, even if this doesn't lead to a change.
A special kind of TextBox?Sounds like a derived class:
class MyTextBox : TextBox
{
public override string Text
{
get => base.Text;
set
{
// if no change, only call OnTextChanged, otherwise call base.Text
if (this.Text.Equals(value))
{
base.OnTextChanged(EventArgs.Empty);
}
else
{
base.Text = value;
}
}
}
}
If desired, use a stringComparer, like OrdinalIgnoreCase.
public IEqualityComparer<string> TextComparer {get; set;} = StringComparer.OrdinalIgnoreCase;
public override string Text
{
get => base.Text;
set
{
if (this.TextComparer.Equals(this.Text, value))
...
The disadvantage of method is that base.Text = value will check for equality again. If you don't wan't this, look at the source code of TextBox
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
If you click on SetValue, you will jump deep inside windows. I'm not sure if you ever want to get there, given the fact that you won't update the Text property several times per second.
I have a question regarding updating Controls in .Net in such a way so that if the user updates one field, the other field will be updated with some data automatically and vice versa. I am using two NumericUpDown controls which convert some data.
The problem I am having is that, I am using the ValueChanged event. Because of this, sometimes these controls get stuck in a loop where one controls updates the other and the other tries to update the first one. The result is somewhat random.
So, what would be the best approach to handle this situation? In short, I only want to update the other control, if the first one was modified by a user himself.
Any help would be greatly appreciated.
Just use a boolean guard in the class to check whether you are inside an update method.
While you are updating, all future events fired from the NUDs will be ignored.
private boolean updating = false; // Class level variable
void event_handler(...) // The function hooked up to the ValueChanged event
{
if( !updating )
{
updating = true;
// Do your calculations and update the NUDs
updating = false;
}
}
I would recommend that you use data binding and bind to an object which serves as your model. Your model then is where the logic goes that alters the other value based on changing of a property. The model also raises IPropertyChanged / IPropertyChanging events, which the UI will pick up on. Not only will this prevent the issue you describe, it also keeps this business logic outside of your UI layer should you move to something else (say from WinForms to WPF, or Asp.Net MVC).
If method Foo handles the event of one control and method Bar handles the event for the other, then Foo should change the values of Bar's control and vice-versa. But you should use a control variable somewhere (say, a reference to the control that fired the event is a good idea). So that if Foo is called:
Foo updates the value of Bar's control;
Bar's control fires its event, and Bar is called;
Bar checks the reference for the control that shot first, sees that it's not its control, and does nothing.
Same logic applies to Bar.
that way you don't get an infinite loop.
In code, it'd, look like this:
nud1.ValueChanged += new Eventhandler(Foo);
nud2.ValueChanged += new Eventhandler(Bar);
NumericUpDown shooter = null;
private void Foo (object sender, EventArgs e)
{
if (this.shooter == null)
{
this.shooter = nud1;
nud2.Value = nud1.Value;
}
else this.shooter = null;
}
private void Bar (object sender, EventArgs e)
{
if (this.shooter == null)
{
this.shooter = nud2;
nud1.Value = nud2.Value;
}
else this.shooter = null;
}
Of course, this is a crude example (for example, it assumes the value of both controls is always changing. Adapt to your situation.
I like Andy's response about going with an MVC pattern, but if that's too radical of a change for this specific situation, you should set the values only if the current value is different than the value being assigned. That would prevent the ValueChanged event from firing again and stop the infinite loop the first time recursion happens.
// Inside your value changed handler for Control2,
// instead of directly setting the value of Control1, do this:
if(Control1.Value != valueBeingSet)
{
Control1.Value = valueBeingSet;
}
I was just wondering. If I have two forms.. Form 1 and Form 2 and you need to access a textbox, a label etc.. for example to make it visible or to change its text..
Which is the best way to access these from Form2?
Making a method in Form 1 like:
public void setTextBoxVisible(){
textBox1.Visible = true;
}
or making an accessor in Form 1, and change the visibility from Form 2, like:
In Form 2:
public TextBox TextBox1 {
get { return textBox1; }
}
In Form 1:
Form1 form1 = new Form1();
form1.TextBox1.Visible = true;
I know it may be a stupid question but I am still new to programming and I wish to know the best way I could do these things. Thanks :)
Option 1 is definitely better, because you are only exposing the part that you want to expose.
Not only are you leaving less room for error on part of the caller, but you are stating the intent of your method.
In option 2, all aspects of your control are accessibly and can be changed from outside code, not only the Visible property.
You could, if it suits your preference better, create a property:
public bool TextBox1Visible
{
get { return TextBox1.Visible; }
set { TextBox1.Visible = value; }
}
Programmers preference I think.. I always prefer method 1 as theres less room for error and if you want to change what it does in a lot of instances then its done in one line of code
In your 2nd scenario you are declaring read-only property , whereas in First approach you are exposing the functionality through a public function, which IMO is more clear and better approach.
EDIT:
One of the problem which I see with your first approach is that you can't stop a user from changing some property other than Visible. The user (developer) can mistakenly set the Text Property of your TextBox as well. e.g.
form1.TextBox1.Text = "Some new text";
The above is not a desired behaviour. (You only wanted to set Visible property)
Use a datamodel as a seperated class. Use the observer / MVVM pattern to get notified when the datamodel gets changed.
I assume that you have some action in Form2 that triggers the change to Form 1. Given your options I would got for 1. Another option is to consider events. Form1 subscribes to an event on Form2. When it gets triggered you can pass whatever you want to Form1 and have it carry out whatever updates or changes you want.
I have a form and I have some buttons doing stuff.
When I press buttons the windows form controls, like textboxes or group-boxes, buttons appear and disappear and change place on my form, for it is a dynamic form :)
However, what I'd like to do is have a button ( BACK ) that will get my form to the state it was before an action of a button, putting back the controls in the place and state they were before action.
I thought of a C class MyState() that will have something like an array of Form1.
I will be saving the form state in that array and when I'll press the back button to get from array that "copy" of the Form state and maybe an index for indexing states.
I have no idea how to implement this, unfortunately. :|
Can anyone show me the right way to do this?
class Mystate
{
private Form1 [] state;
public Mystate(int n)
{
this.state = new Form1[n];
}
public Form1 this[int index]
{
get
{
return state[index];
}
set
{
this.state[index] = value;
}
}
}
Sounds like you want an high level undo/redo feature for your forms.
Here is a framework for such things: http://www.codeproject.com/Articles/10576/An-Undo-Redo-Buffer-Framework
Here is an answer that is close but not exactly the same as your question (The pattern implimented is the same though): How to implement good and efficient undo/redo functionality for a TextBox
MementoPattern: http://www.codeproject.com/Articles/18025/Generic-Memento-Pattern-for-Undo-Redo-in-C
Nothing like this is built-in. You have to do this on your own.
I'd do it like this: First, define precisely what state you want to save. Example:
Control.Bounds
Control.Text
Checkbox.IsChecked
NumericUpDown.Value
...
Now we know exactly what needs to be saved.
Seconds, we need a way to create a snapshot of the current state of the form and recursively for all controls. You can implement this using reflection so that everything will be automatic no matter how many controls you have.
Third, you need to be able to apply a snapshot to an instance of Form. This is the opposite process of (2). This also can be done using reflection.