-->

WPF multiple filters CollectionViewSource, first f

2020-07-22 17:38发布

问题:

I have a ListView that is bounded to a CollectionViewSource. I followed this article (refered to by many) for multiple filtering: http://www.zagstudio.com/blog/456#.UG8r6E1lWLE

I have two checkboxes set up for testing that do nothing but add a filter. Whenever I click on either one first, the filter is added to the CollectionViewSource and it works. Then when I click on the opposite checkbox, instead of the other filter being added to the CollectionViewSource and both filters working, my listview goes blank (when it shouldnt based on the data, and this happens in either order of checking my checkboxes)

Here is the relevant code: (Background: this application deals with filtering "Orders" for shipping software)

Loading Orders:

public class Order
{
    public int index { get; set; }
    public string host { get; set; }
    public Int64 orderNumber { get; set; }
    public string batchStatus { get; set; }
    public string sku { get; set; }
    public int numItems { get; set; }
    public string orderSource { get; set; }
    public string sourceOrderNumber { get; set; }
    public DateTime orderDate { get; set; }
    public DateTime orderTime { get; set; }
    public int customerID { get; set; }
    public string shipMethod { get; set; }
    public string billingState { get; set; }
    public bool statusChanged { get; set; }
    public int numSkus { get; set; }
    public string marketName { get; set; }
    public float weight { get; set; }
}

public class Orders : ObservableCollection<Order>
{
    public Orders()
    {
        SqlDataReader reader1 = cmd.ExecuteReader();
        while (reader1.Read())
        {
            Order order = new Order();

            order.host = (string)safeGetString(reader1, 0);
            order.orderNumber = (Int64)reader1["OrderNumber"];
            order.batchStatus = (string)safeGetString(reader1, 2);
            order.orderSource = (string)safeGetString(reader1, 3);
            order.sourceOrderNumber = safeGetString(reader1, 4);
            order.orderDate = (DateTime)reader1["OrderDate"];
            order.customerID = (int)reader1["CustomerID"];
            order.shipMethod = (string)safeGetString(reader1, 7);
            order.billingState = (string)safeGetString(reader1, 8);
            order.numItems = (int)reader1["NumItems"];
            order.numSkus = (int)reader1["NumSKUs"];
            order.marketName = (string)safeGetString(reader1, 11);
            order.weight = (float)(double)reader1["ShippedWeight"];


            this.Add(order);
        }
        reader1.Close();
    }

Setting up CollectionViewSource:

cvs = (CollectionViewSource)(this.Resources["cvs"]);

Checkbox Functions: (Hardcoded "what to filter by" using filterString for testing)

public void checkBox2_Checked(object sender, RoutedEventArgs e)
    {
        filterString = "TX";
        cvs.Filter += new FilterEventHandler(billingStateFilter);
    }
    public void checkBox1_Checked(object sender, RoutedEventArgs e)
    {
        filterString = "Standard";
        cvs.Filter += new FilterEventHandler(shippingMethodFilter);
    }

And lastly, the filters:

private void shippingMethodFilter(object sender, FilterEventArgs e)
    {
        Order order = e.Item as Order;
        if ((order.shipMethod != filterString))
        {
            e.Accepted = false;
        }
    }

    public void billingStateFilter(object sender, FilterEventArgs e)
    {
        Order order = e.Item as Order;
        if ((order.billingState != filterString))
        {
            e.Accepted = false;
        }
    }

Like I said, the first filter always works. The second always makes the screen blank. Any ideas?

回答1:

You are re-using the filter string for both filters and once you check each box both filters will be applied. So if you:

  1. Check checkBox1, then filterString will be "Standard" and shippingMethodFilter will be hooked up.
  2. Check checkBox2, then filterString will be "TX" and billingStateFilter will be hooked up.

At no point is shippingMethodFilter unhooked. So it will continue to filter based on a filterString of "TX".

You should probably add one filter method that checks to see if checkBox1/checkBox2 is checked and then optionally apply it's filtering logic. Something like:

private string shippingFilterString = "Shipping";
private string billingFilterString = "TX";

private void collFilter(object sender, FilterEventArgs e)
{
    Order order = e.Item as Order;
    if (checkBox1.IsChecked == true && (order.shipMethod != shippingFilterString ))
        e.Accepted = false;
    if (checkBox2.IsChecked == true && (order.billingState != billingFilterString ))
        e.Accepted = false;
}


回答2:

for (i = 0; i < numberItemsFilterStrings.Length; i++)
            {
                switch (numberItemsFilterStrings[i])
                {
                    case "One Item":
                        if (order.numItems != 1)
                        {
                            e.Accepted = false;
                        }
                        break;
                    case "Between 2 - 5":
                        if ((order.numItems < 2) || (order.numItems > 5))
                        {
                            e.Accepted = false;
                        }
                        break;
                    case "Between 6 - 25":
                        if ((order.numItems < 6) || (order.numItems > 24))
                        {
                            e.Accepted = false;
                        }
                        break;
                    case "Greater Than 25":
                        if (order.numItems < 25)
                        {
                            e.Accepted = false;
                        }
                        break;
                }
            }

So this is the code of one of the filters I am creating. It works for the first filter because all of the orders that dont match the filter are not accepted. But when I check a second filter box, then none of the orders show up because out of the orders that were initially accepted true, now they are also not accepted leaving none of the orders



回答3:

I have posted a blog on this: https://hoomanvisualstudio.blogspot.com/b/post-preview?token=mpPyAk0BAAA.ygnewdzslszANX8AchX0hg.uW17QZR22-f_JUqjDTBcyA&postId=4200491240358821673&type=POST

There are so many way. One easy way is to roll your conditions into one single predicate.

MyViewModel myViewModel = new MyViewModel();

// Collection which will take your Filter var _itemSourceList = myViewModel.MyCollection;

var filter = new Predicate(item => ((Model)item).FirstName.ToString().Contains("John") && ((Model)item).LastName.ToString().Contains("Doe"));

_itemSourceList.Filter = filter; myDataGrid.ItemsSource = myViewModel.MyCollection;