Changing string colour - c#

Okay, so this is a carry on from my last question, but I have the code:
private void btnTrans_Click(object sender, EventArgs e)
{
var abrvStr = inputBx.Text;
foreach (var kvp in d)
{
abrvStr = abrvStr.Replace(kvp.Key, kvp.Value);
}
outputBx.Text = abrvStr;
}
Basically it's part of a dictionary program, so that when you enter a line of text in textbox 1 it appears in textbox 2 with a word replaced from textbox 1 in the dicitonary.
So if black,white is in the dictionary and I enter The wall is black. The wall is white will appear in textbox 2. So all's good.
Right now the tricky part, how would I alter that to allow me have the changed word in textbox 2 as the colour red. So in my above example, the wall is white. White would be red in the output line of text.
Note, I am using RichTextBoxes
C# Language!

To build on Oliver Jacot-Descombes answer:
private void btnTrans_Click(object sender, EventArgs e)
{
var abrvStr = inputBx.Text;
foreach (var kvp in d)
{
abrvStr = abrvStr.Replace(kvp.Key, kvp.Value);
int start = abrvStr.IndexOf(kvp.Value);
if(start >= 0)
{
richTextBox1.Text = abrvStr;
richTextBox1.Select(start, kvp.Value.Length);
richTextBox1.SelectionColor = Color.Red;
}
}
}
You would use a switch statement on the value of the dictionary to get what color you want to change the selection. You will need to modify that to fit the values in your dictionary as well as what colors you want.

You can use the SelectionColor property of the RichTextBox. Start by selecting the word that you want to format
string word = "white";
int start = richTextBox1.Find(word);
if (start >= 0) {
richTextBox1.Select(start, word.Length);
richTextBox1.SelectionColor = Color.Red;
}

Add a the reference to the KVP in the dictionary to the Textbox's Tag property. When the user changes the color, get the KVP from the Tag property and change the value in the KVP. I provide insight to the Tag property on my blog C# Winforms and the Hidden Association Tag. WPF/Silverlight also use the Tag property on controls as well..
--- Edit per user request ---
I am not sure why you need to enumerate a dictionary. The whole point of a dictionary is to quickly get the key. My example uses that and does not do the for loop.
... The initialization location....
var myDictionary = new Dictionary<string, Tuple<string, System.Drawing.Color>>()
{
{ "Black", new Tuple<string,System.Drawing.Color>("White", Color.Green) },
{ "White", new Tuple<string,System.Drawing.Color>("Black", Color.Red) }
};
... (later in code)...
private void btnTrans_Click(object sender, EventArgs e)
{
var key = inputBx.Text; // Let us say "White"
if (myDictionary.ContainsKey(key))
{
var targetColor = myDictionary[key]; // Just get the value
outputBx.Select(0, targetColor.Item1.Length);
outputBx.SelectionColor = targetColor.Item2;
outputBx.Text = targetColor.Item1;
}
else
{
outputBx.Text = "Unknown";
}
}
Check out my blog article on dictionaries for more info C# Dictionary Tricks

Related

C# - DropDown ComboBox - how can a user add value using nothing else?

I would like to solve my problem using only a DropDown ComboBox component. Although I have a list of colors, I want to allow user to enter an RGB color code.
In my imagination it would work two ways:
The user opens the Dropdown section and choses the wanted color
The user enters an RGB code (eg. 255;0;123) through the component's editable first line (then presses enter)
(I don't need (neither want) the RGB code to be added to the list of the ComboBox.)
I only need the results of that; I can process the outcoming data.
You need to use the ComboBox's "KeyDown" event. In the below I'm using "exampleProcess" as a method used when you have a colour you wish to use. I'm going to add an array of your example colours as comparisons also.
string[] colours = new string[]{"Red","Green","Blue","Yellow","etc"};//These would be the values in your combobox dropdown list.
Color selectedcolour;
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)//If enter key pressed.
{
if (colours.Contains(ComboBox.Text))//If the colour is one of the default colours.
{
selectedcolour = Color.FromName(ComboBox.Text);
}
else
{
List<string> parts = ComboBox.Text.Split(';');//Split text into parts between each ";".
foreach(string part in parts)
{
if (part == "")
{
parts.Remove(part);
}
}
int r = int.Parse(parts[0]);
int g = int.Parse(parts[1]);
int b = int.Parse(parts[2]);
selectedcolour = Color.FromArgb(r,g,b);
}
exampleProcess(selectedcolour);
}
}
You'll have to add more error checking but I think this should essentially work ^_^.

