Extension Method Solution - c#

I've got some code to indicate when a field on my WinForm has changed.
If the Control is a TextBox, I alter the ForeColor and BackColor depending on if the Text of the Control is equal to the default (or initial) value.
private void LastName_Changed(object sender, EventArgs e) {
if (sender.Equals(txtLastName)) {
if (emp0.LastName != txtLastName.Text) {
txtLastName.ForeColor = changedTxtFont;
txtLastName.BackColor = changedTxtBack;
btnOK.Enabled = true;
} else {
txtLastName.ForeColor = systemTxtFont;
txtLastName.BackColor = systemTxtBack;
}
AuthenticationReset();
}
}
If the Control is a DateTimePicker or ComboBox, I can't really do this (there is no visible ForeColor or BackColor), so I alter the Label that is associated with the Control.
private void TrainDate_Changed(object sender, EventArgs e) {
if (sender.Equals(dtTrainDate)) {
DateTime trainDate = Global.SqlDate(emp0.TrainDate);
if (trainDate != dtTrainDate.Value) {
lblTrainDate.ForeColor = changedLblFont;
lblTrainDate.BackColor = changedLblBack;
btnOK.Enabled = true;
} else {
lblTrainDate.ForeColor = systemLblFont;
lblTrainDate.BackColor = systemLblBack;
}
AuthenticationReset();
}
}
I'd like to create some sort of Extension Method that could change the ForeColor and BackColor of a Control dependent upon what that Control is and if the value had changed.
Here is an attempt, but the use of this in the code is not valid and the extension methods do not show up for any of the Controls I want to use them on.
public static class ColorChange {
public static Color ForeColorChange(this Color owned, bool changed) {
if (this is TextBox) {
return changed ? Color.Red : SystemColors.WindowText;
} else {
return changed ? Color.Red : SystemColors.ControlText;
}
}
public static Color BackColorChange(this Color owned, bool changed) {
if (this is TextBox) {
return changed ? Color.Yellow : SystemColors.Window;
} else {
return SystemColors.Control;
}
}
}
Is there a way to approach what I'm trying to do or is this not what Extension Methods are for?
I'd like to end up with something like this:
private void TrainDate_Changed(object sender, EventArgs e) {
if (sender.Equals(dtTrainDate)) {
DateTime trainDate = Global.SqlDate(emp0.TrainDate);
if (trainDate != dtTrainDate.Value) {
lblTrainDate.ForeColorChange(true);
lblTrainDate.BackColorChange(true);
btnOK.Enabled = true;
} else {
lblTrainDate.ForeColorChange(false);
lblTrainDate.BackColorChange(false);
}
AuthenticationReset();
}
}
Solution:
Using the answer checked below, I wrote the following Extension Method:
public static class ColorChange {
public static void AlteredText(this Control owned, bool changed) {
if (owned is TextBox) {
owned.ForeColor = changed ? Color.Red : SystemColors.WindowText;
owned.BackColor = changed ? Color.Yellow : SystemColors.Window;
} else {
owned.ForeColor = changed ? Color.Red : SystemColors.ControlText;
owned.BackColor = SystemColors.Control;
}
}
}
Here is how I will be using it:
private void TrainDate_Changed(object sender, EventArgs e) {
if (sender.Equals(dtTrainDate)) {
DateTime trainDate = Global.SqlDate(emp0.TrainDate);
if (trainDate != dtTrainDate.Value) {
lblTrainDate.AlteredText(true);
btnOK.Enabled = true;
} else {
lblTrainDate.AlteredText(false);
}
AuthenticationReset();
}
}
I hope others get use out of this as well. ~Joe.

You've defined extension methods for the Color class, not Control, which is why it won;t show for controls. In addition, your methods probably want to inspect the owned control that you'll need to pass in.

This extends Control instead of Color (needs to be defined in a static class):
public static void ForeColorChange(this Control owned, bool changed) {
if (owned is TextBox) {
owned.ForeColor = changed ? Color.Red : SystemColors.WindowText;
} else {
owned.ForeColor = changed ? Color.Red : SystemColors.ControlText;
}
}
public static void BackColorChange(this Control owned, bool changed) {
if (owned is TextBox) {
owned.BackColor = changed ? Color.Yellow : SystemColors.Window;
} else {
owned.BackColor = SystemColors.Control;
}
}
Usage:
TextBox box = ...;
box.ForeColorChange(true);

If you want to call your extension method on your textbox, you have to do this in the toplevel of your namespace:
public static class Extensions
{
public static void myExtensionMethod(this Textbox textbox)
{
//Do what you want to do
}
}
And then it is accessible over your textbox:
Textbox myTextbox = new Textbox();
myTextbox.myExtensionMethod();
If you want, you can also pass parameters to your extension method.

