I am working with Listbox in wpf.My problem is I am getting only one selected item from listbox.I want to get all the item which is selected but don't know how to implement ? please help!
Here is my code :
<ListBox ItemsSource="{Binding Markets}" BorderThickness="0" Background="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Style="{DynamicResource CheckBoxStyle1}" Width="140" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is my viewmodel content:
private string _selectedItem;
public List<string> Markets { get; set;}
public SettingVM()
{
Markets = new List<string>();
Markets.Add("United Kingdom");
Markets.Add("Australia");
}
public string SelectedItem
{
get
{
return _selectedItem;
}
set
{
Set(() => SelectedItem, ref _selectedItem, value);
}
}
You can bind directly the SelectedItem listobx property to your view model but not the SelectedItems property.
I used a behavior to achieve this associated with a delegate command (included in the Prism framework)
Your object
class MyObject
{
}
The behavior
internal class MyObjectSelectionChangedToCommand
{
public static readonly DependencyProperty SelectionCommandProperty =
DependencyProperty.RegisterAttached("SelectionCommand", typeof(DelegateCommand<GridSelectionInfo<MyObject>>),
typeof(ResourceCardGridSelectionChangedToCommand), new PropertyMetadata(null, OnSelectionCommandChanged));
public static DelegateCommand<GridSelectionInfo<MyObject>> GetSelectionCommand(DependencyObject obj)
{
return (DelegateCommand<GridSelectionInfo<MyObject>>)obj.GetValue(SelectionCommandProperty);
}
public static void SetSelectionCommand(DependencyObject obj, DelegateCommand<GridSelectionInfo<MyObject>> value)
{
obj.SetValue(SelectionCommandProperty, value);
}
private static void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var tsci = new GridSelectionInfo<MyObject>
{
Added = e.AddedItems.Cast<MyObject>().ToList(),
Removed = e.RemovedItems.Cast<MyObject>().ToList(),
Selected = ((ListBox)sender).SelectedItems.Cast<MyObject>().ToList()
};
var cmd = GetSelectionCommand((DependencyObject)sender);
cmd.Execute(tsci);
}
private static void OnSelectionCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dg = d as ListBox;
if (dg != null) dg.SelectionChanged += dg_SelectionChanged;
}
}
public class GridSelectionInfo<T>
{
public GridSelectionInfo()
{
Selected = new List<T>();
Added = new List<T>();
Removed = new List<T>();
}
public List<T> Added { get; set; }
public List<T> Removed { get; set; }
public List<T> Selected { get; set; }
public override string ToString()
{
return string.Format("Added: {0}, Removed: {1}, Selected: {2}", Added.Count, Removed.Count, Selected.ToFormattedString());
}
}
The XAML part where you bind the view model command to the behavior
<ListBox ItemsSource="{Binding MyCollection}"
resources:ResourceCardGridSelectionChangedToCommand.SelectionCommand="{Binding CmdObjectSelectionChanged}">
</ListBox>
Then, in you view model you just have to declare the command
public DelegateCommand<GridSelectionInfo<MyObject>> CmdObjectSelectionChanged { get; private set; }
And create it
CmdObjectSelectionChanged = new DelegateCommand<<GridSelectionInfo<MyObject>>(ExecuteSelect,CanExecuteSelect);
Thus, every time the selection changes in your listbox, you will receive all the info about selected items in the execute delegate wrapped in the GridSelectionInfo object;
maybe this help you
You can find them in ListBox.SelectedItems. use foreach
foreach (ListItem li in listBox1.SelectedItems)
{
// your code here
}
You use "SelectedItem="{Binding SelectedItem}">". Acording to MSDN "Gets or sets the first item in the current selection or returns null if the selection is empty "
And you need to bind SelectedItems to a list in your viewmodel.
In the ListBox controls, the SelectedItems property is read-only but you can add a behavior that do the job, see this answer : https://stackoverflow.com/a/8369532/1431524
I had the same problem and when I was binding to the SelectedItems the property was not notifying the model in some cases! I ended extending my entity with a non persisted property ("Selected") and then binded this property to the IsCheched property of the CheckBox.
In your case create another partial class file Market to extend the Market object, add a new bool property (e.g. Selected) and then at your checkbox bind this property:
<CheckBox Content="{Binding}" IsChecked="{Binding Selected}" Style="{DynamicResource CheckBoxStyle1}" Width="140" Margin="10"/>
Related
So I am trying to bind the following ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<ListBoxItem> _PlacesOrCities;
public ObservableCollection<ListBoxItem> PlacesOrCities
{
get { return _PlacesOrCities; }
set { _PlacesOrCities = value; RaisePropertyChanged("PlacesOrCities"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ViewModel()
{
_PlacesOrCities = new ObservableCollection<ListBoxItem>();
}
}
To the following xaml:
<ListBox Name="lbPlacesCity" ItemsSource="{Binding Path=(gms:MainWindow.ViewModel).PlacesOrCities, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate DataType="models:ListBoxItem">
<TextBlock Style="{StaticResource MaterialDesignBody2TextBlock}" Text="{Binding Name}" Visibility="{Binding Visibility}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the codebehind as such:
public ViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
DataContext = ViewModel;
}
And upon firing a button click event- I try to set the values of the observable collection using a in memory list:
private void StateProvince_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_CurrentSelectionPlaces = Canada.Provinces
.FirstOrDefault(x => x.Abbreviation == _SelectedStateProvince_ShortName)
.Place.OrderBy(x => x.Name).ToList();
foreach (var currentSelectionPlace in _CurrentSelectionPlaces)
{
ViewModel.PlacesOrCities.Add(currentSelectionPlace);
}
}
But it seems like none of the items are being added to the collection. Am I binding it incorrectly?
I've tried quite a few solutions but none of them seem to change the result- where no items in the list are being loaded into the collection properly.
EDIT:
It may be worth noting that the ListBoxItem as seen in the ViewModel is a custom model:
public class ListBoxItem
{
[J("Name")] public string Name { get; set; }
[J("PostalCodes")] public string[] PostalCodes { get; set; }
public Visibility Visibility { get; set; } = Visibility.Visible;
}
You should try to fit to the MVVM pattern, so the population of the list should occur at viewmodel level and not in the view's code behind.
You mentioned that you use a click event, instead of doing so, try to bind the command property of the button to a command in the viewmodel, see this link with an explanation of several types of commands and how to use them: https://msdn.microsoft.com/en-us/magazine/dn237302.aspx
In the other hand, if you already set the data context in the window constructor, to bind the ListBox items source you only need the name of the property to bind, "PlacesOrCities":
<ListBox Name="lbPlacesCity" ItemsSource="{Binding Path=PlacesOrCities, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate DataType="models:ListBoxItem">
<TextBlock Style="{StaticResource MaterialDesignBody2TextBlock}" Text="{Binding Name}" Visibility="{Binding Visibility}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It would also be recommendable trying to load the items in the list without any template, you can use ListBox DisplayMemberPath property to display the name, and once you are able to load items, apply the style.
Also in the way you use ObservableCollection, you actually need to replace the whole collection instead of adding to fire RaisePropertyChanged, try a normal property instead.
public ObservableCollection<ListBoxItem> PlacesOrCities {get;set;} = new ObservableCollection<ListBoxItem>();
Modifying the collection will update the UI, so whenever you use Add or Clear, the UI should know it.
Hope it helps.
What I'm trying to do
I'm trying to implement the AutoComplete feature on ComboBox without using WpfToolkit. If I'm not wrong, the ComboBox should support the AutoComplete for a simple string item so for example:
<ComboBox IsEditable="True">
<ComboBoxItem>Apple</ComboBoxItem>
<ComboBoxItem>Banana</ComboBoxItem>
<ComboBoxItem>Pear</ComboBoxItem>
<ComboBoxItem>Orange</ComboBoxItem>
</ComboBox>
Current object implementation
actually my ComboBox bound a custom object called CheckedListItem, this object have the following structure:
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem() { }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
this class show as ComboBoxItem a CheckBox near the Text of the Item, similar to:
[] Item1
[] Item2
...
where [] is the Checkbox.
ComboBox structure
The ComboBox look like this:
<ComboBox IsEditable="True" ItemSource={Binding Countries}/>
where Countries is a List of CheckedListItem<Country>, the object country is implemented in this way:
public class Country
{
public string Name { get; set; }
}
the problem on this code is that when I type some text in the ComboBox this doesn't do anything, but should display on the items that contains the string typed.
What I tried so far
I tried to fix this by implementing a PreviewTextInput event, actually I managed in this way:
MyComboBox.IsDropDownOpen = true;
CountryMenuComboBox.ItemsSource = Countries.Where(c => c.Item.Name.Contains(e.Text)).ToList();
but this doesn't working correctly, cause if I type "England", the ItemSource display even all Items.
Any idea to fix this?
Thanks.
Try to set the TextSearch.TextPath property to "Item.Name":
<ComboBox IsEditable="True" ItemsSource="{Binding Countries}"
TextSearch.TextPath="Item.Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding Item.IsChecked}" />
<TextBlock Text="{Binding Item.Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
my question here is how to know which button is clicked. My buttons are bound to property of type ObservableCollection which contains objects of type Item and I need to use that object in my ViewModel when a button is clicked. Any ideas how to know which button is clicked? I had few ideas, like sending multiple Command Parameters (1.SelectedItems from ListBox 2.The Object from the button) or bind the object from the button to another property of type Item in the ViewModel after the button is clicked in order to use it. Any ideas will be apreciated.
I have this DataTemplate for buttons
<DataTemplate x:Key="ButtonTemplate">
<WrapPanel>
<Button x:Name="OrderButton"
FontSize="10"
Height="80" Width="80"
Content="{Binding Name}"
Command="{Binding OrderCommand,
Source={StaticResource OrderViewModel}}"
CommandParameter="{Binding ElementName=ListBoxUserControl, Path=SelectedItems}">
</Button>
</WrapPanel>
</DataTemplate>
My ViewModel
public class OrderViewModel : ObservableCollection<Order>, INotifyPropertyChanged
{
public CreateOrderCommand CreateOrderCommand { get; set; }
public ObservableCollection<Item> Data { get; set; }
public OrderViewModel()
{
this.CreateOrderCommand = new CreateOrderCommand(this);
DataObservableCollection data= new DataObservableCollection();
Data = data;
}
}
And I populate my buttons like this
<WrapPanel x:Name="OrderButtons">
<ItemsControl ItemTemplate="{StaticResource ButtonTemplate}"
ItemsSource="{Binding Data, Source={StaticResource OrderViewModel}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
Simply change the Button.CommandParameter binding to CommandParamter="{Binding}" if you want the data context of the button (i.e. the item from your items source) as the command parameter or,
CommandParameter="{Binding RelativeSource={RelativeSource Self}}" if you want the actual button that was clicked.
First send the Button DataContext using the CommandParameter. To send the SelectedItem of your Listbox you can use
<Listbox SelectedItem="{Binding SelectedItem}"/>
in your Listbox and make a SelectedItem property in your ViewModel.
private YourItemObject mySelectedItem;
public YourItemObject SelectedItem
{
get { return mySelectedItem; }
set
{
value = mySelectedItem
}
Now you can use the SelectedItem in your ViewModel when the Button gets clicket. If you have multiple selections it gets a little bit more tricky ;).
private ButtonClicked(Parameter object)
{
SelectedItem.UsingIt();
if(object is YourButtonDataContext){
YourButtonDataContext.UsingIt();
}
}
Update with MultiSelection:
With Multiselection you have to do your own Listbox.
public class CustomListBox : ListBox
{
public CustomListBox()
{
this.SelectionChanged += CustomListBox_SelectionChanged;
}
void CustomListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedItemsList = this.SelectedItems;
}
#region SelectedItemsList
public IList SelectedItemsList
{
get { return (IList)GetValue(SelectedItemsListProperty); }
set { SetValue(SelectedItemsListProperty, value); }
}
public static readonly DependencyProperty SelectedItemsListProperty =
DependencyProperty.Register("SelectedItemsList", typeof(IList), typeof(CustomListBox), new PropertyMetadata(null));
#endregion
}
In the ViewModel you have to have a property with the SelectedItems.
private IList mySelectedData = new List<SelectedDataObject>();
public IList SelectedData
{
get { return mySelectedData ; }
set
{
if (mySelectedData != value)
{
mySelectedData = value;
RaisePropertyChanged(() => SelectedData);
}
}
}
The XAML Looks like this:
<local:CustomListBox ItemsSource="{Binding YourList}" SelectionMode="Extended" SelectedItemsList="{Binding SelectedData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
...
</local:CustomListBox>
Source for Multiselection in DataGrid is: https://stackoverflow.com/a/22908694/3330227
I have a few Problems with databinding in WPF.
I have a ListBox which has a binding to a BindingList.
<ListBox x:Name="SampleListBox" ItemsSource="{Binding List1}" ItemContainerStyle="{StaticResource ListBoxStyle}" BorderThickness="0" SelectedIndex="0" Margin="0">
<ListBox.ItemTemplate>
<DataTemplate >
<Border x:Name="border" Width="185">
<TextBlock Text="{Binding name}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Till here, everything works fine. Now I have a DataGrid which should be linked to another BindingList and display some strings of it. So for example, if the first item of the ListBox is selected, the grid should show data of the first item of the second list.
I know, how it would work if both, the ListBox and the Grid get the data from the same list, but I have no idea, what to do, if this is not possible and there are two different lists.
You could bind SelectedIndex for the ListBox control to an property of type Int (Property1) in your ViewModel.
Also two-way bind SelectedItem in the DataGrid to another property (Property2) of the second list type.
In the setter for the Property1, change Property2 to be the item at the index of Property1 - i.e. List2[Property1]. It should change the selected item in the DataGrid.
So you want to use the listbox to, essentially, set a filter on the grid?
Note that LBItem and ViewModel below need to implement INotifyPropertyChanged and fire their PropertyChanged events when properties change, or none of this will work. But I'm leaving out the boilerplate for clarity.
Lots of ways to do that.
C#
public class LBItem {
public ViewModel Parent { get; set; }
public IEnumerable<String> SubItems {
get {
return Parent.List2.Where( /* filter items here */ );
}
}
}
public class ViewModel {
//
public ObservableCollection<LBItem> LBItems { get; set; }
public LBItem SelectedLBItem { get; set; }
public List<String> List2 { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding ElementName=MasterLB, Path=SelectedItem.SubItems}"
/>
That will work whether or not you bind MasterLB.SelectedItem to a property on the ViewModel. But as long as you are binding MasterLB.SelectedItem, you could just as easily bind DataGrid.ItemsSource to SelectedLBItem.SubItems on the ViewModel, like so:
<DataGrid
ItemsSource="{Binding Path=SelectedLBItem.SubItems}"
/>
But the ElementName binding is handy for a lot of things, so I'm giving you both.
You could also do something like this:
C#
public class LBItem {
public IEnumerable<String> Filter(IEnumerable<String> fullList) {
return fullList.Where( /* filter items here */ );
}
}
public class ViewModel {
public ObservableCollection<LBItem> LBItems { get; set; }
private LBItem _selectedItem;
public LBItem SelectedLBItem {
get { return _selectedItem; }
set {
_selectedItem = value;
List2Filtered = (null == _selectedItem)
? new List<String>()
: _selectedItem.Filter(List2).ToList();
}
}
public List<String> List2 { get; set; }
public List<String> List2Filtered { get; set; }
}
XAML
<ListBox
Name="MasterLB"
ItemsSource="{Binding LBItems}"
SelectedItem={Binding SelectedLBItem}"
/>
<DataGrid
ItemsSource="{Binding List2Filtered}"
/>
I am populating an ItemsControl with various elements, including Buttons and ComboBox elements. Accessing and populating the elements is simple, but I'm stuck on how to detect and associate which Item in the ItemsControl the ComboBox (or Button) belongs to.
To help illustrate the problem, consider the following basic UI:
Now, when I use the ComboBox or Button I want to be able to associate that use only with the ItemControl Item it's a part of. However, currently, if I select an item in the ComboBox every ComboBox in the ItemsControl will reflect that change.
I can capture the SelectedItem in the below ListBox, but ideally, I would like to be able to display both the SelectedItem and which ItemControl Item it came from. For instance, ComboBoxItem1, My First Property - From Item (1).
I am strictly adhering to MVVM principals, and consequently, I am not looking for any solutions using code-behind.
TL;DR
I know the code can become unwieldy. I believe the above description is adequate to state my problem, but I am including the basic boiler plate code below in case it's helpful in posting an answer. (Obviously, I have implemented INotifyProperty and ICommand elsewhere):
MainWindowView.xaml
<ItemsControl Width="300" Height="200" ItemsSource="{Binding MyObservableCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" Margin="10">
<StackPanel Margin="0,10,0,10">
<TextBlock Margin="10,0,0,0" Text="{Binding MyProperty}" FontWeight="Bold"/>
<ComboBox Width="270" Text="myBox" ItemsSource="{Binding DataContext.ComboOptions, RelativeSource={RelativeSource AncestorType=ItemsControl}}" DisplayMemberPath="ListItem" SelectedItem="{Binding DataContext.SelectedItem, RelativeSource={RelativeSource AncestorType=Window}}"/>
<RadioButton Width ="270" Content="Button1" Command="{Binding DataContext.GetButtonCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}" CommandParameter="Button1" Style="{DynamicResource {x:Type ToggleButton}}"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
MyComboBoxOptionsViewModel.cs
public class MyComboBoxOptionsViewModel : ObservableObject
{
private MyComboBoxOptionsModel _myComboBoxOptions = new MyComboBoxOptionsModel();
public MyComboBoxOptionsViewModel(MyComboBoxOptionsModel _myComboBoxOptions)
{
this._myComboBoxOptions = _myComboBoxOptions;
}
public string ComboBoxOption
{
get { return _myComboBoxOptions.ComboBoxOption; }
set
{
_myComboBoxOptions.ComboBoxOption = value;
RaisePropertyChangedEvent("ComboBoxOption");
}
}
}
MyComboBoxOptionsModel.cs
public class MyComboBoxOptionsModel
{
public string ComboBoxOption { get; set; }
}
MainWindowViewModel.cs
public class MainWindowViewModel : ObservableObject
{
private ObservableCollection<string> _messages = new ObservableCollection<string>();
private ObservableCollection<MyViewModel> _myObservableCollection = new ObservableCollection<MyViewModel>();
private List<MyComboBoxOptionsViewModel> _comboOptions = new List<MyComboBoxOptionsViewModel>();
private MyComboBoxOptionsViewModel _selectedItem = new MyComboBoxOptionsViewModel(null);
public MainWindowViewModel()
{
_myObservableCollection.Add(new MyViewModel(new MyModel { MyProperty = "My First Property" }));
_myObservableCollection.Add(new MyViewModel(new MyModel { MyProperty = "My Second Property" }));
_comboOptions.Add(new MyComboBoxOptionsViewModel(new MyComboBoxOptionsModel { ComboBoxOption = "Option1" }));
_comboOptions.Add(new MyComboBoxOptionsViewModel(new MyComboBoxOptionsModel { ComboBoxOption = "Option2" }));
_comboOptions.Add(new MyComboBoxOptionsViewModel(new MyComboBoxOptionsModel { ComboBoxOption = "Option3" }));
}
public MyComboBoxOptionsViewModel SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
_messages.Add(_selectedItem.ComboBoxOption);
RaisePropertyChangedEvent("SelectedItem");
}
}
public List<MyComboBoxOptionsViewModel> ComboOptions
{
get { return _comboOptions; }
set
{
if (value != _comboOptions)
{
_comboOptions = value;
RaisePropertyChangedEvent("ComboOptions");
}
}
}
public ObservableCollection<MyViewModel> MyObservableCollection
{
get { return _myObservableCollection; }
set
{
if (value != _myObservableCollection)
{
_myObservableCollection = value;
RaisePropertyChangedEvent("MyObservableCollection");
}
}
}
public ObservableCollection<string> Messages
{
get { return _messages; }
set
{
if (value != _messages)
{
_messages = value;
RaisePropertyChangedEvent("Messages");
}
}
}
}
I'm looking at the UI you want and think you basically need a main view model with a collection of item view models.
In that item view model create a command and a selected item property you can bind in your template to the combo box and button. That gives you a strict mvvm binding to a single instance of the combo box value and a command which is executed by the single instance of the button.
Your bindings for combo box items will then need an explicit source as part of the binding so you can hook into one collection of values from the main view model. Or add a collection to your item view model and keep it all nice a clean and together.
As you mention, you're code is very detailed - which is great - but I may have missed some other meaning from it.
Apologies if this is an answer to the wrong question :)