Background
There is a ListView which contains 2 columns: File Column (string) and Worksheet Column (ComboBox).
For example, the list is "- None -", "1", "2", "3", "4", "5", "6", "7", "8"... When every ComboBox starts as "- None -", every ComboBox has all list items to choose from. But when a ComboBox picks for example "1", all other ComboBoxes no longer have it in their drop-down list. If "1" is then switched to anything else, ComboBoxes can again select it. So, only not selected items and "- None -" must show up in the drop-down list.
Each WorksheetItem has properties like ID (int), Name (string) and Selected (bool).
Last and most important: a ComboBox which selected for example "1" should still have "1" in its drop-down list.
To sum up in a short example, ComboBoxes are "BOY"s, WorksheetColumns are "girl"s. Imagine BOY1 choosing a girlfriend: he can be single ("- None -"), choose girl1 (because she is not CURRENTLY chosen by any BOY), girl2 (the same), but not girl3 (because she was chosen by BOY2). At the same time, BOY2 can choose to become single ("- None -"), switch to girl1 or girl2, or stay with girl3. The task is to include girl3 in the list of options only for BOY2.
Since the number of ComboBoxes is just as undefined as WorksheetColumns, I cannot create variables in XAML or C#.
Previously
I was advised to put a method in a class which contains WorksheetItems to get a subcollection of WorksheetItems. The method returns all not Selected items, an item with ID equal to 0 and the item selected by the current ComboBox (specified by the argument "keepColumn"):
public List<WorksheetColumn> GetWorksheetColumnHeaders(int keepColumn)
{
return WorksheetColumns.Where(header => header.ID == 0 || header.Selected == false || header.ID == keepColumn).ToList();
}
The problem is: I don't know where to call this method. I've read that it's impossible to call a method from XAML but the result of the method should be set as ItemsSource to multiple ComboBoxes (each of them must have a bit different ItemsSource because of that "keepColumn" argument which keeps the WorksheetItem selected by this very ComboBox - that's the goal).
I have XAML, ComboBox_SelectionChanged event and OnWorksheetItemPropertyChanged (happens when "Selected" gets changed).
Code
Right now my XAML uses a property, which does not return the needed item, instead of a method:
<ListView Grid.Row="0" Name="listView" IsSynchronizedWithCurrentItem="True" SelectionMode="Single" Margin="10" ItemsSource="{Binding ImportColumns}" >
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="File column" DisplayMemberBinding="{Binding FileColumnHeader}"/>
<GridViewColumn Header="Worksheet column" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox VerticalAlignment="Center" DataContext="{Binding DataContext,RelativeSource={RelativeSource AncestorType={x:Type ListView}}}" ItemsSource="{Binding ListOfWorksheetColumns.UnselectedWorksheetColumns, UpdateSourceTrigger=PropertyChanged}" SelectionChanged="ComboBox_SelectionChanged" SelectedIndex="0">
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
My main ViewModel:
public class ImportManagerViewModel : BaseViewModel
{
public List<ImportColumn> ImportColumns { get; set; }
public ListOfWorksheetColumnHeaders ListOfWorksheetColumns { get; set; }
public bool IsAnyColumnImported
{
get;
set;
}
public void OnImportItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
IsAnyColumnImported = ImportColumns.Any(x => x.TargetColumnIndex != 0);
}
public void OnWorksheetItemPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
ListOfWorksheetColumns.UnselectedWorksheetColumns = new ObservableCollection<WorksheetColumn>(ListOfWorksheetColumns.WorksheetColumns.Where(header => header.ID == 0 || header.Selected == false /*|| header.ID == 1*/));
}
}
My ListOfWorksheetColumnHeaders class (contains the collection, a subcollection and the method):
public class ListOfWorksheetColumnHeaders : BaseViewModel
{
public ObservableCollection<WorksheetColumn> WorksheetColumns { get; set; } = new ObservableCollection<WorksheetColumn>();
public ObservableCollection<WorksheetColumn> UnselectedWorksheetColumns
{
get;
set;
}
public List<WorksheetColumn> GetWorksheetColumnHeaders(int keepColumn)
{
return WorksheetColumns.Where(header => header.ID == 0 || header.Selected == false || header.ID == keepColumn).ToList();
}
}
My WorksheetColumn class:
public class WorksheetColumn : BaseViewModel
{
public int ID;
public string ColumnName { get; set; }
public bool Selected
{
get;
set;
}
public override string ToString()
{
return ColumnName;
}
}
As of now
The only thing that I need - make the selected WorksheetItem stay in the drop-down list of the ComboBox that selected it. Otherwise, the ComboBox loses this item from the drop-down list immediately because its "Select" property becomes true. Not important: after it is not longer selected by the ComboBox, its "Select" property becomes false, and it appears in the list but the ComboBox has nothing currently selected; in other words, if you select anything, it just clears the selection of the combobox.
try to use converter
[ValueConversion(typeof(List<WorksheetColumn>), typeof(List<WorksheetColumn>))]
public class ListFilterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<WorksheetColumn> worksheets = value as List<WorksheetColumn>;
int keepColumn = (int)parameter;
return worksheets.Where(header => header.ID == 0 || header.Selected == false || header.ID == keepColumn).ToList();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML is..
<ListView Grid.Row="0" Name="listView" IsSynchronizedWithCurrentItem="True" SelectionMode="Single" Margin="10" ItemsSource="{Binding ImportColumns}" >
<ListView.Resources>
<local:ListFilterConverter x:Key="myConverter" />
<sys:Int32 x:Key="keepcolumn1">1</sys:Int32>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="File column" DisplayMemberBinding="{Binding FileColumnHeader}"/>
<GridViewColumn Header="Worksheet column" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox VerticalAlignment="Center" DataContext="{Binding DataContext,RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
ItemsSource="{Binding ListOfWorksheetColumns.UnselectedWorksheetColumns, Converter={StaticResource myConverter}, ConverterParameter={StaticResource keepcolumn1}}"
SelectionChanged="ComboBox_SelectionChanged" SelectedIndex="0">
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>