I have two lists. The first one is hard-coded and the contents never change. The second can be edited by the user to add, change and remove items:
public List<Item> DefaultItems = new List<Item>();
public BindingList<Item> UserItems = new BindingList<Item>();
...
MyTable.DataSource = UserItems;
I would like to bind the contents of both lists, one after the other, to a ComboBox and have it update automatically when the user edits the UserItems list.
The first part I can easily solve with something like:
public List<Items> AllItems
{
get
{
List<Item> Items = new List<Item>();
foreach (Item I in DefaultItems) Items.Add(I);
foreach (Item I in UserItems) Items.Add(I);
return Items;
}
}
...
MyComboBox.DataSource = AllItems;
The problem is that when UserItems changes there is no notification that AllItems has changed so the contents of the combobox remain the same.
I then added an event that is generated when UserItems changes. Now my problem is how to force the ComboBox to refresh. Doing the following:
MyComboBox.DataSource = null;
MyComboBox.DataSource = AllItems;
results in the selecteditem becoming null and the selectedindex becoming -1, which I then have to handle in my code (temporarily remember the current item, restore it afterwards, etc.). It's all becoming very messy and I'm sure there is a clever way of solving this. Is there?
thanks, Andy
UPDATE: I didn't want to add yet more code and complexity just for this in the form of a third party assembly, so I just continued with my messy approach. Thanks.
You need to use a collection that will notify the UI when the collection has been changed.
You can either use the .NET provided BindingList class or, if you want to try something different, you could download the BindingListView class that will wrap your existing collections and provide the UI with the notification it needs.
Related
C# How to prevent ListBox in MultiSimple selection mode to select first item automatically, when you unselect the last one selected item in the box - it happens only if listbox represented by my own class objects, and everything is ok when it represented by string objects. Thnx!
It seems like keeping track of the order of the list is the most important part. I would suggest maybe making an array of a struct. You can make this struct contain whatever you want for example:
struct itemList
{
public string itemName;
public int itemIndex;
//include whatever other variables that you need included in here.
}
Also, make sure you place your struct before the namespace. Then, making the array of the struct would look like this:
itemList[] items1 = new itemList[listBoxName.SelectedItems.Count];
All you would have to do then is to add the items to the array before you reorder the listBox
for (int i = 0; i < listBoxName.SelectedItems.Count; i++)
{
items1[i].itemName = listBoxName.SelectedItems[i].ToString();
items1[i].itemIndex = listBoxName.SelectedIndices[i];
}
Thank you very much, but i already use some like this. I don't understand why first item of listBox selected everytime i assign preloaded list as DataSource. I resolve this problem, by making another one temporal List of my object class, which items readed from binary file, and then push them one by one to my original List in foreach cycle by listOfObjects.Add(object) method. I know that every time after code ListOfTags.DataSource = null;
ListOfTags.DataSource = tags;
ListOfTags.DisplayMember = "Name"; if my tags (it is a List) are preloaded even in code (for example if i write code List<Tag> tags = new List<Tag> {new Tag("1"),new Tag("2"), new Tag("3")}; , this takes situation when first item of listbox selected, and starts selects everytime after that when i try do deselect last selected item in listBox.
I have a collection
ObservableCollection<PathInfo> _pathInfos = new ObservableCollection<PathInfo>();
and the corresponding sorted view:
CollectionView _sortedPathInfos = CollectionViewSource.GetDefaultView(_pathInfos);
_sortedPathInfos.SortDescriptions.Add(new SortDescription("LastAccess", ListSortDirection.Descending));
Now I want to ensure that not more than _maxItems are in the source collection. The oldest items that exceed the max count shall be removed.
To achieve this I have to write quite many lines of code:
private void ensureCount()
{
var removes = new List<PathInfo>();
int count = 0;
foreach (PathInfo info in _sortedPathInfos)
{
if (++count > _maxItems)
removes.Add(info);
}
foreach (var remove in removes)
_pathInfos.Remove(remove);
}
Are there better (shorter) ways to do this?
With LINQ you can use "Take" to get the desired amount of elements out of an enumeralbe:
_sortedPathInfos.Take(20);
Have a look at: http://msdn.microsoft.com/en-us/library/bb503062%28v=vs.90%29.aspx
LINQ can help you.
What about
_sortedPathInfos.View.Skip(_maxItems).ToList().ForEach(x => _pathInfos.Remove(x));
ObservableCollection does not support RemoveRange, and fires "element removed" event each time you remove something from _pathInfos which is pretty inefficient in my opinion, when dealing with large lists.
Why does ObservableCollection not support bulk changes? this thread has few potential fixes, in case you are interested.
I am having a difficulty adding an item to my listbox. I want to add an item at the beginning of the list box to be my 'default' item, however I will also be adding a list of items from a List using the .DataSource ... and for some reason the app is crashing whenever I try to add the items from the List and the default item at the same time. I am trying to add the items by using:
`productList.DataSource = salesManager.Products;
productList.DisplayMember = "IdAndName";
productList.ValueMember = "Id";
productList.Items.Insert(0, "ALL");`
but for some reason VS will not let me. I have also found this method and tried to apply it as well:
public void AddListLine(string lineIn)
{
productList.Items.Insert(0, "ALL");
((CurrencyManager)productList.BindingContext[productList]).Refresh();
}
However it is not working as well. Any idea please? Thanks!
The reason it isn't working is because you are attempting to add an object of type String where the rest are (I assume) of type Product or something similar. The runtime attemps to access the property IdAndName to display and the property Id for the display and value properties of the new list item and they do not exist.
Consider adding some kind of "blank" Product object instead.
public void AddListLine(string lineIn)
{
productList.Items.Insert(0, new Product { Id = "ALL", IdAndName = "ALL" });
((CurrencyManager)productList.BindingContext[productList]).Refresh();
}
I get a large list of data to populate into a jqGrid on my clientside.
List<MyData> lotsOfRecords = getData();
Which I then store in cache, since a lot of people will be using it:
Cache["SharedData"].Add(lotsOfRecords);
This grid allows users to check records for processing. I want to persist which records are checked as a user sorts, filters, pages, etc.
My first thought was to add a property bool Selected { get; set; } to the MyData object, and toggle it whenever someone checks a field. Obviously, that won't work since this is a shared cache. I don't want Joe User checking things that Bill User didn't want checked.
Next idea was to store a Dictionary<int, bool> in session, that maps the id of a record to the checked status. This wasn't bad, but since there is no easy way to combine objects in .NET, I don't see a clean way to send that down to my grid without a clunky anonymous object:
return lotsOfRecords.Select(record => {
record.Id,
record.Name,
...
myDictionary[record.Id] // true/false for checked
};
That would be a solution, but I'm hoping there is a cleaner design pattern considering I have a lot of fields in my object and use it in a similar way across a few pages. Any suggestions are welcome. Thanks in advance!
Btw, my current environment is ASP.NET MVC 3 with jQuery/UI and jqGrid.
You may be caching the list, but the selections will need to be user-specific. I would suggest building a list of the selected indices each time the page is posted back, and store the list session.
Here's a function that we're using to remember selections as the user pages through results:
/// <summary>
/// Iterates through items in the grid and updates the selected vendor
/// list with any selections or deselections on the current page
/// </summary>
private void UpdateSelectedItems()
{
var selectedVendors = new List<int>();
foreach (GridItem Item in grdVendors.Items)
{
if (Item is GridDataItem)
{
int VendorID = (int)((GridDataItem)Item).GetDataKeyValue("SupplierID");
if (Item.Selected)
{
if (!selectedVendors.Contains(VendorID))
selectedVendors.Add(VendorID);
continue;
}
selectedVendors.Remove(VendorID);
}
}
}
I'm not sure why you think "combining" objects is tough. You can simply do this:
public class SelectableDataObject
{
public SelectableDataObject(MyDataObject obj)
{
this.DataObject = obj;
}
public MyDataObject DataObject { get; private set; }
public bool Selected {get;set;}
}
Then you can just do this:
return lotsOfRecords.Select(record => {
return new SelectableDataObject(record){Selected = myDictionary.ContainsKey(record.Id)}
};
Alternatively, in your view model you can have the list of objects and the dictionary as two separate properties, and when you iterate the list of objects in your view to populate your grid, you can check the dictionary if the Id exists and check/uncheck based on that. This way is a bit more clunky, but should work.
Either way, I think your dictionary idea is perfectly fine, the only thing I'd do different is just store the Id's of the ones that are selected, that way you only store a subset for each user.
I have ProperyGrid loaded with categorised PropertySpec and set to CategorizedAlphabetical sort. When form runs categories then items within categories are sorted. An annoying artefact is that PropertyGrid by default selects the first item after list was sorted and sometimes it scrolls view to selection. If item list is long you end up seeing list scrolled to somewhere in the middle.
Since PropertySpec can be created at runtime I want to always show the top of list on form load. PropertyGrid does not 'easily' expose collections and certainly not in ordered sequence. After googling around I am lead to believe this is not possible?
I came up with below code which proves otherwise.
Snippet will select fist category of sorted list. One could also select first item in that category expanding on the method but for my needs that was unnecessary.
// bind the PropertyTable to PropertyGrid
this.pg_Prefs.SelectedObject = proptable;
// get selected item
GridItem gi = this.pg_Prefs.SelectedGridItem;
// get category for selected item
GridItem pgi = gi.Parent.Parent;
//sort categories
List<GridItem> sortedCats = new List<GridItem>(pgi.GridItems.Cast<GridItem>());
sortedCats.Sort(delegate(GridItem gi1, GridItem gi2) { return gi1.Label.CompareTo(gi2.Label); });
// loop to first category
for (int i = 0; i < pgi.GridItems.Count; i++)
{
if (pgi.GridItems[i] == gi) break; // in case full circle done
// select if first category
if (pgi.GridItems[i].Label == sortedCats[0].Label)
{
pgi.GridItems[i].Select();
break;
}
}
Hope this will help others as well.
The simplified method of actually selecting category once you have sorted list would be to sortedCats[0].Select(); instead of looping through and checking each item. You would have to assert the list is not empty if you wanted to use that shortcut but that would gives some performance improvement...