Does anyone know if there is a way to get a list of controls that have the ErrorProvider icon active. ie. any controls that failed validation. I'm trying to avoid looping all controls in the form.
I'd like to display some sort of message indicating how many errors there are on the form. As my form contains tabs I'm trying to make it apparent to the user that errors may exist on inactive tabs and they need to check all tabs.
Thanks
Barry
This falls in the category of "how can you not know". It is your code that is calling ErrorProvider.SetError(), you should have no trouble keeping track of how many errors are still active. Here's a little helper class, use its SetError() method to update the ErrorProvider. Its Count property returns the number of active errors:
private class ErrorTracker {
private HashSet<Control> mErrors = new HashSet<Control>();
private ErrorProvider mProvider;
public ErrorTracker(ErrorProvider provider) {
mProvider = provider;
}
public void SetError(Control ctl, string text) {
if (string.IsNullOrEmpty(text)) mErrors.Remove(ctl);
else if (!mErrors.Contains(ctl)) mErrors.Add(ctl);
mProvider.SetError(ctl, text);
}
public int Count { get { return mErrors.Count; } }
}
Today I had the same problem. My solution is to extend the ErrorProvider control.
See the code below:
public class MyErrorProvider : ErrorProvider
{
public List<Control> GetControls()
{
return this.GetControls(this.ContainerControl);
}
public List<Control> GetControls(Control ParentControl)
{
List<Control> ret = new List<Control>();
if (!string.IsNullOrEmpty(this.GetError(ParentControl)))
ret.Add(ParentControl);
foreach (Control c in ParentControl.Controls)
{
List<Control> child = GetControls(c);
if (child.Count > 0)
ret.AddRange(child);
}
return ret;
}
}
You can use the above derived class in your form, and then (say that myErrorProvider is the class instance in your form) you can get all the controls with errors in your form, by calling:
List<Control> errorControls = myErrorProvider.GetControls();
This is a moderately tricky solution you are talking about.
There is no way to achieve this automatically, as far as I know.
You have to maintain a flag for every control and manually set it every time an error-provider is blinked.
May be a Dictionary<TKey, TValue> can be used to keep track of it.
You have to use SetError to set the error on the control in the first place, right? Perhaps you should store that information in another collection at the same time if you want to have it handy. For example, you could add each control with an error to a hashset.
Just make the errorprovider as a Global variable rather than local variable
public partial class MainForm
{
ErrorProvider errorProvider1 = new ErrorProvider();
void Validate_Working()
{
errorProvider1.SetError(textbox1, "textbox is empty");
errorProvider1.Clear();
}
}
from
public partial class MainForm
{
Void Validate_NotWorking()
{
ErrorProvider errorProvider1 = new ErrorProvider();
errorProvider1.SetError(textbox1, "textbox is empty");
errorProvider1.Clear();
}
}
This should fix your problem, because probably you might have been removing your errors from another method such as btnCancel_click.
This worked for me :)
Related
I'm currently creating a project in Visual Studio 2015 using C# in WinForms; I used the words "ToolBox items" to refer to dateTimePickers, textbox, labels etc. It is a really big project and it will save me a lot of time if I could drag and drop the objects and they already have format; for example the datetimePicker custom format, textbox align etc.
The issue is that I need to customize some properties of these objects. This far I have this piece of code that allows me to change some properties that are overridable.
public partial class MoneyBox : TextBox
{
public override Color BackColor
{
get { return Color.Azure;}
set { base.BackColor = value; }
}
}
But for other properties I cannot do this. Also I cannot inherit from an object already formatted because this object hasn't been initialized and I get null when inheriting. I also tried to customize the initialize component and the paint event of objects, but for some reason the changes don't show on the object.
public partial class DateTimePick : DateTimePicker
{
public void InitializeComponent()
{
InitializeComponent();
this.Format = DateTimePickerFormat.Custom;
this.CustomFormat = "dd/MM/yyyy";
}
}
Any ideas?
Thanks in advance!
Constructor is a suitable place to initialize properties of the control. For most properties, to initialize the control with custom values when you drop it on design surface, it's enough to set new values in constructor, for example:
public class MyDateTimePicker : DateTimePicker
{
public MyDateTimePicker()
{
this.Format = DateTimePickerFormat.Custom;
this.CustomFormat = "dd/MM/yyyy";
}
}
In some cases, for example for Text property, when you drop an instance of the control on design surface, the property is set in InitializeNewComponent method of the Designer of control.
This may not be exactly what you're looking for, I'm sure there is a better way of doing it, but this was my solution for default control properties. It makes a list of all controls in your form (and their child controls) and changes the properties on initialization.
public static void ChangeDefaultProperties(Control C)
{
var ControlQueue = new Queue<Control>();
ControlQueue.Enqueue(C);
while (ControlQueue.Count > 0)
{
Control Current = ControlQueue.Dequeue();
DefaultPropertiesOverride(Current);
foreach (Control c in Current.Controls)
{
ControlQueue.Enqueue(c);
}
}
}
public static void DefaultPropertiesOverride(Control C)
{
if(C is DateTimePicker)
{
((DateTimePicker)C).Format = DateTimePickerFormat.Custom;
((DateTimePicker)C).CustomFormat = "dd/MM/yyyy";
}
if(C is TextBox)
{
((TextBox)C).BackColor = Color.Azure;
}
}
Then just call ChangeDefaultProperties(this); in your main form initialization
How do I write a class for something like this
Example: I have a form that has a panel on it. That panel has an tabbed control. In that tabbed control there is a textbox. If I pass the form as the start control and “textbox1” [the name of the TextBox], the class will return the texbox control as a control. This is using c#, asp.net.
So in a form load event, I should be able to do this search as follows:
Control txtCtrl = Search.FindControl(“textbox1”, this);
Something like this maybe:
class Search
{
public static TextBox FindControl(string controlToFind, Page page)
{
//find your text box
}
}
As #i4v mentioned, a class does not return anything but you could add a function that will return something. That being said, this seems like an odd requirement. Can you explain what you are trying to do?
You can make it a little cleaner by calling a method that receive the id of the control to find and the control where you want to search. Something like:
public class ControlUtils
{
public static Control FindControl(string idToFind, Control mainControl)
{
//Recursively search for the control?
}
}
this code may help you. parentControl is the container that has the Textbox you are looking for.
public TextBox FindTextbox(string name)
{
foreach (Control item in parentControl.Children) //based on parent type it my be .Childern, .Items , ...
{
if (item is TextBox)
{
TextBox temp = item as TextBox;
if (temp.Name == name)
{
return temp;
}
}
}
}
Could I add some custom control to the standard Message Box for read input value, for example text fields for user name and password, or I should create custom winform with "Ok,Cancel" buttons and text fields?
Related: Which control to use for quick text input (inputbox)?
you can use the Interaction.InputBox method wich is located in the Microsoft.VisualBasic namespace
try this
Microsoft.VisualBasic.Interaction.InputBox("Enter a Value Here", "Title", "Your Default Text",200,100);
You will need to create a custom WinForm to do this. You can make it work the same way as a MessageBox by returning a DialogResult on the Show method.
Create your own.
Creating a custom modal (or otherwise) input dialog isn't all that difficult and you can built the extensibility you need for reuse.
public class ValueHolder {
public string SomeInput { get; set; }
public DialogResult Result { get; set; }
}
public class GimmeValues : Form {
//... HAS A TEXTBOX and Okay Buttons...
private GimmeValues() {
okButton.DialogResult = DialogResult.OK;
cancelButton.DialogResult = DialogResult.Cancel;
// ... other stuff
}
public static ValueHolder GetInput(IWin32Window owner) {
using (GimmeValues values = new GimmeValues()) {
DialogResult result = values.ShowDialog(owner);
return new ValueHolder {
SomeInput = values.Textbox1.Text,
Result = result
};
}
}
}
Okay I just wrote that all in this editor so forgive any syntax mistakes.
You could do something like the above but clean it up a little, add the extensibility you need (in terms of buttons and inputs showing that you need etc)... then just call it like ValueHolder value = GimmeValues.GetInput(this); where this would represent an IWin32Window...
The resulting value of value would be the selected nonsense and you could perform your logic..
if(value.Result == DialogResult.OK && !string.IsNullOrEmpty(value.SomeInput)){
//TODO: Place logic....
}
You'll have to create a custom form to handle that.
If you want the Form to behave like MessageBox, just create a static Show() method on your Form that creates an instance and shows the box to the user. That static method can also handle returning the values you are interested in from your custom form (much like DialogResult).
I have 30 textbox and other control on winform, and I want sometimes set their properties (enable, visible) on true/false. I don’t make duplicate code. I am newbie in winforms, and I’d like know what is the good solution for this problem.
I’d like use extender provider, but I’d know if it is suitable. Sory for my english. :)
If someone can give me a code example, I will be very grateful.
IExtenderProvider is not a suitable solution to this, it was meant to add new properties to existing controls. The generic approach is quite simple: use a Panel. Put all the controls you want to disable or hide in that panel. And set the panel's Visible or Enabled property to false. This will automatically disable all the child controls of the panel. And when you hide the panel, its children will be hidden too.
It depends on the situations when you want to toggle visibility and enable/disable status.
If you want to change the state, you can do something like:
private void ToggleVisible(TextBox tb)
{
tb.Visible = !tb.Visible;
}
private void ToggleEnable(TextBox tb)
{
tb.Enabled= !tb.Enabled;
}
Also if you want to update the status of multiple textboxes at the same time, you better define a method and do the batch updating for certain groups.
What really matters is to categorize (group) your textboxes properly. (into panels if you can)
Here's how I've done something like that:
In the base form class from which my forms inherit (which, in turn, inherits from Form)....
#region Form Fields Enablers
protected virtual void EnableFormFields(Control ctl)
{
EnableFormFields(ctl, true);
}
protected virtual void DisableFormFields(Control ctl)
{
EnableFormFields(ctl, false);
}
protected virtual void EnableFormFields(Control ctl, bool enable)
{
EnableFormFields(ctl, enable, new string[0]);
}
protected virtual void EnableFormFields(Control ctl, bool enable, params string[] excludeControlName)
{
bool exclude = false;
foreach (string excludeCtl in excludeControlName)
{
if (string.Compare(ctl.Name, excludeCtl, true) == 0)
{
exclude = true;
break;
}
}
if (!exclude)
{
ctl.Enabled = enable;
foreach (Control childCtl in ctl.Controls)
{
EnableFormFields(childCtl, enable, excludeControlName);
}
}
}
#endregion Form Fields Enablers
Then, in the particular form, I call this.EnableFormFields(someContainerControl) when I want to set all the controls within a given container, such as all controls on all tabs (tabcontrol) or all controls on a single tab (tabpage), or even all the controls on the form (passing "this"), etc. I can even exclude some controls by passing their names as a string array, or each such name as a separate trailing parameter ("params" in the method definition's parameter list).
I think this is what John was seeking. The important note for a newbie is the recursiveness of this code, setting the Enable property for a given control, then calling the method to set the Enable property in each of the control's child controls. Each of these, in turn, set the property on their child controls.
Thanks,
John
You could consider to subclass the Textbox and add your own properties:
class MyTextbox : Textbox {
public bool MyEnable {
get{ return someBool; }
set {
if (yourConditionIsMet) {
someBool = value;
this.Enabled = value;
}
}
}
}
...then iterate over all your controls in your form:
foreach (Control control in this.Controls) {
if (control is MyTextbox)
control.MyEnable = true;
}
[EDIT] To be clear, I know how to get a list of forms via reflection. I'm more concerned with the design time property grid.
I have a user control with a public property of the type Form.
I want to be able to select a form at design time from a dropdown.
I want to populate the form dropdown list from a set namespace: UI.Foo.Forms
This would work like if you have a public property of Control. At design time, the property will automatically populate a dropdown with all the controls on the form, for you to select from. I just want to populate it with all the forms in a namespace.
How do I go about doing this? I hope I'm being clear enough so there is no confusion. I'm looking for some code examples if at all possible. I'm trying to avoid having to spend too much time on this when I have other deadlines to meet.
Thanks for your help in advance.
You can easily get the classes via Reflection:
var formNames = this.GetType().Assembly.GetTypes().Where(x => x.Namespace == "UI.Foo.Forms").Select(x => x.Name);
Assuming you're calling this from code in the same assembly as your forms, you'll get the names of all the types that are in the "UI.Foo.Forms" namespace. You can then present this in the dropdown and, eventually, instantiate whichever is selected by the user via reflection once more:
Activator.CreateInstance(this.GetType("UI.Form.Forms.FormClassName"));
[Edit] Adding code for the design time stuff:
On your control you can create a Form property as such:
[Browsable(true)]
[Editor(typeof(TestDesignProperty), typeof(UITypeEditor))]
[DefaultValue(null)]
public Type FormType { get; set; }
Which references the Editor type that must be defined. The code is pretty self-explanatory, with a minimal amount of tweaking, you'll likely be able to get it to produce exactly what you want.
public class TestDesignProperty : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
var edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
ListBox lb = new ListBox();
foreach(var type in this.GetType().Assembly.GetTypes())
{
lb.Items.Add(type);
}
if (value != null)
{
lb.SelectedItem = value;
}
edSvc.DropDownControl(lb);
value = (Type)lb.SelectedItem;
return value;
}
}
The dropdown does not close when item gets selected by clicking it, so this could be useful:
assign the click event handler for the listbox and add the event handler function
public class TestDesignProperty : UITypeEditor
{
// ...
IWindowsFormsEditorService editorService;
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
// ...
editorService = edSvc ; // so can be referenced in the click event handler
ListBox lb = new ListBox();
lb.Click += new EventHandler(lb_Click);
// ...
}
void lb_Click(object sender, EventArgs e)
{
editorService.CloseDropDown();
}
}