Use your owned instead of this.. oh and as Rowland Shaw says, Control instead of Color.
So your code should finally be
public static Color ForeColorChange(this Control owned, bool changed) {
if (owned is TextBox) {
return changed ? Color.Red : SystemColors.WindowText;
} else {
return changed ? Color.Red : SystemColors.ControlText;
}
}
public static Color BackColorChange(this Control owned, bool changed) {
if (owned is TextBox) {
return changed ? Color.Yellow : SystemColors.Window;
} else {
return SystemColors.Control;
}
}

Related

Is there any reason not to create a class or struct to resolve a 'non boolean' boolean

I am just a hobby programmer but I come across may cases where I want to switch a type value (e.g. label.backgroundcolor) depending on whether it has been clicked or not, or whether the mouse is over or not. This is normally a trivial change but there has to be code for the event in each case and this then involves passing information such as default colour, mouse-over colour or default fontstyle, mouse-over fontstyle or any of many other types of 'switch'. In all cases it is simply a case of toggling the change between one value and another but, because this could potentially happen over several different types (Labels, Textboxes, Panels etc.) I find I have to code for each type separately.
Is there any good reason why I shouldn't just do this
class AnyObjectBoolean
{
private object objOne;
private object objTwo;
public AnyObjectBoolean(object oneValue, object twoValue)
{
objOne = oneValue;
objTwo = twoValue;
}
public object invert(Object val)
{
if (val.ToString() == objOne.ToString())
{
return objTwo;
}
else
{
return objOne;
}
}
I then create a new instance for each object and style I want to change and the resulting event code becomes (for instance)
private void Label_MouseClick(object sender, EventArgs e)
{
var Label = (Label)sender;
Label.BackColor = (Color)SelectColours.invert(Label.BackColor);
}
where SelectColours is an instance of AnyObjectBoolean.
Maybe not a great question but I ask because I've never found anything like this implemented anywhere.
Disclaimer:- this is my first post so I may not have tagged entirely appropriately or completely.
First of all welcome to StackOverflow.
Personally, I'd prefer to handle this through a catch-all function, in which I would deal with all the types of objects that I wanted. Something like:
private Color ToggleColor(object sender, Color currentColor)
{
Color labelBackColor = Color.White;
Color labelHoverColor = Color.Yellow;
Color textBackColor = Color.Wheat;
Color textHoverColor = Color.Turquoise;
Color defaultBackColor = Color.Tomato;
Color defaultHoverColor = Color.SteelBlue;
Label l = sender as Label;
if (l != null)
{
return currentColor == labelBackColor ? labelHoverColor : labelBackColor;
}
TextBox t = sender as TextBox;
if (t != null)
{
return currentColor == textBackColor ? textHoverColor : textBackColor;
}
return currentColor == defaultBackColor ? defaultHoverColor : defaultBackColor;
}
Doing it this way, I keep the entire color scheme in one place. BTW don't use my suggested colors! Note the use of "as". It does the same as cast, with the important difference that it does not throw an Exception if it fails, it simply returns null. Therefore, you can simply try each object in turn safely until you get a hit.
there is no need for an instance, as i understand your program static class will be more appropriate here and you can change it from outside whenever you need.
use one method that will add events to all controls (you can add click event to all control types not just a label).
I think you want to do something like that:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MyColors.firstColor = Color.Blue;
MyColors.secondColor = Color.Yellow;
// add events to all requested controls
AddEvent(new Control[] {
label1,
label2,
label3,
});
}
public void AddEvent(Control[] myControls)
{
foreach (Control c in myControls)
{
c.MouseClick += Control_Click;
}
}
private void Control_Click(object sender, EventArgs e)
{
((Control)sender).BackColor = MyColors.GetColor(((Control)sender).BackColor);
}
}
static class MyColors
{
public static Color firstColor { get; set; } = Color.Black;
public static Color secondColor { get; set; } = Color.White;
public static Color GetColor(Color color)
{
if (color == firstColor)
{
return secondColor;
}
else
{
return firstColor;
}
}
}
Most of the controls such as TextBox, Label and many other controls inherit from the Control class. The property BackColor is a property of the Control class; therefore, you can write a method or a class that takes a Control type and changes its color. Below is a class which takes 2 colors in its constructors and then inverts the passed in Control instance from one color to another.
public class ColorInverter
{
public Color Color1 { get; private set; }
public Color Color2 { get; private set; }
public ColorInverter(Color color1, Color color2)
{
this.Color1 = color1;
this.Color2 = color2;
}
public void Invert(Control control)
{
if (control.BackColor == this.Color1)
{
control.BackColor = this.Color2;
return;
}
control.BackColor = Color1;
}
}
Usage:
ColorInverter c = new ColorInverter(Color.Black, Color.Red);
TextBox box = new TextBox();
c.Invert(box);
Here is the inheritance hierarchy for Label. You can look for other controls there as well.
You may even check if the control inherits Control before calling the Invert method:
if (box is Control)
{
c.Invert(box);
}
else
{
// ...do something
}
You can use access any properties such as FontSize, FontFamily and many other Control properties as well. Obviously change the class name from ColorInverter to something else if you want to change the FontSize etc. as well.

