WPF Listview Textbox Highlight - c#

I have a listview which binds the customers informations. I highlight the listview items by typing in a textbox. For example, when I type "PET" into the textbox then it highlights the "PET"s in the listview items. It works and highlights.
But after that when I click on the highlighted item it gives an error. But it is interesting when I click on a free place in the listview item it works. For example, it highlighted PETER HEINZ. IF I click on to PETER or HEINZ it gives error. But, if I click on the space between PETER HEINZ it works. What a error is this? The error message is
System.InvalidOperationException was unhandled
Message="'System.Windows.Documents.Run' is not a Visual or Visual3D."
The source code is below:
private void HighlightText(Object itx)
{
if (itx != null)
{
if (itx is TextBlock)
{
Regex regex = new Regex("(" + textBox1.Text + ")", RegexOptions.IgnoreCase);
TextBlock tb = itx as TextBlock;
if (textBox1.Text.Length == 0)
{
string str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
string[] substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
Run runx = new Run(item);
runx.Background = Brushes.LightGray;
tb.Inlines.Add(runx);
}
else
{
tb.Inlines.Add(item);
}
}
return;
}
else
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(itx as DependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(itx as DependencyObject, i));
}
}
}
}

Here's a revisement. Can you paste it over yours and see if it works better in your application?
key points:
Added protection to not let non-visual objects make it to the GetChild recursion
Added check for textbox1 being empty
trivial: moved tb.inlines.clear to reduce code repitition
trivial: inverted itx null check to reduce nesting
.
private void HighlightText(Object itx)
{
//safety checks
if (itx == null)
return;
if (String.isNullOrEmpty(textBox1.Text)
return; //just in case the box is empty
if (! (itx is Visual || itx is System.Windows.Media.Media3D.Visual3D)
return; //prevent from getting children on non-visual element
if (itx is TextBlock)
{
Regex regex = new Regex("(" + textBox1.Text + ")", RegexOptions.IgnoreCase);
TextBlock tb = itx as TextBlock;
tb.Inlines.Clear();
if (textBox1.Text.Length == 0)
{
string str = tb.Text;
tb.Inlines.Add(str);
return;
}
string[] substrings = regex.Split(tb.Text);
foreach (var item in substrings)
{
if (!regex.Match(item).Success)
{
Run runx = new Run(item);
runx.Background = Brushes.LightGray;
tb.Inlines.Add(runx);
}
else
{
tb.Inlines.Add(item);
}
}
return;
}
else
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(itx as DependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(itx as DependencyObject, i));
}
}
}

Related

Clear all textbox in a windows form except special ones

I'm using following code to clear all text boxes in a form.
protected static IEnumerable<Control> GetAllChildren(Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
internal static void ResetTextBoxes(Control root, string resetWith = "", params TextBox[] except)
{
try
{
foreach (TextBox txt in GetAllChildren(root).OfType<TextBox>())
{
foreach (TextBox txtException in except)
{
if (txtException.Name != txt.Name)
{
txt.Text = resetWith == "" ? string.Empty : resetWith;
}
}
}
}
catch (Exception ex) { throw ex; }
}
I tried to separate some special text boxes those I don't want to be cleared by using params, but still it clears all the boxes. Need help, please.
Shorter version of GetAllChildren:
protected static IEnumerable<Control> GetAllChildren(Control root) {
return new Control[] { root }
.Concat(root.Controls
.OfType<Control>()
.SelectMany(item => GetAllChildren(item)));
}
And shorter Linq:
var source = GetAllChildren(root)
.OfType<TextBox>()
.Where(ctrl => !except.Contains(ctrl));
foreach (var textBox in source)
textBox.Text = resetWith;
The problem with your current implmentation is in the inner loop:
foreach (TextBox txtException in except)
if (txtException.Name != txt.Name)
txt.Text = resetWith == "" ? string.Empty : resetWith;
if you have at least two exceptions with different names that condition
txtException.Name != txt.Name
will be inevitably satisfied (any txt.Name either not equal 1st exception or 2nd one)
This is happening since you are testing all the elements the first collection against all the elements of the second collection, so even if a textbox exists in the except array, it's name will not match the other textboxes there.
Use Linq's Any extension method instead:
internal static void ResetTextBoxes(Control root, string resetWith = "", params TextBox[] except)
{
foreach (TextBox txt in GetAllChildren(root).OfType<TextBox>())
{
if(!except.Any(t => t.Name == txt.Name))
{
txt.Text = resetWith == "" ? string.Empty : resetWith;
}
}
}
}
Here is the function I use:
public void ClearAllFields(Control con)
{
foreach (Control c in con.Controls)
{
if (c is TextBox && c != null)
{
if (c.Name != "specialtxtbox1name" && c.Name != "specialtxtbox2name" && c.Name != "nameoftextbox") // just put in the name of special textBoxes between double quotes
c.Text = "";
}
else
{
ClearAllFields(c);
}
}
}
Simply call the function wherever needed
Example:
private void InsertDatathenClearForm_Click(object sender, EventArgs e)
{
//Code
//.
//.
File.Create(filepath);
MessageBox.Show("All Textboxes Data saved successfully");
//Clear fxn
ClearAllFields(this);
}
.
.
}

