I am using a multi-selection ListPicker (the new one in the 7.1/Mango control toolkit from Nov '11).
My code is below - a "vanilla" use case for the ListPicker, except that I initialize the SelecetedItems dependency property with a new List so I can add things to it and properly initialize the selected state for the ListPicker. Although this issue repro's whether or not I do this...
The SummaryForSelectedItemsDelegate does get called when initializing the list (e.g. when I call contactPicker.SetValue(ListPicker.SelectedItemsProperty)), but NOT when I click the "done" button on the ListPicker (although my SelectionChanged event handler does get called).
Once I dismiss the ListPicker, I only get the string corresponding to the first selected item in the "summary" for the control (as opposed to the control calling my delegate and getting a comma-delimited list of selected items).
Is this a bug? Has anyone else run into this? Is there a workaround?
var contactPicker = new ListPicker()
{
MinWidth = minWidth,
ExpansionMode = ExpansionMode.FullScreenOnly,
SelectionMode = SelectionMode.Multiple,
SummaryForSelectedItemsDelegate = (list) => { return CreateCommaDelimitedList(list); },
IsTabStop = true
};
contactPicker.ItemsSource = listOfItems;
contactPicker.DisplayMemberPath = "Name";
contactPicker.SetValue(ListPicker.SelectedItemsProperty, new List<Item>());
// initialize the list picker selected values
foreach (var contactRef in listOfSelectedContacts)
contactPicker.SelectedItems.Add(contactRef);
contactPicker.SelectionChanged += new SelectionChangedEventHandler((o, ea) =>
{
// add all the newly added items
foreach (var added in ea.AddedItems)
{
Item addedItem = added as Item;
if (addedItem == null)
continue;
listOfSelectedContacts.Items.Add(addedItem);
}
// remove all the newly removed items
foreach (var removed in ea.RemovedItems)
{
Item removedItem = removed as Item;
if (removedItem == null)
continue;
listOfSelectedContacts.Items.Remove(removedItem);
}
});
I should have posted my my summary delegate... which is actually where my bug was :-(
Even though I was creating the SelectedItems as a List, and each of the elements in the IList passed in are typed "Item", the concrete type of the IList passed in is NOT List. Therefore the null check succeeds and the method returns null. And of course my breakpoint was right after that line so it looked like the method wasn't getting invoked. Duh.
private string CreateCommaDelimitedList(IList ilist)
{
IList<Item> list = ilist as IList<Item>;
if (list == null)
return null;
// build a comma-delimited list of names to display in a control
List<string> names = list.Select(it => it.Name).ToList();
StringBuilder sb = new StringBuilder();
bool comma = false;
foreach (var name in names)
{
if (comma)
sb.Append(", ");
else
comma = true;
sb.Append(name);
}
return sb.ToString();
}
Related
I have a question regarding use of "Tag" :
I have a ListBox, or ListView, in which I have the name of my objects, I addes a "Tag" property to find its corresponding object :
foreach(Operation op_ass in ListOpAss1)
{
op_ass.getNom(Properties.Settings.Default.Langue);
ListViewItem item = new ListViewItem(op_ass.Nom);
item.Tag = op_ass;
listBoxAss1.Items.Add(op_ass.Nom);
}
Now what I would like, is when I select an item in my list(or several), make an action on corresponding objects. But how can I find them back?
For example I want to remove selected objects from a List, or get the list of Operation ID (without displaying ID in my list).
Looks like you are adding the property, op_ass.Nom into the listbox instead of the ListViewItem, item. Modify your code as follows:
foreach (Operation op_ass in ListOpAss1)
{
op_ass.getNom(Properties.Settings.Default.Langue);
ListViewItem item = new ListViewItem(op_ass.Nom);
item.Tag = op_ass;
// Add the list view item instead of op_ass.Nom
listBoxAss1.Items.Add(item);
}
Now you should be able to retrieve the tag from selected item/items as follows:
var operation = ((listBox1.SelectedItem as ListViewItem).Tag) as Operation;
Alternatively, you could think of using data binding as follows:
foreach (Operation op_ass in ListOpAss1)
{
op_ass.getNom(Properties.Settings.Default.Langue);
}
listBoxAss1.DataSource = ListOpAss1;
listBoxAss1.DisplayMember = "Nom";
And access the data bound object as follows:
var operation = listBox1.SelectedItem as Operation;
using foreach is kind of deprecated you can look into implemented functions in list of objects
ListOpAss1.ForEach(x=>
{
x.getNom(Properties.Settings.Default.Langue);
var item = new ListViewItem(x.Nom);
item.Tag = x;
listBoxAss1.Items.Add(x.Nom);
});
in order to select an item in a list you can use SingleOrDefalt() or Skip(count) take (count) for multiple files or you can run native querys with conditions to search the list like this
var items = collection.Where(x=> x.City == "Burgas").ToList(); //You can use select if you want only certain properties of the object to be selected
///then you can use that new item list to remove the objects from the collection list like this
items.ForEach(x=>
{
collection.Remove(x);
});
I am trying to get a list of all elements from ListBox if no or one of items is selected or list of selected items if more than 1 are selected. I have written such a code but it doesn't compile :
ListBox.ObjectCollection listBoXElemetsCollection;
//loading of all/selected XMLs to the XPathDocList
if (listBoxXmlFilesReference.SelectedIndices.Count < 2)
{
listBoXElemetsCollection = new ListBox.ObjectCollection(listBoxXmlFilesReference);
}
else
{
listBoXElemetsCollection = new ListBox.SelectedObjectCollection(listBoxXmlFilesReference);
}
So for this piece of code to work I would need to use something like ListBox.SelectedObjectCollection listBoxSelectedElementsCollection; which I do not want because I would like to use it in such an foreach:
foreach (string fileName in listBoXElemetsCollection)
{
//...
}
I'd simply this a bit and not mess with the ListBox ObjectCollections if you don't need to. Since you want to iterate items on your ListBox as strings, why not use a List and load the list how you show:
List<string> listItems;
if (listBoxXmlFilesReference.SelectedIndices.Count < 2) {
listItems = listBoxXmlFilesReference.Items.Cast<string>().ToList();
} else {
listItems = listBoxXmlFilesReference.SelectedItems.Cast<string>().ToList();
}
foreach (string filename in listItems) {
// ..
}
You need to convert SelectedObjectCollection to an array of object[].
ListBox.SelectedObjectCollection sel = new
ListBox.SelectedObjectCollection(listBoxXmlFilesReference);
ListBox.ObjectCollection col = new
ListBox.ObjectCollection(listBoxXmlFilesReference,
sel.OfType<object>().ToArray());
I can see what you're trying to do and it doesnt compile because the type ListBox.ObjectCollection is not the same as ListBox.SelectedObjectCollection - even though in your case they are lists that contain strings the classes themselves are different hence the compile error.
Assuming your items are strings in the listbox you could do:
var items = listBoXElemetsCollection.Items.OfType<string>();
if (listBoXElemetsCollection .SelectedIndices.Count >= 2)
items = listBoXElemetsCollection.SelectedItems.OfType<string>();
foreach(var item in items)
//do stuff
I have a list box representing the contents of a direcory. I want to allow dropping only on those items that represent directories themselves. I've tried two approaches:
First i set the itemsource of the listbox to a compositeCollection of all directory contents and tried to iterate through those with:
foreach (ListBoxItem lbItem in directoryExplorer.Items)
{
MessageBox.Show(lbItem.DataContext.GetType().ToString());
}
The messagebox comes up with 'Directory' or 'UserFile' for each item. I was hoping to have access to the items, check what they represent, and set AllowDrop as necessary.
My second approach was to add individual items like so:
ListBoxItem nxt;
foreach (Directory d in dir.childdirs)
{
MessageBox.Show(d.name);
nxt = new ListBoxItem();
nxt.DataContext = d;
nxt.AllowDrop = true;
nxt.Name = d.name;
directoryExplorer.Items.Add(nxt);
}
foreach (UserFile f in dir.childfiles)
{
MessageBox.Show(f.name);
nxt = new ListBoxItem();
nxt.AllowDrop = false;
nxt.DataContext = f;
nxt.Name = f.name;
directoryExplorer.Items.Add(nxt);
}
but then it just comes up blank.
look at mdm20's answer wpf treeview blues. I want to select an item
Just make an allowDrop property and bind it appropriately.
I need to know what are the things going on here.Actually my aim is to add a column additionally and display the contents.I Added column but i want to know
the codeflow required to finish to display items in that column
ManagedDeviceCollection list = new ManagedDeviceCollection();
try
{
if(SpoServer == null)
return;
_listSelected.BeginUpdate();
_listAvailable.BeginUpdate();
#region populate the selected list
// Collect selected items.
object[] selected = new object[_listSelected.SelectedItems.Count];
_listSelected.SelectedItems.CopyTo(selected, 0);
// Clear listview.
_listSelected.Items.Clear();
// Add systems.
ResourcePolicySystemsLVI item;
foreach(ManagedDevice md in PolicySystemsList)
{
item = new ResourcePolicySystemsLVI(md);
item.Update();
foreach(object obj in selected)
{
item.Selected = (((ResourcePolicySystemsLVI)obj).Data == item.Data);
break;
}
_listSelected.Items.Add(item);
}
// Sort list.
_listSelected.Sort();
// Ensure selected items are visible.
if(_listSelected.SelectedItems.Count > 0)
{
_listSelected.SelectedItems[_listSelected.SelectedItems.Count
- 1].EnsureVisible();
_listSelected.SelectedItems[0].EnsureVisible();
}
#endregion
#region populate the available list
// Collect selected items.
selected = new object[_listAvailable.SelectedItems.Count];
_listAvailable.SelectedItems.CopyTo(selected, 0);
// Clear listview.
_listAvailable.Items.Clear();
// Add systems.
ResourcePolicyAvailSystemsLVI item2;
foreach(ManagedDevice md in AvailableSystemsList)
{
item2 = new ResourcePolicyAvailSystemsLVI(md);
item2.Update();
foreach(object obj in selected)
{
item2.Selected = (((ResourcePolicyAvailSystemsLVI)obj).Data
== item2.Data);
break;
}
_listAvailable.Items.Add(item2);
}
// Sort list.
_listAvailable.Sort();
// Ensure selected items are visible.
if(_listAvailable.SelectedItems.Count > 0)
{
_listAvailable.SelectedItems[_listAvailable.SelectedItems.Count
- 1].EnsureVisible();
_listAvailable.SelectedItems[0].EnsureVisible();
}
#endregion
_listSelected.EndUpdate();
_listAvailable.EndUpdate();
To display text in columns after the first, use the ListViewItem.SubItems collection. For example, you might modify your main loop as follows:
item2 = new ResourcePolicyAvailSystemsLVI(md);
// skipped some of your code
item2.SubItems.Add("My second piece of info"); // this is what puts data into the second column
I wasn't sure from your question whether you had already added the requisite column to the ListView: if not, you must do so using ListView.Columns.Add in order to display the subitem.
A few days ago, I wrote about issues with implementing a ListView in ASP.NET. Now, with all of the other code written, I'm having trouble saving changed items in a ListView.
A few things of note:
The Save button is not part of the ListView proper; it calls the GetListViewItems() method, which in turns call the Save() method.
The Listview.DataBind() event is invoked when a button is pressed requesting the records to be updated
The Listview shows text using <%#Eval("Key.Name") %> and a named DropDownList using <%#Eval("Value") %>
Getting The Items From the ListView
public void GetListViewItems()
{
List<Foo> Result = FooManager.CreateFooList();
DropDownList ddl = null;
ListViewItem Item = null;
try
{
foreach (ListViewDataItem item in lvFooList.Items)
{
Item = item;
ddl = ((DropDownList) (Item.FindControl("ddlListOfBars")));
if (//something is there)
{
Foo foo = FooManager.CreateFoo();
foo.Id = item.DataItemIndex; //shows null
int barId = int.Parse(ddl.SelectedItem.Value); //works just fine
foo.barId = barId;
Result.Add(foo);
}
}
}
catch (Exception ex)
{
//Irrelevant for our purposes
}
}
DataBinding the ListView
The code to databind the ListView is shown here in my previous question.
Question(s):
Why is it that when I iterate through the ListViewDataItem in the Listview that each item is null?
How can I retrieve the Foo.Id from the Dictionary?
What else might I be missing?
What would I use if I wanted to get that Id Programmatically based on what items were shown? As it is now, the current ListView is shown based on what Foos were selected. Those Foos selected are then displayed, and the user can change the Bar in the DropDownList, hit Save, and those changes are propogated.
Update
As it turns out, my problem was what leppie had said; and that was that I needed to specify DataKeyNames and use those to retain the information from the ListView.
Here's the code I added:
try
{
int DataKeyArrayIndex = 0;
foreach (ListViewDataItem item in lvFooList.Items)
{
Item = item;
ddl = ((DropDownList) (Item.FindControl("ddlListOfBars")));
if (//something is there)
{
Foo foo = FooManager.CreateFoo();
Foo tempFoo = FooManager.CreateFoo();
if (lvFooList != null)
{
tempFoo = ((Foo)(lvFooList.DataKeys[DataKeyArrayIndex].Value));
}
foo.Id = tempFoo.Id;
int barId = int.Parse(ddl.SelectedItem.Value); //works just fine
foo.barId = barId;
Result.Add(foo);
DataKeyArrayIndex++;
}
}
}
And then in the .ascx file, I added DataKeyNames="Key", like so:
<asp:ListView ID="lvFooList" runat="server" DataKeyNames="Key">
This allowed me to use the Key from my previous post to determine which Foo was being looked at.
Any critiques of this approach, as well as methods for making it better are greatly appreciated.
Some quick answers:
Your need to use databinding for that to work, in other words, assign to DataSource and call DataBind(). EDIT: seems you are doing that. But remember it wont persist between postbacks, just the DataKey (see below).
If I recall correctly, you need to specify the DataKeyNames, and they can be retrieved from the DataKey property then.
you can also use the ListViewDataItem.DataItemIndex property instead of keeping your own index, as in:
foreach (ListViewDataItem item in MyListView.Items)
{
// in this example key is a string value
Foo foo = new Foo(MyListView.DataKeys[item.DataItemIndex].Value as string);
// do stuff with foo
}