Setting default size/text of custom control in c#

I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!

How to create numeric Textbox Custom Control with dependency Property in WPF?

I want create a custom control for Numeric Text box with dependency property in WPF , in my solution , I add one WPF application and custom control (WPF) ,then in public class , I create dependency property ....
Now I don't know how can i write my rule for text box and which event is true?
Another question : What is my rule for numeric text box , that this text box must be give number and . and Separating .this custom Text box is for accounting system.
public static readonly DependencyProperty NumberTextbox;
static Numeric()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Numeric), new FrameworkPropertyMetadata(typeof(Numeric)));
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata("Enter Your Text", OnKeyDown);
NumberTextbox =DependencyProperty.Register("Text", typeof(TextBox), typeof(FrameworkElement), metadata);
}
public string NumberTXT
{
get { return (string)GetValue(NumberTextbox); }
set { SetValue(NumberTextbox, value); }
}
I recommend to you add another Dependency Property in example code below I named it Value
Also format your number by comma or NumberFormatInfo.CurrentInfo.NumberDecimalSeparator
and control caret location changes by two property SelectionLength and SelectionStart.
Finally for more detail and complete code WPF Maskable Number Entry TextBox
region Value property
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(double), typeof(NumericTextBox), new PropertyMetadata(new Double(), OnValueChanged));
private static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
//var numericBoxControl = (NumericTextBox)sender;
}
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); Text = value.ToString("###,###,###"); }
}
endregion
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
var txt = e.Text.Replace(",", "");
e.Handled = !IsTextAllowed(txt);
if (IsTextAllowed(txt))
{
if (Text.Length == 3)
{
Text = Text.Insert(1,",");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.Key == Key.Back)
{
if (Text.Length == 5)
{
Text = Text.Replace(",", "");
SelectionLength = 1;
SelectionStart += Text.Length;
}
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
var txt = Text.Replace(",", "");
SetValue(ValueProperty, txt.Length==0?0:double.Parse(txt));
base.OnTextChanged(e);
}
private static bool IsTextAllowed(string text)
{
try
{
double.Parse(text);
return true;
}
catch (FormatException)
{
return false;
}
}
I don't understand your question exactly and why you need dependency proerties to make a numeric text box custom control. What you can do is to inherit from textbox and handle the PreviewTextInput, like it is solved in this question by Ray:
then you get:
public class NumericTextBox : TextBox
{
public NumericTextBox()
{
PreviewTextInput += NumericTextBox_PreviewTextInput;
}
void NumericTextBox_PreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e)
{
e.Handled = !isTextAllowed(e.Text);
}
private static bool isTextAllowed(string text)
{
var regex = new Regex("[^0-9]+");
return !regex.IsMatch(text);
}
}
And you can use it like that:
<myNameSpace:NumericTextBox />
And now you can add any other validation you want.
I would also implement a solution for the pasting issue, something like (see also in the link):
private void textBoxPasting(object sender, DataObjectPastingEventArgs e)
{
if (e.DataObject.GetDataPresent(typeof(String)))
{
var text = (String)e.DataObject.GetData(typeof(String));
if (!isTextAllowed(text))
{
e.CancelCommand();
}
}
else
{
e.CancelCommand();
}
}
Good job, but let me do the following task with a UserControl in C #:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace NumericBox
{
public partial class NumericBox : TextBox
{
public NumericBox
{
this.TextAlign = HorizontalAlignment.Right;
this.Text = "0,00";
this.KeyPress += NumericBox_KeyPress;
}
public double NumericResult
{
get{
double d = Convert.ToDouble(this.Text.Replace('.', ','));
return d;
}
}
private void NumericBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && (e.KeyChar != '.'))
e.Handled = true;
if ((e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1))
e.Handled = true;
if (e.KeyChar == 13)
{
e.Handled = true;
SendKeys.Send("{TAB}");
}
}
}
}

