Search in ListView c# - c#

I've wrote a method to search thru ListView for a given string and mark the ones that are found with Color. It works fine however with lots of information on screen and scrollable ListView it's sometimes hard to find what user is looking for.
Normally I do create special searches by modifying method and SQL query WHERE clause but it's always a pain and requires more work/code for each ListView/Data.
I would like to have some generalized search that would work for all kind of searches in ListView just like one I have now but with ability to hide (rows) what's not needed and show only necessary rows. Of course if one changes it's mind it has to bring back the old rows back.
I guess the biggest problem for me is how to store all the columns and data without over complicating knowing that it can be 3 to 20+ columns and multiple rows.
public static void wyszukajNazweListView(ListView varListView, string varWyszukaj) {
if (varWyszukaj != "") {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
string textToAdd2 = drv.Text;
if (textToAdd2.Length >= 1) {
if (textToAdd2.ToLower().Contains(varWyszukaj.ToLower())) {
drv.BackColor = Color.DarkOrange;
} else {
drv.BackColor = Color.White;
}
}
}
bool varColor = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
if (drv.BackColor == Color.DarkOrange) {
varColor = true;
break;
}
}
if (varListView.SmallImageList != null) {
if (varColor) {
comp.ImageIndex = 2;
} else {
comp.ImageIndex = -1;
}
}
}
} else {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
comp.BackColor = Color.White;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
drv.BackColor = Color.White;
comp.ImageIndex = -1;
}
}
}
}

I'd probably store it as a DataTable object. DataTable type allows setting its rows as hidden (e.g. Visible = false) and you can bind your ListView directly to it.
EDIT: noticed the WinForms tag. Even simpler: no need to mock about with ViewState/Session.

Related

C# can't update ComboBox when Item List is filled

I have a function which clears all Comboboxes on the form and then try to update the text.
The new text is only shown if no Items are in the Item list.
Does anyone have an idea whats wrong?
I deleted all functions from the project so that only the necessary part is available.
https://www.dropbox.com/s/ibst7enrteyk9jb/Digitales_Auftragsformular.zip?dl=0
The project is attached. Simply start, go to tab "Werkzeuganfrage" there are two Comboboxes in red. One without Items = working, one with Items which is not working.
public Oberflaeche()
{
InitializeComponent();
List<ComboBox> myComboBoxes = GetControlsByType<ComboBox>(this, typeof(ComboBox));
foreach (ComboBox txt in myComboBoxes)
{
//txt.Text = "";
txt.SelectedIndex = -1;
}
Werkzeuganfrage_Combobox_rhino1_1.Text = "Rhino1_1";
Werkzeuganfrage_Combobox_rhino1_2.Text = "Rhino1_2";
comboBox1.Text = "Rhino1_1";
comboBox2.Text = "Rhino1_2";
}
public List<T> GetControlsByType<T>(Control container, Type type)
{
List<T> result = new List<T>();
foreach (Control c in container.Controls)
{
if (c.GetType() == type)
{
result.Add((T)Convert.ChangeType(c, typeof(T)));
}
if (c.HasChildren)
{
result.AddRange(GetControlsByType<T>(c, type));
}
}
return result;
}

Run code after multiple events completed

