TextBox AutoComplete Not working properly - c#

I am trying to implement a textbox autocomplete with a custom datasource in the form of an array which shows suggestions on single character input. But when i run the program the autocomplete only starts after the second character input. When i debugged i saw that datas are there in the AutoCompleteNameCollection but it is not showing in the suggestion until the second character input.
This code i have written in the textchange event.
arr = LoadName(empid_txt.Text.Trim()); //arr is string array
namesCollection.AddRange(arr);
this.empid_txt.AutoCompleteMode = AutoCompleteMode.Suggest;
this.empid_txt.AutoCompleteSource = AutoCompleteSource.CustomSource;
this.empid_txt.AutoCompleteCustomSource = namesCollection;
In the LoadEvent I have initialized the AutoCompleteNameCollection with an empty array.
namesCollection.AddRange(arr);// here arr is empty
empid_txt.AutoCompleteMode = AutoCompleteMode.Suggest;
empid_txt.AutoCompleteSource = AutoCompleteSource.CustomSource;
empid_txt.AutoCompleteCustomSource = namesCollection;
But it does't work until the second character input.
However if instead of an array i initialize the AutoCompleteNameCollection with an empty string like : namesCollection.Add(""); it does work for the single character input but some times it gives AccessViolationException: Attempted to read or write protected memory.
So is there any way i can solve this problem?

I can replicate the access violation when setting the AutoCompleteSource in the event handler, it seems like the autocomplete routine may be accessing the AutoCompleteSource while it is being replaced and destroyed.
To prevent this you can put a lock around your code.
lock(this)
{
arr = LoadName(empid_txt.Text.Trim()); //arr is string array
namesCollection.AddRange(arr);
this.empid_txt.AutoCompleteMode = AutoCompleteMode.Suggest;
this.empid_txt.AutoCompleteSource = AutoCompleteSource.CustomSource;
this.empid_txt.AutoCompleteCustomSource = namesCollection;
}
This stopped the access violations.

On load you may populate the TextBox with a sub-set of your data (that can be even cached for future/shared use). If you have a "most common" counter you can use it. As limit condition you may even add a dummy item (if what you get with an empty string is an access violation).
Then, on the TextChange event read the data you need from the database.
I have only one question: you do not want to populate the source until the user starts to type? If there's the problem of network traffic then you move a lot of data. If you move a lot of data then your users will have to wait when they start to type something. Is it acceptable? On the other side if they do not wait too much maybe data stream is not so big and you can put that logic in a BackgroundWorker in the constructor of your form (or not far from that time).

AutoComplete suggests after the second char is being pressed is normal because in the first place, you have initialized the arr (which is your custom datasource) into an empty array. You have populated your arr in TextChanged event and that's why AutoComplete works at the second char because your datasource is filtered based on your first char (which is definitely what you don't want).
Here's a suggestion:
On the FormLoad event of your application, fill arr with all the possible suggestions (I think the source of suggestion is from database right?). This will allow textbox to suggest on your first char.
When you have entered the first char, on the TextChanged event reload your arr datasource based on the prevous character being entered.
Hope it helps.

If 'arr' is empty when you initialize the textbox then there is nothing to compare to. You have to initialize AutoCompleteCustomSource to a valid array before you start typing. You are initializing in textchange event, when the user has already typed a character.
You need to populate the namesCollection before the code is changed - in Initialize.

On Form Load call the Textbox autocomplete method.
public void autocompleteData()
{
//SuggestStrings will have the logic to return array of strings either from cache/db
var CurrentuserId = CloudKaseWSClient.GetUserDetail(tokenUsr, tokenPasswd, Username);
List<string> l = new List<string>();
var SearchResults = ("Select Database Query").ToList();
foreach (var i in SearchResults)
{
l.Add(i.name);
}
string[] arr = l.ToArray();
AutoCompleteStringCollection collection = new AutoCompleteStringCollection();
collection.AddRange(arr);
txtSearchUser.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
txtSearchUser.AutoCompleteSource = AutoCompleteSource.CustomSource;
txtSearchUser.AutoCompleteCustomSource = collection;
}
OR You want to set static data for AutoComplete Textbox than you have to set In Design view for Textbox property of AutocompleteMode to set SuggestAppend,AutocompleteSource to set CustomSource and add static value inAutocompleteCustomSource.
I hope this solution helps to you..
Happy Coding.:)

