I have a custom class of a button and when I trigger PerformClick for any of my custom buttons, nothing happens. This is the code:
Declaration of my custom class
public class NonFocusButton : Button
{
public NonFocusButton()
{
SetStyle(ControlStyles.Selectable, false);
}
}
List<NonFocusButton> buttons = new List<NonFocusButton>();
This is the p function:
void p()
{
for (int i = 1; i <= 5; i++)
{
NonFocusButton aux = new NonFocusButton();
aux.Font = new System.Drawing.Font("Britannic Bold", 15.75F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((byte)(0)));
aux.Size = new System.Drawing.Size(192, 43);
aux.UseVisualStyleBackColor = true;
aux.UseWaitCursor = false;
aux.Visible = false;
buttons.Add(aux);
this.Controls.Add(aux);
}
// button start
buttons[0].Location = new System.Drawing.Point(410, 168);
buttons[0].Text = "START GAME";
buttons[0].Click += new System.EventHandler(this.button0_Click);
}
private void button0_Click(object sender, EventArgs e)
{
this.Close();
}
buttons[0].PerformClick(); // will not work
How are buttons declared and filled? This is how I have it, and it works.
// declaration
List<Button> butons = new List<Button>();
// calling
buttons.Add(new Button());
p();
buttons[0].PerformClick();
Edit:
The button has to get focus before it can be clicked.
Why not do something like:
button0_Click(buttons[0], EventArgs.Empty);
or just call Close() from wherever you are calling PerformClick().
The Button PerformClick source code is:
public void PerformClick() {
if (CanSelect) {
bool validatedControlAllowsFocusChange;
bool validate = ValidateActiveControl(out validatedControlAllowsFocusChange);
if (!ValidationCancelled && (validate || validatedControlAllowsFocusChange))
{
//Paint in raised state...
//
ResetFlagsandPaint();
OnClick(EventArgs.Empty);
}
}
}
And CanSelect:
public bool CanSelect {
// We implement this to allow only AxHost to override canSelectCore, but still
// expose the method publicly
//
get {
return CanSelectCore();
}
}
internal virtual bool CanSelectCore() {
if ((controlStyle & ControlStyles.Selectable) != ControlStyles.Selectable) {
return false;
}
for (Control ctl = this; ctl != null; ctl = ctl.parent) {
if (!ctl.Enabled || !ctl.Visible) {
return false;
}
}
return true;
}
My guess as to the restriction is the Selectable flag was used instead of adding another control flag such as AllowsPerformClick.
Instead of using PerformClick, you could use reflection, e.g.
MethodInfo methodOnClick = typeof(Button).GetMethod("OnClick", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
// and then...
methodOnClick.Invoke(myButton, new Object[] { EventArgs.Empty });
Related
I created a custom Control which inherits from: Panel Control (using C# Class Library) then I used it inside a Form.
When I select any of the Control Properties (during design time), my custom control won’t update !
However if I close the Form and i reopen it; the changes will take place !
I want to have the Control updated when any of the Properties are changed.
Please find bellow my custom control code:
public class PersoCont : Panel
{
private int borderSize = 2;
private Color borderColor = Color.DarkRed;
private bool isBorder = true;
private int paddingTopTitle = 0;
private int paddingBorder = 0;
public int padding_TopTitle
{
get { return paddingTopTitle; }
set { paddingTopTitle = value; Invalidate(); }
}
public int padding_border
{
get { return paddingBorder; }
set { paddingBorder = value; Invalidate(); }
}
public int border_size
{
get { return borderSize; }
set { borderSize = value; Invalidate(); }
}
public Color border_color
{
get { return borderColor; }
set { borderColor = value; Invalidate(); }
}
public bool is_border
{
get { return isBorder; }
set { isBorder = value; Invalidate(); }
}
public PersoCont()
{
}
protected override void OnHandleCreated(EventArgs e)
{
if (this.Controls.Find("xlblTitle", true).Length == 0)
{
if (isBorder == true)
{
Label lblUp = new Label();
lblUp.Text = "";
lblUp.AutoSize = false;
lblUp.BackColor = borderColor;
int lblUpWidth = this.Width - (2 * paddingBorder) - (2 * borderSize);
lblUp.Size = new Size(lblUpWidth, borderSize);
lblUp.Location = new Point(borderSize + paddingBorder, paddingBorder);
lblUp.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
Label lblDown = new Label();
lblDown.Text = "";
lblDown.AutoSize = false;
lblDown.BackColor = borderColor;
lblDown.Size = new Size(lblUpWidth, borderSize);
int lblDownTop = this.Height - paddingBorder - borderSize;
lblDown.Location = new Point(borderSize + paddingBorder, lblDownTop);
lblDown.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;
Label lblLeft = new Label();
lblLeft.Text = "";
lblLeft.AutoSize = false;
lblLeft.BackColor = borderColor;
int lblLeftHeight = this.Height - (2 * paddingBorder);
lblLeft.Size =new Size(borderSize,lblLeftHeight);
lblLeft.Location = new Point(paddingBorder, paddingBorder);
lblLeft.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom;
Label lblRight = new Label();
lblRight.Text = "";
lblRight.AutoSize = false;
lblRight.BackColor = borderColor;
lblRight.Size = new Size(borderSize, lblLeftHeight);
int lblRightLeft = this.Width - paddingBorder - borderSize;
lblRight.Location = new Point(lblRightLeft, paddingBorder);
lblRight.Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;
this.Controls.Add(lblUp);
this.Controls.Add(lblDown);
this.Controls.Add(lblLeft);
this.Controls.Add(lblRight);
}
}
base.OnHandleCreated(e);
}
}
Use Refresh instead of Invalidate which only signals to the system that a repaint is needed, but won't actually do it: it will be a call to Refresh which will repaint it.
https://learn.microsoft.com/dotnet/api/system.windows.forms.control.refresh
https://learn.microsoft.com/dotnet/api/system.windows.forms.control.update
You may test if the internal value has changed, before calling Refresh to do it (only if necessary):
public int padding_TopTitle
{
get
{
return paddingTopTitle;
}
set
{
if ( paddingTopTitle != value )
{
paddingTopTitle = value;
Refresh();
}
}
}
You may consider using Framework Design Guidelines and C# Naming Conventions.
For example:
public int PaddingTopTitle
{
get
{
return paddingTopTitle;
}
set
{
if ( paddingTopTitle != value )
{
paddingTopTitle = value;
Refresh();
}
}
}
Or:
public int PaddingTopTitle
{
get
{
return _PaddingTopTitle;
}
set
{
if ( _PaddingTopTitle != value )
{
_PaddingTopTitle = value;
Refresh();
}
}
}
I personally prefer _PaddingTopTitle (or _paddingTopTitle) for private field of a property because paddingTopTitle is used for a method parameter as well as local var.
Therefore you may have for example : BorderSize and _BorderSize (or _borderSize).
Thank you for your help.
Initially I used the OnPaint event, but I really didn't like it because it would fire many times (I don't know why).
My problem was solved as follows:
I created a simple private method that will repaint (refresh the graphics data) according to all my properties. Now everything is working fine.
public class PersoCont2 : Panel
{
Label lblUp = null;
bool isBorder;
Color borderColor;
public bool is_border
{
get { return isBorder; }
set { isBorder = value; Rearrange( ); }
}
public Color border_color
{
get { return borderColor; }
set { borderColor = value; Rearrange( ); }
}
protected override void OnHandleCreated(EventArgs e)
{
Rearrange();
}
private void Rearrange( )
{
if( lblUp != null )
{
Controls.Remove( lblUp );
lblUp = null;
}
if( is_border )
{
lblUp = new Label( );
lblUp.BackColor = borderColor;
// TODO: set properties
// . . .
lblUp.Location = new Point( 10, 10 );
Controls.Add( lblUp );
}
}
}
You can try the following code to rewrite panel control and get what you want.
public class MyPanel : Panel
{
private Color colorBorder = Color.Transparent;
public MyPanel()
: base()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(
new SolidBrush(colorBorder), 6),
e.ClipRectangle);
}
public Color BorderColor
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
}
}
}
Result:
This behaviour however is a little bit awkward because it will only repaint after clicking the form designer.
I usually used the following code to show form:
frmEmployeeManage em = null;
private void ShowEmployee_Click(object sender, EventArgs e)
{
if (em == null || em.IsDisposed)
{
em = new frmEmployeeManage();
em.MdiParent = this;
em.FormBorderStyle = FormBorderStyle.None;
em.WindowState = FormWindowState.Maximized;
em.Show();
}
else
{
em.Activate();
}
}
Now I want to write a function for showing form. The following code I don't know how to pass a form class as parameter to the function.
class CommonService
{
public static void ShowFrom(Form frmChild, Form frmParent)
{
if (frmChild == null || frmParent.IsDisposed)
{
frmChild = new Form(); // How passing the form class here?
frmChild.MdiParent = frmParent;
frmChild.FormBorderStyle = FormBorderStyle.None;
frmChild.WindowState = FormWindowState.Maximized;
frmChild.Show();
}
else
{
frmParent.Activate();
}
}
}
Finally I use the show form function like the following example:
frmEmployeeManage em = null;
CommonService.ShowForm(frmEmployee, this);
I think what you need is to use a ref parameter:
public static void ShowFrom<T>(ref T frmChild, Form frmParent) where T : Form, new()
{
if (frmChild == null || frmParent.IsDisposed)
{
frmChild = new T(); // How passing the form class here?
frmChild.MdiParent = frmParent;
frmChild.FormBorderStyle = FormBorderStyle.None;
frmChild.WindowState = FormWindowState.Maximized;
frmChild.Show();
}
else
{
frmParent.Activate();
}
}
And call it like this:
frmEmployeeManage em = null;
CommonService.ShowForm(ref em, this);
ref allows you to change the value of the parameter in a method, and the changes are reflected on the variable passed in as well.
I'm stuck with some legacy code that I want to upgrade a bit. I want to change the way the ErrorProvider shows the error status on a Control. Default behavior is the Icon, and a ToolTip if you hover on the icon.
I would like to change this behavior to be more similar to what we use in our WPF controls. Which is a red back-color(Salmon pink) and the tool-tip on the control itself.
Any tips, links or some way forward
EDIT.
See my answer below, on what i ended up with.
ErrorProvider component doesn't support this feature and if you need it you can create it yourself.
You can subscribe to BindingComplete event of a BindingManagerBase and then you can use the event arg which is of type BindingCompleteEventArgs that contains some useful properties:
ErrorText to determine if there is an error in data-binding
Binding.Control to determine the control which is bounded to
These are enough for us to implement our solution.
Code
Here is a sample code which shows how can you handle BindingComplete event and use it to change BackColor and tool-tip of a control based on it's valid or invalid state.
Suppose you have a binding source, myBindingSource which is bound to a SampleModel class which is implemented IDataErrorInfo. You can subscribe to BindingComplete event of this.BindingContext[this.myBindingSource]:
private void Form1_Load(object sender, EventArgs e)
{
this.myBindingSource.DataSource = new SampleModel();
var bindingManager = this.BindingContext[this.myBindingSource];
bindingManager.BindingComplete += bindingManager_BindingComplete;
}
Dictionary<Control, Color> Items = new Dictionary<Control, Color>();
private void bindingManager_BindingComplete(object sender, BindingCompleteEventArgs e)
{
var control = e.Binding.Control;
//Store Original BackColor
if (!Items.ContainsKey(control))
Items[control] = control.BackColor;
//Check If there is an error
if (!string.IsNullOrEmpty(e.ErrorText))
{
control.BackColor = Color.Salmon;
this.errorToolTip.SetToolTip(control, e.ErrorText);
}
else
{
e.Binding.Control.BackColor = Items[e.Binding.Control];
this.errorToolTip.SetToolTip(control, null);
}
}
Thank you Reza Aghaei. This is what i came up with based on your comment and some additional searching... Some of this code comes from msdn resource
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ErrorProvider
{
class BackgroundColorErrorProvider: Component, IExtenderProvider, ISupportInitialize
{
public BackgroundColorErrorProvider()
{
currentChanged = new EventHandler(ErrorManager_CurrentChanged);
}
public BackgroundColorErrorProvider(ContainerControl parentControl)
: this()
{
this.parentControl = parentControl;
propChangedEvent = new EventHandler(ParentControl_BindingContextChanged);
parentControl.BindingContextChanged += propChangedEvent;
}
public BackgroundColorErrorProvider(IContainer container)
: this()
{
if (container == null) {
throw new ArgumentNullException("container");
}
container.Add(this);
}
public bool CanExtend(object extendee)
{
return extendee is Control && !(extendee is Form) && !(extendee is ToolBar);
}
private bool inSetErrorManager = false;
private object dataSource;
private string dataMember = null;
private ContainerControl parentControl;
private BindingManagerBase errorManager;
private bool initializing;
private EventHandler currentChanged;
private EventHandler propChangedEvent;
private Dictionary<Control, Color> originalColor = new Dictionary<Control, Color>();
private Color errorBackgroundColor;
public ContainerControl ContainerControl
{
[UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
[UIPermission(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows)]
get
{
return parentControl;
}
set
{
if (parentControl != value)
{
if (parentControl != null)
parentControl.BindingContextChanged -= propChangedEvent;
parentControl = value;
if (parentControl != null)
parentControl.BindingContextChanged += propChangedEvent;
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
}
}
public string DataMember
{
get { return dataMember; }
set
{
if (value == null) value = "";
Set_ErrorManager(this.DataSource, value, false);
}
}
public object DataSource
{
get { return dataSource; }
set
{
if ( parentControl != null && value != null && String.IsNullOrEmpty(this.dataMember))
{
// Let's check if the datamember exists in the new data source
try
{
errorManager = parentControl.BindingContext[value, this.dataMember];
}
catch (ArgumentException)
{
// The data member doesn't exist in the data source, so set it to null
this.dataMember = "";
}
}
Set_ErrorManager(value, this.DataMember, false);
}
}
public override ISite Site
{
set
{
base.Site = value;
if (value == null)
return;
IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host != null)
{
IComponent baseComp = host.RootComponent;
if (baseComp is ContainerControl)
{
this.ContainerControl = (ContainerControl)baseComp;
}
}
}
}
private ToolTip toolTip;
public ToolTip ToolTip
{
get { return toolTip; }
set { toolTip = value; }
}
public Color ErrorBackgroundColor
{
get { return errorBackgroundColor; }
set { errorBackgroundColor = value; }
}
private void Set_ErrorManager(object newDataSource, string newDataMember, bool force)
{
if (inSetErrorManager)
return;
inSetErrorManager = true;
try
{
bool dataSourceChanged = this.DataSource != newDataSource;
bool dataMemberChanged = this.DataMember != newDataMember;
//if nothing changed, then do not do any work
//
if (!dataSourceChanged && !dataMemberChanged && !force)
{
return;
}
// set the dataSource and the dataMember
//
this.dataSource = newDataSource;
this.dataMember = newDataMember;
if (!initializing)
{
UnwireEvents(errorManager);
// get the new errorManager
//
if (parentControl != null && this.dataSource != null && parentControl.BindingContext != null)
{
errorManager = parentControl.BindingContext[this.dataSource, this.dataMember];
}
else
{
errorManager = null;
}
// wire the events
//
WireEvents(errorManager);
// see if there are errors at the current
// item in the list, w/o waiting for the position to change
if (errorManager != null)
UpdateBinding();
}
}
finally
{
inSetErrorManager = false;
}
}
public void UpdateBinding()
{
ErrorManager_CurrentChanged(errorManager, EventArgs.Empty);
}
private void UnwireEvents(BindingManagerBase listManager)
{
if (listManager != null)
{
listManager.CurrentChanged -= currentChanged;
listManager.BindingComplete -= new BindingCompleteEventHandler(this.ErrorManager_BindingComplete);
CurrencyManager currManager = listManager as CurrencyManager;
if (currManager != null)
{
currManager.ItemChanged -= new ItemChangedEventHandler(this.ErrorManager_ItemChanged);
currManager.Bindings.CollectionChanged -= new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged);
}
}
}
private void WireEvents(BindingManagerBase listManager)
{
if (listManager != null)
{
listManager.CurrentChanged += currentChanged;
listManager.BindingComplete += new BindingCompleteEventHandler(this.ErrorManager_BindingComplete);
CurrencyManager currManager = listManager as CurrencyManager;
if (currManager != null)
{
currManager.ItemChanged += new ItemChangedEventHandler(this.ErrorManager_ItemChanged);
currManager.Bindings.CollectionChanged += new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged);
}
}
}
private void ErrorManager_BindingsChanged(object sender, CollectionChangeEventArgs e)
{
ErrorManager_CurrentChanged(errorManager, e);
}
private void ParentControl_BindingContextChanged(object sender, EventArgs e)
{
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
private void ErrorManager_ItemChanged(object sender, ItemChangedEventArgs e)
{
BindingsCollection errBindings = errorManager.Bindings;
int bindingsCount = errBindings.Count;
// If the list became empty then reset the errors
if (e.Index == -1 && errorManager.Count == 0)
{
for (int j = 0; j < bindingsCount; j++)
{
if ((errBindings[j].Control != null))
{
// ...ignore everything but bindings to Controls
SetError(errBindings[j].Control, "");
}
}
}
else
{
ErrorManager_CurrentChanged(sender, e);
}
}
private void SetError(Control control, string p)
{
if (String.IsNullOrEmpty(p))
{
if (originalColor.ContainsKey(control))
control.BackColor = originalColor[control];
toolTip.SetToolTip(control, null);
}
else
{
control.BackColor = ErrorBackgroundColor;
toolTip.SetToolTip(control, p);
}
}
private void ErrorManager_BindingComplete(object sender, BindingCompleteEventArgs e)
{
Binding binding = e.Binding;
if (binding != null && binding.Control != null)
{
SetError(binding.Control, (e.ErrorText == null ? String.Empty : e.ErrorText));
}
}
private void ErrorManager_CurrentChanged(object sender, EventArgs e)
{
if (errorManager.Count == 0)
{
return;
}
object value = errorManager.Current;
if (!(value is IDataErrorInfo))
{
return;
}
BindingsCollection errBindings = errorManager.Bindings;
int bindingsCount = errBindings.Count;
// We can only show one error per control, so we will build up a string...
//
Hashtable controlError = new Hashtable(bindingsCount);
for (int j = 0; j < bindingsCount; j++)
{
// Ignore everything but bindings to Controls
if (errBindings[j].Control == null)
{
continue;
}
string error = ((IDataErrorInfo)value)[errBindings[j].BindingMemberInfo.BindingField];
if (error == null)
{
error = "";
}
string outputError = "";
if (controlError.Contains(errBindings[j].Control))
outputError = (string)controlError[errBindings[j].Control];
// VSWhidbey 106890: Utilize the error string without including the field name.
if (String.IsNullOrEmpty(outputError))
{
outputError = error;
}
else
{
outputError = string.Concat(outputError, "\r\n", error);
}
controlError[errBindings[j].Control] = outputError;
}
IEnumerator enumerator = controlError.GetEnumerator();
while (enumerator.MoveNext())
{
DictionaryEntry entry = (DictionaryEntry)enumerator.Current;
SetError((Control)entry.Key, (string)entry.Value);
}
}
public void BeginInit()
{
initializing = true;
}
public void EndInit()
{
initializing = false;
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
}
}
New C# problem...
I'm supposed to write 6 separate methods to clear 6 groups of checkboxes (all at once) when I click the clear button. I know how to code it with individual checkboxes, but the problem asks for me to create a method and then call all 6 of them upon clicking the clear button. Help?
I have nothing associated with the clearButton_Click event yet.
private void ClearOilLube
{
set {oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;}
}
private void ClearFlushes
{
set {radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;}
}
private void ClearMisc
{
set {inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;}
}
private void ClearOther
{
set {partsCostInputTextBox.Text = null;
laborInputTextBox.Text = null;}
}
private void ClearFees
{
set {servicesLaborDispLabel.Text = null;
partsDispLabel.Text = null;
partsTaxDispLabel.Text = null;
totalFeesDispLabel.Text = null;}
}
When the clear button is clicked just call the methods you created above to clear the data.
public void clearButton_Click(Object sender, EventArgs e)
{
ClearOilLube();
ClearFlushes();
ClearMisc();
ClearOther();
ClearFees();
// May be required to be called to ensure the UI is up to date.
Update();
}
Edit
The syntax on your question is a little strange. They are written kind of like properties when they should be methods. No 'set' or 'get' is needed. Just make them regular helper methods.
This is just typed in no testing on a compiler or IDE but what I can see is fixed.
private void ClearOilLube()
{
oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;
}
private void ClearFlushes()
{
radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;
}
private void ClearMisc()
{
inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;
}
private void ClearOther()
{
partsCostInputTextBox.Text = "";
laborInputTextBox.Text = "";
}
private void ClearFees()
{
servicesLaborDispLabel.Text = "";
partsDispLabel.Text = "";
partsTaxDispLabel.Text = "";
totalFeesDispLabel.Text = "";
}
Your methods (or is that properties?) are written quite badly. I will just assume that you want to write methods not properties.
Method headers take the following form: (simplified)
[access modifier] {return value type} {method name} ([parameter list])
So your methods should look like this:
private void ClearOilLube ()
{
oilChangeCheckBox.Checked = false;
lubeJobCheckBox.Checked = false;
}
private void ClearFlushes ()
{
radiatorFlushCheckBox.Checked = false;
transFlushCheckBox.Checked = false;
}
private void ClearMisc ()
{
inspectionCheckBox.Checked = false;
replaceMufflerCheckBox.Checked = false;
tireRotationCheckBox.Checked = false;
}
private void ClearOther ()
{
partsCostInputTextBox.Text = null;
laborInputTextBox.Text = null;
}
private void ClearFees ()
{
servicesLaborDispLabel.Text = null;
partsDispLabel.Text = null;
partsTaxDispLabel.Text = null;
totalFeesDispLabel.Text = null;
}
And then, you can call these methods one by one in the onClick method.
However, a better approach is to loop through (or iterate) through the Controls of the form.
private void ClearButtonClick (object sender, EventArgs e) {
foreach (Control control in this.Controls) {
if (control is CheckBox) {
((CheckBox)control).Checked = false;
}
}
}
If you still don't understand, tell me in the comments!
To give you an alternative, though it is more complex:
private void GetAllControlsOfType<TControl>(TControl collection, List<TControl> container) where TControl : Control
{
foreach(Control control in collection)
{
if(control is TControl)
container.Add(control);
if(control.HasControls())
GetAllControlsOfType<TControl>(control.Controls, container);
}
}
Then to utilize, you would simply do:
var checkboxes = new List<Checkbox>();
GetAllControlsOfType<Checkbox>(Page.Controls, checkboxes);
After that is executed it will contain a List of all the checkbox controls. Then you could simply map them to the state of the object or you can simply do the following:
foreach(var checkbox in checkboxes)
checkbox.Checked = false;
That would uncheck all, keep in mind this hasn't been tested so it may need some fine tuning.
I'm using PropertyGrid to edit an object containing a collection.
Collection is edited using the CollectionEditor.
I have to make sure elements in collection are unique.
How can I add validation to CollectionEditor:
By either overloading CollectionEditor's OnFormClosing
Or adding validation for creating/editing items?
You can create your own collection editor, and hook into events on the default editor's controls. You can use these events to, say, disable the OK button. Something like:
public class MyCollectionEditor : CollectionEditor
{
private static Dictionary<CollectionForm, Button> okayButtons
= new Dictionary<CollectionForm, Button>();
// Inherit the default constructor from CollectionEditor
public MyCollectionEditor(Type type)
: base(type)
{
}
// Override this method in order to access the containing user controls
// from the default Collection Editor form or to add new ones...
protected override CollectionForm CreateCollectionForm()
{
CollectionForm collectionForm = base.CreateCollectionForm();
collectionForm.FormClosed +=
new FormClosedEventHandler(collectionForm_FormClosed);
collectionForm.Load += new EventHandler(collectionForm_Load);
if (collectionForm.Controls.Count > 0)
{
TableLayoutPanel mainPanel = collectionForm.Controls[0]
as TableLayoutPanel;
if ((mainPanel != null) && (mainPanel.Controls.Count > 7))
{
// Get a reference to the inner PropertyGrid and hook
// an event handler to it.
PropertyGrid propertyGrid = mainPanel.Controls[5]
as PropertyGrid;
if (propertyGrid != null)
{
propertyGrid.PropertyValueChanged +=
new PropertyValueChangedEventHandler(
propertyGrid_PropertyValueChanged);
}
// Also hook to the Add/Remove
TableLayoutPanel buttonPanel = mainPanel.Controls[1]
as TableLayoutPanel;
if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
{
Button addButton = buttonPanel.Controls[0] as Button;
if (addButton != null)
{
addButton.Click += new EventHandler(addButton_Click);
}
Button removeButton = buttonPanel.Controls[1] as Button;
if (removeButton != null)
{
removeButton.Click +=
new EventHandler(removeButton_Click);
}
}
// Find the OK button, and hold onto it.
buttonPanel = mainPanel.Controls[6] as TableLayoutPanel;
if ((buttonPanel != null) && (buttonPanel.Controls.Count > 1))
{
Button okayButton = buttonPanel.Controls[0] as Button;
if (okayButton != null)
{
okayButtons[collectionForm] = okayButton;
}
}
}
}
return collectionForm;
}
private static void collectionForm_FormClosed(object sender,
FormClosedEventArgs e)
{
CollectionForm collectionForm = (CollectionForm)sender;
if (okayButtons.ContainsKey(collectionForm))
{
okayButtons.Remove(collectionForm);
}
}
private static void collectionForm_Load(object sender, EventArgs e)
{
ValidateEditValue((CollectionForm)sender);
}
private static void propertyGrid_PropertyValueChanged(object sender,
PropertyValueChangedEventArgs e)
{
ValidateEditValue((CollectionForm)sender);
}
private static void addButton_Click(object sender, EventArgs e)
{
Button addButton = (Button)sender;
ValidateEditValue((CollectionForm)addButton.Parent.Parent.Parent);
}
private static void removeButton_Click(object sender, EventArgs e)
{
Button removeButton = (Button)sender;
ValidateEditValue((CollectionForm)removeButton.Parent.Parent.Parent);
}
private static void ValidateEditValue(CollectionForm collectionForm)
{
if (okayButtons.ContainsKey(collectionForm))
{
Button okayButton = okayButtons[collectionForm];
IList<MyClass> items = collectionForm.EditValue as IList<MyClass>;
okayButton.Enabled = MyCollectionIsValid(items);
}
}
private static bool MyCollectionIsValid(IList<MyClass> items)
{
// Perform validation here.
return (items.Count == 2);
}
}
You will also need to add an Editor attribute to you collection:
class MyClass
{
[Editor(typeof(MyCollectionEditor),
typeof(System.Drawing.Design.UITypeEditor))]
List<Foo> MyCollection
{
get; set;
}
}
NOTE: I found that the value of items in removeButton_Click was not correct - so some tweaking may need to take place.
Try collectionForm.Context.Instance and typecast it to your data type this should do the trick.