I was just wondering if anyone has come across how to apply an input mask for a Tool Strip Combo Box in C#?
My drop down box gets populated over time with IP addresses, and I would like to restrict what the users are able to write in (Ex: can only input 3 '.'s, only numbers, etc).
So if anyone is able to help out I would really appreciate it!
Thanks in advance.
Edit
My design has changed so I now need to have a ToolStripComboBox
You could try catching the KeyUp event, then check that the input is valid. If not revert it to the last valid input. You would probably want to do something similar with the Validating event (make sure CausesValidation is true).
Another option would be to create a MaskedTextBox and place it so it covers the text box portion of the drop down menu. You would then need to wire up the events so the two form controls remained synced.
You could also look into the ErrorProvider class.
There are a couple of other ways (like a timer which runs ever .3 seconds), but those are usually performance hogs or difficult to maintain.
Update for regular expression comment:
If I was to do this I might use a regular expression or I might manually parse the string.
Either way the KeyUp and Validating events is where I would check the validation of the control. The KeyUp event gives me the option to check as they type while the Validating event allows me to validate when the control loses focus. Which you use will depend on what you want the user experience to be.
If you do not use the KeyUp event to validate, you could add a timer which runs 5 seconds after the last key press. This way the control would not have to lose focus for the error to show.
Update for edited question and comment:
You could not use Format event as your question was on how to format user input, not how things are added to the list. As such that solution does not work with ToolStripComboBox or with ComboBox.
After reading the documentation for ToolStripControlHost, you might be able to cast ToolStripComboBox to ComboBox. If not then you could use the ToolStripControlHost to place the ComboBox onto your form. - This is incorrect or unnecessary, please see update below the quote.
ToolStripControlHost is the abstract base class for ToolStripComboBox, ToolStripTextBox, and ToolStripProgressBar. ToolStripControlHost can host other controls, including custom controls, in two ways:
Construct a ToolStripControlHost with a class that derives from Control. To fully access the hosted control and properties, you must cast the Control property back to the actual class it represents.
Extend ToolStripControlHost, and in the inherited class's default constructor, call the base class constructor passing a class that derives from Control. This option lets you wrap common control methods and properties for easy access in a ToolStrip.
Use the ToolStripControlHost class to host your customized controls or any other Windows Forms control.
To customize a ToolStripItem, derive from ToolStripControlHost and create a custom implementation. You can override methods such as OnSubscribeControlEvents to handle events raised by the hosted controls, and you can put custom functionality into properties to enhance the hosted control.
Update:
According to the ToolStripComboBox documentation you can access the underlying ComboBox through ToolStripComboBox's ComboBox property.
This is why I usually read the documentation on a control before I use it. I might not understand it, but at least I will have an idea what to look for. :)
You should create Format event like this:
private void comboBox1_Format(object sender, ListControlConvertEventArgs e)
{
e.Value = GetFullIpFormat((string)e.Value);
}
And here is code for formating values:
string GetFullIpFormat(string value)
{
string[] ip = new string[4];
for (int i = 0; i < ip.Length; i++)
{
ip[i] = GetIpPart(i, value);
}
return string.Format("{0:###}.{1:###}.{2:###}.{3:###}", ip[0], ip[1], ip[2], ip[3]);
}
string GetIpPart(int partNumber, string ip)
{
string result = "000";
int iLen = 3;
ip = ip.Replace(".", "");
int iStart = partNumber * iLen;
if (ip.Length > iStart)
{
result = ip.Substring(iStart);
if (result.Length > iLen)
{
result = result.Substring(0, iLen);
}
}
return result;
}
This will do formating for you.
Alternativly you can check input on same event for numbers.
This will do the job for you, happy coding! :)
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.
What is the best way to create and read multiple textboxes in a windows form? In my application, I have a windows form where customer can enter multiple addresses, email addresses
Right now I have a form like this,
TextBoxAddress1 TextBoxEmail1
TextBoxAddress2 TextBoxEmail2
.....
.....
.....
TextBoxAddressN TextBoxEmailN
For this I dragged and dropped multiple controls on a form and named each one of them.
If I use this method I had to write lengthy code to see if first row (TextBoxAddress1 TextBoxEmail1) is filled for validation and even for reading I had to write many lines of code.
Is there a better to way achieve this?
You can use the following code to add a TextBox dynamically to your form:
private int m_CurrTexboxYPos = 10;
private List<TextBox> m_TextBoxList = new List<TextBox>();
private void CreateCheckBox()
{
m_CurrTexboxYPos += 25;
TextBox textbox = new TextBox();
textbox.Location = new System.Drawing.Point(0, m_CurrTexboxYPos);
textbox.Size = new System.Drawing.Size(100,20);
Controls.Add(textbox);
m_TextBoxList.Add(textbox);
}
I would have a listbox/listview with your emails and Add/Edit/Delete buttons which show a popup form - the logic for validating emails, etc. would then be in the one place and your list can grow without you ever needing to add controls to the form.
You could dynamically create textboxes - but you end up writing code to make sure they layout nicely on the form, etc. - having some type of list is easier IMO and also lends itself to binding (e.g. to an email object)
Dynamically adding controls is pretty simple, provided you can use DockStyle and an exclusive container for them (e.g. a Panel). If you can't use DockStyle, then you need to write logic to determine Location and Size (which isn't fun).
On a simple form, I have two buttons and a panel, Button1 adds a new TextBox to Panel1, Button2 iterates through the controls in Panel1 and then checks that they are the correct type or throws an exception. This is where you you would put validation or reading logic. Panel1 needs to have AutoScroll = true; otherwise you will run controls off of the viewable screen.
This concept can be switched for anything that inherits from UserControl (all .Net native controls or your own custom controls).
private void button1_Click(object sender, EventArgs e)
{
TextBox NewEmailBox = new TextBox();
NewEmailBox.Name = "NewEmailBox" + this.panel1.Controls.Count;
NewEmailBox.Dock = DockStyle.Top;
this.panel1.Controls.Add(NewEmailBox);
}
private void button2_Click(object sender, EventArgs e)
{
foreach (Control item in this.panel1.Controls)
{
if (item is TextBox)
{
//Do your reading/validating here.
}
else
{
throw new InvalidCastException(string.Format("{0} was in Panel1 and is of type {1} not TextBox!", item.Name, item.GetType()));
}
}
}
Write a user control for each of the groupings you need. at least one One for address, one for email etc. then all of your validation, calls to your database access is contained in a single location
That is just good design. this way if you have multiple tabs for things like Home Information, Work Information, Emergency Contact Information, you can just place them on the form. This is pretty common for a user profile.
Then a listview for each grouping on a user profile page or whatever, that has edit/delete/add then popup a dialog with the appropriate user control in it.
Most simply, ListBox adove TextBox with Button.
Also you can use DataGridView, BuiltIn functionality for Add\Edit\Delete.
Here using DataGridView (ShowHeader set to false, EditMode to On Enter, with one Column with AutoSizeMode in Fill property)
The less of repeatable code you have, the better programmer you are.
Whenever you see a pattern (something what is repeatable), you could and you should try to optimize it. Unless it's something too small to worry.
In your case, determine first what is the basic of repeatable thing. Do you always have to enter address and email address? Then combine them into a control, which can carry out validation. Do you have to use this control often (or repeat N times)? Then maybe it make sense to switch to a list instead (ListBox, ListView or DataGridView).
Are you too lazy to bother configuring things? Then just optimize something what is obviously going to repeat: put validation into common method and call it from each TextBox event. Or make own TextBox with method build-in. Or do validation at once in the Ok button event by using loop.. or not by using loop.
To find best method you have to first decide best for who. Because customer want something shiny,easy to use, animated, with cats and boobs.. ok, without cats and boobs. The point is: how much work are you willing to put to have it best for the customer.
If I would have to enter table data (or data which form table), I'd go with DataGridView so it would looks like this.. or better:
I'm working in a WPF project and I need one requirement.
This is my main window:
http://img210.imageshack.us/img210/9752/rule.jpg
So, I want that when I drag and drop a custom activity (aggregate Functions), display his name in Compute TextBox... Is there some way to do it????
For example:
1) Drag and drop MaxActivity
2) Display in TextBox Compute: MaxActivity
3) Drag and drop SumActivity
4) Display in TextBox Compute: MaxActivity + SumActivity
5) Drag and Drop MaxActivity
6) Display in TextBox Compute: MaxActivity + SumActivity + MaxActivity1
Thanks a lot!
I would strongly suggest you avoid this design, as however you end up implementing it will introduce brittleness to your code without any benefit (I can already see you are aggregating max and sums, why tell me again?)
If you go ahead, get rid of that "Compute" TextBox. TextBoxes = enter text. You are just rephrasing what is already in the design surface. It makes no sense to allow me to change the text you generate. Use a TextBlock or Label.
Do not, I repeat, not save this in your Activity. This is all UI tasks and should not be saving this information in your Activity configuration
If you have this
public sealed class MyActivity : Activity
{
public string Compute {get;set;} // NO!
and this
<!-- NO! -->
<Label>Compute</Label><TextBox Text="{Binding ModelItem.Compute}"/>
you're probably doing this wrong.
So, how do you do it?
You can parse out your ModelItem and generate this string by listening to changes in the ModelItem and constructing this string every time. Note, your ModelItem will not be set when the constructor is called, so you are going to have to listen to changes to the ActivityDesigner.ModelItem property. There is no event for this, so you will have to know how to listen to changes in a DependencyProperty.
Once you are listening to changes in your ModelItem, whenever a change happens, you can walk down your ModelItem (which is hard) or just get the Activity from the ModelItem and examine it.
var child = ModelItem.Properties["Child"].Value.GetCurrentValue();
if(child == null)
computeTextBox.Text = "Please add some computation stuff.";
else
// hard stuff goes here...
Walking down the path from your Activity's child to whatever activities are held inside can be treacherous, as you can't assume your user has thrown in a Sequence rather than a single MaxActivity, or that they don't have seven nested Sequences, etc etc. So you have to have a pretty complex and brittle set of if else logic to try and parse out what is contained below. And if you start changing properties of the Activity tree outside of the ModelItem you're going to get out of sync with the UI.
Now, after reading that, go back to point 1. I listed in this answer and follow my advice. Drop the idea completely.
I created this User Control:
I added that User Control to the main Form and now I want to customize it.
So I will have to add text to those 3 Buttons, text in Label, populate ListBox and setting Click Events for the buttons.
What is the proper way to do that?
I looked around on the web and apparently the way to do it is to add public properties in user control that would expose individual property of control that I need.
Something like:
public string Button1Text
{
get
{
return btn1.Text;
}
set
{
btn1.Text = value;
}
}
If I go this route, I would have to add quite a few public properties to this simple user control.
But isnt it easier just to expose whole control in user control like this?
public Button MyButton1
{
get { return this.btn1; }
set { this.btn1 = value; }
}
That way the Main Form can simply access control and its properties as they are needed.
First method is better from the perspective of encapsulation. Second method causes users (forms) of your control to depend on the view of your control, and this prevents changes to the view in the future.
The first bit of code is the correct way to do it. You will have to create a lot of them but it is the proper way to do it.
The first one is much better where you only create properties for each individual property of the button you wish to be able to access from the Parent control.
If you use the second way, then anyone who wishes to use your control will be able to move and resize individual controls inside your control. Then it really isn't a custom control anymore, but more of a panel that is harder to use than a panel. I can't think of any reason why to be able to allow the Parent to move around individual elements in a subcontrol.
I have 3 buttons on my Data Entry form, OK, APPLY, CANCEL.
This form is used to edit a doctor's details, things like first name, phone # etc...
I have one object doctorObj which at any given time is either empty (a new doctor) or an object pulled from a Linq query.
I deep clone the doctorObj to EditCopyDoctor which is of the same type but used for form editing (so if Cancel is hit, the database do not need to update).
What I want to achieve is observe the EditCopyDoctor for changes against the original doctorObj so
If(doctor.Changed() && doctor.IsNotNew)
{
ApplyButton.Enabled = true;
}
else
{
ApplyButton.Enabled = false;
}
So I thought writting an event to trigger when something changed on EditCopyDoctor is the best way to do it but I'm not sure how.
I can of course put the ApplyButton.Enabled code in the TextChanged events from the form but I was wondering if there are any quicker ways to do this, I don't really want to do this as there are 10+ textbox and other controls.
Since your "Doctor" type sounds like it's generated by LINQ to SQL/Entities you should find that it already implements the INotifyPropertyChanged interface. Therefore, you should just need to watch the PropertyChanged event and act accordingly.