Related

C# Windows Forms - Using StringBuilder to generate a large string then using SendKeys to type it out. Often happening twice in one call?

I'm almost finished writing a small add-on for League of Legends that allows players to click a checkbox in my form, and the checkbox's function clicks in the search bar (which automatically clears it. Already a feature in the LoL Client) and types out the names of all the champions the player has assigned to that group via a 2nd form. However, if the text is (example) 12345, I've been getting a lot of results like this: 1231234545.
What I've tried:
I've tried adding in ctrl-a + backspace with sendkeys because maybe when it clicks in the search bar it isn't always deleting the stuff there! This however did not change anything
I then commented out the code that types out the actual string (12345) and made the checkbox ONLY type ctrl-a. I then wrote out a string longer than the search bar that I'm typing into and pasted it, clicked a checkbox, and tried to see if it would highlight the text (meaning the text was not always being cleared when clicked). I did this about 50 times and every single time the text was cleared
This leads me to believe that the problem is in the code itself, not the actual integration with the LoL Client. So maybe it's a problem with StringBuilder? I haven't used it very often so I'm not too familiar with it. Commented out all the stringbuilder stuff and changed to String concatenation. Same problem
I tried adding a boolean runningCheckboxFunction to make sure the function would only run once but still have the issue as well.
So this is where I'm at right now. I'm pretty sure it's a problem with my code looping for some reason or the checkbox function as a whole being called twice instead of once when I click the checkbox.
My code:
private void checkboxFunction()
{
if (runningCheckboxFunction == true) return;
runningCheckboxFunction = true;
//CLICK IN THE SEARCH BAR (Automatically clears the text that WAS there)
Process[] LolClient = Process.GetProcessesByName("LolClient"); //should only return one client (i.e. get [0])
if (LolClient.Length < 1) return;
ClickOnPoint(LolClient[0].MainWindowHandle, searchBarCheckPoint);
//MAKE THE STRING TO TYPE IN THE SEARCH BAR
StringBuilder whatToTypeInSearchBar = new StringBuilder();
foreach (CheckBox cb in checkboxes)
{
if (cb.Visible == true && cb.Checked == true)
{
List<String> thisCheckboxsChamps = settingsForm.getChampionListForGroup(checkboxes.IndexOf(cb));
if (thisCheckboxsChamps == null) continue;
foreach (String champ in thisCheckboxsChamps)
{
whatToTypeInSearchBar.Append("|");
whatToTypeInSearchBar.Append(champ);
whatToTypeInSearchBar.Append("$");
}
}
}
//had an issue that sometimes clicking in search bar did not automatically clear it, so select all text and backspace just in case
//^ = ctrl -- ^A = ctrl-A (select all)
SendKeys.Send("^A");
//backspace
SendKeys.Send("{BACKSPACE}");
//TYPE IN THE SEARCH BAR
if (whatToTypeInSearchBar.Length > 0)
{
//remove the first "|"
whatToTypeInSearchBar.Remove(0, 1);
SendKeys.Send(whatToTypeInSearchBar.ToString());
}
runningCheckboxFunction = false;
}
Which event are you using for the check box control (CheckedChanged or CheckStateChanged)? Also, what is the value for CheckBox.ThreeState property? It is possible that the event you use fires more than once. If that is the case, you could check for a specific state before calling the function you have listed above.
Also, when you use SendKeys, try sending the keys to your own application and monitor the ActiveControl.KeyUp/ActiveControl.KeyDown/ActiveControl.KeyPress events to ensure that the string you generate is the riight sequence.

can sort some arrays but not others?

