I have a ListBox which I populate like this:
var dtCustomers = db.GetTableBySQL(query).AsEnumerable().Select(rows =>
new CustomersModel
{
Name = rows.Field<string>("Name"),
ProjectKey = rows.Field<int>("ProjectKey")
});
lstCustomers.DataSource = dtCustomers.ToList();
lstCustomers.DisplayMember = "Name";
lstCustomers.ValueMember = "ProjectKey";
lstCustomers.ClearSelected();
Now I want to create TextBox with search button to look inside this list and search by item selected as:
private void btnSearch_Click(object sender, EventArgs e)
{
lstCustomers.SelectedItems.Clear();
for (int i = lstCustomers.Items.Count - 1; i >= 0; i--)
{
if (lstCustomers.Items[i].ToString().ToLower().Contains(txtSearch.Text.ToLower()))
{
lstCustomers.SetSelected(i, true);
}
}
lblitems.Text = lstCustomers.SelectedItems.Count.ToString() + "items found";
}
Problem is it never finds anything. I think it is because it is comparing by ValueMember instead of DisplayMember. Can I search in the list by DisplayMember?
You can use pattern matching for this since the underlying items will be your CustomersModel:
private void btnSearch_Click(object sender, EventArgs e)
{
lstCustomers.SelectedItems.Clear();
int matchCount = 0;
for (int i = lstCustomers.Items.Count - 1; i >= 0; i--)
{
if (lstCustomers.Items[i] is CustomersModel customer &&
customer.Name.IndexOf(txtSearch.Text, StringComparison.OrdinalIgnoreCase) > -1)
{
matchCount++;
lstCustomers.SetSelected(i, true);
}
}
lblItems.Text = $"{matchCount} item{(matchCount > 1 ? "s" : "")} found";
}
Related
I have a listview with diffrent entries (see figure (A). I would like to extract some specific rows based on a condition. So far, i have this code:
private void Button2_Click(object sender, EventArgs e)
{
ArrayList listing = new ArrayList();
for (int i = 0; i < listView2.Items.Count; i++)
{
string columnOne = listView2.Items[i].Text;
string columnTwo = listView2.Items[i].SubItems[1].Text;
int numb = int.Parse(listView2.Items[i].SubItems[2].Text);
string columnThree = listView2.Items[i].SubItems[3].Text;
if(numb >= 2)
{
listing.Add($"{columnOne},{columnTwo},{numb},{columnThree}");
}
}
foreach (string item in listing)
{
listView2.Items.Clear();
ListViewItem listItem = new ListViewItem();
var separ = item.Split(',');
listItem.Text = separ[0].Trim();
listItem.SubItems.Add(separ[1]);
listItem.SubItems.Add(separ[2]);
listItem.SubItems.Add(separ[3]);
listView2.Items.Add(listItem);
}
}
I get figure (B), but normally i should get figure (C). How can this be achieved?
you shouldn't clear listview in foreach loop. do it once:
listView2.Items.Clear();
foreach (string item in listing)
{
// listView2.Items.Clear();
ListViewItem listItem = new ListViewItem();
var separ = item.Split(',');
listItem.Text = separ[0].Trim();
listItem.SubItems.Add(separ[1]);
listItem.SubItems.Add(separ[2]);
listItem.SubItems.Add(separ[3]);
listView2.Items.Add(listItem);
}
Removing the non matching items from the list makes more sense here. For your problem, execute a backward loop, try to convert the text of the third subitem to integer value using the int.TryParse method, and remove the ListViewItem if the value is less than 2.
private void button2_Click(object sender, EventArgs e)
{
for (var i = listView2.Items.Count - 1; i >= 0; i--)
{
if (int.TryParse(listView2.Items[i].SubItems[2].Text, out var num) && num < 2)
{
listView2.Items.RemoveAt(i);
}
}
}
Yet, if you want to get a list of matching items:
// +
using System.Collections.Generic;
private void button2_Click(object sender, EventArgs e)
{
var items = new List<ListViewItem>();
for (var i = 0; i < listView2.Items.Count; i++)
{
if (int.TryParse(listView2.Items[i].SubItems[2].Text, out var num) && num >= 2)
{
items.Add(listView2.Items[i]);
}
}
// ...
}
Or LINQ way:
// +
using System.Linq;
private void button2_Click(object sender, EventArgs e)
{
var items = listView2.Items.Cast<ListViewItem>()
.Where(x => int.TryParse(x.SubItems[2].Text, out var num) && num >= 2)
.ToList();
// ...
}
As a side note, using the ArrayList class is not recommended, use the List<T> class instead.
I have a gridview with multiple groups and I use the CustomDrawGroupRow Event to display the row count for each group:
private void gridView_CustomDrawGroupRow(object sender, DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
info.GroupText = $"{caption} : {info.GroupValueText} ({view.GetChildRowCount(e.RowHandle)})";
}
Now I would like to display the row count recursively, so that the first level shows a count of 2171 (1913 + 135 + 123).
This is what I have tried, but it throws a StackOverflowException and I cannot see the problem here:
private void gridView_CustomDrawGroupRow(object sender, DevExpress.XtraGrid.Views.Base.RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
info.GroupText = $"{caption} : {info.GroupValueText} ({GetRowCountRecursive(view, e.RowHandle)})";
}
private int GetRowCountRecursive(GridView view, int rowHandle)
{
int totalCount = 0;
int childrenCount = view.GetChildRowCount(rowHandle);
for (int i = 0; i < childrenCount; i++)
{
var childRowHandle = view.GetChildRowHandle(rowHandle, i);
totalCount += GetRowCountRecursive(view, childRowHandle);
}
return totalCount;
}
I was missing to check if childRowHandle is a group row with IsGroupRow(). If not, the recursion have to stop and totalCount need to be increased by 1.
private int GetRowCountRecursive(GridView view, int rowHandle)
{
int totalCount = 0;
int childrenCount = view.GetChildRowCount(rowHandle);
for (int i = 0; i < childrenCount; i++)
{
var childRowHandle = view.GetChildRowHandle(rowHandle, i);
if (view.IsGroupRow(childRowHandle))
{
totalCount += GetRowCountRecursive(view, childRowHandle);
}
else
{
totalCount++;
}
}
return totalCount;
}
You can use GroupRowInfo.ChildControllerRowCount property to get the row count. The instance of GroupRowInfo class you can get from GridGroupRowInfo.RowKey property.
Here is example:
private void gridView1_CustomDrawGroupRow(object sender, RowObjectCustomDrawEventArgs e)
{
var view = (GridView)sender;
var info = (GridGroupRowInfo)e.Info;
var caption = info.Column.Caption;
if (info.Column.Caption == string.Empty)
{
caption = info.Column.ToString();
}
var groupInfo = info.RowKey as GroupRowInfo;
info.GroupText = $"{caption} : {info.GroupValueText} ({groupInfo?.ChildControllerRowCount})";
}
Here is the screenshot:
Basically I have two categories:
Category - A
Category - B
I will select category - A and generate number serials from 1 to 10.
display in combobox 2 and save it.
I will then select category B and generate number serials from 1 to 10, display in combobox 2 and save it.
when I close and open back the application, I want to see only those serials generated in category A in comboBox2.
I have been trying to do it by adding a datagridview but it merges both category serials. Screenshot
Category - A -- > Serials{1,2,3,4,5,6,7,8,9,10}
Category - B -- > Serials{11,12,13,14,15,16,17,18}.
so when I select Category A, I want to see only A serials in comboBox2 and nothing else.
when I select Category B, I want to see only B serials in comboBox2 and nothing else.
private void GenSerialBookButton_Click(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
{
from = int.Parse(textBox2.Text);
to = int.Parse(textBox3.Text);
result = to - from;
for (int i = 0; i <= result; i++)
{
comboBox2.Items.Add(from + i);
this.SerialBookDataBaseBindingSource.AddNew();
dataGridView1.Rows[i].Cells[1].Value = from + i;
}
MessageBox.Show("Serial Book Generated Success", "Success");
}
if (comboBox1.SelectedIndex == 1)
{
from = int.Parse(textBox2.Text);
to = int.Parse(textBox3.Text);
result = to - from;
for (int i = 0; i <= result; i++)
{
comboBox2.Items.Add(from + i);
this.SerialBookDataBaseBindingSource.AddNew();
dataGridView1.Rows[i].Cells[1].Value = from + i;
}
MessageBox.Show("Serial Book Generated Success", "Success");
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
// Clear previous list
a: if (comboBox2.Items.Count > 0)
{
comboBox2.Items.RemoveAt(0);
goto a;
}
if (comboBox1.SelectedIndex == 0)
{
comboBox2.Items.Clear();
foreach (DataGridViewRow row in dataGridView1.Rows)
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
comboBox2.Items.Add(row.Cells[1].Value);
MessageBox.Show("Adding: " + (row.Cells[1].Value.ToString()));
comboBox2.Refresh();
}
}
if (comboBox1.SelectedIndex == 1)
{
comboBox2.Items.Clear();
foreach (DataGridViewRow row in dataGridView1.Rows)
{
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
comboBox2.Items.Add(row.Cells[1].Value);
MessageBox.Show("Adding: " + (row.Cells[1].Value.ToString()));
comboBox2.Refresh();
}
}
}
}
}
Here is a quick sample for you;
I tried to set a form like you (not have textboxes to decide "to and from", I gave it myself directly)
Here is the trick, I defined it on global,
List<KeyValuePair<int, string>> vals = new List<KeyValuePair<int, string>>();
In FormLoad I add 2 string categories,
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add("CategoryA");
comboBox1.Items.Add("CategoryB");
}
And I have a button to generate serials,
private void button1_Click(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
{
for (int i = 0; i <= 10; i++)
{
string item = i + "A"; // Given"A" to seperate from each other
comboBox2.Items.Add(item);
vals.Add(new KeyValuePair<int, string>(0, item)); // CatA has 0 key value
}
MessageBox.Show("Serial Book Generated Success", "Success");
}
if (comboBox1.SelectedIndex == 1)
{
for (int i = 0; i <= 5; i++)
{
string item = i + "B"; // Given "B" to seperate from each other
comboBox2.Items.Add(item);
vals.Add(new KeyValuePair<int, string>(1, item)); // CatB has 1 key value
}
MessageBox.Show("Serial Book Generated Success", "Success");
}
}
And combobox's selectedindexchanged event, (Category's combobox)
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
{
comboBox2.Items.Clear();
foreach (var item in vals)
{
if (item.Key == 0) //If Key value is 0, if it is CategoryA
{
comboBox2.Items.Add(item.Value);
// MessageBox.Show("Adding: " + (item.Value.ToString()));
comboBox2.Refresh();
}
}
}
if (comboBox1.SelectedIndex == 1)
{
comboBox2.Items.Clear();
foreach (var item in vals)
{
if (item.Key == 1) //If Key value is 1, if it is CategoryB
{
comboBox2.Items.Add(item.Value);
//MessageBox.Show("Adding: " + (item.Value.ToString()));
comboBox2.Refresh();
}
}
}
}
Outputs,
Hope Helps,
I have three combobx for time and I want to edit the text of this comboboxes from another view this is the code for combobpx view
public partial class Combo : Window
{
public Combo()
{
InitializeComponent();
}
private void hrComboBox_Loaded(object sender, RoutedEventArgs e)
{
List<string> hours = new List<string>();
for (int i = 0; i <= 24; i++)
{
if (i < 10)
hours.Add("0" + i);
else
hours.Add(i + "");
}
var comboBox = sender as ComboBox;
comboBox.ItemsSource = hours;
comboBox.SelectedIndex = 0;
}
private void MinComboBox_Loaded(object sender, RoutedEventArgs e)
{
List<string> Minutes = new List<string>();
for (int i = 0; i <= 60; i++)
{
if (i < 10)
Minutes.Add("0" + i);
else
Minutes.Add(i + "");
}
var comboBox = sender as ComboBox;
comboBox.ItemsSource = Minutes;
comboBox.SelectedIndex = 0;
}
private void SecComboBox_Loaded(object sender, RoutedEventArgs e)
{
List<string> Seconds = new List<string>();
for (int i = 0; i <= 60; i++)
{
if (i < 10)
Seconds.Add("0" + i);
else
Seconds.Add(i + "");
}
var comboBox = sender as ComboBox;
comboBox.ItemsSource = Seconds;
comboBox.SelectedIndex = 0;
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
the second view invokes this view as dialog my problem is when I add the text to one of this comboboxes the text isn't set and still "00" the code I use as follows:
var dialog = new Combo();
string[] timeArr = delay.Split(':');
//here is my problem when the dialog is loading the text
//of three comboboxes isn't change
dialog.hr.Text = timeArr[0];
dialog.Min.Text = timeArr[1];
dialog.Sec.Text = timeArr[2];
if (dialog.ShowDialog() == true)
{
string time = dialog.hr.Text+":"+dialog.Min.Text+":"+dialog.Sec.Text;
delay = time;
nextDelay = time;
}
anyone help me, thanks :)
The thing I want to do is that i want to select 1,2 or 3 items from the listbox and save them into a session and then display them all on another form in a listbox.
Here's my code!
This is my first post on stack overflow, so no hate please <3
//WebForm1
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lstProducts.Items.Add("Soap");
lstProducts.Items.Add("Schampoo");
lstProducts.Items.Add("Conditioner");
}
}
protected void cmdBuy_Click(object sender, EventArgs e)
{
string[] products = new string[3];
for (int i = 0; i < lstProducts.Items.Count; ++i)
{
if (lstProducts.Items[i].Selected)
products[i] = lstProducts.Items[i].Text;
else
products[i] = "0";
}
Session["Cart"] = products;
}
protected void cmdCart_Click(object sender, EventArgs e)
{
if (Session["Cart"] != null)
{
Response.Redirect("WebForm2.aspx");
}
}
}
//WebForm2
protected void Page_Load(object sender, EventArgs e)
{
string[] products = (string[])Session["Cart"];
for (int i = 0; i < 3; ++i)
{
if (products[i] != "0")
{
lstCart.Items.Add(products[i]);
}
}
}
}
}
The thing is that I only get the last selected item to display in the listbox on form2???
Try this
To store all items of the of the list box, you can add that items in array as:
string[] a = new string[]{"item 1","item 2","item 3"};
Session["values"] = a;
And in the next page, you can retrieve it like this.
string[] a = (string[])Session["values"]
EDIT #1
your case you can do like
ArrayList al = new ArrayList();
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected == true)
{
al.Add(ListBox1.Items[i].Value);
}
}
Session["selectedValues"] = al;
now you can use this sessiom variable in another page, but don't forget to cast in ArrayList type of object.