How to use variable names that are created in a loop and stored in List<string>?

I have a List object that gets automatically filled with the names of textboxes. Now I'd like to cycle through all these text boxes.
How can I let C# know that the string-names in the List are actually TextBox objects with that string-name?
List<string> txtOppsNames = new List<string>();
for (int i = 1; i < numOpps; i++)
{
txtOppsNames.Add("txtOpp" + i);
}
foreach (var txtName in txtOppsNames)
{
if (txtName.Text != "")
{
// do stuff
}
}
The current code reads txtName as a string. I would like it to read as a TextBox.
Edit - the below code contains the solution for me.
List<string> txtOppsNames = new List<string>();
for (int i = 1; i < numOpps; i++)
{
txtOppsNames.Add("txtOpp" + i);
}
foreach (var txtName in txtOppsNames)
{
TextBox textBox = this.Controls.Find(txtName, true).FirstOrDefault() as TextBox;
if (textBox.Text != "")
{
MessageBox.Show("Thanks Amir Popovich");
}
}
Try this :
List<string> txtOppsNames = new List<string>();
for (int i = 1; i < numOpps; i++)
{
txtOppsNames.Add("txtOpp" + i);
}
foreach (var txtName in txtOppsNames)
{
var cntrl= FindControl(txtName);
if (cntrl!=null && cntrl is TextBox)
// do something with
((TextBox)cntrl)
}
Use Control.ControlCollection.Find:
string textBoxName = "txtOpp1";
TextBox textBox = this.Controls.Find(textBoxName, true).FirstOrDefault() as TextBox;
In your case:
List<string> txtOppsNames = new List<string>();
for (int i = 1; i < numOpps; i++)
{
txtOppsNames.Add("txtOpp" + i);
}
foreach (var txtName in txtOppsNames)
{
var control = this.Controls.Find(txtName, true).FirstOrDefault();
if(control != null && control is TextBox)
{
TextBox textBox = control as TextBox;
if(textBox.Text != string.Empty)
{
//logic
}
}
}
Assuming it is Winform, There seems to be no pretty way to do it.
But you could use Loop through Textboxes.
once you have the text box collection, check the names with your string.

Sorting a WPF datagrid by word preceding search word

