I have a TreeView with checkboxes like
I have added some code to show what is selected:
/// <summary>
/// Highlight checked nodes
/// </summary>
private void HighlightCheckedNodes()
{
foreach (TreeNode topNode in treeView1.Nodes)
{
if (topNode.Checked)
{
topNode.BackColor = Color.Yellow;
}
else
{
topNode.BackColor = Color.White;
}
foreach (TreeNode myNode in topNode.Nodes)
{
// Check whether the tree node is checked.
if (myNode.Checked)
{
// Set the node's backColor.
myNode.BackColor = Color.Yellow;
}
else
{
myNode.BackColor = Color.White;
}
}
}
}
This function is called from treeView1_AfterSelect and treeView1_AfterCheck.
If I click a few times I get some nodes that are marked as checked but not yellow (the data says it's not checked) or vice versa.
So how do I make sure the data and display are in sync?
Long story short, the TreeView is buggy!
Here's an implementation that uses NativeWindow instead of deriving from TreeView. This will allow you to use the existing TreeView on your Form:
public partial class Form1 : Form
{
private TreeViewSuppressDoubleClick treeViewHelper;
private void Form1_Load(object sender, EventArgs e)
{
treeViewHelper = new TreeViewSuppressDoubleClick(this.treeView1);
}
public class TreeViewSuppressDoubleClick : NativeWindow
{
public TreeViewSuppressDoubleClick(TreeView treeView)
{
if (treeView != null && treeView.IsHandleCreated)
{
this.AssignHandle(treeView.Handle);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg != 0x203)
base.WndProc(ref m);
}
}
private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
{
e.Node.BackColor = e.Node.Checked ? Color.Yellow : Color.White;
}
}
After this you'll notice that the TreeView isn't as responsive to clicks, but it does stay in sync now.
Note the simplified method for changing the BackColor on a check/uncheck.
Also, here's an alternate HighlightCheckedNodes() using recursion to get all the nodes:
private void HighlightCheckedNodes(TreeNode node)
{
if (node == null)
{
foreach (TreeNode topNode in treeView1.Nodes)
{
HighlightCheckedNodes(topNode);
}
}
else
{
node.BackColor = node.Checked ? Color.Yellow : Color.White;
foreach (TreeNode curNode in node.Nodes)
{
HighlightCheckedNodes(curNode);
}
}
}
You'd call it using HighlightCheckedNodes(null);.
I am currently writing a program that when a button is clicked on a final UserControlit clears all the TextBox and ComboBox in all UserControls within the program.
I have created a method within my OverviewControl that I am trying to call on a button that would clear all the Textbox's and ComboBox's within the GeneralControl and StatsControl classes.
MainForm.cs
public MainForm()
{
overviewControl1.GeneralControl = generalControl1;
overviewControl1.StatsControl = statsControl1;
}
OverviewControl.cs
public partial class OverviewControl : UserControl
{
public GeneralControl GeneralControl { get; set; }
public StatsControl StatsControl { get; set; }
private void GeneralSaveButton_Click(object sender, EventArgs e)
{
ClearItems();
}
private void ClearItems()
{
foreach (Control c in GeneralControl?.Controls)
{
if (c is TextBox tb)
tb.Clear();
else if (c is ComboBox cb)
cb.SelectedIndex = -1;
}
foreach (Control c in StatsControl?.Controls)
{
if (c is TextBox tb)
tb.Clear();
else if (c is ComboBox cb)
cb.SelectedIndex = -1;
}
}
}
I am expecting that when I click my button, it calls the GeneralSaveButton_Click() method and it clears all fields from the GeneralControl and StatsControl but all fields remain there after calling the method.
Any help getting pointed in the right direction would be helpful.
I have a method that changes the text of the controls on my site.
These changes should be visible right when the user loads the page.
But the changes are first visible after the next postback.
I tried to call it in Page_PreInit, Page_Init, Page_PreLoad and and all the other methods described here.
But none of them worked.
Some code:
The class with the methods: (partly)
namespace MyNamespace {
public class ControlTextCorrection {
Page _page;
public ControlTextCorrection(Page page) {
_page = page;
}
public void Correct() {
HtmlEncodeControls(_page);
}
private void HtmlEncodeControls(Page page) {
Control control = (Control)page;
HtmlEncodeControls(control);
}
private void HtmlEncodeControls(Control parentControl) {
if (!parentControl.HasControls()) {
return;
}
foreach (Control control in parentControl.Controls) {
if (control.HasControls()) {
HtmlEncodeControls(control);
}
if (control is Label) {
Label label = (Label)control;
label.Text = HtmlTextCorrection(label.Text);
}
else if (control is CheckBox) {
CheckBox checkBox = (CheckBox)control;
checkBox.Text = HtmlTextCorrection(checkBox.Text);
}
//Correction for more controls...
}
}
protected string HtmlTextCorrection(string text) {
bool encode = true;
while (encode) {
string newText = _page.Server.HtmlDecode(text);
if (newText == text) {
encode = false;
}
text = newText;
}
text = _page.Server.HtmlEncode(text);
return text;
}
}
}
Example for calling the method:
protected void Page_PreLoad(object sender, EventArgs e) {
ControlTextCorrection correction = new ControlTextCorrection(this.Page);
correction.Correct();
}
So, where (when) should i call it so that the changes are visible at the first sight of the site?
This is probably a long shot, but I'm trying to minimize the repition in the program I'm working on, and have run into a snag. As can be seen in the ClearTextBoxes() method below, I have a very repetitive bit of code that I would prefer to place inside a foreach loop for succinctness. (Originally the foreach (object box in customBoxes) loop was not there). I tried to do this with the following List, but to no avail. I'm not sure if this is just not possible to do, or if I'm simply doing it wrong. I would appreciate any help you could give, and if this can't be done, then how can I shrink this code block?
Thanks!
List<object> customBoxes = new List<object>();
customBoxes.AddRange(new[] { "TextBox", "DateBox", "DigitBox", "PhoneBox", "WaterTextBox" });
public void ClearTextBoxes()
{
ChildControls ccChildren = new ChildControls();
foreach (object o in ccChildren.GetChildren(rvraDockPanel, 2))
{
foreach (object box in customBoxes)
{
if (o.GetType() == typeof(TextBox))
{
TextBox txt = (TextBox)o;
txt.Text = "";
}
if (o.GetType() == typeof(DigitBox))
{
DigitBox digit = (DigitBox)o;
digit.Text = "";
}
if (o.GetType() == typeof(PhoneBox))
{
PhoneBox phone = (PhoneBox)o;
phone.Text = "";
}
if (o.GetType() == typeof(DateBox))
{
DateBox date = (DateBox)o;
date.Text = "";
}
if (o.GetType() == typeof(WatermarkTextBox))
{
WatermarkTextBox water = (WatermarkTextBox)o;
water.Text = "";
}
}
}
}
List<Type> customBoxes = new List<Type>();
customBoxes.AddRange(new[] { typeof(PhoneBox), typeof(DigitBox), ....." });
foreach (Control c in this.Controls)
{
if (customBoxes.Contains(c.GetType()))
{
c.Text = string.Empty;
}
}
I would create an interface with a ClearText() method.
interface IClearable
{
public void ClearText();
}
Then you can inherit from each control and apply that interface:
class ClearableDigitBox : DigitBox, IClearable
{
public void ClearText() {
Text = String.Empty;
}
}
// etc...
So it's just:
var list = new List<IClearable>;
// ...
foreach (IClearable control in list) control.ClearText();
You could use reflection in some way to mimic some ductyping behavior but i wouldnt go for that solution since it's not performant and ugly.
foreach (object box in customBoxes)
{
var boxType = box.GetType();
var textProperty = boxType.GetProperty("Text");
if (textProperty != null && textProperty.CanWrite)
{
textProperty.SetValue(box, "", null);
}
}
Or you can use dynamic to achieve the same result:
foreach (dynamic box in customBoxes)
{
box.Text = "";
}
The way to go would be to make your custom controls implement a single interface IWithTextProperty which ofcourse exposes the text property.
Aren't all the input boxes a part of Control object?
if so, and you want to clear all the text from the controls
then i would probably have a method like:
public void ClearText(List<Control> items)
{
foreach (Control control in items)
{
control.Text = string.Empty;
}
}
if you just want to locate controls of a specific type
public void ClearText(List<Control> items)
{
foreach (Control control in items)
{
if (control is TextBox)
((TextBox)control).Text = string.Empty;
else if (control is DigitBox)
((DigitBox)control).Text = string.Empty;
else
{ // Handle anything else.}
}
}
In response to a couple of the replies so far, this is the class file I have for the custom boxes. The NumberTextBox class is the default snippet that VS added. I haven't used it, just haven't deleted it either. In addition to the DateBox(which is collapsed to save space) class, there is also a PhoneBox class that inherits from DigitBox as well. the WatermarkTextBox class that DigitBox inherits from is in the WpfToolkit.Extended.dll. The only real difference in these classes is that each adds a method to allow/disallow formatting keys being pressed (parenthesis, periods, hyphens, etc).
This class basically came about as a result of trying to merge several different snippets I found around the web, but the purpose of these boxes is to enable a watermark and also restrict the characters that can be entered into those boxes.
public class NumberTextBox : Control
{
static NumberTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumberTextBox), new FrameworkPropertyMetadata(typeof(NumberTextBox)));
}
}
public class DigitBox : WatermarkTextBox, IClearable
{
#region Constructors
///<summary>
///The default constructor
/// </summary>
public DigitBox()
{
TextChanged += new TextChangedEventHandler(OnTextChanged);
KeyDown += new KeyEventHandler(OnKeyDown);
PreviewKeyDown += new KeyEventHandler(OnPreviewDown);
}
#endregion
#region Properties
new public String Text
{
get { return base.Text; }
set
{
base.Text = LeaveOnlyNumbers(value);
}
}
#endregion
#region Functions
public bool IsNumberKey(Key inKey)
{
if (inKey < Key.D0 || inKey > Key.D9)
{
if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
{
return false;
}
}
return true;
}
public bool IsActionKey(Key inKey)
{
return inKey == Key.Delete || inKey == Key.Back || inKey == Key.Tab || inKey == Key.Return;
}
public string LeaveOnlyNumbers(String inString)
{
String tmp = inString;
foreach (char c in inString.ToCharArray())
{
if (!IsDigit(c))
{
tmp = tmp.Replace(c.ToString(), "");
}
}
return tmp;
}
public bool IsSpaceKey(Key inKey)
{
if (inKey == Key.Space)
{
return true;
}
return false;
}
public bool IsDigit(char c)
{
return (c >= '0' || c <='9');
}
#endregion
#region Event Functions
protected virtual void OnKeyDown(object sender, KeyEventArgs e)
{
e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key) && !IsSpaceKey(e.Key);
}
protected virtual void OnTextChanged(object sender, TextChangedEventArgs e)
{
base.Text = LeaveOnlyNumbers(Text);
}
protected virtual void OnPreviewDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
e.Handled = true;
}
}
#endregion
}
public class DateBox : DigitBox
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.