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
Related
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();
}
}
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 :)
I have a numericupdown control on a C# Windows Form, and am interested in adding a leading zero to its value if it is < 10. (It is for the user to enter the minutes value of a time.)
I am not very familiar with overrides/inheriting yet in C# but it is looking as though I may have to do that.
It looks like this post on EggheadCafe has the answer I need. Is it as simple as making a new class and then creating a control of that new class?
public class TestNum : NumericUpDown
{
protected override void ValidateEditText()
{
if (base.UserEdit)
{
base.ValidateEditText();
}
}
protected override void UpdateEditText()
{
Text = Convert.ToInt32(base.Value).ToString("00");
}
}
When I try this, I am not sure how to create the new control that takes advantage of this class. I am using Visual Studio 2008. Still very new to windows forms. Thanks for any advice.
EDIT
I was able to make this work by editing the Designer-created code so that instead of the new control being of the original class, it was of the new one. So after adding the class above, I did the following (these changes are in two different locations, but I am only showing the lines that mattered):
Changed:
this.numTest = new System.Windows.Forms.NumericUpDown();
private System.Windows.Forms.NumericUpDown numTest;
To:
this.numTest = new SampleForm.TestNum();
private TestNum numTest;
Why not just use a DateTimePicker control? Set its ShowNumericUpDown property to true and set its Format property to Custom and set the CustomFormat property to hh:mm:ss.
Perhaps this will be useful for you.
You need to use this newly created class in your form. It doesn't replace all NumericUpDown controls, it's a subclass.
Add the project which contains this class to the toolbox (Tools->Toolbox Items... - if memory serves) and you should be able to drag the control onto the form where you want to use it.
Stumbled across this by looking for "NumericUpDown Display Hex with Leading Zero's"
public class HexNumericUpDown : System.Windows.Forms.NumericUpDown
{
public HexNumericUpDown()
{
Hexadecimal = true;
}
protected override void ValidateEditText()
{
if (base.UserEdit)
{
base.ValidateEditText();
}
}
protected override void UpdateEditText()
{
Text = System.Convert.ToInt64(base.Value).ToString("X" + HexLength);
}
[System.ComponentModel.DefaultValue(4)]
public int HexLength
{
get { return m_nHexLength; }
set { m_nHexLength = value; }
}
public new System.Int64 Value
{
get { return System.Convert.ToInt64(base.Value); }
set { base.Value = System.Convert.ToDecimal(value); }
}
private int m_nHexLength = 4;
}
Use date time picker with following properties:
Format = Custom
CustomFormat = "hh:mm:ss"
ShowUpDown = True
I've made a C# usercontrol with one textbox and one richtextbox.
How can I access the properties of the richtextbox from outside the usercontrol.
For example.. if i put it in a form, how can i use the Text propertie of the richtextbox???
thanks
Cleanest way is to expose the desired properties as properties of your usercontrol, e.g:
class MyUserControl
{
// expose the Text of the richtext control (read-only)
public string TextOfRichTextBox
{
get { return richTextBox.Text; }
}
// expose the Checked Property of a checkbox (read/write)
public bool CheckBoxProperty
{
get { return checkBox.Checked; }
set { checkBox.Checked = value; }
}
//...
}
In this way you can control which properties you want to expose and whether they should be read/write or read-only. (of course you should use better names for the properties, depending on their meaning).
Another advantage of this approach is that it hides the internal implementation of your user control. Should you ever want to exchange your richtext control with a different one, you won't break the callers/users of your control.
Change the access modifier ("Modifiers") of the RichTextBox in the property grid to Public.
Add a property to the usercontrol like this
public string TextBoxText
{
get
{
return textBox1.Text;
}
set
{
textBox1.Text = value;
}
}
I recently had some issues doing this with a custom class:
A user control had a public property which was of a custom class type. The designer by default tries to assign some value to it, so in the designer code, the line userControlThing.CustomClassProperty = null was being automatically added.
The intent was to be able to provide the user control with a custom class at any point while running the program (to change values visible to the user). Because the set {} portion did not check for null values, various errors were cropping up.
The solution was to change the property to a private one, and use two public methods to set and get the value. The designer will try to auto-assign properties, but leaves methods alone.
You need to make a public property for the richtextbox, or expose some other property that does the job of setting the richtextbox text like:
private RichTextBox rtb;
public string RichTextBoxText
{
get
{
return rtb.Text;
}
set
{
rtb.Text = value;
}
}