Search Listview subitem with string - c#

I have a Listview that contains all the guests from the database
public FormGuestManagement()
{
InitializeComponent();
listViewGuests.View = View.Details;
listViewGuests.GridLines = true;
listViewGuests.Scrollable = true;
listViewGuests.FullRowSelect = true;
listViewGuests.HideSelection = false;
var guests = Repository.GetAllGuests();
foreach (var guest in guests)
{
ListViewItem lvData = new ListViewItem(guest.AccountID.ToString());
lvData.SubItems.Add(guest.Username);
lvData.SubItems.Add(guest.Email);
lvData.SubItems.Add(guest.FirstName);
lvData.SubItems.Add(guest.LastName);
lvData.SubItems.Add(guest.TelephoneNumber);
lvData.SubItems.Add(guest.AddressLine1);
lvData.SubItems.Add(guest.AddressLine2);
lvData.SubItems.Add(guest.City);
lvData.SubItems.Add(guest.State);
lvData.SubItems.Add(guest.Postcode);
lvData.SubItems.Add(guest.Country);
listViewGuests.Items.Add(lvData);
}
}
How would it be possible to search one single column (in this case guest.Firstname) with a string and only show the values that match the string (hide the others) in the listview?
Thank you for your time!

The ListView control does not provide the functionality you want. In order to simulate what you describe, you would have to rebuild the desired list every time a filter condition changes.
Or you could do as one commenter suggested and use something more robust, such as a DataGridView. This control provides true Row/Column behaviors and takes a DataSource that can be bound.

It's possible with LINQ and should be very easy:
using System.Linq;
var result = guests.Where(guest => guest.FirstName.Equals("Test"));

Related

C# WinForms ComboBox: AutoComplete does not sort descending

In a WinForms dataviewer project I've made a ComboBox to select a filter value. The list items come from of a database query. They are sorted descending. The ComboBox uses AutoCompleteMode.Append. Although the dropdown list is sorted descending, the AutoComplete always suggests the lowest matching value instead of the highest. This happens even if I explicitly populate the AutoCompleteCustomSource with descending data.
Does anyone know how to make the AutoComplete suggesting the highest matching value?
The ComboBox looks like this after typing "010":
This is a part of the dropdown list:
...
012-0020-00
010-0070-00
010-0069-00
010-0068-00
008-1018-00
...
Why this matters:
I will use this filter for various string data containing numbers, like parts codes, document codes, project codes etc. Newer entries have higher numbers. And the newest entries are queried most often. In the above example, 010-0070-00 ist the newest part code of the 010 group. Therefore I expect the AutoComplete to show 010-0070-00 after I have typed 010.
This project replaces an MS Access front end. An Access ComboBox suggests the highest value if the list is sorted descending resp. the lowest value if sorted ascending. But Access ComboBoxes are not WinForms controls.
Any suggestions are welcome.
Example using a ToolStripDropDown:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
String[] data = "012-0020-00,010-0070-00,010-0069-00,010-0068-00,008-1018-00".Split(',');
ComboBox combo = new ComboBox();
//ToolStripDropDownMenu menu = new ToolStripDropDownMenu(); // has image/icon border
ToolStripDropDown menu = new ToolStripDropDown(); // plain menu, no image/icon border
menu.AutoClose = false;
bool isAdjusting = false;
combo.LostFocus += delegate {
if (!isAdjusting)
menu.Close();
};
menu.ItemClicked += (o, e) => {
isAdjusting = true;
combo.Text = e.ClickedItem.Text;
menu.Close();
isAdjusting = false;
};
combo.TextChanged += delegate {
if (isAdjusting)
return;
isAdjusting = true;
String prefix = combo.Text;
menu.SuspendLayout();
for (int i = menu.Items.Count - 1; i >= 0; i--) {
var item = menu.Items[i];
menu.Items.RemoveAt(i);
item.Dispose();
}
foreach (String part in data) {
if (part.StartsWith(prefix))
menu.Items.Add(new ToolStripMenuItem(part));
}
menu.ResumeLayout();
menu.Show(combo, 0, combo.Height);
combo.Focus();
combo.SelectionStart = prefix.Length;
isAdjusting = false;
};
Form f7 = new Form();
f7.Controls.Add(combo);
Application.Run(f7);

How to remember the sorted

In a "Refresh" function for a WPF DataGrid, I'm trying to save the sorting criteria prior to the reload of items to the DataGrid, the new items loaded and then set the previous sorting criteria. I'm using this code:
void Refresh(DataGrid docsDataGrid) {
var sd = ListSortDirection.Ascending;
DataGridColumn sortCol = null;
foreach (var column in DocsDataGrid.Columns)
{
if (column.SortDirection != null)
{
sd = (ListSortDirection)column.SortDirection;
sortCol = column;
}
}
docsDataGrid.ItemsSource = GetLatestItems();
sortCol.SortDirection = sd;
}
With the code above, I do get the visual cue of "sorted column", but the newly loaded items are not really sorted, so it is a bit of a mirage. I tried with docsDataGrid.Items.Refresh() after sortCol.Direction = sd but the datagrid does not pick up the sorting direction set programatically.
How can I leverage the built-in sorting facilities of the DataGrid and programatically set the column to be sorted (and actually sort rows by that column)?
After some additional research the SortDataGrid example in this SO Q&A helped me: Sort a wpf datagrid programmatically
It seems that I should have dealt also with DataGridItems.SortDescriptions as well.
Not sure if this will help you but why not save the selected SortDirection in a setting and load it when you need it?

