I have an assignment about ListView sort by Column using C# Windows Form and the codes that I got from MSDN didn't work. Can anybody find out what's wrong with the codes? Everytime I click the ListView Column nothing happens.
Here's the code, I also added the items that will show in my ListView
private int sortColumn = -1;
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine whether the column is the same as the last column clicked.
if (e.Column != sortColumn)
{
// Set the sort column to the new column.
sortColumn = e.Column;
// Set the sort order to ascending by default.
listView1.Sorting = SortOrder.Ascending;
}
else
{
// Determine what the last sort order was and change it.
if (listView1.Sorting == SortOrder.Ascending)
listView1.Sorting = SortOrder.Descending;
else
listView1.Sorting = SortOrder.Ascending;
}
// Call the sort method to manually sort.
listView1.Sort();
// Set the ListViewItemSorter property to a new ListViewItemComparer
// object.
this.listView1.ListViewItemSorter = new ListViewItemComparer(e.Column,
listView1.Sorting);
}
private void FillItems()
{
// Add items
ListViewItem item1 = new ListViewItem("Nipun Tomar");
item1.SubItems.Add("1");
item1.SubItems.Add("10/11/2000");
ListViewItem item2 = new ListViewItem("First Last");
item2.SubItems.Add("2");
item2.SubItems.Add("12/12/2010");
ListViewItem item3 = new ListViewItem("User User");
item3.SubItems.Add("3");
item3.SubItems.Add("12/01/1800");
ListViewItem item4 = new ListViewItem("Sample");
item4.SubItems.Add("4");
item4.SubItems.Add("05/30/1900");
// Add the items to the ListView.
listView1.Items.AddRange(
new ListViewItem[] {item1, item2, item3, item4});
}
private void Form1_Load(object sender, EventArgs e)
{
FillItems();
}
public class ListViewItemComparer : IComparer
{
private int col;
private SortOrder order;
public ListViewItemComparer()
{
col = 0;
order = SortOrder.Ascending;
}
public ListViewItemComparer(int column, SortOrder order)
{
col = column;
this.order = order;
}
public int Compare(object x, object y)
{
int returnVal= -1;
returnVal = String.Compare(((ListViewItem)x).SubItems[col].Text,
((ListViewItem)y).SubItems[col].Text);
// Determine whether the sort order is descending.
if (order == SortOrder.Descending)
// Invert the value returned by String.Compare.
returnVal *= -1;
return returnVal;
}
}
Note: I added the columns in the design form.
Here's what my assignment looks like:
You dont have any columns in your list view. They are just items. thats why the event listView1_ColumnClick never fires. (also make sure you have added this event to your list view.)
Add this at first of your Form1_Load event to initialize columns.
// set view mode to see columns
listView1.View = View.Details;
// 100 is just a length of column. HorizontalAlignment.Left starts from left side
listView1.Columns.Add("Name", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Number", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Date", 100, HorizontalAlignment.Left);
Now you see the columns which you can select them to sort items by that column.
Note that i just added 3 columns. so list view will show each item with 2 of their SubItems under columns by order.
As you requested to post the gif. Here is it :)
You call listView1.Sort()before setting the comparer: this.listView1.ListViewItemSorter = ...
Just invert the two lines.
Also, note that you are using string.Compare for all columns, which, I think, it's not what you want for column 3 (date)
[Edit]:
Just realized now the setting the value for ListviewItemSorter cause the LV to sort: your code seems to work even without calling listView1.Sort()
Problem must be somewhere else. Try with debugger setting breakpoints...
private void lvw_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
ListViewColumnSorter sorter = new ListViewColumnSorter();
sorter.SortColumn = e.Column;
sorter.Order = System.Windows.Forms.SortOrder.Ascending;
lvw.ListViewItemSorter =sorter;
lvw.Sort();
}
Related
assume I have 2 columns in gridview : column A and column B. I want to delete the whole row if Column B has no value. this code checks column B if it doesn't have any value. next, how to enter the delete command based on the value obtained from the code ?
private void Button1_Click(object sender, EventArgs e)
{
foreach (GridViewRowInfo row in DGV1.Rows)
{
if (row.Cells[1].Value == null || Convert.ToString(row.Cells[1].Value) == string.Empty)
{
MessageBox.Show("Null value");
}
}
}
Hopefully I can offer a couple of tips for working with RadGridView or really most any grid view. First, use BeginInvoke to avoid blocking the Click message which allows the UI thread to return from the click handler (mouse returns to the up position, any new button state is painted).
private void buttonRemove_Click(object sender, EventArgs e)
{
// Do not block the click event.
BeginInvoke((MethodInvoker)delegate
{
onButtonRemove();
});
}
Next make an array of records (or rows) that need to be removed. If your view is bound to a DataSource this is especially easy using System.Linq. Then simply remove from DataSource.
void onButtonRemove()
{
// Perform Linq query to see what needs to be removed
var removes =
DataSource
.Where(record => string.IsNullOrWhiteSpace(record.ColumnB));
// Cast to an array before iterating to
// avoid "CollectionWasModified" exception.
foreach (var record in removes.ToArray())
{
DataSource.Remove(record);
}
}
This particular code defines a row with this Record class:
class Record
{
public string ColumnA { get; set; } = "SomeValue";
public string ColumnB { get; set; }
}
If you were using a Winforms DataGridView it could initialize like this:
BindingList<Record> DataSource = new BindingList<Record>();
private void InitializeDataGridView()
{
dataGridView1.AllowUserToAddRows = false;
dataGridView1.DataSource = DataSource;
// Add one or more records to auto-create columns.
DataSource.Add(new Record { ColumnB = "Not empty or null"});
DataSource.Add(new Record { ColumnB = String.Empty});
DataSource.Add(new Record { ColumnB = null});
// Column formatting
dataGridView1.Columns[nameof(Record.ColumnA)].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
dataGridView1.Columns[nameof(Record.ColumnB)].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
Your post is for Telerik.WinControls.UI.RadGridView but the initialization is very similar:
BindingList<Record> DataSource = new BindingList<Record>();
private void InitializeDataGridView()
{
DGV1.DataSource = DataSource;
// Add one or more records to auto-create columns.
DataSource.Add(new Record { ColumnB = "Not empty or null"});
DataSource.Add(new Record { ColumnB = String.Empty});
DataSource.Add(new Record { ColumnB = null});
// Column formatting
DGV1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
}
Hope this helps get you where you want to be.
I have the following class which fills a list as shown bellow from an event when a button bound to each column is clicked on a DataGridView called MenuGrid:
public class orderedfood
{
public string price;
public string item;
}
List<orderedfood> order = new List<orderedfood>();
private void MenuGrid_CellClick(object sender, DataGridViewCellEventArgs e)
{
order.Add(new orderedfood { item = MenuGrid.Rows[e.RowIndex].Cells[1].Value.ToString(), price = subtotal.ToString() });
}
This MenuGrid has the following format:
What I want to do is to reload the DataGridView bound to the order List, hence I tried the following code:
MenuGrid.DataSource = null;
MenuGrid.Rows.Clear();
for (int i = 0; i < order.Count; i++)
{
MenuGrid.Rows.Add(order[i].item, order[i].price);
}
MenuGrid.Refresh();
This gives the following output, which is not what I want:
The final screenshot is correct on the number of rows but it doesn't include the name and the price of the the item.
Any suggestions?
Dont set DataSource to null. And also you can try this inside your for loop,
DataGridViewRow row = new DataGridViewRow();
dataGridView1.Rows.Add(row);
dataGridView1.Rows.Insert(0, order[i].item, order[i].price);
I have a datagridview named PTable which shows a table from database. I also have a button that functions to copy the data from selected rows to the textboxes I have.
This code that I have right now copies two selected rows from the datagridview but when I only select one row or when I select more than 2 rows, it says that "Index was out of range"
What should happen is that it should copy any number of rows and not only 2 rows
private void button11_Click(object sender, EventArgs e)
{
productid.Text = PTable.SelectedRows[0].Cells[0].Value + string.Empty;
productname.Text = PTable.SelectedRows[0].Cells[1].Value + string.Empty;
unitprice.Text = PTable.SelectedRows[0].Cells[4].Value + string.Empty;
productid2.Text = PTable.SelectedRows[1].Cells[0].Value + string.Empty;
productname2.Text = PTable.SelectedRows[1].Cells[1].Value + string.Empty;
unitprice2.Text = PTable.SelectedRows[1].Cells[4].Value + string.Empty;
}
If a user have not selected two rows, your index 1 (PTable.SelectedRows[1]) is not valid, because the item is not there.
Before you can run this code, you have to check, if the user selected two rows:
if (PTable.SelectedRows.Count == 2)
{
//Your code ...
}
else
{
//Not two rows selected
}
First, make sure SelectionMode of DataGridView to FullRowSelect(Hope you have done it already)
Second, Use foreach or any looping logic to go through each selected rows.
Third, It's always better to write modular code. Write a validation method which returns TRUE or FALSE based on selection of the grid rows.
Now based on returned value you need to continue writing your business logic.
Fourth, Make sure to use NULL checks
So let's start by re-factoring the code.
private void button11_Click(object sender, EventArgs e)
{
If(IsRowOrCellSelected())
{
//loop through selected rows and pick your values.
}
}
I am just writing sample, make sure PTable is accessible.
private boolean IsRowOrCellSelected()
{
if (PTable.SelectedRows.Count > 0)
{
DataGridViewRow currentRow = PTable.SelectedRows[0];
if (currentRow.Cells.Count > 0)
{
bool rowIsEmpty = true;
foreach(DataGridViewCell cell in currentRow.Cells)
{
if(cell.Value != null)
{
rowIsEmpty = false;
break;
}
}
}
if(rowIsEmpty)
return false;
else
return true;
}
Code can be still improved.
To work with varying number of selected rows I suggest the following.
My approach is:
There may be more than 5 rows, that You have to display. First create 3 panels for the TextBoxes. I have named the one for the productids as here. Generate the textboxes from code, this way it's easier to maintain more than 5 selected rows:
// This code goes to the constructor of the class. Not in the button click
List<TextBox> productids = new List<TextBox>();
List<TextBox> productnames = new List<TextBox>();
List<TextBox> unitprices = new List<TextBox>();
for (int i = 0; i < 5; i++)
{
productids.Add(new TextBox { Top = i * 32 });
productnames.Add(new TextBox { Top = i * 32 });
unitprices.Add(new TextBox { Top = i * 32 });
here.Controls.Add(productids[i]);
here2.Controls.Add(productnames[i]);
here3.Controls.Add(unitprices[i]);
}
Than You can in the button click set the value for each selected row:
// This code goes to the button click
// first empty the Textboxes:
foreach (TextBox productid in productids)
{
productid.Text = string.Empty;
}
foreach (TextBox productname in productnames)
{
productname.Text = string.Empty;
}
foreach (TextBox unitprice in unitprices)
{
unitprice.Text = string.Empty;
}
for (int i = 0; i < PTable.SelectedRows.Count; i++)
{
productids[i].Text = PTable.SelectedRows[i].Cells[0].Value.ToString();
productnames[i].Text = PTable.SelectedRows[i].Cells[1].Value.ToString();
// or better... Name the columns
// unitprices[i].Text = PTable.SelectedRows[i].Cells["unitPrices"].Value.ToString();
unitprices[i].Text = PTable.SelectedRows[i].Cells[4].Value.ToString();
}
Is there any built-in method for getting selected index from selected value in ComboBox control C#. If not, how can I built my own one
Thanks in advance
I think you're looking for the SelectedIndex property.
int index = comboref.SelectedIndex
As you're looking for the index of a specific value not the selected one you can do
int index = comboref.Items.IndexOf("string");
and it will tell you which Index has "string" on the combobox
You can use combobox1.Items.IndexOf("string") which will return the index within the collection of the specified item
Or use combobox1FindString("string") or findExactString("string") which will return the first occurence of the specified item. You can also give it a second parameter corresponding to the startIndex to start searching from that index.
I Hope I answered your question !!
OP: What I want is to get index from value. i.e: int seletedIndex = comboBox.getIndexFromKnownSelectedValue(value)
Get Item by Value and Get Index by Value
You need to scan the items and for each item get the value based on the SelectedValue field and then get the index of item. To do so, you can use this GetItemValue extension method and get item and index this way:
//Load sample data
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DataSource = Enumerable.Range(1, 5)
.Select(x => new { Name = $"Product {x}", Id = x }).ToList();
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "Id";
}
private void button1_Click(object sender, EventArgs e)
{
//Knows value
var value = 3;
//Find item based on value
var item = comboBox1.Items.Cast<Object>()
.Where(x => comboBox1.GetItemValue(x).Equals(value))
.FirstOrDefault();
//Find index
var index = comboBox1.Items.IndexOf(item);
//Set selected index
comboBox1.SelectedIndex = index;
}
No, there is no any built-in method for getting selected index from selected value in ComboBox control C#.
But you can create your own function to get the same.
Usage:
int index = CmbIdxFindByValue("YourValue", YourComboBox);
Function:
private int CmbIdxFindByValue(string text, ComboBox cmbCd)
{
int c = 0;
DataTable dtx = (DataTable)cmbCd.DataSource;
if (dtx != null)
{
foreach (DataRow dx in dtx.Rows)
{
if (dx[cmbCd.ValueMember.ToString()].ToString() == text)
return c;
c++;
}
return -1;
} else
return -1;
}
This question already has answers here:
Sorting A ListView By Column
(15 answers)
Closed 8 years ago.
How to sort a listview control by a specific column number in WinForms .NET 2.0? e.g. I have a column called "Line Number" whose index is 1, and I want to sort my items in the listview box by that in ascending order.
There is example on MSDN ListView.ColumnClick article: very short and simple. Essentially, you write a ListViewItemComparer and use it each time you click a column:
class ListViewItemComparer : IComparer
{
private int col = 0;
public ListViewItemComparer(int column)
{
col = column;
}
public int Compare(object x, object y)
{
return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
}
}
class MyForm : Form
{
// private System.Windows.Forms.ListView listView1;
// ColumnClick event handler.
private void ColumnClick(object o, ColumnClickEventArgs e)
{
this.listView1.ListViewItemSorter = new ListViewItemComparer(e.Column);
}
}
I have used this column sorter in many Winform Projects:
private void listView1_ColumnClick(object sender,
System.Windows.Forms.ColumnClickEventArgs e)
{
ListView myListView = (ListView)sender;
// Determine if clicked column is already the column that is being sorted.
if ( e.Column == lvwColumnSorter.SortColumn )
{
// Reverse the current sort direction for this column.
if (lvwColumnSorter.Order == SortOrder.Ascending)
{
lvwColumnSorter.Order = SortOrder.Descending;
}
else
{
lvwColumnSorter.Order = SortOrder.Ascending;
}
}
else
{
// Set the column number that is to be sorted; default to ascending.
lvwColumnSorter.SortColumn = e.Column;
lvwColumnSorter.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
myListView.Sort();
}
Source: Click Here