I have a listview with a checkbox on each row and I need to make it so that if the user exits the app and later comes back to that liistview the same items remain checked, I have succesfully saved the checked items, but If I try to recheck them in the getview() method the list starts to lag, and random checkboxes start getting checked.
Below is my code that rechecks the boxes
using (VehicleFeaturesDB vfdb = new VehicleFeaturesDB())
{
selectedfeatures = vfdb.GetSelectedFeatures(Selector.vehicleId);
if (listOfSelectedFeatures != null)
{
foreach (Features f in listOfSelectedFeatures)
{
if (feature.FeatureID == f.FeatureID)
{
CheckBox.Checked = true;
}
}
}
}
Tha
If that using block is in GetView(), you're connecting to the database and pulling the list of features every time a new item is displayed. That's why you're seeing the lag.
You're also not setting Checked to false if the feature is not in the list, so when you reuse the convertview the checkbox may already be checked. That's why you're seeing the random boxes checked.
I would probably get the selected features in your adapter's constructor, then reference that list in your GetView(). Something like this should work for you.
public class MyAdapter...
{
private IEnumerable<SelectedFeature> selectedFeatures;
public MyAdapter()
{
...your code...
using (VehicleFeaturesDB vfdb = new VehicleFeaturesDB())
{
selectedfeatures = vfdb.GetSelectedFeatures(Selector.vehicleId);
}
}
public override View GetView(int pos, View convertView, ViewGroup parent)
{
...your code...
CheckBox.Checked = selectedFeatures.Any(sf => sf.FeatureID == feature.FeatureID);
}
Related
I'm using a WPF ListView to show and select/deselect some items but there are some conditions that user cannot select/deselect an item and I should handle it in the code behind.
I tried to use ListView.SelectionChanged event to handle it but the problem is that when I change the selected item in the code behind (select it again if it was deselected or the other way), the event triggers again and I don't want that.
What is the best way to set conditions on select/deselect ListView item?
I tried to solve it using ListView.PreviewMouseLeftButtonDown event instead of ListView.SelectionChanged. But I wanted to know if there is a better way?
There might be other WPF controls (especially third party ones) that can handle this a little more gracefully. Additionally you can hook into the style of the ListView to alter the mouse-over behavior.
As a crude approach; however, I was able to accomplish what you're looking for by using the SelectionChanged event to effectively "undo" any selections we deem invalid.
First a simple ListView in xaml:
<ListView
x:Name="ItemsLv"
SelectionChanged="ItemsLv_SelectionChanged"
SelectionMode="Single" />
The SelectionMode being Single is important here as the approach for undoing a selection when multiple items are selected is more complicated.
Then, an object to represent our list items with the ToString() overload implemented so we don't have to fiddle with data templates and binding.
public class MyListViewItem
{
public string Text { get; set; }
public bool IsSelectable { get; set; }
public override string ToString()
{
return Text;
}
}
This object just represents a string (our data) paired with a boolean of whether or not we want this item to be selectable.
Then, a quick little setup to populate the list with a few item for testing:
public MainWindow()
{
InitializeComponent();
var items = new List<MyListViewItem>
{
new() { Text = "Item One", IsSelectable = true },
new() { Text = "Item Two", IsSelectable = true },
new() { Text = "Item Three", IsSelectable = false },
new() { Text = "Item Four", IsSelectable = true }
};
ItemsLv.ItemsSource = items;
}
And finally, the "magic". The SelectionChanged event handler:
private void ItemsLv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// if this event wasn't caused by a new item that wasn't already
// selected being selected, don't do anything extra
if (e.AddedItems.Count <= 0)
{
return;
}
// get the newly selected item and cast it to a MyListViewItem so we can inspect the IsSelectable property
var selectedItem = (MyListViewItem)ItemsLv.SelectedItem;
// if it's a selectable item, we don't need to intervene
if (selectedItem.IsSelectable)
{
return;
}
// we made it this far that means we tried to select an item that should NOT be selectable
// if the new selected item caused us to UNselect an old item, put the selection back
if (e.RemovedItems.Count > 0)
{
ItemsLv.SelectedItem = e.RemovedItems[0];
}
// otherwise (the first selection ever?) just set selection back to null
else
{
ItemsLv.SelectedItem = null;
}
}
Hopefully the code comments in there make it clear what's going on.
Please forgive me for such a stupid question. I am sure many of you will find this easy, where I have sent almost half the day reading trying to figure this out.
Here is the problem:
I have a FORM (Form1.cs) made. In that form I created a listview, and named it "ListView1".
Within the Form1.cs, I call a function called FileManager(this), where I pass in the THIS object.
In FileManager.cs I was able to listviewArray= originalForm.Controls.Find("listView1", true) and find that 'listview'.
When I do a listviewArray[0]<-- I can't seem to add a list to it.
FileManager.cs
FileManager(object sender)
{
if (sender != null)
{
originalForm = (Form)sender;
}
}
public void getFiles()
{
filePaths = Directory.GetFiles(hsocDir);
if(filePaths != null)
{
listviewArray= originalForm.Controls.Find("listView1", true);
if(listviewArray != null)
{
ListViewItem lvi = new ListViewItem("text");
// My Array is listViewArray
// How to add things to Lvi to it.
}
}
== Form1.cs
public Form1()
{
InitializeComponent(`enter code here`);
mysql = new MySQLCheck(this);
fileManager = new FileManager(this);
fileManager.getFiles();
}
You can't access element 0 of the collection because the collection is empty. To add an item, use:
listViewArray.Items.Add(lvi);
You need to modify the Items collection instead of the ListView itself for this to work, as ListView is not a collection (its a control).
listViewArray.Items.Add(lvi);
Also in your listview,setting this properties will help :
// Set the view to show details.
listViewArray.View = View.Details;
// Select the item and subitems when selection is made.
listViewArray.FullRowSelect = true;
// Display grid lines.
listViewArray.GridLines = true;
I have a BindingList< KeyValuePair < string, string > > that is bound to a ComboBox control. Based on some conditions, the BindingList will be added a new KeyValuePair. Now, the Newly added item shows up at index 0 of the Combobox, instead of at the end.
While debugging, I found that the BindingList has got the right order. (i.e, the new KeyValuePair is appended)
Also, I check the SelectedValue of the ComboBox in it's SelectedIndexChanged handler and it seems to be not of the ListItem that got selected. Instead, it is that of the supposed ListItem, if the ComboBox had got the right order as in its DataSource, - the BindingList..
The code is a small part of a large project.. Plz let me know if the question is not clear. I can put the relevant parts of the code as per our context.
How could something like this happen? What can I do differently?
I have this class something like this.
public class DropdownEntity
{
//removed all except one members and properties
private string frontEndName
public string FrontEndName
{
get {return this.frontEndName; }
set {this.frontEndName= value; }
}
//One Constructor
public DropdownEntity(string _frontEndName)
{
this.FrontEndName = _frontEndName;
//Removed code which initializes several members...
}
//All methods removed..
public override string ToString()
{
return frontEndName;
}
}
In my windows form, I have a tab control with several tabs. In one of the tabs pages, I have a DataGridView. The user is supposed to edit the cells and click on a Next - button. Then, some processing will be done, and the TabControl will be navigated to the next tab page.
The next tab page has the combobox that has the problem I mentioned. This page also has a back button, which will take back.. the user can modify the gridview cells again.. and click on the next button. This is when the order gets messed up.
I am posting here the Click event handler of the Next Button.. Along with the class, with the rest of the code removed.
public partial class AddUpdateWizard : Form
{
//Removed all members..
BindingList<KeyValuePair<string, string>> DropdownsCollection;
Dictionary<string, DropdownEntity> DropdownsDict;
//Defined in a partial definition of the class..
DataGridView SPInsertGridView = new DataGridView();
ComboBox DropdownsCmbBox = new ComboBox();
Button NextBtn2 = new Button();
Button BackBtn3 = new Button();
//Of course these controls are added to one of the panels
public AddUpdateWizard(MainForm mainForm)
{
InitializeComponent();
DropdownsDict = new Dictionary<string, DropdownEntity>();
}
private void NextBtn2_Click(object sender, EventArgs e)
{
string sqlArgName;
string frontEndName;
string fieldType;
for (int i = 0; i < SPInsertGridView.Rows.Count; i++)
{
sqlArgName = "";
frontEndName = "";
fieldType = "";
sqlArgName = SPInsertGridView.Rows[i].Cells["InsertArgName"].Value.ToString().Trim();
if (SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value != null)
{
frontEndName = SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value.ToString().Trim();
}
if (SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value != null)
{
fieldType = SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value.ToString().Trim();
}
//I could have used an enum here, but this is better.. for many reasons.
if (fieldType == "DROPDOWN")
{
if (!DropdownsDict.ContainsKey(sqlArgName))
DropdownsDict.Add(sqlArgName, new DropdownEntity(frontEndName));
else
DropdownsDict[sqlArgName].FrontEndName = frontEndName;
}
else
{
if (fieldType == "NONE")
nonFieldCount++;
if (DropdownsDict.ContainsKey(sqlArgName))
{
DropdownsDict.Remove(sqlArgName);
}
}
}
//DropdownsCollection is a BindingList<KeyValuePair<string, string>>.
//key in the BindingList KeyValuePair will be that of the dictionary.
//The value will be from the ToString() function of the object in the Dictionary.
DropdownsCollection = new BindingList<KeyValuePair<string,string>>(DropdownsDict.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.ToString())).ToList());
DropdownsCmbBox.DataSource = DropdownsCollection;
DropdownsCmbBox.DisplayMember = "Value";
DropdownsCmbBox.ValueMember = "Key";
//Go to the next tab
hiddenVirtualTabs1.SelectedIndex++;
}
private void BackBtn3_Click(object sender, EventArgs e)
{
hiddenVirtualTabs1.SelectedIndex--;
}
//On Selected Index Changed of the mentioned Combobox..
private void DropdownsCmbBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (DropdownsCmbBox.SelectedValue != null)
{
if (DropdownsDict.ContainsKey((DropdownsCmbBox.SelectedValue.ToString())))
{
var dropdownEntity = DropdownsDict[DropdownsCmbBox.SelectedValue.ToString()];
DropdownEntityGB.Text = "Populate Dropdowns - " + dropdownEntity.ToString();
//Rest of the code here..
//I see that the Datasource of this ComboBox has got the items in the right order.
// The Combobox's SelectedValue is not that of the selected item. Very Strange behavior!!
}
}
}
}
The very first time the user clicks the Next Button, it's fine. But if he clicks the Back Button again and changes the Data Grid View cells.. The order will be gone.
I know, it can be frustrating to look at. It's a huge thing to ask for help. Any help would be greatly appreciated!
Please let me know if you need elaboration at any part.
Thanks a lot :)
I think you have two problems here.
First, if you want to retain the order of the items you should use an OrderedDictionary instead of a regular one. A normal collection will not retain the order of the items when you use Remove method. You can see more info about this related to List here.
You could use such dictionary like this:
DropDownDict = new OrderedDictionary();
// Add method will work as expected (as you have it now)
// Below you have to cast it before using Select
DropDownCollection = new BindingList<KeyValuePair<string, string>>(DropDownDict.Cast<DictionaryEntry>().Select(kvp => new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString())).ToList());
The second problem could be that you change the display name (FrontEndName) of already existing items, but the key is preserved. When you add a new item, try to remove the old one that you're not using anymore and add a new item.
The Sorted Property of the Combobox is set to True! I didn't check that until now. I messed up. Terribly sorry for wasting your time Adrian. Thanks a lot for putting up with my mess here.. :)
I have a method that adds items to my listbox called refreshInterface which is called as soon as the programe starts, adding names of homeforms in the listbox using the FormItems class, here is the rereshInterface method below
public void refreshInterface()
{
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var forms = new FormItems(homeForms);
listBox1.Items.Add(forms);
}
}
The FormItems class is this below
public class FormItems
{
public DataSet1.xspGetAnalysisUsageTypesRow types { get; set; }
public FormItems(DataSet1.xspGetAnalysisUsageTypesRow usageTypes)
{
types = usageTypes;
}
public override string ToString()
{
// returns the rows that are relating to types.xlib_ID
var libtyps = types.GetxAnalysisUsageRows();
var cnt = 0;
foreach (DataSet1.xAnalysisUsageRow ty in libtyps)
{
//returns true if ty is null
bool typeNull = ty.Isxanu_DefaultNull();
// if its false, if xanu_Default is set
if (!typeNull)
{
cnt += 1;
}
}
var ret = String.Format("set {0} [Set: {1}]", types.xlib_Desc, cnt);
//return this.types.xlib_Desc;
return ret;
}
}
Each listbox (the listbox is on the left of the homeform) item has a number of reports that can be added to it, so for instance, i select an homeform from my listbox, there are 12 textboxes on the right hand side and each textbox has a pair of buttons which are Browse and Clear. If I click on the browse button a new form appears, and i select a report from that form and add it to a particular textbox, the count for that homeform should update, and i clear a textbox for a particular homeform, the count should also update.
At the moment when i debug the application, it shows me the count of each Homeform depending on the amount of reports added to the homeform, but while the programe is running, if i add a new report to a homeform, the count does not update until i restart the debug session. I was told about using a Databinding method but not sure of how i could use it here
How do i ge my listbox item to update ?
You should probably look into binding. Here is a good place to start:
http://www.codeproject.com/Articles/140621/WPF-Tutorial-Concept-Binding
If you want a GUI to respond to data changes then binding is your best friend.
You should bind List Box component source to Observable Collection, every update you do to Observable Collection will update List Box data.
Might not be exact but should give you an idea.
public void refreshInterface()
{
Dictionary<int,string> items = new Dictionary<int,string>();
//int number = 0;
foreach (DataSet1.xspGetAnalysisUsageTypesRow homeForms in myDataSet.xspGetAnalysisUsageTypes)
{
var formitem = new FormItems(homeForms);
items.Add(formitem.someprop, formitem.toString());
}
listbox.DataSource = items;
listbox.DisplayMember = "Value";
listbox.ValueMember = "Key";
}
I have a datagridview which we will call dataGridViewExample.
My object (the uncommon datatypes is because my database is SQLite):
class MyObject
{
public Int64 Vnr { get; set; }
public string Name { get; set; }
public Single Price { get; set; }
public int Amount { get; set; }
}
Here is the relevant code:
//This form gets called with a .ShowDialog(); in my form1.
private List<MyObjecte> ExampleList = new List<MyObject>();
public MyForm()
{
dataGridViewExample.DataSource = OrdreInkøbsListe;
}
private void AddtoDataGridViewExample()
{
//Add a new MyObject to the list
ExampleList.Add(new myObject()
{
Vnr = newVnr,
Amount = newAmount,
Price = newPrice,
Name = newName
});
//refresh datasource
dataGridViewExample.DataSource = null;
dataGridViewExample.Refresh();
dataGridViewExample.DataSource = OrdreInkøbsListe;
ddataGridViewExample.Refresh();
}
When MyForm gets called with a .ShowDialog, it shows up fine and displays my DataGridView example just fine. As you can read from the code, the ExampleListis initially empty, so it just shows an empty datagridview with 4 columns: Vnr, Name, Price & Amount. If I click inside it etc. nothing happens - so everything is working as planned, so far.
Everytime I call AddtoDataGridViewExample() it adds the new object to the Datagridview, and the datagridview does update, listing all the objects added so far (they show themself as rows, again according to plan).
Now, remember that I just said that nothing happened if you clicked inside DataGridViewExample before I have called AddtoDataGridViewExample()?
Well, after having called AddtoDataGridViewExample() once or more, the program will crash if I click inside DataGridViewExample (for example: the users wants to select a row). It throws an IndexOutOfRangeException and talks about an -1 index.
It also throws the exception in the other form, on the line where I call MyForm with .ShowDialog();
I really am stuck on this, do you guys have any idea what is wrong??
My only clue is that I do believe the refresh of DataGridViewExample's datasource might be the cause of the problem.
Another important note: I have yet bound any events to my DataGridViewExample. So you can rule that idea out.
Here is all DataGridViewExample's properties:
this.dataGridViewExample.AllowUserToAddRows = false;
this.dataGridViewExample.AllowUserToDeleteRows = false;
this.dataGridViewExample.AllowUserToResizeColumns = false;
this.dataGridViewExample.AllowUserToResizeRows = false;
this.dataGridViewExample.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
this.dataGridViewExample.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.dataGridViewExample.Location = new System.Drawing.Point(591, 53);
this.dataGridViewExample.MultiSelect = false;
this.dataGridViewExample.Name = "dataGridViewExample";
this.dataGridViewExample.ReadOnly = true;
this.dataGridViewExample.RowHeadersVisible = false;
this.dataGridViewExample.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
this.dataGridViewExample.ShowEditingIcon = false;
this.dataGridViewExample.Size = new System.Drawing.Size(240, 150);
this.dataGridViewExample.TabIndex = 31;
I guess the click event tries to get the currently selected row and do something with it, while dataGridViewExample.DataSource = null; clears the datasource, and the currently selected row becomes null.
If you set the DataGridView.DataSource to the list, you don't need to reset it to null, refresh, and reset it to the list again (and refresh again) to see the changes. It will be enough to just refresh the DataGridView.
You can also just try using an BindingList<T> object instead of a List<T>, which will automatically notify your grid of its internal changes (Adding and removing elements), and there's also an INotifyPropertyChanged interface you can implement on your MyObject class, that will make every property change in an object show on the grid (For any changes made to the object in the code, and not through the grid itself).
Have you tried running the debugger and break when InedxOutOfRangeException is thrown to see where the exception is thrown?
Select Debug > Exceptions then there's a Find button on the dialog so you don't have to browse through all of the possibilities.
I had similar situation. I assigned generic list of certain object to DataGridView. Then I was setting null to DataSource and after that refresh. After that I assign list of objects to DataSource. While clicked on grid while runtime error occured IndexOutOfRange. My solution was to assign new empty list of my object to that grid and refresh and after changes on my working list I do assign to DataSource and call Refresh. Now, it is working without any crashes. Please look on my code before:
grid.DataSource = null;
grid.Refresh();
if(cases.Count() > 0)
{
grid.DataSource = cases;
grid.Refresh();
}
And now on my code after:
grid.DataSource = new List<MyCase>();
grid.Refresh();
//do something with cases
if(cases.Count() > 0)
{
grid.DataSource = cases;
grid.Refresh();
}