ComboBox searching in string, not just the first letter

I have problem making my combobox searching inside the strings in the items.
I want to narrow down a list of members. They are formatted in this way (unique member id) - First name - last name.
When i leave all the settings "as is", then it will only "allow" me to search at the first char in the string.
The DataSource is set from a list, what are made from looping through all the files in a folder. ​
The code i have been using is as follows(partial code)
private void searchForShooterComboBox_KeyUp(object sender, KeyEventArgs e)
{
//if(e => KeyCode == Keys::Down || e => KeyCode == Keys::Down)
//string comboBoxValue = searchForShooterComboBox.Text;
//searchForShooterComboBox.DataSource = null;
//searchForShooterComboBox.DataSource = fliterComboBox(searchForShooterComboBox, memberFileNames);
//searchForShooterComboBox.Text = comboBoxValue;
}
private void searchForShooterComboBox_TextChanged(object sender, EventArgs e)
{
searchForShooterComboBox.DataSource = null;
searchForShooterComboBox.DataSource = fliterComboBox(searchForShooterComboBox, memberFileNames);
}
private List<string> fliterComboBox(ComboBox cobx, List<string> stringList)
{
List<string> returnList = new List<string>();
if (cobx.Text != ""){
try
{
foreach (string s in stringList)
{
if (s.Contains(cobx.Text))
{
returnList.Add(s);
}
}
}catch{
}
}
return returnList;
}
some of the code i tried seemed to filter the list OK, but after the methods ran it fills what seems to be the first item in the new list into the "text field", so the user wont be able to continue typing a name ex.
Will it make any difference using ComboBox.Items.Add() and ComboBox.Items.Remove() instead of using DataSource?
edit: the comboBox DataSource is initially set in the form_load event handler. Where the following code regarding the combobox is:
searchForShooterComboBox.DropDownStyle = ComboBoxStyle.DropDown;
searchForShooterComboBox.AutoCompleteMode = AutoCompleteMode.Suggest;
searchForShooterComboBox.AutoCompleteSource = AutoCompleteSource.ListItems
Thanks for taking the time to look.
Okay seems i figured something out myself,don't know if its the best way, but seems to get the job done :)
firstly i added the string's into both the ComboBox.items and into a list<string>. The reason for adding them both ways is for the user to see all available options on load.
for (int i = 0; i < membersFiles.Length; i++)
{
searchForShooterComboBox.Items.Add(membersFiles[i].Replace(".txt", "").Replace(#"C:\Users\Nicolai\Desktop\skytter\", "").Replace("-", " "));
memberFileNames.Add(membersFiles[i].Replace(".txt", "").Replace(#"C:\Users\Nicolai\Desktop\skytter\", "").Replace("-", " "));
}
After that i added a combobox_keydown event from the property window.
private void searchForShooterComboBox_KeyDown(object sender, KeyEventArgs e)
{
try
{
//checking if the key pressed is RETURN, in that case try to fill the combobox with the selected item,
//and continuing with other method
if (e.KeyValue == 13)
{
searchForShooterComboBox.Text = (string)searchForShooterComboBox.SelectedItem;
fillInfoInForm();
}
//making sure the key pressed IS NOT DOWN, UP, LEFT, RIGHT arrow key.
else if (e.KeyValue > 40 || e.KeyValue < 37)
{
filterComboBox(searchForShooterComboBox, searchForShooterComboBox.Text);
searchForShooterComboBox.Select(searchForShooterComboBox.Text.Length, 0);
searchForShooterComboBox.DroppedDown = true;
}
}
catch (FileNotFoundException ex) {
MessageBox.Show("Der blev ikke fundet nogen fil med flg. sti " + ex.FileName + "\nHusk at vælge hele navnet i listen, eller skriv det nøjagtigt som det står!");
}
}
made this method to search through the list items, clear the items in the combobox, and add the ones that match.
private void filterComboBox(ComboBox cobx, string enteredSearch)
{
//clearing ComboBox items before adding the items from the LIST that meets the search
cobx.Items.Clear();
//looping over the items from the list, comparing them to the search from the combobox text field.
//if the item in the list does not contain the string searched it will return an index of -1.
for (int i = memberFileNames.Count-1; i >= 0; i--)
{
if (memberFileNames[i].IndexOf(enteredSearch, 0, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
cobx.Items.Add(memberFileNames[i]);
}
}
}
if you are having trouble finding the right KeyValues, try looking at
https://msdn.microsoft.com/en-us/library/system.windows.forms.keyeventargs.keyvalue(v=vs.110).aspx
and copy paste the code from there, and add it to you key_down event handler, and it will show most info(if not all) in a message box.
That was my workaround, if you have a better way of doing it, i am all ears :)

Reference and store Data from Dynamically created Controls?

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!

Wpf Richtextbox selection starting column and row number returns different values

I am initializing my Richtextbox,
void InitRTBFlowDocument()
{
Style noSpaceStyle = new Style(typeof(Paragraph));
noSpaceStyle.Setters.Add(new Setter(Paragraph.MarginProperty, new Thickness(0)));
rtbTextEditor.Resources.Add(typeof(Paragraph), noSpaceStyle);
}
I want to get Richtext box selection words row and column numbers. I wrote the code as follows, First time it is returning correctly.
void rtbTextEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
//Get the richtext box selected text
Init.RTBSelectionText = rtbTextEditor.Selection.Text.Trim();
Init.SelectionText = rtbTextEditor.Selection.Text.Trim();
Init.isSelect = true;
if (Init.RTBSelectionText != string.Empty)
{
TextPointer tp = rtbTextEditor.Selection.Start.GetPositionAtOffset(-2, LogicalDirection.Forward);
if (tp != null)
GetStartingIndex();
}
Init.RTBContent = new TextRange(rtbTextEditor.Document.ContentStart, rtbTextEditor.Document.ContentEnd).Text;
}
void GetStartingIndex()
{
TextPointer tp1 = rtbTextEditor.Selection.Start.GetLineStartPosition(0);
TextPointer tp2 = rtbTextEditor.Selection.Start;
int SelectionColumnIndex = tp1.GetOffsetToPosition(tp2)-1;//column number
int someBigNumber = int.MaxValue;
int lineMoved;
rtbTextEditor.Selection.Start.GetLineStartPosition(-someBigNumber, out lineMoved); //Line number
int SelectionRowIndex = -lineMoved;
Init.RTBTextPoint = new RTBTextPointer();
Init.RTBTextPoint.Row = SelectionRowIndex;
Init.RTBTextPoint.Column = SelectionColumnIndex;
}
After clearing and added new content, The position returns wrong number,
public void DisplayContent(string content)
{
//Clear the rich text box
rtbTextEditor.Document.Blocks.Clear();
rtbTextEditor.Document.Blocks.Add(new Paragraph(new Run(content)));
}
Is anything rong in the above code.
Please help me
Thanks in advance!
This is because the contents in the RTB does not only contain text, it contains these things called TextPointerContext's. TextPointer's take this into account. You can check what the TextPointer is adjacent to by using:
TextPointer.GetPointerContext(LogicalDirection);
To get the next TextPointer:
TextPointer.GetNextContextPosition(LogicalDirection);
Some sample code I used in a recent project, this makes sure that the pointer context is of type Text:
while (start.GetPointerContext(LogicalDirection.Forward)
!= TextPointerContext.Text)
{
start = start.GetNextContextPosition(LogicalDirection.Forward);
if (start == null) return;
}
When you clear your RTB, you're probably removing some of these pointer contexts. So be careful when using GetPositionAtOffset(). This should give you a "pointer" in the right direction. If you need any more help me know.
Good hunting!

Why can't I leave a TextBox using tab?

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.

Categories

Resources