I'm sorting the lines of a text box in reverse alphabetical order, and the code I have works fine:
string[] temp = textBox.Lines;
Array.Sort(temp);
Array.Reverse(temp);
textBox.Lines = temp;
But I'm confused as to why Visual Studio wouldn't let me do:
Array.Sort(textBox.Lines);
Array.Reverse(textBox.Lines);
Mostly I'm trying to figure out the subtleties of C# since I'm still new to it.
EDIT: The second snippet doesn't error out, but it doesn't execute any code (i.e. doesn't appear to do anything).
Here's a few lines of the getter for TextBox.Lines:
public string[] Lines
{
get
{
string text = Text;
ArrayList list = new ArrayList();
...
...
return(string[]) list.ToArray(typeof(string));
}
You're getting a new collection, which has a copy of the data in it from the TextBox. You're not actually sorting the data in the TextBox.
When void Array.Sort is done sorting the array passed back from that property, the original TextBox remains unchanged.
Indeed, your array will be sorted and reversed, but what happens is that will not be assigned to TextBox. Modifying the Lines array has no effect since it is computed property.
You need to assign it to Lines property to see the effect.
Here is the reference to source
This is because textBox.Lines is not exposed as a member variable on the class but rather as a property. Behind the scenes, a property is really two methods, e.g. get_Lines and set_Lines. All you know about the array you get back from get_Lines is that it's an array of strings. There's no guarantee that that property is backed by an actual array--maybe TextBox keeps the text internally as one big string that it then breaks into pieces for you when you call get_Lines. So you're not necessarily modifying the internal list when you call Sort() and Reverse() on the result of get_Lines.

make a program run a number of times based on user input

I have a windows form program that I would like to 'run' a certain number of times based on a number that the user specifies.
I am using Visual Studio 2013 and C#.
I have a program that has 16 picture boxes and randomly assigns pictures to 5 of these boxes. The person has to select the correct order of pictures. If they do, they are rewarded, if they choose wrong, they are punished by an annoying sound.
My first form has a TextBox that the user can specify the 'numberOfTrials'
I have a second form that takes the value of the TextBox and converts it into an int.
I want to have the main program on my second form run the number of times that the user specifies.
My program does work if I run it once, without using this variable.
I have tried using a for loop inside the method that starts the program, but this did not work. It just made all of the picture boxes white.
I then tried to use the for loop around the InitializeComponent() method but, again, this just made all of the picture boxes white.
My for loop uses the textbox variable as such:
for (int cycles = 0; cycles < numberOfTimesThrough; cycles++)
I create the numberOfTimesThrough variable by parsing the textbox variable.
Perhaps I am doing this wrong?
On the first form:
at the top of the class:
public static string trialNumberString;
inside a method that is called when a confirm button is pressed:
trialNumberString = tbTrialNumber.Text.ToString();
On the second form:
at the top of the class:
//Integer value for the string of trials
public static int numberOfTimesThrough;
bool canConvert = Int32.TryParse(Settings.trialNumberString, out numberOfTimesThrough);
Is this the correct way to get the string value of the textbox on the first form?
I am sure that adding a for loop should make the program repeat itself, so there must be something wrong with the way I am parsing the textbox string to an int.
The people using this program are not wanting to break it so the data entered into the textbox on the first form will always be between 1 and, say, 25. Do I still have to use a try catch around the string conversion?
Any help with this will be greatly appreciated.
You don't need to re-run the entire program just to show a form multiple times: instead, just instantiate multiple instances of the second form and show them in sequence.
To safely parse text as a number, you can use int.TryParse()
You can get the value of the textBox by accessing it's Text property and from there parse the string it returns to an int.
string s = textBox.Text;
int i = int.Parse(s);
Use whatever validation logic is necessary as well.
When application closes, static variables are deleted from memory too. In order to save setting, you need to save it to separate file and then read that. Simple way of read/write settings is using build-in options of ApplicationSettings: http://msdn.microsoft.com/en-us/library/vstudio/a65txexh(v=vs.100).aspx

Quickest way to Update Multiline Textbox with Large Amount of Text

I have a .NET 4.5 WinForm program that queries a text-based database using ODBC. I then want to display every result in a multiline textbox and I want to do it in the quickest way possible.
The GUI does not have to be usable during the time the textbox is being updated/populated. However, it'd be nice if I could update a progress bar to let the user know that something is happening - I believe a background worker or new thread/task is necessary for this but I've never implemented one.
I initially went with this code and it was slow, as it drew out the result every line before continuing to the next one.
OdbcDataReader dbReader = com.ExecuteReader();
while (dbReader.Read())
{
txtDatabaseResults.AppendText(dbReader[0].ToString());
}
This was significantly faster.
string resultString = "";
while (dbReader.Read())
{
resultString += dbReader[0].ToString();
}
txtDatabaseResults.Text = resultString;
But there is a generous wait time before the textbox comes to life so I want to know if the operation can be even faster. Right now I'm fetching about 7,000 lines from the file and I don't think it's necessary to switch to AvalonEdit (correct me if my way of thinking is wrong, but I would like to keep it simple and use the built-in textbox).
You can make this far faster by using a StringBuilder instead of using string concatenation.
var results = new StringBuilder();
while (dbReader.Read())
{
results.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = results.ToString();
Using string and concatenation creates a lot of pressure on the GC, especially if you're appending 7000 lines of text. Each time you use string +=, the CLR creates a new string instance, which means the older one (which is progressively larger and larger) needs to be garbage collected. StringBuilder avoids that issue.
Note that there will still be a delay when you assign the text to the TextBox, as it needs to refresh and display that text. The TextBox control isn't optimized for that amount of text, so that may be a bottleneck.
As for pushing this into a background thread - since you're using .NET 4.5, you could use the new async support to handle this. This would work via marking the method containing this code as async, and using code such as:
string resultString = await Task.Run(()=>
{
var results = new StringBuilder();
while (dbReader.Read())
{
results.Append(dbReader[0].ToString());
}
return results.ToString();
});
txtDatabaseResults.Text = resultString;
Use a StringBuilder:
StringBuilder e = new StringBuilder();
while (dbReader.Read())
{
e.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = e.ToString();
Despite the fact that a parallel Thread is recommended, the way you extract the lines from file is somehow flawed. While string is immutable everytime you concatenate resulString you actually create another (bigger) string. Here, StringBuilder comes in very useful:
StringBuilder resultString = new StringBuilder ()
while (dbReader.Read())
{
resultString = resultString.Append(dbReader[0].ToString());
}
txtDatabaseResults.Text = resultString;
I am filling a regular TextBox (multiline=true) in a single call with a very long string (more than 200kB, loaded from a file. I just assign the Text property of TextBox with my string).
It's very slow (> 1 second).
The Textbox does anything else than display the huge string.
I used a very simple trick to improve performances : I replaced the multiline textbox by a RichTextBox (native control).
Now same loadings are instantaneous and RichTextBox has exactly the same appearance and behavior as TextBox with raw text (as long as you didn't tweaked it). The most obvious difference is RTB does not have Context menu by default.
Of course, it's not a solution in every case, and it's not aiming the OP question but for me it works perfectly, so I hope it could help other peoples facing same problems with Textbox and performance with big strings.

Clearing a TextBox in ASP.NET

I have webpage where there is textbox with some default value. I have to clear that value from the textbox. I have two options:
textbox.text="";
or
textbox.text.remove(0,length);
Which one should I use? Does it make any impact on page performance (there are many textboxes placed on my page)?
The best way to do this is
textbox.text = string.Empty;
Also remember that string type is immutable!
It makes no difference - do what is most readable for you and your colleagues.
Many prefer to use string.Empty.
The performance difference between the two options will be too small to measure, most likely.
TextBox.Text = String.Empty; is a lot more readable. It clearly states what you're trying to do: "set the text property of this text box to an empty string".
I recommend you go with the assignment, as it is both faster, and much more clear.
Presumably, you mean clear the value with javascript when a user clicks int the box? If so, it won't make any difference to performance.
I use jQuery in most pages, so I just hook up this function to clear default values onClick, using the ClientId of the textbox:
$('#ctl00_TextBox').click(function() { $('#ctl00_TextBox').val('');
If you mean clear it in codebehind use this:
yourTextBox.Text = String.Empty;
textbox.text="";
or
foreach (var item in Page.Controls)
{
if (item is TextBox)
{
((TextBox)item).Text = "";
}
}
The impact on performance will be minimal even with hundreds/thousands of calls, however
textbox.text = "";
should in theory be very slightly quicker, since you're just assigning a new value rather than processing the string (as .Remove does)
The best way to do this would be to do
textbox.text = String.Empty;
Update: if you're counting clock-cycles, String.Empty will actually execute faster as well, because it doesn't create a new object, whereas "" will create a new String object.
However, you should really not be too concerned about this, there aren't many ways to set a string to empty that will cause performance issues! You should use whichever is the most readable...

Categories