Programmatically selecting Items/Indexes in a ListBox - wpf

In WPF, I'd like to set the selected indexes of a System.Windows.Controls.ListBox
I best way I've found so far is to remove all the items from the control, insert the selected, call SelectAll(), then insert the rest, but this solution neither works in my situation nor is very efficient.
So, how do you set items in a Listbox to be selected, programmatically?

You can set multiple items as selected by using the SelectedItems collection. This isn't by index, but by what you have bound:
foreach (var boundObject in objectsBoundToListBox)
{
ListBox.SelectedItems.Add(boundObject);
}

One way you can do this is to add a Selected field to your data object. Then you need to overide the default listboxitem style and bind the isselected property to the Selected property in your object. Then you just need to go through your data items and update the Selected value.
If you don't implement that Selected property as a dependency property, you need your class to implented the INotifyPropertyChanged interface and raise the propertychanged event when you set the value.

You have to do this:
ListBoxObject.SelectedItem = ListBoxObject.Items.GetItemAt(itemIndex);
Where itemIndex would be the item you want to select.
If you want to select multiple items, you need to use the ListBox.SelectedIndexCollection property.

You can do this for multiple sections:
ListBoxObject.SelectedItems.Add(ListBoxObject.Items.GetItemAt(i));
Where i is the item index.

Thanks to mdm20.
My case was actually checking a CheckBox within the ListBox, and this Dependency Property worked like a charm.
I had to inherit my custom class from DependencyObject and implement the property
public class ProjectListItem : DependencyObject{
public Boolean IsChecked
{
get { return (Boolean)this.GetValue(CheckedProperty); }
set { this.SetValue(CheckedProperty, value); }
}
public static readonly DependencyProperty CheckedProperty =
DependencyProperty.Register("IsChecked", typeof(Boolean), typeof(ProjectListItem),
new PropertyMetadata(false));
}

how to programmatically select multiple items in listbox in wpf
foreach (var boundObject in objectsBoundToListBox)
{
ListBox.SelectedItems.Add(boundObject);
}

Related

Setting observable object to NULL == CRASH

I have a List bound to a (Telerik) GridView. The selected item is a separate variable of type T which is assigned the object of the selected row in the GridView when the user clicks on a row. T is derived from ObservableObject. This means I am using MVVM Light Toolkit.
I need to deselect the row from my ViewModel in certain situations. On the GridView control this works, if the selected item is set to NULL in the ViewModel. Whenever I do this, MVVM reports a crash (NPE). I debugged it and saw that it is failing in ObservableObject.cs. It calls a method
protected bool Set<T>(
Expression<Func<T>> propertyExpression,
ref T field,
T newValue)
and crashes one line before return when calling RaisePropertyChanged(propertyExpression)
I don't know if this is working as designed or not. My problem is, that I need to set the selected Object to NULL in the ViewModel to deselect a row of my GridView in the View. I CANNOT use CodeBehind for the deselection!
Code I have:
public ObservableCollection<ContractTypeDto> ContractTypes { get; private set; }
public ContractTypeDto SelectedContractType
{
get { return _selectedContractType; }
set
{
Set(() => SelectedContractType, ref _selectedContractType, value);
RaisePropertyChanged(() => SelectedContractType);
}
}
When you click on a row in the grid it opens a new UserControl containing lots of details of this record. This control has its own ViewModel. I store the calling view Model (where the selected item is stored). When the page (control) is closed (destroyed) I have to deselect the row in the grid. I call a method like so:
protected void DeselectCallersSelectedItem()
{
if (CallingObject == typeof(ContractTypeListViewModel))
{
var vm = SimpleIoc.Default.GetInstance<ContractTypeListViewModel>();
vm.SelectedContractType = null;
}
}
Any ideas?
To remove the collection you can either set the SelectedItem property to null or clear the SelectedItems.
gridViewName.SelectedItem = null;
gridViewName.SelectedItems.Clear();
Without showing the code, we cannot precisely help you. A solution I think you can do is to implement the INotifyPropertyChanged interface in your view model and bind the selected item to a property of that type. Also check the output window if there is any binding failure.

Extended WPF Toolkit - CheckComboBox

Is anyone aware of a way to manually enable (turning on the tick) on the Check Boxes within the CheckComboBox for WPFToolkit?
Unfortunately, the Items in the Combo-box are all strings.
I'm trying to enable all flags when "Select All" checkbox is ticked.
This is a rather late response but I thought it best to post this in case it helps someone out. I have used the following approach for the WPFToolkit version:
public class Descriptor : INotifyPropertyChanged
{
private bool isSelected;
public bool IsSelected
{
get
{
return this.isSelected;
}
set
{
if (this.isSelected != value)
{
this.isSelected = value;
// Raise INotifyPropertyChanged
}
}
}
public string Name { get; set; }
}
Create a collection of these and then assign them to the ItemsSource of the CheckComboBox.
To handle select all we have an option labelled: "" as the first item in the collection, then if this item is ticked all the items are de-selected and the all case is handle under the hood. To handle the selection Changed it does involve adding an event to the Descriptor class and firing it each time the IsSelected property is changed.
I eventually tossed out Extended WPFToolkit due to it's inability to access the checkboxes directly.
Instead I created a ComboBox and manually defined Checkboxes within it, which I access directly by name, and there able to implement a "Select All" by using it's [Checked/Unchecked[ event, and use the ComboBox SelectionChanged to show a default value that expresses what has been selected in a CSV format.
Maybe be clunky, but it gets the job done.
PS. I did not need to even bother with a DataTemplate for the ComboBox
One way in the code Behind is
var ComboSelector = MyCheckComboBox as Xceed.Wpf.Toolkit.Primitives.Selector;
foreach(var item in MyCheckComboBox.Items)
ComboSelector.SelectedItems.Add(item);

