使用在运行时创建的复选框绑定/应用过滤器

时间:2016-01-12 15:37:39

标签: c# wpf listview checkbox

我有一个ListView,里面有很多MovieItem个。 ListView绑定到“FtMainList”:

    ObservableCollection<MovieItem> MainList = new ObservableCollection<MoviItem>();
    public ICollectionView FtMainList
    {
        get
        {
            var source = CollectionViewSource.GetDefaultView(MainList);
            source.Filter = a => ListStatusFilter((MovieItem)a);
            return source;
        }
    }

几天前,我为他们创建了一个过滤器:

Filter by status

        private bool ListStatusFilter(MovieItem movie)
    {
        return (LvFilter_Watching.IsChecked.HasValue && LvFilter_Watching.IsChecked.Value && movie.lMyStatus == "Watching") ||
               (LvFilter_OnHold.IsChecked.HasValue && LvFilter_OnHold.IsChecked.Value && movie.lMyStatus == "On-Hold") ||
               (LvFilter_PlanToWatch.IsChecked.HasValue && LvFilter_PlanToWatch.IsChecked.Value && movie.lMyStatus == "Plan To Watch") ||
               (LvFilter_Dropped.IsChecked.HasValue && LvFilter_Dropped.IsChecked.Value && movie.lMyStatus == "Dropped") ||
               (LvFilter_Completed.IsChecked.HasValue && LvFilter_Completed.IsChecked.Value && movie.lMyStatus == "Completed");
    }

这些是双状态CheckBoxes。因此,如果我选中“已完成”,ListView将显示所有“已完成的电影”。否则,所有已完成的电影都将被隐藏,反之亦然。它按预期工作。

但是,我不想停在那里,我想创建更多的过滤器。现在我想用“流派”过滤它们。问题是,有26种类型(可能正在增加),我要懒惰(lol)创建26个复选框及其过滤器。所以我有一个想法:

    <DataTemplate x:Key="GenericList3StateCheckbox">
        <CheckBox IsThreeState="True" Checked="FilterChanged" Unchecked="FilterChanged" Content="{Binding}" IsChecked="{x:Null}"/>
    </DataTemplate>
...
<StackPanel Orientation="Vertical" Panel.ZIndex="1" Margin="0,0,0,10" HorizontalAlignment="Stretch">
    <TextBlock Text="Genre" Margin="0,0,0,10" Panel.ZIndex="1" Foreground="White" FontSize="28" VerticalAlignment="Top" HorizontalAlignment="Center" FontFamily="Ebrima" TextDecorations="Underline" FontWeight="Bold"/>
    <ListBox x:Name="LvFilter_Genres" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemTemplate="{StaticResource GenericList3StateCheckbox}" ScrollViewer.CanContentScroll="False" Height="130"/>
</StackPanel>

代码隐藏:

            List<string> LvGenres = new List<string>();             
            foreach (MovieItem ame in MainList)
            {
                string[] spt = ame.gGenres.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string gen in spt)
                {
                    bool isDuplicate = false;
                    foreach (string chk in LvGenres)
                    {
                        if (chk == gen)
                        {
                            isDuplicate = true;
                            break;
                        }
                    }
                    if (isDuplicate == false)
                    {
                        LvGenres.Add(gen);
                    }
                }
             }
             LvGenres.Sort();
             LvFilter_Genres.ItemsSource = LvGenres;

结果:

Filter by genre

与上述不同,这些Checkbox是3状态。因此,如果我选中“动作”,ListView将仅显示包含“动作”类型的电影。如果它是Null(a.k.a Intermediate),ListView可能会显示不包含“Action”类型的电影。如果未选中,则将隐藏所有不包含“动作”类型的电影。

现在提出问题,我应该怎么做,这些复选框将按预期工作?

仅供参考,MovieItem课程:

public class MovieItem : INotifyPropertyChanged
{
   public string Title { get; set; }
   public string Genres { get; set; }
   public int Duration { get; set; }
   public string lMyStatus { get; set; }

   public event PropertyChangedEventHandler PropertyChanged;
}

Genres是一个单独的字符串,包含一些用昏迷和空格分隔的“流派”。例如:“动作,惊悚片,文件管理员”

1 个答案:

答案 0 :(得分:2)

最好使用ListBox项的自定义View Model而不是字符串。在这种情况下,您可以更灵活地实现您想要的任何内容。

<DataTemplate x:Key="GenericList3StateCheckbox">
    <CheckBox IsThreeState="True" Checked="FilterChanged" Unchecked="FilterChanged" Content="{Binding Caption}" IsChecked="{Binding State}"/>
</DataTemplate>


List<FilterItemVM> LvGenres = new List<FilterItemVM>();

public class FilterItemVM : INotifyPropertyChanged
{
    private string _caption;
    public string Caption
    {
        get { return _caption; }
        set
        {
            _caption= value;
            OnPropertyChanged();
        }
    }

    private bool? _state;
    public bool? State
    {
        get { return _state; }
        set
        {
            _state= value;
            OnPropertyChanged();
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string property = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

}


private bool ListStatusFilter(MovieItem movie)
{
    List<string> itemGengres = movie.Genres.Split(',').Select(g => g.Trim()).ToList();

    List<string> obligatoryGengres = LvGenres.Where(fi => fi.State.HasValue && fi.State.Value == true).Select(fi => fi.Caption).ToList();
    List<string> forbiddenGengres = LvGenres.Where(fi => fi.State.HasValue && fi.State.Value == false).Select(fi => fi.Caption).ToList();

    bool isGenresMatched = obligatoryGengres.All(g => itemGengres.Contains(g)) && !forbiddenGengres.Any(g => itemGengres.Contains(g));

    return isGenresMatched && (LvFilter_Watching.IsChecked.HasValue && LvFilter_Watching.IsChecked.Value && movie.lMyStatus == "Watching") ||
           (LvFilter_OnHold.IsChecked.HasValue && LvFilter_OnHold.IsChecked.Value && movie.lMyStatus == "On-Hold") ||
           (LvFilter_PlanToWatch.IsChecked.HasValue && LvFilter_PlanToWatch.IsChecked.Value && movie.lMyStatus == "Plan To Watch") ||
           (LvFilter_Dropped.IsChecked.HasValue && LvFilter_Dropped.IsChecked.Value && movie.lMyStatus == "Dropped") ||
           (LvFilter_Completed.IsChecked.HasValue && LvFilter_Completed.IsChecked.Value && movie.lMyStatus == "Completed");
}