Databinding between 2 listViews selecting/deselecting records

Viewed 41

I have 2 listviews, one that is prepopulated with a list of values and an empty list that should populate based on what is selected/deselected in the first view. I can get the second view to populate when a value is selected but I'm not sure how to remove an item if it's been deselected in the first view. If I do deselect a record it adds a null value to the 2nd list. I'm thinking there is a way around this with if/else statements but I'm thinking there might be a more elegant way to accomplish this.

View Model

private ObservableCollection<string> _firstList;
public ObservableCollection<string> FirstList
{
    get => _firstList;
    set
    {
        if (_firstList!= value)
        {
            _firstList= value;
            RaisePropertyChanged(nameof(FirstList));
        }
    }
}

private string _selectedRecord;
public string SelectedRecord
{
    get => _selectedRecord;
    set
    {
        if (_selectedRecord!= value)
        {
            _selectedRecord= value;
            RaisePropertyChanged(nameof(SelectedRecord));
            _secondList.Add(_selectedRecord);
        }
    }
}

private ObservableCollection<string> _secondList= 
    new ObservableCollection<string>();
public ObservableCollection<string> SecondList
{
    get => _secondList;
    set
    {
        if (_secondList!= value)
        {
            _secondList= value;
            RaisePropertyChanged(nameof(SecondList));
        }
    }
}

XAML -

<ListView ItemsSource="{Binding FirstList}"
SelectedItem="{Binding SelectedRecord}">
<ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel Orientation="Vertical"></StackPanel>
    </ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <CheckBox IsChecked="{Binding IsSelected, 
                RelativeSource={RelativeSource 
                AncestorType=ListViewItem}}"/>
            <TextBlock Text="{Binding}" />
        </StackPanel>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

<ListView ItemsSource="{Binding SecondList}">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"></StackPanel>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
1 Answers

Replace the string with a custom type and handle your logic of adding and removing items in the view model:

public class ListItem : INotifyPropertyChanged
{
    public ListItem(string value) =>
        Value = value;

    public string Value { get; }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set { _isSelected = value; RaisePropertyChanged(nameof(IsSelected)); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

View Model:

public ViewModel()
{
    InitializeComponent();
    DataContext = this;

    FirstList.CollectionChanged += (s, e) =>
    {
        if (e.NewItems != null)
            foreach (var newItem in e.NewItems.OfType<INotifyPropertyChanged>())
                newItem.PropertyChanged += OnItemIsSelectedChanged;
        if (e.OldItems != null)
            foreach (var oldIrem in e.OldItems.OfType<INotifyPropertyChanged>())
                oldIrem.PropertyChanged -= OnItemIsSelectedChanged;
    };

    FirstList.Add(new ListItem("a"));
    FirstList.Add(new ListItem("b"));
    FirstList.Add(new ListItem("c"));
}

private void OnItemIsSelectedChanged(object sender, PropertyChangedEventArgs e)
{
    ListItem listItem = (ListItem)sender;
    if (listItem.IsSelected)
    {
        if (!SecondList.Contains(listItem))
            SecondList.Add(listItem);
    }
    else
        SecondList.Remove(listItem);
}

public ObservableCollection<ListItem> FirstList { get; } =
    new ObservableCollection<ListItem>();

public ObservableCollection<ListItem> SecondList { get; }
    = new ObservableCollection<ListItem>();

View:

<ListView ItemsSource="{Binding FirstList}" SelectedItem="{Binding SelectedRecord}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding IsSelected}"/>
                <TextBlock Text="{Binding Value}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>



<ListView ItemsSource="{Binding SecondList}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Value}" />
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

This is MVVM in a nutshell.

Related