Implementing a PasswordChar property to my custom C# Windows Form TextBox class that supports placeholders

I'm trying to implement a PasswordChar property to my TextBox class that cooperates with my Placeholder class. This is my code:
class MyTextBox : TextBox
{
public Placeholder Placeholder;
public MyTextBox()
{
Placeholder = new Placeholder(this);
GotFocus += new EventHandler(_GotFocus);
LostFocus += new EventHandler(_LostFocus);
}
private void _GotFocus(object sender, EventArgs e)
{
Placeholder.Active = false;
}
private void _LostFocus(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(Text))
Placeholder.Active = true;
}
}
class Placeholder
{
private MyTextBox textBox;
private bool active = false;
public bool Active
{
get { return active; }
set
{
if (value)
{
textBox.ForeColor = ForeColor;
textBox.Text = Text;
}
else if (active && !value)
{
textBox.ForeColor = previousForeColor;
textBox.Text = string.Empty;
}
active = value;
}
}
private Color previousForeColor;
private Color foreColor = Color.Gray;
public Color ForeColor
{
get { return foreColor; }
set
{
previousForeColor = ForeColor;
ForeColor = value;
}
}
private string text = "Placeholder";
public string Text
{
get { return text; }
set
{
text = value;
if (active)
textBox.Text = Text;
}
}
public Placeholder(MyTextBox textBox)
{
this.textBox = textBox;
}
}
The way I want it to work is if I set the TextBox's property PasswordChar to anything but '\0' then it should be set to '\0' when the TextBox's property's Placeholder's property Active is true, and should be set back to whatever the PasswordChar was when it's set to false.
In other words, if PasswordChar is set then unmask the text when the Active property is true, and remask the text when it's set back to false.
It sounds as simple as storing the original PasswordChar in a temporary variable:
class Placeholder
{
private MyTextBox textBox;
private bool active = false;
private char? originalPasswordChar = null;
public bool Active
{
get { return active; }
set
{
if (value)
{
textBox.ForeColor = ForeColor;
textBox.Text = Text;
if (originalPasswordChar == null) originalPasswordChar = textBox.PasswordChar;
}
else if (active && !value)
{
textBox.ForeColor = previousForeColor;
textBox.Text = string.Empty;
textBox.PasswordChar = originalPasswordChar.Value;
originalPasswordChar = null;
}
active = value;
}
}
// (...)
}

How to redraw Node in TreeView (WinForms)

I need to implement own TreeView with blinked TreeNode. My prototype is:
public class BlinkTreeView : TreeView
{
private int blinkInterval;
private bool blinkState;
[Category("Behavior"), Browsable(true)]
public Icon BlinkIcon { get; set; }
[Category("Behavior"), Browsable(true)]
public Icon SelectedBlinkIcon { get; set; }
[Category("Behavior"), Browsable(true), DefaultValue(1000)]
public int BlinkInterval {
get
{
return blinkInterval;
}
set
{
blinkInterval = value;
if (value > 0)
{
blinkTimer.Interval = value;
blinkTimer.Start();
}
else
{
blinkTimer.Stop();
blinkState = false;
Invalidate();
}
}
}
private Timer blinkTimer;
public BlinkTreeView()
: base()
{
blinkTimer = new Timer();
blinkTimer.Tick += new EventHandler(blinkTimer_Tick);
blinkState = false;
this.DrawMode = TreeViewDrawMode.OwnerDrawAll;
}
void blinkTimer_Tick(object sender, EventArgs e)
{
if (BlinkInterval > 0)
{
blinkState = !blinkState;
}
else
{
blinkState = false;
}
Invalidate();
}
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
e.DrawDefault = true;
base.OnDrawNode(e);
if (blinkState)
{
//here i want to draw blinked item, but i can't redraw item icons and text.
}
}
}
In OnDrawNode i can't redraw icon and text of node.
Any idea how to solve this?
Just a thought, but you could invert (xor) over the item without making the tree into an owner-draw control. I think it works something like the following:
using (Graphics g = Graphics.FromHwnd(Tree.Handle))
{
TreeNode node = myBlinkyNode;
if (node != null)
{
using(Region myRegion = new Region(node.Bounds))
myRegion.Xor(xorRect);
}
}
You'll need to keep track if the blink is visible or not and handle the Paint event so that you can re-draw the inverted rectangle.
Have a timer toggle the state of the blinking nodes, i.e.:
Node.ForeColor = Node.ForeColor == Color.White ? Color.Black : Color.White;

Categories