I have an event set up to fire when an item in a ListView is checked. The event calls a function which updates various controls inside my form. Among other things, I need to enable or disable buttons based on how many items are checked. This function is quite expensive.
Example:
private void listView_ItemCheck(object sender, ItemCheckEventArgs e)
{
UpdateForm();
}
Now the problem arises when a user wants to check many items at once. This causes the application to be unresponsive for a little while.
So, I would like to call UpdateForm() once after all the items were checked, instead of every time a single item is checked.
EDIT:
Here's a part of UpdateForm():
private void UpdateForm()
{
// Puts all files in the mod list in a new list
List<string> modListFiles = new List<string>(lvFiles.Items.Count);
foreach (ListViewItem lvi in lvFiles.Items)
{
modListFiles.Add(lvi.Text);
}
// Adds found files to the file list
foreach (string file in Files)
{
lvFiles.Items.Add(new ListViewItem(file));
}
// Removes files from mod list that no longer exist
List<string> deleteQue = new List<string>(lvFiles.Items.Count);
foreach (string file in modListFiles)
{
// If a file in the list doesn't exist anymore, que it to delete
if (!Files.Contains(file))
{
deleteQue.Add(file);
}
}
// Remove queued files
foreach (string file in deleteQue)
{
foreach (ListViewItem lvi in lvFiles.Items)
{
if (lvi.Text == file)
{
lvFiles.Items.Remove(lvi);
break;
}
}
}
// Grays out mod list if a profile is installed
if (InstalledProfile == null)
{
lvFiles.BackColor = SystemColors.Window;
lvFiles.ForeColor = SystemColors.WindowText;
}
else
{
lvFiles.BackColor = SystemColors.Control;
lvFiles.ForeColor = SystemColors.ControlDark;
}
// Fills out the game path if it exists
if (Directory.Exists(GamePath))
{
txtGamePath.Text = GamePath;
}
else
{
txtGamePath.Text = "Game directory does not exist!";
}
// Makes sure that the cbxProfiles_SelectedIndexChanged doesn't run UpdateForm() again
handleProfileChanged = false;
// Adds profiles to the combobox
foreach (string profile in Profiles)
{
if (!cbxProfiles.Items.Contains(profile))
{
cbxProfiles.Items.Add(profile);
}
}
// Removes nonexistant profiles from the combobox
foreach (string profile in cbxProfiles.Items)
{
if (!Profiles.Contains(profile))
{
cbxProfiles.Items.Remove(profile);
}
}
if (InstalledProfile == null)
{
btnInstallUninstall.Text = "Install";
}
else
{
btnInstallUninstall.Text = "Uninstall";
}
if (Directory.Exists(GamePath) && lvFiles.CheckedItems.Count > 0)
{
btnInstallUninstall.Enabled = true;
}
else
{
btnInstallUninstall.Enabled = false;
}
}
I've had to simplify some things so please excuse any errors I have probably made.
Some context:
I'm trying to make a program that copies files from a set directory \mods to a user specified directory GamePath. It shows all files found in \mods, then allows the user to check some of them. Clicking btnInstall will copy these files to GamePath. After this so called installing, the copied files can be removed by clicking btnInstall again.
All of the properties I made (Profiles, GamePath) get and set their values using an XML file on disk. The main ListView is called lvFiles, sometimes called mod list or file list in comments.
I've managed to speed the process of checking files up considerably by not calling UpdateForm(). Instead, I made a function UpdateButtons() that only enables/disables buttons.
This way, we do not call UpdateForm() until either the form is activated or the process exits.
Although the current code is far from perfect, all of your help was very useful and is greatly appreciated. I will probably think about the updating mechanism a bit more and apply some good threading later on.
Thank you all!
Here is the code if you want to see it:
private void UpdateButtons()
{
#region btnOpenPath
if (Directory.Exists(GamePath))
{
btnOpenPath.Enabled = true;
}
else
{
btnOpenPath.Enabled = false;
}
#endregion
#region btnInstallUninstall
if (InstalledProfile == null)
{
btnInstallUninstall.Text = "Install";
}
else
{
btnInstallUninstall.Text = "Uninstall";
}
if (Directory.Exists(GamePath) && lvFiles.CheckedItems.Count > 0)
{
btnInstallUninstall.Enabled = true;
}
else
{
btnInstallUninstall.Enabled = false;
}
#endregion
#region btnDelete, btnCheckAll, btnUncheckAll
if (InstalledProfile == null)
{
btnDelete.Enabled = true;
btnCheckAll.Enabled = true;
btnUncheckAll.Enabled = true;
}
else
{
btnDelete.Enabled = false;
btnCheckAll.Enabled = false;
btnUncheckAll.Enabled = false;
}
#endregion
}

Listitems and if Statements with charts

