I have this code:
public static void AddDefaultTextFromTag(params TextBox[] textBoxes)
{
foreach (TextBox oTextBox in textBoxes)
{
bool isPasswordChar = oTextBox.UseSystemPasswordChar;
oTextBox.Enter += (sndr, evnt) =>
{
if (((TextBox)sndr).Text == ((TextBox)sndr).Tag.ToString())
{
((TextBox)sndr).Text = "";
((TextBox)sndr).UseSystemPasswordChar = isPasswordChar;
((TextBox)sndr).ForeColor = SystemColors.WindowText;
}
};
oTextBox.Leave += (sndr, evnt) =>
{
if (((TextBox)sndr).Text.Trim().Count() == 0)
{
((TextBox)sndr).UseSystemPasswordChar = false;
((TextBox)sndr).CharacterCasing = CharacterCasing.Normal;
((TextBox)sndr).Text = ((TextBox)sndr).Tag.ToString();
((TextBox)sndr).ForeColor = SystemColors.GrayText;
}
};
if (oTextBox.Text.Trim().Count() == 0)
{
oTextBox.UseSystemPasswordChar = false;
oTextBox.CharacterCasing = CharacterCasing.Normal;
oTextBox.Text = oTextBox.Tag.ToString();
oTextBox.ForeColor = SystemColors.GrayText;
}
}
}
But when the TextBox.UseSystemPasswordChar I input in this method's parameter is true and it's TextBox.Text property is empty, the TextBox can't leave using a Tab button on the keyboard, only a MouseClick can be used to lose the focus of that TextBox.
Why is this happening?
My code is in C#, framework 4, build in VS2010 Pro, project is in WinForms.
I use a TextBox from the VS ToolBox.
Please help. Thanks in advance.
The reason you can't leave the textbox is because you are changing the CharacterCasing property in the textbox.
Not sure why it works like this, but it has happened to me before, what I ended up doing was capture the keypress event, and if it was a letter I'd switch it to it's uppercase value. It's not optimal, but it works
I did something similar to this (writing it from the top of my head, but it should work):
void YourTextbox_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsLetter(e.KeyChar))
{
if (this.CharacterCasing == CharacterCasing.Upper && char.IsLower(e.KeyChar))
{
this.Text = this.Text.Insert(this.SelectionStart, char.ToUpper(e.KeyChar) + string.Empty);
this.SelectionStart++;
e.Handled = true;
}
else if (this.CharacterCasing == System.Windows.Forms.CharacterCasing.Lower && char.IsUpper(e.KeyChar))
{
this.Text = this.Text.Insert(this.SelectionStart, char.ToLower(e.KeyChar) + string.Empty);
this.SelectionStart++;
e.Handled = true;
}
}
}
You also should use the new keyword to "override" (I know that's not the right term here) the Character casing, so it doesn't do it's own thing
public new CharacterCasing CharacterCasing { get; set; }
The code basically checks if the pressed key is a letter, then, if it's marked as Upper, and the char is lower, replaces it with it's upper version (in the position of the cursor) then moves the cursor to the next part, and Viceversa (toLower)
NOTE:
This code will have may (should) have some trouble if the user has more than one character selected (SelectionLenght > 0), if you want to keep the normal Textbox functionality, you should delete all the selected characters
So I set up a WinForms app, drew two textboxes, set one to UseSystemPasswordChar=true then set it up like so:
private void Form1_Load(object sender, EventArgs e)
{
textBox2.Tag = "test2";
textBox1.Tag = "test1";
TextBox[] tb = { textBox1, textBox2 };
AddDefaultTextFromTag(tb);
}
Your function works fine and I have no problems tabbing through the controls on the form no matter what the textboxes contain. (added a button also that does nothing for tabbing test) so... no repro unless my test setup is not valid
What I found in the answer of this post was the solution for me. Instead of setting UseSystemPasswordChar to true and then to false, you can set PasswordChar to '●' and then to '\0' to have normal text. You should not set the UseSystemPasswordChar because it has precedence over PasswordChar.
Related
I'm writing a Visual Studio Extension to customize my editor. I would like to change the text that is displayed when a block of code is collapsed.
I have tried the following code :
ITagAggregator<IntraTextAdornmentTag> aggregator;
[...]
aggregator.BatchedTagsChanged += OnBatchedTagsChanged;
[...]
public void OnBatchedTagsChanged(object sender, BatchedTagsChangedEventArgs e)
{
string newText;
bool textCreated;
NormalizedSnapshotSpanCollection list = new NormalizedSnapshotSpanCollection(e.Spans.SelectMany(x => x.GetSpans(textView.TextBuffer)));
if (list.Count != 0)
{
IEnumerable<IMappingTagSpan<IntraTextAdornmentTag>> tags = aggregator.GetTags(list);
foreach (IMappingTagSpan<IntraTextAdornmentTag> tag in tags)
{
if (tag.Tag.Adornment is OutliningCollapsedAdornmentControl)
{
NormalizedSnapshotSpanCollection spans = tag.Span.GetSpans(textView.TextSnapshot);
if (spans.Count == 0) continue;
OutliningCollapsedAdornmentControl adornmentControl = (OutliningCollapsedAdornmentControl)tag.Tag.Adornment;
TextBlock textBlock = adornmentControl.GetChild<TextBlock>();
textCreated = TryCreateText(spans[0], out newText);
if (textCreated)
{
adornmentControl.Content = newText;
textBlock.Text = newText;
}
}
}
}
}
I does change the text, but when it's scrolled out of the the screen and back in, the text reverts back to the default value.
Edit :
I also tried MSDN's walkthrough.
I works fine if I collapse blocks by clicking on the "+" sign, but the blocks don't collapse when I use Ctrl+M+O.
I guess the problem comes from the fact that I'm creating regions that already exist.
Could someone please tell me what I could do?
(Tested both in VS2010 and VS2013 with the same result)
I have a problem I've been looking into for a few days now, I just can't think of a logical way of doing what I want.
I have an app which has a task list. It starts of with 3 controls: a textbox, datetimepicker and a PictureBox which changes image on click. The user can then press an image which will add another row of controls below (It gets the properties of the dynamic controls from the controls already created):
https://www.dropbox.com/s/o2pub6orww24w25/tasklist.png (This is a screenshot to make it clearer)
Now what I want to do is save the values from each of the rows (A row being defined as: Textbox, Date, Status) into an SQLite DB.
For the first row it is easy, because that has a unique design name (and is a 'static' control).
However, the problem hits when I attempt to save values from the dynamic controls:
Problem a) I cannot reference the dynamic control because 'It does not Exist in the current Context'. -The function for creating the controls has a public access modifier so I thought that should do the trick? -It didn't. I've also tried:Panel1.pb.blah but it still didn't recognize the control?
Problem b) How can I tell my program that each row is a new set of data? In other words, how can I run a new insert command for each row? -I thought of doing this as a for-each-textbox loop, however would that not just pick up the first dynamic date everytime?
I've also thought of using the tag property and setting it to the counter variable, to group the controls in the row. (The counter being an integer which increments every time a new row is added.) However I cannot do that because the picture box uses the tag property as part of its function to change image on click (Changes multiple times).
Code:
Adding the Controls:
public void pictureBox1_Click(object sender, EventArgs e)
{
//TextBox Control
int tbh = tasktb.Location.Y + (counter*25);
int tbsh = tasktb.Size.Height;
int tbsw = tasktb.Size.Width;
TextBox tb = new TextBox();
tb.Location = new Point(9, tbh);
tb.Size = new System.Drawing.Size(tbsw, tbsh);
tb.Tag = counter.ToString();
//Date Time Control
int dth = duedatedb.Location.Y + (counter * 25);
int dtsh = duedatedb.Size.Height;
int dtsw = duedatedb.Size.Width;
DateTimePicker dtp = new DateTimePicker();
dtp.Location = new Point(300, dth);
dtp.Size = new Size(dtsw, dtsh);
dtp.Format = System.Windows.Forms.DateTimePickerFormat.Short;
//Picture Box Control
int stsh = status.Location.Y + (counter * 25);
int stssh = status.Size.Height;
int stssw = status.Size.Width;
PictureBox pb = new PictureBox();
pb.Location = new Point(429, stsh);
pb.Size = new Size(stssw, stssh);
pb.Image = Red;
pb.Click += new System.EventHandler(pb_Click);
panel1.Controls.Add(tb);
panel1.Controls.Add(dtp);
panel1.Controls.Add(pb);
++counter;
}
Trying to Reference the control: (For purposes of changing the image on click) [Found the Control.Find function from researching this in the MSDN Website]
public void pb_Click(object sender, EventArgs e)
{
PictureBox pb = (panel1.Controls.Find("pb",false));
if (pb.Image == Red) { pb.Image = Orange; status.Tag = "Orange"; }
else if (pb.Image == Orange) { pb.Image = green; status.Tag = "Green"; }
else if (pb.Image == green) { pb.Image = Red; status.Tag = "Red"; }
}
The essential problem here is Problem a, if you guys could see where I have gone wrong with that, I'd be able to go away and attempt to write some code to get around problem b.
(I have included Problem b in this for your suggestions on the best way to do this. -At the moment I have no clue!)
Thank you for any help received! It really is appreciated!
ControlCollection.Find looks for a control with the specified name, and you haven't set any. The variable names in your code aren't related. So, either:
pb.Name = "pb";
But that would mean you'd eventually have several items with the same name. So, seeing how you want to change the picture of the clicked PictureBox, just do this:
public void pb_Click(object sender, EventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (pb.Image == Red) { pb.Image = Orange; status.Tag = "Orange"; }
else if (pb.Image == Orange) { pb.Image = green; status.Tag = "Green"; }
else if (pb.Image == green) { pb.Image = Red; status.Tag = "Red"; }
}
The sender argument always contains a reference to whichever control raised the event, in this case whichever picturebox was clicked!
Edit: As for your other question, I assume you'll need to do stuff to the controls later on, so I suggest you store a reference to all of them (or at least the ones you need), something like this:
// helper class
private class Entry
{
public TextBox TextBox { get; private set; }
public DateTimePicker DateTimePicker { get; private set; }
public PictureBox PictureBox { get; private set; }
public Entry( TextBox tb, DateTimePicker dtp, PictureBox pb )
{
this.TextBox = tb;
this.DateTimePicker = dtp;
this.PictureBox = pb;
}
}
// member field
private List<Entry> m_Entries = new List<Entry>();
// at the end of pictureBox1_Click
public void pictureBox1_Click(object sender, EventArgs e)
{
....
m_Entries.Add( new Entry( tb, dtp, pb ) );
}
Then you can use the items in that list to interact with your rows. You might also want to add an index, or a reference to whatever the original data structure is. Also, you might want to think about if you really should be creating the controls yourself like that or actually use some kind of table/grid control to host them!
Or perhaps just wrap up all those controls in a single UserControl, with logic included and all!
I am trying to change the color of an empty textbox, I have more than one textbox on this form and i wish to highlight the empty one when a user clicks submit. I have written the loop below which is in my btnSubmit function after checking if all the textbox have a value. Can anyone help in completing this loop for me??
foreach (Control txtbxs in this.Controls)
{
if (txtbxs is TextBox)
{
var TBox = (TextBox)txtbxs;
if (TBox.Text == string.Empty)
{
TBox.ForeColor = Color.Red;
}
}
}
lblTopError.Text = "Please fill in the missing billing information";
pnlTopError.Visible = true;
When your string is empty, changing the ForeColor will do nothing since you don't have Text to display in red. Consider using BackColor and remember to have an event when text is entered to switch it back to the appropriate BackColor.
If this is what you are trying to do, have you considered using the error provider? this would help you to signal the user and prompt them to put in the information.
errorProvider= new System.Windows.Forms.ErrorProvider();
errorProvider.BlinkRate = 1000;
errorProvider.BlinkStyle = System.Windows.Forms.ErrorBlinkStyle.AlwaysBlink;
private void TextValidated(object sender, System.EventArgs e)
{
var txtbox = Sender as TextBox;
if(IsTextValid(txt))
{
// Clear the error, if any, in the error provider.
errorProvider.SetError(txtbox, String.Empty);
}
else
{
// Set the error if the name is not valid.
errorProvider.SetError(txtbox, "Please fill in the missing billing information.");
}
}
You can apply any CSS you want like this:
TBox.Attributes.Add("style", "color: red; border: solid 1px #FC3000")
I would use this instead of:
TBox.ForeColor = Color.Red;
Well since there aren't much textboxes in this form, i went the easy route and it worked, code bellow:
List<TextBox> boxes = new List<TextBox>();
if (string.IsNullOrWhiteSpace(txtFname.Text))
{
//highlightTextBox= txtFname;
boxes.Add(txtFname);
}
if (string.IsNullOrWhiteSpace(txtLname.Text))
{
//highlightTextBox = txtLname;
boxes.Add(txtLname);
}
if (string.IsNullOrWhiteSpace(txtAddOne.Text))
{
//highlightTextBox = txtAddOne;
boxes.Add(txtAddOne);
}
if (string.IsNullOrWhiteSpace(txtTown.Text))
{
//highlightTextBox = txtTown;
boxes.Add(txtTown);
}
if (string.IsNullOrWhiteSpace(txtPostCode.Text))
{
//highlightTextBox = txtPostCode;
boxes.Add(txtPostCode);
}
foreach (var item in boxes)
{
if (string.IsNullOrWhiteSpace(item.Text))
{
item.BackColor = Color.Azure;
}
}
lblTopError.Text = "Please fill in the missing billing information highlighted below";
pnlTopError.Visible = true;
Consider a SilverLight project that has 31 hyperlinkbuttons. Those represent the days of the month. I'm using this code to highlight the hyperlinkbutton that respresent today's day.
var daynumberHyperButton = this.FindName("Day" + DateTime.Today.Day) as HyperlinkButton;
//Highlighting the day of the month
if (daynumberHyperButton != null)
{
daynumberHyperButton.Background = new SolidColorBrush(Colors.Gray);
}
Then if I click on this highlighted hyperlinkbutton, it will open a childwindow to write some report.
private void dayHyperLink_Click(object sender, RoutedEventArgs e)
{
//This will initite and show the report window
ReportWindow rapport = new ReportWindow();
rapport.Closed += new EventHandler(rapport_Closed);
rapport.Show();
}
When I close the childwindows by clicking the OK button, it changes the color of the hyperlinkbutton that was highlighted (todays day) because I'm using this code to do that:-
private void rapport_Closed(object sender, EventArgs e)
{
ReportWindow rapport = (ReportWindow)sender;
var daynumberHyperButton = this.FindName("Day" + DateTime.Today.Day) as HyperlinkButton;
if (rapport.UsersValue == "Röd" && rapport.DialogResult==true)
{
daynumberHyperButton.Background = new SolidColorBrush(Colors.Red);
}
else if (rapport.UsersValue == "Gul")
{
daynumberHyperButton.Background = new SolidColorBrush(Colors.Yellow);
}
else
{
daynumberHyperButton.Background = new SolidColorBrush(Colors.Green);
}
}
But if I click on any other hyperlinkbutton that is not highlighted, it still only change the color of the highlighted hyperlinkbutton. I know this because my rapport_Closed event has:
var daynumberHyperButton = this.FindName("Day" + DateTime.Today.Day) as HyperlinkButton;
How can I change the above code, which is part of my rapport_Closed event, so that it changes the color of the event firing (the one that opens the childwindow) hyperlinkbutton, no matter which hyperlinkbuttonis the one that fires the event?
Ok now i can say i've done it. Here is what i did if anyone have a similar problem.
In may Home.xaml.cs, i added a public property like this:-
public HyperlinkButton dayHyperLink { get; set; }
To the Click event i added this code:-
dayHyperLink = (HyperlinkButton)sender;
To the rapport_Closing event i changed the if statment to the code below:-
if (rapport.UsersValue == "Röd" && rapport.DialogResult == true)
{
dayHyperLink.Background = new SolidColorBrush(Colors.Red);
}
This made me feel happy ;)
I have TextBox which allow insert only numeric values (filtering), But when I paste copied text it's allow any kind of symbol. How can I prevent or filter text before pasting?
You could backup your text before any manual input and then when the input provided isn't valid restore the previous text like so:
_backupText = string.Empty;
doNotPasteTextBox.TextInputStart += (sender, e) =>
{
int textParsed;
if(int.TryParse(e.Text,out textParsed))
{
_backupText = doNotPasteTextBox.Text.Insert(doNotPasteTextBox.SelectionStart, e.Text);
}else
{
e.Handled = true;
}
};
doNotPasteTextBox.TextChanged += (sender, e) =>
{
int textParsed;
int selectionStart = doNotPasteTextBox.SelectionStart;
if(!int.TryParse(doNotPasteTextBox.Text, out textParsed))
{
doNotPasteTextBox.Text = _backupText;
}
doNotPasteTextBox.SelectionStart = selectionStart;
};
I wouldn't recommend trying to capture the control keys or anything because when you're on a mac or on linux then you're screwed.
Adjust my sample and pour it inside a new textbox control to make it cleaner but you get the idea.
You could use Clipboard.GetText() to get the text that is inserted into the textbox, but this will pop up a message, and the user must give the application access to the Clipboard.
If its no problem for you then i would use this.