Cannot retrieve multiple values from a listbox c#

I am populating a listBox at runtime from a database as follows:
List<FILE_REPORT_TYPES> ReportTypes = GetReportTypesFromDatabase(ReportMappingIds)
BindingList<FILE_REPORT_TYPES> pbReportTypesBindingList = new BindingList<FILE_REPORT_TYPES>(ReportTypes);
listBoxReports.DataSource = ReportTypesBindingList;
listBoxReports.DisplayMember = "REPORT_DESCRIPTION";
listBoxReports.ValueMember = "REPORT_ID";
I then would like select multiple items on the listBox when running the windows form and retrieve each individual Value of my selections. If only one selection is made one could do the following:
listBoxReports.SelectedValue;
I would like to do the following:
var list = listBoxReports.SelectedValues;
However this is not allowed i.e. "SelectedValues" does not exist.
Some people are erroneously suggesting that in this particular case SelectedIndices may be used. It cannot be used, I am trying to retrieve the "VALUE". This cannot be done (in this particular case):
listBox.Items[i].Value;
I think the solution should be along the lines of:
foreach(var line in listBox.Items)
{
var res= ((SOME CASTING)line).Value;
}
To get the selected items you have 2 options
a.) ListBox.SelectedIndices which returns the indices of the selected items which you then need to use to look up in the Items property what the value is or
b.) ListBox.SelectedItems which returns you a collection with the selected items themselves (be aware that it is an objectlist so you need to transform the items into your appropriate datatype).
Edit: With the additional information the following is possible
List<FILE_REPORT_TYPES> mySelectedList = new List<FILE_REPORT_TYPES>();
foreach (Object selectedItem in ListBox.SelectedItems)
{
mySelectedList.Add( ((FILE_REPORT_TYPES)selectedItem) );
}
You can use ListBox.SelectedIndices or ListBox.SelectedItems.
If you want to get all selected-items, you can let the foreach cast:
foreach(FILE_REPORT_TYPES frt in listBox.SelectedItems)
{
// ...
}
or if you want to get the ReportID into a list with the help of LINQ:
List<decimal> reportIds = listBox.SelectedItems.Cast<FILE_REPORT_TYPES>()
.Select(frt => frt.REPORT_ID)
.ToList();
Alternative to the selected value you could do the following
listBoxReports.SelectedItems;
Answer (the casting is the trick):
List<decimal> reportIds = new List<decimal>();
foreach(var line in listBoxReports.SelectedItems)
{
reportIds.Add(((PB_FILE_REPORT_TYPES)line).REPORT_ID);
}
You may try like below
List<FILE_REPORT_TYPES> reportList = new List<FILE_REPORT_TYPES>();
foreach(var item in listBox.SelectedItems)
{
reportList.Add((FILE_REPORT_TYPES)item);
}

Add items to a ListBox and bind them to an item in another Listbox

