在过去的几天里,我遇到了一个问题,导致我的头撞在桌子上。
本质上,我要做的是生成两个单独的复选框列表,这些复选框对应于类的两个属性(制造商和类别),并根据用户检查的内容过滤它们。
我所做的是创建两个对应于每个属性的高级集合视图,并用它们各自的对象填充它们。我创建了过滤器来根据用户的选择过滤每个视图,但无论我做什么,行为都与我无法确定将问题追溯到哪里的地方不一致。
应该发生的情况是,如果用户选择制造商,则类别列表应过滤掉不包括所选制造商的类别,反之亦然。到目前为止,唯一能以这种方式工作的复选框是为附件类别生成的复选框。
下面是我看到的行为的视频。在视频的第一部分,您可以看到使用附件类别和 Microsoft 制造商时它应该如何工作,但随着视频的进行,我尝试不同的组合,您可以看到其余选项的奇怪但随机的行为。
我只是想不通的是为什么它可以完美地工作,并且正如我打算针对一个类别/制造商而不是其他任何一个类别/制造商一样。UI 都是动态生成的,填充列表的对象都是相同的类型,数据都是相同的格式,所有的列表都会正确生成。我真的很茫然,我真的希望有一些外界的看法!
以下是一些相关代码:
XAML:
<Grid Background="#FF1B1B1B">
<StackPanel Orientation="Horizontal">
<StackPanel HorizontalAlignment="Left" Width="470">
<TextBlock Text="Product Catalog" FontSize="28" Foreground="#FFCDCDCD" Margin="5"/>
<Button x:Name="AddCustomerButton" HorizontalAlignment="Stretch" Content="Clear All Filters" VerticalAlignment="Stretch" Height="60" Background="#FF007ACC" Margin="15" Foreground="#FFCDCDCD" />
<ScrollViewer Margin="5,5,0,5" Height="115" BorderThickness="3" BorderBrush="#FF575454">
<StackPanel Height="146" Width="435">
<TextBlock Text="Category:" Foreground="#FFCDCDCD"/>
<ListView x:Name="CategoryList" ItemsSource="{Binding CategoryCheckboxes}" >
<ListView.ItemsPanel >
<ItemsPanelTemplate >
<ItemsWrapGrid Orientation="Horizontal" Width="435" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate >
<CheckBox Foreground="#FFCDCDCD" IsChecked="{Binding IsCheckedCategory, Mode=TwoWay}" >
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:InvokeCommandAction CommandParameter="{Binding}" Command="{Binding ElementName=CategoryList, Path=DataContext.CheckCategory}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:InvokeCommandAction CommandParameter="{Binding}" Command="{Binding ElementName=CategoryList, Path=DataContext.UncheckCategory}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<TextBlock Text="{Binding Category}" Foreground="#FFCDCDCD"/>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</ScrollViewer>
<ScrollViewer Margin="5,5,0,5" Height="200" BorderThickness="3" BorderBrush="#FF575454">
<StackPanel>
<TextBlock Text="Manufacturer:" Foreground="#FFCDCDCD"/>
<ListView x:Name="ManufacturerList" ItemsSource="{Binding ManufacturerCheckboxes}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal" Width="435" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate >
<CheckBox Foreground="#FFCDCDCD" IsChecked="{Binding IsCheckedMfg, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:InvokeCommandAction CommandParameter="{Binding}" Command="{Binding ElementName=ManufacturerList, Path=DataContext.CheckManufacterer}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:InvokeCommandAction CommandParameter="{Binding}" Command="{Binding ElementName=ManufacturerList, Path=DataContext.UncheckManufacterer}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<TextBlock Text="{Binding Manufacturer}" Foreground="#FFCDCDCD"/>
</CheckBox>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</ScrollViewer>
</StackPanel>
<StackPanel Margin="3">
<TextBlock Text="Products" FontSize="28" Foreground="#FFCDCDCD" Margin="5"/>
<StackPanel Orientation="Horizontal" Margin="5">
<TextBlock Text="Search: " FontSize="24" Foreground="#FFCDCDCD" Width="86" Margin="3"/>
<TextBox x:Name="SearchTextBoxt" TextChanged="SearchTextBoxt_TextChanged" Width="725" Margin="3" TextChanging="SearchTextBoxt_TextChanging"/>
</StackPanel>
<ListView ItemsSource="{x:Bind pcvm.ProductCatalog}" Width="837" Margin="5" Height="728">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Product">
<controls:Expander x:Name="Expander1" VerticalAlignment="Top" Margin="0,0,0,10"
BorderBrush="Bisque"
Header="{Binding Name}"
IsExpanded="False" Width="810" Foreground="#FFCDCDCD">
<Grid Height="auto" Background="#FF272727" Width="805" BorderThickness="3" BorderBrush="DimGray">
<StackPanel Orientation="Horizontal" Height="auto">
<StackPanel Width="275" Height="auto">
<TextBlock Text="{Binding Manufacturer}" Foreground="#FFCDCDCD" FontSize="24" Height="auto" Margin="3"/>
<Image Source="{Binding ImageUrl}" Margin="3" Height="151"/>
<TextBlock Text="{Binding Price}" Foreground="#FFCDCDCD" FontSize="24" Height="auto" Margin="3" FlowDirection="RightToLeft"/>
</StackPanel>
<StackPanel Width="522">
<TextBlock Text="{Binding Name}" Foreground="#FFCDCDCD" FontSize="18" FontWeight="Bold" Height="auto" Margin="2"/>
<TextBlock Text="{Binding Description}" Foreground="#FFCDCDCD" FontSize="16" Height="auto" Margin="3" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Processor}" Foreground="#FFCDCDCD" FontSize="14" FontStyle="Italic" Height="auto" Margin="5,8,5,5"/>
<TextBlock Text="{Binding RAM}" Foreground="#FFCDCDCD" FontSize="14" FontStyle="Italic" Height="auto" Margin="5"/>
<TextBlock Text="{Binding HDDCap}" Foreground="#FFCDCDCD" FontSize="14" FontStyle="Italic" Height="auto" Margin="5"/>
<TextBlock Text="{Binding GPU}" Foreground="#FFCDCDCD" FontSize="14" FontStyle="Italic" Height="auto" Margin="5"/>
<TextBlock Text="{Binding Display}" Foreground="#FFCDCDCD" FontSize="14" FontStyle="Italic" Height="auto" Margin="5"/>
</StackPanel>
</StackPanel>
</Grid>
</controls:Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</StackPanel>
</Grid>`
C#虚拟机:
public class ProductCatalogViewModel
{
public enum FilterTypes
{
Category, Manufacterer
}
public AdvancedCollectionView ProductCatalog { get; set; }
public List<Product> Products { get; set; }
public AdvancedCollectionView ManufacturerCheckboxes { get; set; }
public AdvancedCollectionView CategoryCheckboxes { get; set; }
public AdvancedCollectionView CheckBoxesCollectionView { get; set; }
private List<string> _searchFilters;
private string searchParam = "";
private bool _mfgSeclected, _categorySelected, _subCategorySelected = false;
private string _mfgFilter, _categoryFilter, _subCategoryFilter;
public ProductCatalogProduct selectedMfgCheckbox { get; set; }
public ProductCatalogProduct selectedCatCheckbox { get; set; }
public ICommand CheckCategory { get; set; }
public ICommand UncheckCategory { get; set; }
public ICommand CheckManufacterer { get; set; }
public ICommand UncheckManufacterer { get; set; }
public ProductCatalogViewModel()
{
Products = (DataManager._productDatabase);
ProductCatalog = new AdvancedCollectionView(Products);
GenerateCheckBoxLists();
CheckCategory = new RelayCommand<ProductCatalogProduct>(CheckCategoryFilter);
UncheckCategory = new RelayCommand<ProductCatalogProduct>(UncheckCategoryFilter);
CheckManufacterer = new RelayCommand<ProductCatalogProduct>(CheckManufacturerFilter);
UncheckManufacterer = new RelayCommand<ProductCatalogProduct>(UncheckManufacturerFilter);
}
private void GenerateCheckBoxLists() //Generates the object collections that will represent the filter checkboxes in the View
{
ManufacturerCheckboxes = new AdvancedCollectionView(new ObservableCollection<ProductCatalogProduct>());
ManufacturerCheckboxes = GenerateDistinctList(ProductCatalog, ProductComparer.ListTypes.Manufacturer);
foreach (ProductCatalogProduct mfgCheckBoxItem in ManufacturerCheckboxes)
{
foreach (Product product in Products)
{
if (mfgCheckBoxItem.Manufacturer == product.Manufacturer && !mfgCheckBoxItem.CategoriesWithSameManufacterer.Contains(product.Category) )
{
mfgCheckBoxItem.CategoriesWithSameManufacterer.Add(product.Category);
}
}
}
CategoryCheckboxes = new AdvancedCollectionView(new ObservableCollection<ProductCatalogProduct>());
CategoryCheckboxes = GenerateDistinctList(ProductCatalog, ProductComparer.ListTypes.Category);
foreach (ProductCatalogProduct catCheckBoxItem in CategoryCheckboxes)
{
foreach (Product product in Products)
{
if (catCheckBoxItem.Category == product.Category && !catCheckBoxItem.ManufacturersWithSameCategory.Contains(product.Manufacturer))
{
catCheckBoxItem.ManufacturersWithSameCategory.Add(product.Manufacturer);
}
}
}
}
private AdvancedCollectionView GenerateDistinctList(AdvancedCollectionView viewToFilter, ProductComparer.ListTypes list)
{
AdvancedCollectionView newACV = new AdvancedCollectionView(new ObservableCollection<ProductCatalogProduct>());
List<Product> tempList = new List<Product>();
foreach (Product pcp in viewToFilter)
{
tempList.Add(pcp);
}
IEnumerable<Product> NoDuplicatesInitialCat = tempList.Distinct(new ProductComparer(list));
foreach (var p in NoDuplicatesInitialCat)
{
ProductCatalogProduct pCP = new ProductCatalogProduct(p);
newACV.Add(pCP);
}
return newACV;
}
private void UncheckAllCategories()
{
CategoryCheckboxes.ToList().ForEach(x =>
{
(x as ProductCatalogProduct).UncheckCategory();
});
}
private void UncheckAllManufacterers()
{
ManufacturerCheckboxes.ToList().ForEach(x =>
{
(x as ProductCatalogProduct).UncheckMfg();
});
}
public void CheckCategoryFilter(ProductCatalogProduct product)
{
if (selectedCatCheckbox != null)
{
selectedCatCheckbox.UncheckCategory();
selectedCatCheckbox = null;
}
_categoryFilter = product.Category;
FilterButtons(FilterTypes.Category);
UncheckAllCategories();
selectedCatCheckbox = product;
if (selectedMfgCheckbox != null)
{
selectedMfgCheckbox.CheckMfg();
}
selectedCatCheckbox.CheckCategory();
// ExecuteProductFilter();
}
public void UncheckCategoryFilter(ProductCatalogProduct product)
{
if (selectedCatCheckbox != null)
{
selectedCatCheckbox.UncheckCategory();
selectedCatCheckbox = null;
}
_categorySelected = false;
_categoryFilter = String.Empty;
ManufacturerCheckboxes.Filter = null;
if (selectedMfgCheckbox != null)
{
selectedMfgCheckbox.CheckMfg();
}
// ExecuteProductFilter();
}
public void CheckManufacturerFilter(ProductCatalogProduct product)
{
if (selectedMfgCheckbox != null)
{
selectedMfgCheckbox.UncheckCategory();
selectedMfgCheckbox = null;
}
_mfgFilter = product.Manufacturer;
FilterButtons(FilterTypes.Manufacterer);
UncheckAllManufacterers();
selectedMfgCheckbox = product;
if (selectedCatCheckbox != null)
{
selectedCatCheckbox.CheckMfg();
}
selectedMfgCheckbox.CheckMfg();
//ExecuteProductFilter();
}
public void UncheckManufacturerFilter(ProductCatalogProduct product)
{
if (selectedMfgCheckbox != null)
{
selectedMfgCheckbox.UncheckCategory();
selectedMfgCheckbox = null;
}
_mfgSeclected = false;
_mfgFilter = String.Empty;
CategoryCheckboxes.Filter = null;
if (selectedCatCheckbox != null)
{
selectedCatCheckbox.CheckMfg();
}
//ExecuteProductFilter();
}
private List<Predicate<Product>> _criteria = new List<Predicate<Product>>();
private bool dynamic_Filter(object item)
{
Product p = item as Product;
bool isIn = true;
if (_criteria.Count() == 0)
return isIn;
isIn = _criteria.TrueForAll(x => x(p));
return isIn;
}
private void FilterButtons(FilterTypes filterSelected)
{
switch (filterSelected)
{
case FilterTypes.Category:
ManufacturerCheckboxes.Filter = null;
ManufacturerCheckboxes.Filter = x => (x as ProductCatalogProduct).CategoriesWithSameManufacterer.Contains(_categoryFilter);
break;
case FilterTypes.Manufacterer:
CategoryCheckboxes.Filter = null;
CategoryCheckboxes.Filter = x => (x as ProductCatalogProduct).ManufacturersWithSameCategory.Contains(_mfgFilter);
break;
}
}
private void ExecuteProductFilter()
{
ProductCatalog.Filter = null;
_criteria.Clear();
if (_categorySelected)
{
_criteria.Add(new Predicate<Product>(x => x.Category.Contains(_categoryFilter)));
}
if (_subCategorySelected)
{
_criteria.Add(new Predicate<Product>(x => x.SubCategory.Contains(_subCategoryFilter)));
}
if (_mfgSeclected)
{
_criteria.Add(new Predicate<Product>(x => x.Manufacturer.Contains(_mfgFilter)));
}
ProductCatalog.Filter = dynamic_Filter;
}
public void FilterProductDatabase(string searchParameters) //filters the advancedcollectionview using the SearchFilter method
{
searchParam = searchParameters;
ProductCatalog.Filter = SearchFilter;
}
private bool SearchFilter(object item) //Filters each object in the advancedcollectionview based on user input into a textbox
{
Product p = item as Product;
string searchTerms = "";
searchTerms = p.Manufacturer + " " + p.Category + " " + p.SubCategory;
if (searchTerms.Contains(searchParam))
{
return true;
}
else
{
return false;
}
}
class ProductComparer : IEqualityComparer<Product>
{
public enum ListTypes
{
Manufacturer, Category, Subcategory
}
public ListTypes lists;
public ProductComparer(ListTypes listToFill)
{
lists = listToFill;
}
// Products are equal if their names and product numbers are equal.
public bool Equals(Product x, Product y)
{
switch (lists)
{
case ListTypes.Category:
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y.Manufacturer, null))
return false;
return x.Category == y.Category;
case ListTypes.Manufacturer:
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y.Manufacturer, null))
return false;
return x.Manufacturer == y.Manufacturer;
case ListTypes.Subcategory:
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y.Manufacturer, null))
return false;
return x.SubCategory == y.SubCategory;
}
return false;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Product product)
{
switch (lists)
{
case ListTypes.Category:
if (Object.ReferenceEquals(product, null)) return 0;
int hashProductCategory = product.Category == null ? 0 : product.Category.GetHashCode();
return hashProductCategory;
case ListTypes.Manufacturer:
if (Object.ReferenceEquals(product, null)) return 0;
int hashProductManufacturer = product.Manufacturer == null ? 0 : product.Manufacturer.GetHashCode();
return hashProductManufacturer;
case ListTypes.Subcategory:
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
int hashProductSubCategory = product.SubCategory == null ? 0 : product.SubCategory.GetHashCode();
//Calculate the hash code for the product.
return hashProductSubCategory;
}
return 0;
}
}
}`
数据模型:
public class ProductCatalogProduct : Product, INotifyPropertyChanged
{
private bool _isCheckedCategory;
public bool IsCheckedCategory
{
get { return _isCheckedCategory; }
set { _isCheckedCategory = value; NotifyPropertyChanged("IsCheckedCategory"); }
}
private bool _isCheckedMfg;
public bool IsCheckedMfg
{
get { return _isCheckedMfg; }
set { _isCheckedMfg = value; NotifyPropertyChanged("IsCheckedMfg"); }
}
public ProductCatalogProduct(Product productBase)
{
Manufacturer = productBase.Manufacturer;
Category = productBase.Category;
Price = productBase.Price;
ManufacturersWithSameCategory = new List<string>();
CategoriesWithSameManufacterer = new List<string>();
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public List<string> ManufacturersWithSameCategory { get; set; }
public List<string> CategoriesWithSameManufacterer { get; set; }
public void UncheckAll()
{
IsCheckedCategory = false;
IsCheckedMfg = false;
}
public void CheckAll()
{
IsCheckedCategory = true;
IsCheckedMfg = true;
}
public void CheckCategory()
{
IsCheckedCategory = true;
}
public void UncheckCategory()
{
IsCheckedCategory = false;
}
public void CheckMfg()
{
IsCheckedMfg = true;
}
public void UncheckMfg()
{
IsCheckedMfg = false;
}
public bool GetCategoryChecked()
{
return IsCheckedCategory;
}
public bool GetMfgChecked()
{
return IsCheckedMfg;
}
}`