I made quite research to implement selected items via MVVM in WPF. I thought I had success but now the selection is made according to scroll position. I select all items in the listbox but only first 11 marked as selected. If I scroll more, more selected. If I scroll to the bottom all items selected. Is there solution for this problem?
XAML:
<ListBox x:Name="DataListBox" SelectionMode="Extended" HorizontalAlignment="Left" Margin="5,5,0,0" Grid.Row="1" Grid.Column="0" Grid.RowSpan="8"
VerticalAlignment="Top" Height="200" Width="200"
ItemsSource="{Binding DataListBoxItemsSource, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding DataListBoxSelectedItem, UpdateSourceTrigger=PropertyChanged}"
>
<ListBox.InputBindings>
<KeyBinding Command="ApplicationCommands.SelectAll" Modifiers="Ctrl" Key="A" />
</ListBox.InputBindings>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<i:CallMethodAction TargetObject="{Binding}" MethodName="DataListBox_SelectionChanged"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
View Model
public async void CreateLayersTOC()
{
if (MapView.Active != null)
{
if (DataListBoxSelectedItem != null || FavoriteTabsSelectedItem != null)
MainStackPanelIsEnabled = false;
LayerNames = new List<string>();
await Task.Run(() =>
{
MessageBox.Show("source count " DataListBoxItemsSource.Count);//58 items all selected
if (DataListBoxSelectedItem != null)
foreach (ItemPresenter itemP in DataListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
if (FavoriteTabsSelectedItem != null)
{
foreach (ItemPresenter itemP in FavListBoxItemsSource)
{
if (itemP.IsSelected)
{
if (LayerNames.Contains(itemP.ToString()) == false)
LayerNames.Add(itemP.ToString());
}
}
}
MessageBox.Show("Coll" LayerNames.Count);//Count depends on scroll position
});
//do stuff
}
else
MessageBox.Show("Make sure to have a map available before adding layers to a map");
MainStackPanelIsEnabled = true;
}
public class ItemPresenter : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private readonly string _value;
public ItemPresenter(string value)
{
_value = value;
}
public override string ToString()
{
return _value;
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
OnPropertyChanged();
}
}
}
}
CodePudding user response:
Here is a simple example of everything that is necessary to bind the SelectedItem of a ListBox and the IsSelected property of the ListBoxItems.
XAML:
<ListBox ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}"
SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Selected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
View Model:
public class DataItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
private bool selected;
public bool Selected
{
get { return selected; }
set
{
selected = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(Selected)));
Debug.WriteLine(Name " selected: " selected);
}
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<DataItem> Items { get; }
= new ObservableCollection<DataItem>();
public IEnumerable<DataItem> SelectedItems
{
get { return Items.Where(i => i.Selected); }
}
private DataItem selectedItem;
public DataItem SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new ViewModel();
vm.Items.Add(new DataItem { Name = "Item 1" });
vm.Items.Add(new DataItem { Name = "Item 2" });
vm.Items.Add(new DataItem { Name = "Item 3" });
vm.Items.Add(new DataItem { Name = "Item 4" });
vm.Items.Add(new DataItem { Name = "Item 5" });
DataContext = vm;
}
}