WPF datagrid binding complex type property back

I have a simple Datagrid binded to an ObservableCollection from the ViewModel. This ObservableCollection is composed by a Custom Type, say ObservableCollection.
The ComplexType only have 2 properties, and only one is editable on the screen. The other one is a bool type that depends on the first.
When I edit the first property, it gets reflected to the ComplexType and it also change the second property. But the second property is not changed on the screen.
How can I update the second property on the screen?
Try this:
public class ComplexType:INotifyPropertyChanged
{
private object someProperty1;
public object SomeProperty1
{
get{return someProperty1;}
set
{
someProperty1=value;
SomeProperty2=somefunc(someProperty1);
If(PropertyChanged!=null){PropertyChanged(this, new PropertyChangedEventArgs(SomeProperty1));}
}
}
private object someProperty2;
public object SomeProperty2
{
get{return someProperty2;}
set
{
someProperty2=value;
If(PropertyChanged!=null){PropertyChanged(this, new PropertyChangedEventArgs(SomeProperty2));}
}
public event PropertyChangedEventHandler PropertyChanged;
}
An observable collection provides notification only when items are added, removed, or the whole collection is refreshed. You need to make sure that each property either raises the PropertyChanged event or is a dependency property if you want your UI to refresh when it changes.

WPF: How do I limit number of items in Combobox ItemsSource?

I've created a WPF custom ComboBox which has the ability to filter items according to a "search string". The ComboBox ItemsSource is bound to a ObservableCollection.
The ObservableCollection is a collection of "Person" object. It exposes a property "Usage Count".
Now if the "search string" is empty i have to show the Top 30 records from the ObservableCollection. The "UsageCount" property in the "Person" class decides the Top 30 Records(i.e. the the Top 30 records with the maximum UsageCount has to be displayed).
The UsageCount property changes dynamically.
How do i achieve this..
Please help. Thanks in advance :)
To handle your searchable sorted collection, you can build your own object, inheriting from ObverservableCollection, overloading Item default property, adding a (notifying) SearchString property, listening to the changes of your Person entire list, building on change (change in SeachString Or in the UsageCount of a Person) a new private list of person, and using NotifyCollectionChanged event to notify it.
here's an idea, if you need filtering why not bind to a ListCollectionView
in the View
ComboBox ItemsSource="{Binding PersonsView}" //instead of Persons
in your ViewModel:
public ListCollectionView PersonsView
{
get { return _personsView; }
private set
{
_personsView= value;
_personsView.CommitNew();
RaisePropertyChanged(()=>PersonsView);
}
}
once you populate your List
PersonsView= new ListCollectionView(_persons);
somewhere in your view you obviously have a place responding to combobox's change, where you update the filter, you can put apply filter there
_viewModel.PersonsView.Filter = ApplyFilter;
where ApplyFilter is an action that decides what gets displayed
//this will evaluate all items in the collection
private bool ApplyFilter(object item)
{
var person = item as Person;
if(person == null)
{
if(person is in that 30 top percent records)
return false; //don't filter them out
}
return true;
}
//or you can do some other logic to test that Condition that decides which Person is displayed, this is obviously a rough sample
}

MVVM and (dynamically) filling a combobox from the value of another combobox

I have a form with two ComboBoxes. One of them is being filled with objects coming from a collection in the ViewModel. When I select a value in this ComboBox, it then should fill the second ComboBox.
What I want to know is what the best way is to go about filling the second ComboBox. I think having yet another collection with the details of the selected value of the first ComboBox in the ViewModel might be a bit wasteful. I think the best way might be to hit the database with the selected value, collecting the corresponding details, and then send them back. How I think this would work is to have the details ComboBox have a binding with the 'master' ComboBox so it can get the selected value. Then ideally, the details ComboBox would then somehow get the values from the database.
Problem is that I just don't know how to implement this with MVVM, and any help would be appreciated!
Just call OnPropertyChanged of the details collection once the selected item changes.
You can pre-populate a background dictionary whose key is the possible master items and whose values are a list of detail list.
Note for the below to work you ViewModel must implement INotifyPropertyChanged
e.g.
public class MyViewModel : INotifyPropertyChanged
{
public IEnumerable<MasterOption> MasterList {get;set;}
public IEnumerable<DetailOption> DetailList {get;set;}
Dictionary<MasterOption,List<DetailOption>> DetailLookup;
MasterOption _SelectedMasterOption;
public MasterOption SelectedMasterOption
{
get { return _SelectedMasterOption;}
set
{
_SelectedMasterOption = value;
LoadDetailsList();
OnPropertyChanged("SelectedMasterOption");
}
void LoadDetailsList()
{
InitDictionary();
if (DetailLookup.ContainsKey(SelectedMasterOption))
DetailList = DetailLookup[SelectedMasterOption];
else
DetailList = null;
OnPropertyChanged("DetailList");
}
void InitDictionary()
{
if (DetailLookup == null)
{
//Grab fill the lookup dictionary with information
}
}
}
Create a method in your ViewModel that gets the data for the second combobox and update with BindingExpression in your codebehind.
private void FirstComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_viewModel.SelectionChange();
BindingExpression bindingExpression = BindingOperations.GetBindingExpression(SecondComboBox, ComboBox.ItemsSourceProperty);
bindingExpression.UpdateTarget();
}

Resources