Why is ListBox.Items.IsReadOnly=true? (F#/Silverlight) - silverlight

I'm trying to remove some objects from a ListBox that I have created, and for some reason ListBox.Items.IsReadOnly is true.
The following calls don't work:
myListBox.Items.Add("whatever")
myListBox.Items.Add("stuff")
myListBox.Items.Remove("whatever")
I get an exception:
{System.InvalidOperationException: Operation not supported on read-only collection.
at System.Windows.Controls.ItemCollection.RemoveImpl(Object value)
at System.Windows.Controls.ItemCollection.RemoveInternal(Object value)
at System.Windows.PresentationFrameworkCollection`1.Remove(T value)
I can set ListBox.ItemsSource, but working with .Items is much easier. I'm creating the ListBox like this:
let mutable myListBox= new ListBox()
Any ideas/suggestions would be greatly appreciated. Thanks.

I'm not sure of the F# syntax, but you should be setting the ListBox.ItemsSource.
If you create an ObservableCollection and then set that to the ItemsSource you can add and remove items from the collection and the list box will update automatically.

The following code works fine for me:
open System.Windows.Controls
let l = ListBox()
l.Items.Add("An item")
l.Items.Add("Another item")
l.Items.Remove("An item")
Are you doing something else in between the creation of the list box and attempting to add items?

Related

ItemsCollection's ContainerFromItem fails for an object that I'm pretty sure is in the collection

I am trying to add search functionality to a ListBox that will scroll down to the item being searched for.
I have a ListBox that is bound to an ObservableCollection that I have filled with a bunch of RecipeNameDTO objects. I can easily find an object in the Items collection with a simple search.
string searchItem = tbSearchString.Text;
var recipenameitem = lbRecipeNames.Items.Cast<DTO.RecipeNameDTO>().Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).FirstOrDefault();
I can reproducibly find items with this method.
However, if I then try to find the object's container using ContainerFromItem, the method returns a null unless the object is visible in the ListBox when I execute the method:
ListBoxItem lbi = (ListBoxItem)lbRecipeNames.ItemContainerGenerator.ContainerFromItem(recipenameitem);
I am certain (I think) that the actual object exists in the ItemsCollection before I execute ContainerFromItem because I use a non null result from the search I documented in the beginning of this post. Also, I can scroll down the ListBox and find the object I'm searching for.
It must be something with the way the ListBox caches the objects in the ItemsCollection that is preventing ContainerFromItem from returning the container. Is there a fix to my code (or understanding of the problem)?
Michael
I took Andy's suggestion and made my ListBox IsSynchronizedWithCurrent = True and then used the following code to set the current item and scroll it into view:
string searchItem = tbSearchString.Text;
CollectionViewSource cvs = (CollectionViewSource)this.FindResource("cvsRecipeName");
ObservableCollection<DTO.RecipeNameDTO> itemsCollection = (ObservableCollection<DTO.RecipeNameDTO>)cvs.Source;
List<DTO.RecipeNameDTO> recipenameitems = itemsCollection.Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).ToList();
if (recipenameitems.Count > 0) { cvs.View.MoveCurrentTo(recipenameitems[0]);}
lbRecipeNames.ScrollIntoView(lbRecipeNames.SelectedItem);
I'm sure I could modify this to make it more flexible, but here's the first fix.

Unable to fill JavaFX ComboBox on runtime

I am doing a project where I have to fill a ComboBox with attributes from objects the user has just stored in a Hashmap before. This means I cannot assign the Items to the ComboBox in advance. This is the first time I am trying to use javaFX. So far it was ok, but when I try to fill the ComboBox "loggedOnUsersDropDown", nothing happens. It just stays empty. I created the UI including the ComboBox with scene builder. The code of the method looks like this:
#FXML
protected void loadLoggedOn(){
ArrayList<String> loggedOn = new ArrayList();
for (User LOGGED_Onkey : bd.currentSSO.LOGGED_ON.keySet()) {
loggedOn.add(LOGGED_Onkey.getAttribute(LOGGED_Onkey.USER_NAME)); //System.out.println(LOGGED_Onkey.getAttribute(LOGGED_Onkey.USER_NAME));
}
ObservableList<String> obList = FXCollections.observableArrayList(loggedOn);
//loggedOnUsersDropDown.getItems().clear();
loggedOnUsersDropDown = new ComboBox<String>();
loggedOnUsersDropDown.getItems().addAll(obList);
System.out.println(loggedOn.size());
}
I would appreciate any answer. Thanks in advance and soory if i forgot some important information.
You are creating a new ComboBox and populating it. That ComboBox is not a part of your scene graph, so you don't see the result of populating it.
Assuming the #FXML injection is set up correctly, you should be able to just remove the line
loggedOnUsersDropDown = new ComboBox<String>();
and it will work correctly.

WPF DataGrid checkbox binding

I'm not sure what I'm missing here, but I'm having trouble getting a checkbox to bind to a list properly. The rest of the properties of the list bind just fine, but the checkbox is having issues. Here's what I have:
In the class that serves as the template for each object in the list I have:
Property Process As New CheckBox
In the MainWindow_Loaded Event I have:
Dim ProcessCol As new DataGridCheckBoxColumn
ProcessCol.Header = "P?"
ProcessCol.IsReadOnly = False
...
InputGrid.ItemsSource = InputData 'Which is a list of my Order Allocation objects which contains the checkbox property
...
Dim ProcessBinding As New Binding("Process")
ProcessBinding.Mode = BindingMode.TwoWay
ProcessCol.Binding = ProcessBinding
...
InputGrid.Columns.Add(ProcessCol)
When I try to populate this collection and look at the items I get checkbox = nothing. I'm not sure what I'm missing here... I know I couldn't be too far off...
Edit: I changed the property to "new CheckBox" and now I get an initialzied checkbox object in the list item as "System.Windows.Controls.CheckBox Content: IsChecked:False which in this case should have been true. So maybe a step closer, but still not there.
I found an answer here: WPF: CheckBox in DataGrid
Technically not an answer to my original question, but it works. Rather than a checkbox property in my class I now have a boolean property. The column is still created as a checkbox column. It works.
If a column can be bound to a chebox property of a list I'd still be interested in hearing that, but for me this solution works.

Adding an Item to ItemsSource from a control

A control has an ItemsSource Property of type IEnumerable. If I try to add items to Items collection when ItemsSource is set I get an error "Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."
A method for Removing Items is present here:
WPF - Best way to remove an item from the ItemsSource
However I cannot find a method to (based on the same interface) to add a new Item. The AddNew method does not take any arguments. From the sample at : http://msdn.microsoft.com/en-us/library/system.componentmodel.ieditablecollectionview.canaddnew.aspx I felt this to be the correct code:
IEditableCollectionView items = paneToDropInto.Items;
if (items.CanAddNew)
{
object newitem = items.AddNew();
newitem = contentToTransfer;
items.CommitNew();
}
However it does not work. It does add the new item. But it is a blank Item. Note contentToTransfer.
Figured it out. As pointed out by Tom and Djerry +1 both (thanks). I am just re referencing the new item which will not cause the original new item generated by AddNew to be saved (very stupid of me).
However there is another interface I can use (and did use):
IEditableCollectionViewAddNewItem items = paneToDropInto.Items;
if (items.CanAddNewItem)
{
object newitem = items.AddNewItem(contentToTransfer);
}

WPF bindings not refreshing

I have a listbox on my form that looks like this:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged"/>
I am binding the following collection to it:
ObservableCollection<Hand> oHands = new ObservableCollection<Hand>();
using the following code:
lbResults.DataContext = oHands;
Binding binding = new Binding();
lbResults.SetBinding(ListBox.ItemsSourceProperty, binding);
The oHands collection gets populated via a background worker that announces via an event whenever a new Hand object is available. The ListBox refreshes perfectly when something is added. The ToString() result of the Hand object is displayed and that is what I want - so far so good. However, when the background worker finishes
void finder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
oHands = new ObservableCollection<Hand>(oHands.OrderBy(o => o.PotSize));
lbResults.SetBinding(ListBox.ItemsSourceProperty, new Binding());
}
The items in the list are still showing up in the original order. I can confirm that the list is re-ordered but the items are still showing up in the original order. How do I refresh this binding?
you dont want to assign oHands a new collection. just Clear() the collection then add the results from the operation. don't update the binding
instead of replacing the entire observable collection, you could just clear it and add all your new items. that wouldn't affect your binding.
You could also use a CollectionViewSource as your binding, and set the order on that instead of reordering the whole collection.
Wouldn't it be a lot easier to just set the itemsource directly?
lbResults.ItemsSource = oHands;
You're really just supposed to inherit from the INotifyPropertyChanged interface, but heres another way to force an update to a binding:
BindingExpression exp = BindingOperations.GetBindingExpression(lbResults, Listbox.ItemsSourceProperty)
exp.UpdateTarget()
Edit: I also just noticed you aren't setting any binding in the XAML and appear to be doing it programmatically with an empty Binding. I haven't tried that way before, so see if changing your XAML to this might help:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged" ItemsSource="{Binding Path=oHands}"/>
Then you set lbResults.DataContext to point to the class that has the member oHands. This is what worked for me in my project (in IronPython, so forgive me if my examples didn't convert to C# perfectly).

Resources