I have a chart in which i want to be able to add or remove lines based on a checkboxlist.
To this end i came up with this code:
foreach (ListItem lstitem in CBLYear.Items)
{
if (lstitem.Value == "2014")
{
Chart3.Series["2014"].XValueMember = "Month";
Chart3.Series["2014"].YValueMembers = "2014";
}
if (lstitem.Value == "2013")
{
Chart3.Series["2013"].XValueMember = "Month";
Chart3.Series["2013"].YValueMembers = "2013";
}
if (lstitem.Value == "2012")
{
Chart3.Series["2012"].XValueMember = "Month";
Chart3.Series["2012"].YValueMembers = "2012";
}
if (lstitem.Value == "2011")
{
Chart3.Series["2011"].XValueMember = "Month";
Chart3.Series["2011"].YValueMembers = "2011";
}
if (lstitem.Value == "2010")
{
Chart3.Series["2010"].XValueMember = "Month";
Chart3.Series["2010"].YValueMembers = "2010";
}
if (lstitem.Value == "2009")
{
Chart3.Series["2009"].XValueMember = "Month";
Chart3.Series["2009"].YValueMembers = "2009";
}
}
On page load nothing shows up on the graph (as wanted). However once click any one of the items all the series show up not just the one i clicked why is that?
you need to use else if from 2nd condition, you should use switch statement as in your code you are checking many conditions, in switch it will execute only the particular case
switch(Convert.ToString(lstitem.Value))
{
case"":
{
// put all your conditions in cases
}
break;
default :
break;
}
You are iterating over the entire items collection, so of course each series will show up.
You need to check the "Selected" property instead. You can do this easily using LINQ:
foreach (ListItem lstitem in CBLYear.Items.Where(l => l.Selected))
{
....
}
See the MSDN Documentation: CheckBoxList (Specifically the "Introduction" section)

Retain the user defined sort order in WPF DataGrid

I have a WPF DataGrid that is populated with data from DataSet. I have CanUserSortColumns set to true.
Is it possible to retain the sorting that the user specified when the grid is refreshed? I have it retaining the item that was selected using
object selectedItem = dgInvoiceHeads.SelectedItem;
before the refresh takes place and then placing
dgInvoiceHeads.SelectedItem = selectedItem;
after the refresh takes place.
But I can't seem to get it to retain the specified sort.
The following code was pulled from this forum post and it shows how to obtain the sort descriptions and column information and restore it.
List<DataGridColumn> GetColumnInfo(DataGrid dg) {
List<DataGridColumn> columnInfos = new List<DataGridColumn>();
foreach (var column in dg.Columns) {
columnInfos.Add(column);
}
return columnInfos;
}
List<SortDescription> GetSortInfo(DataGrid dg) {
List<SortDescription> sortInfos = new List<SortDescription>();
foreach (var sortDescription in dg.Items.SortDescriptions) {
sortInfos.Add(sortDescription);
}
return sortInfos;
}
void SetColumnInfo(DataGrid dg, List<DataGridColumn> columnInfos) {
columnInfos.Sort((c1, c2) => { return c1.DisplayIndex - c2.DisplayIndex; });
foreach (var columnInfo in columnInfos) {
var column = dg.Columns.FirstOrDefault(col => col.Header == columnInfo.Header);
if (column != null) {
column.SortDirection = columnInfo.SortDirection;
column.DisplayIndex = columnInfo.DisplayIndex;
column.Visibility = columnInfo.Visibility;
}
}
}
void SetSortInfo(DataGrid dg, List<SortDescription> sortInfos) {
dg.Items.SortDescriptions.Clear();
foreach (var sortInfo in sortInfos) {
dg.Items.SortDescriptions.Add(sortInfo);
}
}
Have you tried getting the collectionview for the dataset?
CollectionViewSource.GetDefaultView(yourDataSet).SortDescriptions
This will give you an array of the current sortdescriptions. You can then persist these, and the next time round apply them as follows
CollectionViewSource.GetDefaultView(yourDataSet).SortDescriptions.Add(...)
Hope it helps.
One of my colleagues came up with this. It seems to be working correctly. The only thing is I think the column headers need to be the same in the DataGrid as they are in the DB.
string sortHeader;
string prevSortHeader;
SortDescription sd;
private void dgInvoiceHeads_Sorting(object sender, DataGridSortingEventArgs e) {
sortHeader = e.Column.Header.ToString();
if (sortHeader == prevSortHeader) {
sd = new SortDescription(sortHeader, ListSortDirection.Descending);
}
else {
sd = new SortDescription(sortHeader, ListSortDirection.Ascending);
}
prevSortHeader = sortHeader;
}
HTH
private void testGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
ListSortDirection direction = (e.Column.SortDirection != ListSortDirection.Ascending) ?
ListSortDirection.Ascending : ListSortDirection.Descending;
// You will get the current direction in direction
}
This is another solution

