Unable to fill JavaFX ComboBox on runtime - combobox

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.

Related

WPF UserControl does not update contents

I create an Window and stylezed it with MahApps, i also created an UserControl. In my UserControl i create a method that populate some data in the UserControl elements.
In my window, i created a button that do the following:
EdicaoFisioterapeuta ed = new EdicaoFisioterapeuta();
ed.LoadContents("My Text");
when i debug the code, i see the elements of user control being populated, here is the code:
public void LoadContents(string text)
{
textBox1.Text = text;
lbl.Text = text;
}
After all the job, the textBox1 and the lbl does not get the content "My Text".
I create another Project and repeated the process, everything is working fine in the new project.
In my "old" project i removed MahApps, but ir doesnt get effect.
I know, it is so simple, but i cant find an solution for this trouble.
Oh man, i was instatiating the wrong class, it seems i need to sleep a little more.

Where the combobox bound items are coming from?

May be it's a silly (or more than trivial) kinda question, but it seems i just don't know the answer. Here's the case -
I assigned a UserList as the ItemsSource of a combobox. So what i did essentially is assigning a reference type to another.
I cleared the UserList. So now i get the Count of the ItemsSource 0 as well.
I still get the items present in my combobox. And i also can cast the SelectedItem of the combobox to a User object.
Here's the complete code -
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public partial class MainWindow : Window
{
private List<User> _userList;
public MainWindow()
{
InitializeComponent();
_userList = new List<User>()
{
new User() {Id = 1, Name = "X"},
new User() {Id = 2, Name = "Y"},
new User() {Id = 3, Name = "Z"}
};
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.comboBox1.ItemsSource = _userList;
this.comboBox1.DisplayMemberPath = "Name";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
_userList.Clear();
/* ItemsSource is cleared as well*/
IEnumerable userList = this.comboBox1.ItemsSource;
/*I can still get my User*/
User user = this.comboBox1.SelectedItem as User;
}
}
So, where the items are coming from? What actually happens under-the-hood when i make such binding? Does the control have some kind of cache? It's a royal pain to realize not having such basic ideas. Can anybody explain the behind-the-scene detail?
EDIT : I wrote the code in WPF, but i have the same question for WinForms Combobox.
EDIT : Doesn't a combobox display its items from it's in-memory Datasource? When that datasource contains 0 items, how does it display the items?
When you set an ItemsSource of any ItemsControl it copies the ref to the list into its Items property. Then it subscribes to the OnCollectionChanged event, and creates a CollectionView object. So, on the screen you can see that collectionView.
as I have found in source code ItemCollection holds two lists:
internal void SetItemsSource(IEnumerable value)
{
//checks are missed
this._itemsSource = value;
this.SetCollectionView(CollectionViewSource.GetDefaultCollectionView((object) this._itemsSource, this.ModelParent));
}
How could you get SelectedItem?
This is my assumption from quick look into the source code:
ItemsControl has a collection of "views" and each View sholud store a ref to the item (User instance), because it has to draw data on the screen. So, when you call SelectedItem it returns a saved ref.
Upd about references
Assume there is an User instance. It has the adress 123 in memory. There is a list. It stores references. One of them is 123.
When you set an ItemsSource ItemsControl saves a reference to the list, and creates a Views collection. Each view stores a references to an item. One view stores an address 123.
Then you cleared a list of users. Now list doesn't contains any references to Users. But in memory there is an adrress 123 and there is an instance of User by this adress. Garbage Collector doesn't destroy it, because View has a reference to it.
When you get SelectedItem it returns User instance from the 123 adress.
var user = new User();
var list = new List<User>();
list.Add(user);
list.Clear();
Console.WriteLine(list.Count()); //prints 0 - list is empty
Console.WriteLine(user == null); //prints false. - user instance is sill exists;
In answer to your comment to #GazTheDestroyer ("... why it doesn't get cleared, and how it holds the items?")
In WPF, when you set the ItemsSource property of an ItemsControl, the control will wrap the list of items in a CollectionView, which is a collection type optimised for use by the UI framework. This CollectionView is assigned to the Items property of the control and is what the display-drawing code actually works from. As you see, this collection is entirely separate of the object you originally assigned to ItemsSource, and so there is no propogation of changes from one to the other. This is why the items are still in the control when you clear the original list: the control is ignoring the original list, and has its own list that contains your objects.
It's for this reason that an ItemsSource value needs to raise events - specifically INotifyCollectionChanged.NotifyCollectionChanged - so that the control knows to refresh the Items list. ObservableCollection implements this interface and raises the correct event, and so the functionality works as expected.
It's hugely important to note that this is nothing like what happens in WinForms, which is why I've been pressing you for the clarification.
EDIT: To clarify, there is no "deep copy." The code that is happening is similar in principle to the following:
private List<object> myCopy;
public void SetItemsSource(List<object> yourCopy)
{
myCopy = new List<object>();
foreach (var o in yourCopy)
{
myCopy.Add(o);
}
}
Once this code has run, there's only one copy of every item in your list. But each of the items is in both of the lists. If you change, clear or otherwise manipulate yourCopy, myCopy knows nothing about it. You cannot "destroy" any of the objects that are within the list my clearing yourCopy - all you do is release your own reference to them.
Assuming you are using WPF:
List<User> doesn't fire any event that the UI will recognise to refresh itself. If you use ObservableCollection<User> instead, your code will work.
The key difference is that ObservableCollection implements INotifyCollectionChanged, which allows the UI to recognise that the content of the collection has changed, and thus refresh the content of the ComboBox.
(Note that this does not work in WinForms. In WinForms you can set the DataSource property of the control, but the same ObservableCollection trick does not work here.)
When you set a collection reference to ItemsControl, all the combo gets is a reference, that it knows is enumerable.
It will enumerate the reference and display the items. Whether it does a deep copy or shallow copy is irrelevant, all it has is a reference (memory address effectively).
If you change your collection in some way, the combo has no way of knowing unless you tell it somehow. The reference (address) hasn't changed, everything looks the same to the combo. You seem to be thinking that the object is somehow "live" and the combo can watch the memory changing or something? This isn't the case. All it has is a reference that it can enumerate over. The contents can change but without some trigger the combo doesn't know that, and so will sit doing nothing.
ObservableCollection is designed to overcome this. It implements INotifyCollectionChanged that fires events when it changes, so the Combo knows that it must update its display.