I have a datagrid which i'm trying to sort. The datagrid has a column of strings. On this column I perform filtering by a search string. Once completed i want to sort the datagrid by the word preceding or the word after the search string within the cell. How would i go about sorting like this?
Update
Here's what ive got so far.
private void tbSearch_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as Textbox;
_searchstr = tb.Text;
ICollectionView view = CollectionViewSource.GetDefaultView(SentenceLists);
view.Filter = new Predicate<object>(FilterBySearch);
}
This is the filter function, I was thinking of adding another property to my SentenceLists object called sortWord and sorting on that. But am having trouble getting it to work.
private bool FilterBySearch(object item)
{
ServiceTemplateController.SentenceList sl = item as ServiceTemplateController.SentenceList;
if (_searchstr.ToUpper() == "") return true;
if (sl.sentence.Contains(_searchstr.ToUpper()))
{
string[] strArray = sl.sentence.Split(' ');
for (int i = 0 ; i < strArray.Count(); i++)
{
if (strArray[i].Contains(_searchstr.ToUpper()))
{
if (radioBefore.IsChecked == true)
{
if (i == 0) sl.sortWord = strArray[i];
else sl.sortWord = strArray[i - 1];
}
else //radio after is selected then.
{
if (i == strArray.Count() - 1) sl.sortWord = strArray[i];
else sl.sortWord = strArray[i + 1];
}
}
}
return true;
}
return false;
}
Ended up being pretty easy. Just added another hidden column to the datagrid bound to sortWord. I would then sort by this column using the ICollectionView sortdescriptions.
code below
private void tbSearchRun()
{
TextBox textbox = tbSearch as TextBox;
if (textbox != null)
{
_searchstr = textbox.Text;
if (_searchstr == null) return;
ICollectionView view = CollectionViewSource.GetDefaultView(SentenceLists);
view.Filter = new Predicate<object>(FilterBySearch);
view.SortDescriptions.Clear();
view.SortDescriptions.Add(new SortDescription("sortWord", ListSortDirection.Ascending));
view.Refresh();
}
}
private bool FilterBySearch(object item)
{
ServiceTemplateController.SentenceList sl = item as ServiceTemplateController.SentenceList;
if (_searchstr.ToUpper() == "") return true;
if (sl.sentence.Contains(_searchstr.ToUpper()))
{
string[] strArray = sl.sentence.Split(' ');
for (int i = 0; i < strArray.Count(); i++)
{
if (strArray[i].Contains(_searchstr.ToUpper()))
{
if (radioBefore.IsChecked == true)
{
if (i == 0) sl.sortWord = strArray[i];
else sl.sortWord = strArray[i - 1];
}
else //radio after is selected then.
{
if (i == strArray.Count() - 1) sl.sortWord = strArray[i];
else sl.sortWord = strArray[i + 1];
}
break;
}
}
return true;
}
return false;
}

How to Show AutoComplete Programmatically without entering a text

C# TextBox
AutoCompleteCustomSource has a List<string>,
AutoCompleteMode = Suggest.
I can see the List when I type a Letter.
How to show entire list without Typing a Letter Programmatically? This must be done while the User presses the Down Arrow Key in the TextBox.
Is there any Win32 API Available?
My Solution
I refined a Better Solution.
Add a ListBox Control to the form and make it as Visible = false
int curSelIndex = -1;
The below given Code will be executed Form_Load Event.
txtEmpId.AutoCompleteCustomSource.AddRange(EmpIds.ToArray());
lstAutoComplete.Items.Clear();
lstAutoComplete.Items.AddRange(EmpIds.ToArray());
txtEmpId.KeyDown += (ks, ke) =>
{
if (!(ke.KeyCode == Keys.Down ||
ke.KeyCode == Keys.Up ||
ke.KeyCode == Keys.Enter))
{
lstAutoComplete.Visible = false;
return;
}
ke.Handled = true;
if (ke.KeyCode == Keys.Enter)
{
if (lstAutoComplete.Visible)
{
var str = lstAutoComplete.SelectedItem + "";
// Process the Selected Item and set to TextBox.
}
}
if (!lstAutoComplete.Visible && txtEmpId.Focused)
{
var loc = txtEmpId.Location;
loc.Y += txtEmpId.Height;
lstAutoComplete.Location = loc;
lstAutoComplete.Size = txtEmpId.Size;
lstAutoComplete.Height = 100;
lstAutoComplete.SelectedIndex = 0;
curSelIndex = 0;
lstAutoComplete.Visible = true;
}
else if(lstAutoComplete.Visible && txtEmpId.Focused)
{
if (ke.KeyCode == Keys.Down)
{
curSelIndex++;
if (curSelIndex >= lstAutoComplete.Items.Count)
curSelIndex = lstAutoComplete.Items.Count - 1;
if (lstAutoComplete.Items.Count > 0)
lstAutoComplete.SelectedIndex = curSelIndex;
}
else if (ke.KeyCode == Keys.Up)
{
curSelIndex--;
if (curSelIndex < 0)
curSelIndex = 0;
if (lstAutoComplete.Items.Count > 0)
lstAutoComplete.SelectedIndex = curSelIndex;
}
}
};
txtEmpId.Leave += (ls, le) => lstAutoComplete.Visible = false;
I didn't find any API for your problem, so I just make a my own suggestion box by using ListBox to show when the Down Arrow Key is pressed, when you do other operation, it disappeares. I hope it is useful to you. code sample is bellow:
//string datasource
List<string> strList = null;
//suggestion listbox
ListBox sugBox = null;
public FrmTextSuggest()
{
InitializeComponent();
//setting the textbox control
strList = new List<string>()
{
"USA",
"England",
"China",
"Japan",
"Korea",
"India",
"France",
"Canada"
};
var autoCollection = new AutoCompleteStringCollection();
autoCollection.AddRange(strList.ToArray());
this.txtCountry.AutoCompleteCustomSource = autoCollection;
this.txtCountry.AutoCompleteMode = AutoCompleteMode.Suggest;
this.txtCountry.AutoCompleteSource = AutoCompleteSource.CustomSource;
//register the Down Arrow Key event
this.txtCountry.KeyDown += new KeyEventHandler(txtCountry_KeyDown);
}
void txtCountry_KeyDown(object sender, KeyEventArgs e)
{
//show the your own suggestion box when pressing down arrow and the text box is empty
if (e.KeyCode == Keys.Down && txtCountry.Text.Trim().Equals(""))
{
sugBox = new ListBox();
//define the box
sugBox.Width = txtCountry.Width;
Point p = txtCountry.Location;
p.Y += txtCountry.Height;
sugBox.Location = p;
sugBox.Items.AddRange(strList.ToArray());
//copy the value to the textbox when selected index changed.
sugBox.SelectedIndexChanged += new EventHandler(sugBox_SelectedIndexChanged);
//show box
if (sugBox.Items.Count > 0)
{
sugBox.SelectedIndex = 0;
this.Controls.Add(sugBox);
sugBox.Focus();
}
}
//remove and hide your own suggestion box when other operation
else
{
if (sugBox != null && this.Controls.Contains(sugBox))
{
this.Controls.Remove(sugBox);
sugBox.Dispose();
sugBox = null;
}
}
}
void sugBox_SelectedIndexChanged(object sender, EventArgs e)
{
string selText = this.sugBox.SelectedItem.ToString();
if (!string.IsNullOrEmpty(selText))
{
this.txtCountry.Text = selText;
}
}
here is my result of test:

