Home > Blockchain >  Selected Items implementation in a ListBox acts odd in WPF and MVVM
Selected Items implementation in a ListBox acts odd in WPF and MVVM

Time:07-21

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;
    }
}
  • Related