Loop through the second column of WPF ListView and

2019-09-18 17:43发布

问题:

I have a two-column ListView with CheckBoxes in the first column and ComboBoxes in the second column. I need to loop through the ComoboBoxes in the second column and retrieve the selected values (or indexes) from each ComboBox, along with some index or identifier of the ComboBox, and put the values in an array. For example, the layout looks like this:

COLUMN 1  COLUMN 2
========  ========
ChBx 1    Combo1
ChBx 2    Combo2

I need to grab the SelectedValue or SelectedIndex of each ComboBox in the second column and put it into an array in the right order. But, what I've found on the internet is to use: myListView.Items(0).SubItems(1).Text, to loop through the second column. However, my second column contains a ComboBox and I want its' value (not some Text property). Any ideas? My XAML markup is below.

            <ListView IsSynchronizedWithCurrentItem="True" Margin="0,0,10,10" Name="patternList" Height="139" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="112" BorderBrush="{x:Null}" BorderThickness="1" Background="White" >
                 <ListView.View>
                    <GridView>
                        <GridView.Columns>
                            <GridViewColumn Header="Pattern">
                                <GridViewColumn.CellTemplate>
                                    <DataTemplate>
                                        <CheckBox Content="{Binding outContent}" 
                                                  ToolTip="{Binding outToolTip}"
                                                  IsThreeState="False"
                                                  IsChecked="{Binding Path=outIsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                                    </DataTemplate>
                                </GridViewColumn.CellTemplate>
                            </GridViewColumn>
                            <GridViewColumn Header="Freq" Width="55">
                                <GridViewColumn.CellTemplate>
                                    <DataTemplate>
                                        <ComboBox HorizontalContentAlignment="Center" 
                                                  Height="14" 
                                                  Padding="0" 
                                                  SelectionChanged="FrequencyChanged_OnSelectionChanged"
                                                  FontSize="10">
                                            <ComboBoxItem Content="0%"/>
                                            <ComboBoxItem Content="10%"/>
                                            <ComboBoxItem Content="20%"/>
                                            <ComboBoxItem Content="30%"/>
                                            <ComboBoxItem Content="40%"/>
                                            <ComboBoxItem Content="50%"/>
                                            <ComboBoxItem Content="60%"/>
                                            <ComboBoxItem Content="70%"/>
                                            <ComboBoxItem Content="80%"/>
                                            <ComboBoxItem Content="90%"/>
                                            <ComboBoxItem Content="100%"/>
                                        </ComboBox>
                                    </DataTemplate>
                                </GridViewColumn.CellTemplate>
                            </GridViewColumn>
                        </GridView.Columns>
                    </GridView>
                </ListView.View>
   </ListView>

回答1:

Bind SelectedValue to a property in your ViewModel. And in the Setter of that property, also update the collection.

Second approach, in your FrequencyChanged_OnSelectionChanged event handler. You can keep updating your collection there too.



回答2:

From my perspective it would be better to generate items for the combobox in list item viewmodel, and bind to selected item in that view model. Below is the code that illustrates the approach.

XAML

<Window x:Class="ComboboxesInGrid.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ComboboxesInGrid"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <ListView IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding ListItems}" Margin="0,0,10,10" Name="patternList" BorderBrush="{x:Null}" BorderThickness="1" Background="White" >
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Pattern" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <CheckBox Content="{Binding outContent}" 
                                                  ToolTip="{Binding outToolTip}"
                                                  IsThreeState="False"
                                                  IsChecked="{Binding Path=outIsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Freq" Width="155">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <ComboBox HorizontalContentAlignment="Center" 
                                                  Height="14" 
                                                  Padding="0" 
                                                  SelectedItem="{Binding outComboSelected}"
                                                  ItemsSource="{Binding outComboValues}"
                                                  FontSize="10">

                                    </ComboBox>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        <TextBlock Grid.Row="1" Text="{Binding SelectedComboItems}" />
    </Grid>
</Window>

C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;


namespace ComboboxesInGrid
{
    public class MainWindowViewModel : ViewModelBase
    {
        private ObservableCollection<ListItemViewModel> _listItems = new ObservableCollection<ListItemViewModel>();

        public string SelectedComboItems
        {
            get { return String.Join(",", _listItems.Select(li => $"{li.outContent}-{li.outIsChecked}-{li.outComboSelected}")); }
        }

        public ObservableCollection<ListItemViewModel> ListItems
        {
            get { return _listItems; }
            set { _listItems = value; OnProperyChanged(); }
        }

        public MainWindowViewModel()
        {
            AddListItem(new ListItemViewModel() { outContent = "ChBx 1 ", outToolTip="Tooooool", outIsChecked = false, outComboSelected="30%" });
            AddListItem(new ListItemViewModel() { outContent = "ChBx 2 ", outComboSelected = "70%" });

        }

        private void AddListItem(ListItemViewModel item)
        {
            item.PropertyChanged += (s, e) => OnProperyChanged(nameof(SelectedComboItems));
            _listItems.Add(item);
        }
    }

    public class ListItemViewModel : ViewModelBase
    {
        private string _outContent;

        public string outContent
        {
            get { return _outContent; }
            set { _outContent = value; OnProperyChanged(); }
        }


        private string _outToolTip;
        public string outToolTip
        {
            get { return _outToolTip; }
            set { _outToolTip = value; OnProperyChanged(); }
        }

        private bool? _outIsChecked;
        public bool? outIsChecked
        {
            get { return _outIsChecked; }
            set { _outIsChecked = value; OnProperyChanged(); }
        }

        private string _outComboSelected;
        public string outComboSelected
        {
            get { return _outComboSelected; }
            set { _outComboSelected = value; OnProperyChanged(); }
        }

        public IEnumerable<string> outComboValues
        {
            get
            {
                return Enumerable.Range(0, 11).Select(i => $"{i*10}%");
            }
        }
    }

    public class ViewModelBase : INotifyPropertyChanged
    {
        protected void OnProperyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

    }
}