silverlight 4: setting selectedvaluepath, how do i extract it?>

im completely new to the wcf service things so im a bit lost on the approach here. I have an operation in the service called GetHoldsJoined. The listbox im binding to is called lbxOpenHolds. I am able to set the result as an itemsource with the following:
public frmHoldsDashBoard()
{
InitializeComponent();
dbServiceClient db = new dbServiceClient();
db.GetHoldsJoinedCompleted +=new EventHandler<GetHoldsJoinedCompletedEventArgs>(db_GetHoldsJoinedCompleted);
db.GetHoldsJoinedAsync();
}
private void db_GetHoldsJoinedCompleted(object sender, GetHoldsJoinedCompletedEventArgs e)
{
lbxOpenHolds.ItemsSource = e.Result;
}
but what I want to do is set the selectedvaluepath to an attribute in one of the result list items. The result is a List collection. There is an attribute in each one of the list objects called a.HoldID, it is composite. I want this to be the selectedvalue member. How do I unbox this from GetHoldsCompletedEventArgs ?? Or is there some other way to do this?
Sorry if the question is asked kind of scatterbrained, I really didn't know how else to explain it.
What about something like this:
lbxOpenHolds.SelectedValuePath = "HoldID";
This should go right after setting the ItemsSource on lbxOpenHolds

Why is ListBox.Items.IsReadOnly=true? (F#/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?

Validating new rows in Silverlight DataGrid

We're using RIA Services / Silverlight 4, and I'm binding a datagrid to something like Context.Foo.
I can see validation errors appearing in the datagrid's validation summary when users try to enter empty strings into mandatory fields and so forth, life is good.
However when I add a new item by calling something like Context.Foo.Add(new Foo) in the viewModel, the new row appears in the datagrid, but is never validated unless the user clicks on a cell.
Is there a way to ask the DataGrid is validate all items?
Rather than asking the DataGrid to validate the row for you, you will need to validate the object itself that the new row is bound to. You can use the Validator class to do this for you. For example, say your object is assigned to a variable named newRowObject, you can do the following:
List<ValidationResult> validationResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(newRowObject, null, null);
bool isValid = Validator.TryValidateObject(newRowObject, validationContext, validationResults, true);
This should achieve what you are after (I emphasis should, simply because I didn't check it myself in an example before I wrote this).
Hope this helps...
Chris

Resources