Compare datatables

I built an application which displays the records from database in the window and checks the the database for new records every couple of seconds. The problem is that the window blinks each time I check for new records and I want to fix it. I have tried to compare the old datatable with the new one and refresh only if they are different.
Does anyone know what is the best practice for such cases? I tried to do it the following way but it doesn't work:
private bool GetBelongingMessages()
{
bool result = false;
DataTable dtTemp = OleDbWorks.GetBelongingMessages(currentCallID);
if(dtTemp != dtMessages)
{
dtMessages = dtTemp;
result = true;
}
else
{
result = false;
}
return result;
}
First off, it's important to recognize that what you're comparing in your code is the references of the datatables, not the contents of the datatables. In order to determine if both datatables have the same contents, you're going to have to loop through all of the rows and columns and see if they're equal:
//This assumes the datatables have the same schema...
public bool DatatablesAreSame(DataTable t1, DataTable t2) {
if (t1.Rows.Count != t2.Rows.Count)
return false;
foreach (DataColumn dc in t1.Columns) {
for (int i = 0; i < t1.Rows.Count; i++) {
if (t1.Rows[i][dc.ColumnName] != t2.Rows[i][dc.ColumnName]) {
return false;
}
}
}
return true;
}
I've been trying to find a way to do DataTable comparison for a while and ended up writing up my own function, here is what I got:
bool tablesAreIdentical = true;
// loop through first table
foreach (DataRow row in firstTable.Rows)
{
foundIdenticalRow = false;
// loop through tempTable to find an identical row
foreach (DataRow tempRow in tempTable.Rows)
{
allFieldsAreIdentical = true;
// compare fields, if any fields are different move on to next row in tempTable
for (int i = 0; i < row.ItemArray.Length && allFieldsAreIdentical; i++)
{
if (!row[i].Equals(tempRow[i]))
{
allFieldsAreIdentical = false;
}
}
// if an identical row is found, remove this row from tempTable
// (in case of duplicated row exist in firstTable, so tempTable needs
// to have the same number of duplicated rows to be considered equivalent)
// and move on to next row in firstTable
if (allFieldsAreIdentical)
{
tempTable.Rows.Remove(tempRow);
foundIdenticalRow = true;
break;
}
}
// if no identical row is found for current row in firstTable,
// the two tables are different
if (!foundIdenticalRow)
{
tablesAreIdentical = false;
break;
}
}
return tablesAreIdentical;
Compared to Dave Markle's solution, mine treats two table with same records but in different orders as identical. Hope this helps whoever stumbles upon this thread again.
You have to cast objects t1.Rows[i][dc.ColumnName] and t1.Rows[i][dc.ColumnName] otherwise the statement t1.Rows[i][dc.ColumnName] != t2.Rows[i][dc.ColumnName] is always true. I modified the code the following way:
for(int i = 0; i < t1.Rows.Count; i++)
{
if((string)t1.Rows[i][1] != (string)t2.Rows[i][1])
return false;
}
and it works but it's not an elegant solution.
public Boolean CompareDataTables(DataTable table1, DataTable table2)
{
bool flag = true;
DataRow[] row3 = table2.Select();
int i = 0;// row3.Length;
if (table1.Rows.Count == table2.Rows.Count)
{
foreach (DataRow row1 in table1.Rows)
{
if (!row1.ItemArray.SequenceEqual(row3[i].ItemArray))
{
flag = false;
break;
}
i++;
}
}
else
{
flag = false;
}
return flag;
}
// here this function will gave boolean as result return true if both are same else return false if both are not same

Categories