My apologies in advance, I might have difficulty in making myself clear. I have searched, but the discussions were far to complicated for me to follow, and I just started dwelling in C#.
Project: C# WindowsForm / .NET 4.5 / Visual Studio 2012
Challenge:
I want to add items from a listbox to another listbox (I can do it easily with Lists and foreach loops), and make the end listbox show specific item depending on selections made in listbox2.
Explanation:
The selected items are to incorporate a group that I create in yet another listbox, so that if I select a handful of items in listbox1, I send them to listbox3, but they only should appear when I select a specific item in listbox2.
Imagine selecting games from a list, adding it to the "Nintendo" group in a new list, so they don't get mixed with the Sega ones when Sega is selected in listbox2.
I can add values in all listboxes, copy the ones I want from 1 to 3, but I am at a loss on how to make the selection respect the selection on 2.
I've read about databinding and etc, but the examples gives were too complicated (and maybe a bit of language barrier was present), is there a resource that can provide the simplest solution to a really small project?
Care to enlighten a fool using layman terms, please?
Thanks
EDIT:
Nice of you (whoever you were) to downvote my question. Fair enough. You could at least tell me what the problem was, or where the question was answered so I could resolve my problem. Wouldn't that be nice? I am a starter in C#, so it's only natural that the first question may seem ridiculous/lazy...
DataBinding would be the way to go. The main component to focus on is BindingSource. You could also investigate DataSets for your underlying data because they would give you some flexibility with filtering. But, if this is a small application, and if you're just learning, the following example might be a good start:
Drag a BindingSource for each of your listboxes onto your form. Then, connect each of the ListBox's DataSource properties to a corresponding BindingSource.
Here is an example code behind that shows how to bind your underlying data to each of the BindingSources which are in turn already bound to the listboxes:
namespace WindowsFormsApplication1
{
public class Game
{
public string Name { get; set; }
public string Group { get; set; }
}
public class Group
{
public string Description { get; set; }
}
public partial class Form1 : Form
{
List<Game> _allGames;
public Form1()
{
InitializeComponent();
_allGames = new List<Game>
{
new Game { Name = "Alpha", Group = "" },
new Game { Name = "Bravo", Group = "One" },
new Game { Name = "Charlie" , Group = "One"},
new Game { Name = "Delta", Group = "Two" }
};
bindingSource1.DataSource = _allGames;
listBox1.DisplayMember = "Name";
listBox1.ValueMember = "Name";
var groups = new List<Group>
{
new Group { Description = "One" },
new Group { Description = "Two" },
new Group { Description = "Three" }
};
bindingSource2.DataSource = groups;
listBox2.DisplayMember = "Description";
listBox2.ValueMember = "Description";
}
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
var group = listBox2.SelectedValue.ToString();
bindingSource3.DataSource = _allGames.Where(x => x.Group == group);
listBox3.DisplayMember = "Name";
}
}
}
All this code is doing is binding data to the BindingSource and telling each ListBox which property of the underlying data to display. This example ignores the mechanism that assign each item from listBox1 to the group in listBox2 because I assume you know how to do that or can figure that out.
The event handler for when listBox2 changes just gets which selection was made and creates a list of items from listBox1 that match that item, and then displays those items in listBox3.
For the sake of simplicity, lets assume the items bound to your listboxes are strings. Then you can use a dictionary to hold your group assignment (you have to hold it somewhere):
Dictionary<string, List<string>> listOfGroups = new Dictionary<string, List<string>>();
Where key is a name of group, and list of strings holds items in group.
Then, somewhere, you create a group to which you will be assigning values
void CreateGroup(string groupName)
{
listBox2.Items.Add(groupName);
if (!listOfGroups.ContainsKey(groupName)
listOfGroups.Add(groupName, new List<string>());
}
Then, you manage items in groups and add/remove them when, for example, selection on listBox2 changes:
string item = (string)listBox1.SelectedItem;
if(!listOfGroups.ContainsKey((string)listBox2.SelectedItem))
listOfGroups.Add((string)listBox2.SelectedItem,new List<string>());
((List<string>)listOfGroups[(string)listBox2.SelectedItem]).Add(item);
listBox3.Items.Add(item);
listBox1.Items.Remove(item);
And finaly:
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
listBox3.Items.Clear();
if (!listOfGroups.ContainsKey((string)listBox2.SelectedItem))
listOfGroups.Add((string)listBox2.SelectedItem, new List<string>());
listBox3.Items.AddRange(listOfGroups[(string)listBox2.SelectedItem].ToArray());
List<string> list = listOfAllItems.Where(a => !listBox3.Items.Contains(a)).ToList();
listBox1.Items.Clear();
listBox1.Items.AddRange(list.ToArray());
}
That's only an idea, though. If you're using DataSet, maybe you can just use linq (instead of a dictionary) to select items you want to display after selection in listbox2 changes.
You can save some kind of Dictionary, key is element in first ListBox first, and value is the item of the second ListBox.

set combobox text from textbox

I have user submitted content that is loaded into c# winform in our office for processing before officially added to database. The user can submit a 'Referrer' as two text fields-first and last name. In the office I want to have a combobox will all existing referrers loaded in, then the first couple letters of the name to advance the combobox down to the area it needs to be at. I want to do something like this, taking the first two letters of the name and use that to initialize the combobox.
if (txtrefFirstName.TextLength > 2)
{
string firstStart = "" + txtrefFirstName.Text[0] + txtrefFirstName.Text[1];
firstStart = firstStart.ToUpper();
ddlReferring.SelectedText.StartsWith(firstStart);
}
else
ddlReferring.Text = "";
Any ideas or suggestions to get this to work?
Thanks
David K.
You could write something like this...
foreach (string item in ddlReferring.Items)
{
if (item.StartsWith(firstStart))
{
ddlReferring.SelectedText = item;
break;
}
}
Assuming the ddl's datasource is a List of String objects, you should be able to do some comparison on the datasource itself. I tend to use Linq for things like this but it isn't strictly necessary, just shorter.
if (txtrefFirstName.TextLength > 2)
{
string firstStart = txtrefFirstName.Text.Substring(0,2).ToUpper();
string Selection = ddlReferring.DataSource.Where(a=>a.StartsWith(firstStart)).FirstOrDefault();
ddlReferring.SelectedText = Selection ?? "";
}
else
ddlReferring.Text = "";
The selection line can also come from the items collection directly
string Selection = ddlReferring.Items.OfType<string>().Where(a=>a.StartsWith(firstStart)).FirstOrDefault();
Or if you REALLY dont want to use Linq...
string Selection = "";
foreach (object item in ddlReferring.Items)
if (item.ToString().StartsWith(firstStart))
{
Selection = item.ToString();
break;
}
Similar methods can be used even if the ddl's data is not a list of strings, just make sure to cast the items appropriately and compare the correct values.

Categories

Resources