I have a listView that is being updated from multiple threads, with the threads status and id being displayed. When a thread has ended the item is removed.
My problem is that the way i use to update my listView is by using the listViews ID (to locate the subitems i wish to update). Now, as listView items start getting removed i run into problems because that ID for a specific thread has changed..
Here is the code i currently use to update and remove:
private void AddToListViewThread(string user, string status, int threadNumber)
{
Invoke(new MethodInvoker(
delegate
{
listView2.BeginUpdate();
this.listView2.Items[threadNumber].SubItems[1].Text = user;
this.listView2.Items[threadNumber].SubItems[2].Text = status;
listView2.EndUpdate();
}
));
}
private void RemoveFromListViewThread(int threadNumber)
{
Invoke(new
MethodInvoker(
delegate
{
listView2.BeginUpdate();
this.listView2.Items.RemoveAt(threadNumber);
listView2.EndUpdate();
}
));
}
I now understand that i cannot use the threadNumber as the item index (as items get removed) is there any other way i could achieve this? maybe by targeting the "user" subitem? and then getting subitems from that?
Use the tag property of the ListViewItem to store a different index (similar to the SQL autoincrement ).
The Tag property is a field used to store metadata about the item.
A simple binary search would let you find the item to remove in Log(n) time.
Your code would looks like this:
private void AddToListViewThread(string user, string status, int threadNumber)
{
Invoke(new MethodInvoker(
delegate
{
listView2.BeginUpdate();
int i = SearchItem(listView2, threadNumber);
if ( i > -1)
{
this.listView2.Items[i].SubItems[1].Text = user;
this.listView2.Items[i].SubItems[2].Text = status;
}
listView2.EndUpdate();
}
));
}
private void RemoveFromListViewThread(int threadNumber)
{
Invoke(new MethodInvoker(
delegate
{
listView2.BeginUpdate();
int i = SearchItem(listView2, threadNumber);
if ( i > -1)
{
this.listView2.Items.RemoveAt(i);
}
listView2.EndUpdate();
}
));
}
private int SearchItem(ListView list, int id)
{
for (int i = 0; i < list.Items.Count; i++) // I used sequential search but you can implement binary instead
{
if (((int)list.Items[i].Tag) == id) return i;
}
return -1;
}
You may want to look at ListViewItem.Tag to see if it works in your specific scenario. You can store the thread's ID in the item's tag at creation and it won't change if you remove other items in the ListView.
http://msdn.microsoft.com/en-us/library/system.windows.forms.listviewitem.tag.aspx
Try this -> this.listView2.Remove(this.listView2.Items.FindByValue(threadNumber));
Related
I have a checkedListBox where the user can select whatever they want to update. I want them to be able to freely update 1 up to 5 characteristics of a machine. So when they only want to update 1 thing, they do not have to provide the other 4 characteristics. Also, when they want to update 5 characteristics, they
can do it in one go. For that purpose I have the following if statements:
if (clbCharacteristicsToUpdate.CheckedItems.Count != 0)
{
if (clbCharacteristicsToUpdate.GetSelected(0))
{
currentITime = Convert.ToDouble(tbCurrentITime.Text);
MessageBox.Show(currentITime.ToString());
dh.UpdateCurrentITime(machineNr, currentITime);
}
if (clbCharacteristicsToUpdate.GetSelected(1))
{
cycleTime = Convert.ToDouble(tbCycleTime.Text);
MessageBox.Show(cycleTime.ToString());
dh.UpdateCycleTime(machineNr, cycleTime);
}
if (clbCharacteristicsToUpdate.GetSelected(2))
{
nrOfLinesPerCm = Convert.ToInt32(tbNrOfLinesPerCm.Text);
MessageBox.Show(nrOfLinesPerCm.ToString());
dh.UpdateNrOfLinesPerCm(machineNr, nrOfLinesPerCm);
}
if (clbCharacteristicsToUpdate.GetSelected(3))
{
heightOfLamallae = Convert.ToDouble(tbHeightOfLamallae.Text);
MessageBox.Show(heightOfLamallae.ToString());
dh.UpdateHeightOfLamallae(machineNr, heightOfLamallae);
}
if (clbCharacteristicsToUpdate.GetSelected(4))
{
if (rbLTB.Checked)
{
machineType = 2;
MessageBox.Show(machineType.ToString());
}
else if (rbSTB.Checked)
{
machineType = 1;
MessageBox.Show(machineType.ToString());
}
if(!rbLTB.Checked && !rbSTB.Checked)
{
MessageBox.Show("Select a machine type to update!");
return;
}
dh.UpdateType(machineNr, machineType);
}
}
My problem is that, when I choose and update 1 thing it works perfectly. But when I choose multiple ones, it only executes the last if statement that returns true. I thought about using if-else but then only the first one that returns true will be executed. I also thought about having if statements for each possibility. But since I have 5 characteristics I can update, this would make 25 possibilities and I do not want to have 25 if statements. Thanks in advance!
GetSelected does not check whether the item has been checked, but that it is actually selected.
For example, in the below image "Item 2" will will return true for GetSelected. It is selected, not checked.
Instead you could do something like checking the clbCharacteristicsToUpdate.CheckedItems property to get the items that are actually checked.
The GetSelected method actually comes from the ListBox class, which CheckedListBox inherits from.
Try this:
This is a helper method I created on a button.
private void button1_Click(object sender, EventArgs e)
{
CheckList();
}
This method looks if items are checked and iterates over the collection of checked ones, calling the last metod.
private void CheckList()
{
if (clbCharacteristicsToUpdate.SelectedItems.Count != 0)
{
foreach (int indexChecked in clbCharacteristicsToUpdate.CheckedIndices)
{
CheckSelectedItem(indexChecked);
}
}
}
And finally, here the appropriate actions are taken based on indexes.
private void CheckSelectedItem(int index)
{
if (index == 0)
{
//Do stuff on Zero
MessageBox.Show("Zero");
}
if (index == 3)
{
//Do stuff on One
MessageBox.Show("Three");
}
}
I have a question about the ProcessBar on C#,
How would I add the value of 1 to a label if the method used when passing an item within a list box was successful ?
I have a method like this
private static Form1 f1 = Application.OpenForms["Form1"] as Form1;
public static void GroupList1(processBar bar)
{
f1.listBox1.Items.Add("User1");
bar.Value = 100;
}
public static void GroupList2(processBar bar2)
{
f1.listBox1.Items.Add("User2");
bar.Value = 100;
} // Etc, etc - up to GroupList6
I would also like to have a label that tells me how many user's were successfully added (using the bar), I was thinking of adding a method like this :
if (bar.Value = 100)
{
f1.label1.Text = "" + 1;
}
Inside of my GroupList1/2 method, but the label always appears as the value 1 .
This method within the main form of my code loads a separate label :
for(int i = 0; int i < listBox1.Items.Count; i++)
{
label2.Text = i.ToString();
}
So, I would like label 1 to increase by 1 if the user has been loaded into my list box successfully, how would I do this ?
Obviously this isn't actually the code I'm using within my program, a method is used if the selected index changes (which is why I want to increase by 1, to ensure the user parsed the method successfully), but the question still remains as described, thanks.
I dont quite understand well what are you trying to achieve, but if you only look in increasing the value of such label then you can easily do this by
if (bar.Value = 100)
{
f1.label1.Text = ""+(int.Parse(f1.label1.Text)+1);
}
or even a better way to initialize the string if for some reason the Text of the label is not an integer
if (bar.Value == 100)
{
int value;
if(!int.TryParse(f1.label1.Text,out value))
{
f1.label1.Text = "1";
}
else
{
f1.label1.Text = ""+(value+1);
}
}
but the best way to do this is to keep track of the value in a separate variable and just update the content of the label.
It's a bad idea to store the actual count in the label's Text property, instead, create a variable:
private static int count;
Now, change your code to something like this:
if (bar.Value = 100)
{
// Add 1
count += 1;
// Update the UI
f1.label1.Text = count.ToString();
}
I am trying to use the drag re-ordering on my radgrid. The code I have works well for me (it fires on the RowDrop event) but my client cant get it to work and I have troubleshooted it down to show that when he does the drop, the DestDataItem property of the args is null, so the drop logic never triggers?!? here is my code:
protected void questionGrid_RowDrop(object sender, GridDragDropEventArgs e)
{
if (e.DestDataItem != null)
{
int tempId = int.Parse(editingId.Value);
theTemplate = ent.SurveyTemplates.Where(i => i.Id == tempId).FirstOrDefault();
int id = int.Parse(e.DraggedItems[0]["Id"].Text);
SurveyQuestion draggedQuestion = ent.SurveyQuestions.Where(i => i.Id == id).FirstOrDefault();
List<SurveyQuestion> tempArray = theTemplate.Questions.OrderBy(i => i.Rank).ToList();
tempArray.Remove(draggedQuestion);
tempArray.Insert(e.DestDataItem.ItemIndex, draggedQuestion);
int j = 0;
foreach (SurveyQuestion sq in tempArray)
{
sq.Rank = j;
j++;
}
ent.SaveChanges();
questionGrid.Rebind();
}
else
{
Exceptions.LogException(new Exception("NULL DEST"));
}
}
It just references the dragged item and pulls it from the list of items and re-inserts it at the new index, then it updates the rank property of each item to its new index and saves.
Why would this work for me and not for him? Could this server side code be bothered by browser differences?
As mentioned in this thread, if the item isn't dropped on an actual data row in the grid, the DestDataItem will be null.
You can prevent your RowDrop event from firing, if the target isn't a data row, by handling the OnRowDropping event on the client side and ignoring the things you don't want:
function gridRowDropping(sender, args)
{
if (!args.get_targetGridDataItem())
args.set_cancel(true);
}
Using ms visual studio and csharp .net4.
This is the code i have to check for duplicates
public void CheckForDuplicate()
{
DataGridViewRowCollection coll = ParetoGrid.Rows;
DataGridViewRowCollection colls = ParetoGrid.Rows;
List<string> listParts = new List<string>();
int count = 0;
foreach (DataGridViewRow item in coll)//379
{
foreach (DataGridViewRow items in colls)//143641
{
if (items.Cells[5].Value == item.Cells[5].Value)
{
if (items.Cells[2].Value != item.Cells[2].Value)
{
listParts.Add(items.Cells["Keycode"].Value.ToString());
count++;
dupi = true;
//txtDupe.Text = items.Cells["Keycode"].Value.ToString();
//this.Refresh();
}
}
}
}
MyErrorGrid.DataSource = listParts;
}
This is the check before it allows the user to save.
private void butSave_Click(object sender, EventArgs e)
{
CheckForDuplicate();
if (dupi == true)
{
txtDupe.Clear();
dupi = false;
}
else
{
SaveMyWorkI();
dupi = false;
}
}
This is the data that it is looking at:
Now, I know the logic must be flawed since it saving regardless.
I'm basically searching through each cell on pareto1 to see if the user has made any duplicates, if so it will not save and instead displays the part number etc in another datagridview....well that's the plan.
So could someone look through this and tell me
1) Where in my logic is this failing, also what about if the checks are correct?
2) Will the list work adding the info, if so is a simple bind to a datagrid view enough to display the results?
3) If this is just a really bad way of searching through could someone provide code that reflects what I am Trying to achieve.
Many thanks for your future comments.
UPDATE:: Ok thanks for the help, my algorithm now works, but my very last problem is displaying the part number that is duplicated on the pareto column, instead it displays the length.
public void CheckForDuplicate()
{
DataGridViewRowCollection coll = ParetoGrid.Rows;
DataGridViewRowCollection colls = ParetoGrid.Rows;
List<string> listParts = new List<string>();
int count = 0;
foreach (DataGridViewRow item in coll)//379
{
foreach (DataGridViewRow items in colls)//143641
{
count++;
if ((items.Cells[5].Value != null))
{
if ((items.Cells[5].Value != null) && (items.Cells[5].Value.Equals(item.Cells[5].Value)))
{
if ((items.Cells[2].Value != null) && !(items.Cells[2].Value.Equals(item.Cells[2].Value)))
{
listParts.Add(items.Cells["Keycode"].Value.ToString());
dupi = true;
}
}
}
}
}
MyErrorGrid.DataSource = listParts;
var message = string.Join(Environment.NewLine, listParts);
//MyErrorGrid.DataSource = message;
MessageBox.Show(message);
}
Even though the message box correctly displays the results? is it something im missing out when binding to my datagrid?
Here is a simple example showing how to perform validation during dataentry. There are various ways you can customise how the errors appear (including some sort of custom dialog to resolve errors) that might give you a better solution.
public partial class Form1 : Form
{
BindingSource bs;
DataTable dt; public Form1()
{
InitializeComponent();
BindingList<BindingClass> data = new BindingList<BindingClass>
{
new BindingClass{ Name = "one" },
new BindingClass { Name = "two"}
};
dataGridView1.DataSource = data;
dataGridView1.CellValidating += new DataGridViewCellValidatingEventHandler(dataGridView1_CellValidating);
}
void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Index != e.RowIndex & !row.IsNewRow)
{
if (row.Cells[0].Value.ToString() == e.FormattedValue.ToString())
{
dataGridView1.Rows[e.RowIndex].ErrorText =
"Duplicate value not allowed";
e.Cancel = true;
return;
}
}
}
dataGridView1.Rows[e.RowIndex].ErrorText = string.Empty;
}
}
public class BindingClass
{
public string Name { get; set; }
}
}
Naturally this won't always fit your requirements of what users like to work with but I thought it could help to see another option.
You are doing comparisons with == and != .
items.Cells[5].Value exposes an object.
In your case this is most likely doing an equality check based on reference equality, which is probably not what your want. Try using something like items.Cells[5].Value.Equals(item.Cells[5].Value)
Please also consider solving such problems on the simplest abstractions available. E.g. if you had the grid bound to a collection of objects, then you could perform the cleanup operation against that collection of objects, disregarding any UI you bolt on top of it.
You can also consider using the Distinct extension method from the LINQ namespace and provide it an IEqualityComparer* to make sure the most efficient code for removing duplicates available in the .NET Framework is used by you.
*) IEqualityComparer is an abstraction that allows you to define in one place when you consider two objects to be equal. Distinct provides an overload where you can specify such a comparer.
See if this can work for you
var dup = dataGridView1.Rows.Cast<DataGridViewRow>().Distinct().Where(g => g.Index != 0);
Excluding row with index 0. It is header row.
I have a TreeView in my a C# winform. I would like to be able to add a search functionality through a search box.
Basically as the user types in letters (I'm guessing on the _TextChanged event), I show only the nodes that contain childnodes with the inputed letters...
My TreeView contains 53 parent nodes for a total of over 15000 Nodes so I need something a bit performant. I build my TreeView from a csv that I load into a DataTable and then make queries on to get the Parent nodes with associated child nodes...
UPDATE
I have an idea.
The final aim is that when a user doubleclicks on a child node it gets added to a listView.
I had first implemented this search function in a simple list view where I didn't separate my data into categories.
My idea is that once the user starts typing in things, I turn off my Tree view and show the list view instead...
I'll try and implement and see what it gives performance wise... Any critics on this idea are welcome.
Finally this is what I did, it suits my requirements.
I first make a copy of my TreeView and store into fieldsTreeCache. I then clear the fieldsTree. I then search through the cache and add any node containing my search parameter to the fieldsTree. Note here that once you search, you no longer have the parent nodes that show. You just get all of the end nodes. I did this because if not I had 2 choices:
Expand all the parent nodes containing childs that match but then it was slow and one parent might have 50 children which isn't great visually.
Not expand the parent nodes but then you just get the categories and not the children nodes that you're searching for.
void fieldFilterTxtBx_TextChanged(object sender, EventArgs e)
{
//blocks repainting tree till all objects loaded
this.fieldsTree.BeginUpdate();
this.fieldsTree.Nodes.Clear();
if (this.fieldFilterTxtBx.Text != string.Empty)
{
foreach (TreeNode _parentNode in _fieldsTreeCache.Nodes)
{
foreach (TreeNode _childNode in _parentNode.Nodes)
{
if (_childNode.Text.StartsWith(this.fieldFilterTxtBx.Text))
{
this.fieldsTree.Nodes.Add((TreeNode)_childNode.Clone());
}
}
}
}
else
{
foreach (TreeNode _node in this._fieldsTreeCache.Nodes)
{
fieldsTree.Nodes.Add((TreeNode)_node.Clone());
}
}
//enables redrawing tree after all objects have been added
this.fieldsTree.EndUpdate();
}
Here's a small simple example (with code from msdn) is that a very simple way to filter out the TreeView node displays.
winforms in a tree view you can only add or remove TreeNode.
The search for the nodes can still be improved if the nodes are stored with their data into a dictionary (with a unique key).
using System.Collections;
using System.Windows.Forms;
namespace FilterWinFormsTreeview
{
// The basic Customer class.
public class Customer : System.Object
{
private string custName = "";
protected ArrayList custOrders = new ArrayList();
public Customer(string customername) {
this.custName = customername;
}
public string CustomerName {
get { return this.custName; }
set { this.custName = value; }
}
public ArrayList CustomerOrders {
get { return this.custOrders; }
}
}
// End Customer class
// The basic customer Order class.
public class Order : System.Object
{
private string ordID = "";
public Order(string orderid) {
this.ordID = orderid;
}
public string OrderID {
get { return this.ordID; }
set { this.ordID = value; }
}
}
// End Order class
public static class TreeViewHelper
{
// Create a new ArrayList to hold the Customer objects.
private static ArrayList customerArray = new ArrayList();
public static void FilterTreeView(TreeView treeView1, string orderText) {
if (string.IsNullOrEmpty(orderText)) {
FillMyTreeView(treeView1);
} else {
// Display a wait cursor while the TreeNodes are being created.
Cursor.Current = Cursors.WaitCursor;
// Suppress repainting the TreeView until all the objects have been created.
treeView1.BeginUpdate();
foreach (TreeNode customerNode in treeView1.Nodes) {
var customer = customerNode.Tag as Customer;
if (customer != null) {
customerNode.Nodes.Clear();
// Add a child treenode for each Order object in the current Customer object.
foreach (Order order in customer.CustomerOrders) {
if (order.OrderID.Contains(orderText)) {
var orderNode = new TreeNode(customer.CustomerName + "." + order.OrderID);
customerNode.Nodes.Add(orderNode);
}
}
}
}
// Reset the cursor to the default for all controls.
Cursor.Current = Cursors.Default;
// Begin repainting the TreeView.
treeView1.EndUpdate();
}
}
public static void FillMyTreeView(TreeView treeView1) {
// Add customers to the ArrayList of Customer objects.
if (customerArray.Count <= 0) {
for (int x = 0; x < 1000; x++) {
customerArray.Add(new Customer("Customer" + x.ToString()));
}
// Add orders to each Customer object in the ArrayList.
foreach (Customer customer1 in customerArray) {
for (int y = 0; y < 15; y++) {
customer1.CustomerOrders.Add(new Order("Order" + y.ToString()));
}
}
}
// Display a wait cursor while the TreeNodes are being created.
Cursor.Current = Cursors.WaitCursor;
// Suppress repainting the TreeView until all the objects have been created.
treeView1.BeginUpdate();
// Clear the TreeView each time the method is called.
treeView1.Nodes.Clear();
// Add a root TreeNode for each Customer object in the ArrayList.
foreach (Customer customer2 in customerArray) {
var customerNode = new TreeNode(customer2.CustomerName);
customerNode.Tag = customer2;
treeView1.Nodes.Add(customerNode);
// Add a child treenode for each Order object in the current Customer object.
foreach (Order order1 in customer2.CustomerOrders) {
var orderNode = new TreeNode(customer2.CustomerName + "." + order1.OrderID);
customerNode.Nodes.Add(orderNode);
}
}
// Reset the cursor to the default for all controls.
Cursor.Current = Cursors.Default;
// Begin repainting the TreeView.
treeView1.EndUpdate();
}
}
}
Every node in TreeView has Expanded and IsVisible properties. The number of items which are visible at the same time is limited (TreeView.VisibleCount). Based on this information you can reduce the number of nodes to probe dramatically.
When scanning the node and it's child nodes you can abort recursion as you find the first match inside collapsed node, thus you already know it has at least one child and will be visible anyway.
Perform filtering asynchronously. (use new Task() for instance) Start the first task after minimal number of chars was typed (let's say 3). Every next typed char must cancel the running task and start the new one.