Count of the matched items WPF

I have a listview which binds the customer infos. Also, I have a textbox which provides searching in the listview. If the textbox's typed character or symbol matches with a datalist item then it highlights the matched item. Concerning this, I want to count the matched items number. But every search the count is "0". When I debug it then the count is true in the foreach loop but it is always zero in the TxtSearch_PreviewKeyDown. Please have a look. How can I found out the matched item amount?
private int highlightcount;
public int highlightCount
{
get;
set;
}
private void FindListViewItem(DependencyObject obj)
{
try
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
ListViewItem lv = obj as ListViewItem;
if (lv != null)
{
HighlightText(lv);
}
else
FindListViewItem(VisualTreeHelper.GetChild(obj as DependencyObject, i));
}
}
catch
{
MessageBox.Show("Bevor Sie die Suche Stauten, wählen Sie bitte eine Adresse in der linken Spalte aus.");
}
}
/// <summary>
/// Method for highlighting matched listview item
/// </summary>
/// <param name="itx"></param>
public void HighlightText(Object itx)
{
try
{
if (itx != null)
{
if (itx is TextBlock)
{
Regex regex = new Regex("(" + TxtSearch.Text + ")", RegexOptions.IgnoreCase);
TextBlock tb = itx as TextBlock;
if (TxtSearch.Text.Length == 0)
{
string str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
string[] substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
highlightCount = 0;
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
Run runx = new Run(item);
runx.Background = Brushes.Lime;
tb.Inlines.Add(runx);
highlightCount++;
if (tb.IsMouseOver)
{
tb.IsEnabled = false;
}
}
else
{
tb.Inlines.Add(item);
tb.IsEnabled = false;
}
}
return ;
}
else
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(itx as DependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(itx as DependencyObject, i));
}
}
}
}
catch
{
MessageBox.Show("Suche Error");
}
}
private void TxtSearch_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (TxtSearch.Text.Length > 1 && e.Key==Key.Enter)
{
Mouse.OverrideCursor = Cursors.Wait;
ListControl lc = getactivListview();
FindListViewItem(lc);
Mouse.OverrideCursor = null;
MessageBox.Show(highlightCount.ToString());
FocusManager.SetFocusedElement(this, TxtSearch);
}
Try removing highlightCount = 0; from your HighlightText method and add it to the TxtSearch_PreviewKeyDown Eventhandler (over ListControl lc = getactivListview(); or under MessageBox.Show(highlightCount.ToString());)

Categories

Resources