Combo Box in Silverlight 4 - c#

I have a combo box that is bound to a collection that is essentially a list of Name/Value pairs. The collection can have multiple items with different names, but the values may be the same.
public class NameValuePair
{
public string Name { get; set; }
public string Value { get; set; }
}
public class NameValuePairCollection : List<NameValuePair>
{
public NameValuePairCollection(): base() { }
}
So inside my User Control I have a private field called items which is an instance of that NameValuePair collection:
private NameValuePairCollection items = new NameValuePairCollection()
Somewhere along the lines that collection gets initialized and items get added to it. However, the problem I see is when I try to set the selected index of the combo box that is bound to this collection:
this.CboItemsSelector.SelectedIndex = 3;
or
this.CboItemsSelector.SelectedItem = this.items[3];
The selected item is there but the UI is not synchronized. The UI's selector still defaults to the first item in the list, even thought the SelectedItem's Name and Value properties DO IN FACT CORRESPOND to whatever is in index 3 of the underlying collection!
Any ideas on how to force the ComboBox to refresh itself? Or just plain fix the issue? I know it's quite small issue, but it is big enough to force me to rewrite quite a bit of code.... :( :( :(
Thanks!

You need to inherit from ObservableCollection, not List. Otherwise no OnPropertyChanged events will be fired and the bound control wont know the data has been updated.

Do the selected Item's Name and Value properties match or is the SelectedItem an instance from within the same collection? .Net will not know to compare the items by name and value unless you tell it to, else it will use object equality to try and find the item in your list. If you are setting the selected item to an instance that is not actually in the list (but has the same properties) .net will not find it in the list. You have 2 options, override equality for your object and force comparison of properties, or ensure that you always set the selected item to an item in the list. Also try what Andy May suggested and do 2 way binding on the ItemsSource and on the SelectedItem, should work then

Related

Combo Box Two Way Binding resets Properties

I have a combo box in a datagrid located in an activity. Based on combobox selection I populate another grid with controls programatically. User enters some data in these controls and then saves it. The object that the combo box is bound has many properties of which two are used in selected value path and display member path. The data is bound using two way binding for combo box. When the saved activity that has been placed on a workflow is reopened the data is reloaded correctly and the correct object is value is set in the combo box. But on UI rendering only the values that are attached with the combo box remain intact (i.e those in selected value path and display member path) the rest are reset.
Any idea why this might be happening?
P.S: Setting the binding to OneTime solves the problem of retrieval but any changes that are made on the UI after loading are not reflected back.
Code-Behind:
public ObservableCollection<MyRule> AllRules {get;set;}
public MyRule myRule{get;set;}
In datagrid Loaded Event I populate the AllRules as:
AllBusinessRules.Add(new MyRule () { RuleId = item.Id, RuleName = item.Name});
where item.Id and item.Name are obtained from the database via service call.
In the same event if I also load any previously saved rules as:
myRule=SelectedRule;
where SelectedRule has RuleId, RuleName, Inputs and Outputs as well.
Code:
<ComboBox
ItemsSource="{Binding Path=AllRules}"
SelectedItem="{Binding Path=myRule,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
SelectedValuePath="RuleId"
DisplayMemberPath="RuleName">
<DataTemplate>
<TextBox Text="{Binding Path=myRule.RuleName}"/>
</DataTemplate>
</ComboBox>
Class:
public class MyRule{
public int RuleId{get;set;}
public string RuleName{get;set;}
public List<string> Inputs{get;set;} //properties that are reset when the UI renders
public List<string> Outputs{get;set;} //properties that are reset when the UI renders
}
The Inputs and Outputs properties are obtained from the programatically generated controls via reflection and added to the object populated by combobox and saved.
I have studied about this problem here but the solution does not solve my problem. Any help would be great.
SelectedValuePath, DisplayMemberPath are set wrongly. DisplayMemberPath should be "RuleName". SelectedValue, SelectedValuePath are not needed as you have set SelectedItem. SelectedItem will get the chosen item automatically because of Binding. From the myRule object you can access other properties.
It took a lot of time to investigate what was wrong but now that I know its just quite simple.
As I have shown that in the datagrid's Loaded event I used to set the ItemsSource of the combo box and in the item source I have only set the properties RuleId and RuleName.
Problem:
So the problem was that when I assigned the value i.e the selected value on reloading the combobox e.g. myRule=SelectedRule the other properties i.e. Inputs and Outputs are not there in the ItemsSource. That is why the selected object though correct did not have Inputs and Outputs as the SelectedItem was from ItemsSource of the Combo Box giving me the impression that the two way binding had somehow reset the values of properties not bound with the combo box.
Solution:
In the end I wrapped my MyRule Object in another object like RuleInformation i.e
public class RuleInformation{
public List<string> Inputs;
public List<string> Outputs;
public MyRule myRule{get;set;}
}
where MyRule is like:
public class MyRule{
public int RuleId{get;set;}
public string RuleName{get;set;}
}
So the combo box is bound to the MyRule object whereas the inputs and output properties remain untouched in the upper object.

ComboBox binded to observable collection how to add 1 extra value

I have a combobox binded to an observable collection through
cmbBladesTab1.ItemsSource = easyRunData.olstBlades;
that works fine.
I want the combobox to be binded to all that values plus one.
E.g.
easyRunData.olstBlades; contains "PL1", "PL2", "PL3", "PL4"
while cmbBladesTab1 contains "ALL BLADES", "PL1", "PL2", "PL3", "PL4"
--ADD all work has to be done from code-behind
Thanks for your help.
You could add a property, that adds the particular item to the list.
ObservableCollection<string> myCollection;
ObservableCollection<string> MyCollectionViewProp
{
get
{
var tempCollection = new ObservableCollection<string>(myCollection);
tempCollection.Add("Extra element");
return tempCollection;
}
}
Depending on the size of the collection and the number of times it is accessed, this is probably the programmatically simplest solution. If you need to access it often, the worse this solution gets, as it creates a new collection every time.
In this case you should probably listen to the CollectionChanged event and keep a separate redundant list.
Easiest way would be to add an extra item in the observable collection with some prefixed text / key.
That way, because it's in the collection, it will be visible in the combobox and when the user selects this item you can evaluate it to see if it's the added item or not.
A good example is indeed given as an answer on this question add an item to combobox before bind data from data base

Remove or add items in DataGridComboboxColumn on row add or delete

Here's my problem: I need to make a DataGrid with dynamic comboboxes using the WPF. If the value of a combobox is already used in the previous rows, the next ones, that will be added by the user, shouldn't contain the item already used.
In this image, the ITEM A shouldn't apear on the combobox of the second line.
I don't have ideia how to accomplish this, can anyone show me a light?
OBS: The DataGrid ItemsSource is binded to an ObservableCollection, and the DataGridComboBoxColumn ItemsSource is a List.
Thanks !!!
The ItemsSource of the combo doesn't have to be bound to an ObservableCollection, but it can help depending on exactly how you solve this.
When that cell goes in to edit mode the property the ItemsSource is bound to gets hit - so you can return a new list of items each time the getter is hit. Here is a very basic example to give you an idea:
public List<string> MyItemsSource
{
get
{
var myNewList = MyMasterList.ToList(); //create a (reference) copy of the master list (the items are not copied though, they remain the same in both lists)
if (PropertyA != null)
myNewList.Remove(PropertyA);
return myNewList;
}
}
So what you are creating and returning is a filtered version of your master list of all possible items. LINQ will be of great help to you here.
Alternatively you could keep just one static copy of the master list as an ObservableCollection, and simply remove items from that static copy as they get selected (and add them back in as they get unselected). Which option you choose will depend on how many times the list can be modified due to items being selected and how complicated it is to generate the list. I've used the dynamically generated list many times in the past, it's an option that works well in most cases.

The alternate style for ListBox in WPF not working as expected if the collection has some items which needs to be hidden

I am trying to bind my collection to a listbox. The collection contains some items which are to be hidden and are to be shown based on certain conditions.But when I do this the alternate styles are not applied properly.
For example:
Case 1 : where all items are visible, I get the output like this:
Item1(grey)
Item2(white)
Item3(grey)
Item4(white)
Item5(grey)
Item6(white)
Item7(grey)
Case2: where Item2 is hidden, I get the output as:
Item1(grey)
Item3(grey)
Item4(white)
Item5(grey)
Item6(white)
Item7(grey)
How do I resolve this without rebinding the collection?
Rather than hide the items (presumably in the ListBoxItem control template, or the ListBox ItemTemplate), you should use a CollectionViewSource to filter them out.
This is because the items are still technically there - you cannot see them but the listbox can.
See this link for details on filtering.
http://wpftutorial.net/DataViews.html
To filter a collection view you can define a callback method that
determines if the item should be part of the view or not. That method
should have the following signature: bool Filter(object item). Now set
the delegate of that method to the Filter property of the
CollectionView and you're done.
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _filterString );
}

Keep SelectedItem during filter of CollectionViewSource

I have a listbox that is being filter like this:
var view = CollectionViewSource.GetDefaultView(FilterSource);
view.Filter = FilterCode;
I am running into a problem where the SelectedItem is getting lost when the filter is used with viewmodel code like this:
VM
{
public ObservableCollection<Model> Items{get;set;}
public Model SelectedItem
{
get{return _selectedItem;}
set{_selectedItem = value; NotifyPropertyChanged();}
}
}
When the filter is applied, the SelectedItem is set to null. However, I want to keep that selected item unless the user actually clicks off of it. If the filter is removed, then the selected item should still be selected. The Model does have an IsSelected property, and I have been trying to think of ways to query for the IsSelected property. However, then the view's binding would not work the way I expect....or at least I am going in circles thinking it cannot
My only way of accomplishing a fix here is the following in the SelectionChanged event:
if (e.AddedItems.Count == 0 && e.RemovedItems.Count >= 0)
SpecialtyListBox.SelectedItem = e.RemovedItems[0];
But, this seems really hacky and forces that there must always be an item selected once an initial one is selected. In this case, that might work, but I would still like to see if anybody has a better solution?
I had a similar problem, where the listboxes were shown as tabbed views. I solved the problem by creating a Converter to produce a Boolean flag for "isActive" and assigning it to CollectionViewSource.IsLiveFilteringRequested. This prevented the non-active list boxes from updating the selected item.
You can fix this by creating a filter that always adds the current selected item to the filtered items. If this cannot be done directly, just hold the selected item in a separate variable.

Categories

Resources