de-vraag
  • 質問
  • タグ
  • ユーザー
通知:
報酬:
登録
登録すると、質問に対する返答やコメントが通知されます。
ログイン
すでにアカウントをお持ちの方は、ログインして新しい通知を確認してください。
追加された質問、回答、コメントには報酬があります。
さらに
ソース
編集
ゲストユーザ
質問

別のDataGrid(ObservableCollection)フィルタリング方法

パフォーマンスを向上させるために DataGrid フィルタリング方法を書き直しました。これが最も速い方法ですが、私のプログラムを実行する遅いマシンではまだ改善の余地があると思います。

ユーザーがフィルタに一致するテキストを searchBox に入力すると、 DataGrid は ObservableCollection にバインドされます ListBox フィルタに追加されます。 ListBox は、パフォーマンスを向上させるため、最初の25の CompanyModels に制限されています。これはユーザタイプとして呼ばれるメソッドです。

private void FilterCompanies()
{
    FilteredCompanies = new ObservableCollection();
    FilteredCompanies.Clear();

    string searchText = CleanseText(searchBox.Text.ToLower());

    foreach (CompanyModel company in AllCompanies)
    {
        if (!string.IsNullOrEmpty(searchText))
        {
            if (FilteredCompanies.Count <= 25)
            {
                if (CompanyMatchesFilters(company))
                {
                    if (CleanseText(company.Name.ToLower()).Contains(searchText) ||
                        CleanseText(company.Town.ToLower()).Contains(searchText) ||
                        CleanseText(company.Postcode.ToLower()).Contains(searchText))
                    {
                        FilteredCompanies.Add(company);
                    }
                }
            }
        }
    }

    if (FilteredCompanies.Count > 0)
    {
        companiesListBox.ItemsSource = FilteredCompanies;
        companiesListBox.Visibility = Visibility.Visible;
    }
    else
    {
        companiesListBox.ItemsSource = null;
        companiesListBox.Visibility = Visibility.Collapsed;
    }
}

コードから、フィルタリングにはさらに2つの方法があることがわかります。

CompanyMatchesFilters

private bool CompanyMatchesFilters(CompanyModel company)
{
    foreach (FilterItem item in firstListBoxItems)
    {
        if (item.ID == 1 && company.CurrentStatus != 1)
        {
            return false;
        }
        if (item.ID == 2 && company.Subcontractor != 1)
        {
            return false;
        }
        if (item.ID == 3 && company.Supplier != 1)
        {
            return false;
        }
        if (item.ID == 4 && company.Planthire != 1)
        {
            return false;
        }
        if (item.ID == 5 && company.Architect != 1)
        {
            return false;
        }
        if (item.ID == 6 && company.QS != 1)
        {
            return false;
        }
        if (item.ID == 7 && company.ProjectManager != 1)
        {
            return false;
        }
        if (item.ID == 8 && company.StructEng != 1)
        {
            return false;
        }
        if (item.ID == 9 && company.ServiceEng != 1)
        {
            return false;
        }
    }
    return true;
}

CleanseText

private static Regex badChars = new Regex("[^A-Za-z0-9']");

private string CleanseText(string text)
{
    return badChars.Replace(text, "");
}

何かアドバイスがあれば、とても感謝しています。

  • A:コード全般を改善する方法
  • B(おそらくもっと重要):パフォーマンスをさらに向上させる方法 可能であれば私の方法のリスト
7 2016-11-18T12:27:30+00:00 5
コードレビュー
c#
performance
wpf
yogman
18日 11月 2016 в 2:18
2016-11-18T14:18:00+00:00
さらに
ソース
編集
#84950707

これをLINQクエリにリファクタリングして、(おそらく)読みやすさを向上させることができます。

AllCompanies.Where(c => CompanyMatchesFilters(c))
            .Where(c => CleanseText(company.Name).Contains(searchText)
                     || CleanseText(company.Town).Contains(searchText) 
                     || CleanseText(company.Postcode).Contains(searchText))
            .Take(25);

パフォーマンスに関して - あなたが示したコードの中で、潜在的なパフォーマンスのボトルネックとして私を襲うものは何もありません。おそらくそれを特定するためにプロファイラーを実行する必要があるでしょう。あなたが特に見たいと思うかもしれない1つの事は何が検索を引き起こすのか、そしてそれがどのくらいの頻度で実行されるのかです。たとえば、検索ボックスに10文字を入力した場合、1回または10回検索しますか。フィルタリングロジックはかなり軽量に見えますが、キー入力ごとに25個のアイテムテンプレートをUIで再作成することによるパフォーマンスへの影響は深刻です( DataTemplate の複雑さによって異なります)。 Rx には、UIイベントの調整に役立つ可能性のあるいくつかの優れた拡張機能があります。

P.S CleanseText 内のすべての意味のある文字を削除することの意味は何ですか?その時あなたはどんなキャラクターに興味がありますか?これは非常に不明瞭です、あなたはあなたのコードのその部分を文書化するべきです。

6
0
echinodermata
18日 11月 2016 в 1:37
2016-11-18T13:37:45+00:00
さらに
ソース
編集
#84950699

私が提案するいくつかの変更点:

    private void FilterCompanies()
    {
        FilteredCompanies = new ObservableCollection();
        //FilteredCompanies.Clear();  <- Pointless, you already cleared it when setting a new instance

        string searchText = CleanseText(searchBox.Text.ToLower());

        if (!string.IsNullOrEmpty(searchText)) //put outside foreach to avoid pointless foreach iteration
        {
            foreach (CompanyModel company in AllCompanies)
            {
                if (CompanyMatchesFilters(company))
                {
                    //.Contains() is not case sensitive, no point on .ToLower()
                    if (CleanseText(company.Name).Contains(searchText) || CleanseText(company).Contains(searchText) || CleanseText(company.Postcode).Contains(searchText))
                    {
                        FilteredCompanies.Add(company);
                        if (FilteredCompanies.Count <= 25)
                            break;
                    }
                }
            }
        }

        companiesListBox.ItemsSource = FilteredCompanies.Any() ? FilteredCompanies : null;
        companiesListBox.Visibility = FilteredCompanies.Any() ? Visibility.Visible : Visibility.Collapsed;
    }
3
0
Ruadhan2300
18日 11月 2016 в 2:57
2016-11-18T14:57:51+00:00
さらに
ソース
編集
#84950709

CompanyMatchesFilters can be refactored as well. Just put all the company properties into an array. This will work because they seem to be of the same type.

private bool CompanyMatchesFilters(CompanyModel company)
{
    var values = new[]{
        company.CurrentStatus,
        company.Subcontractor,
        company.Supplier,
        company.Planthire,
        company.Architect,
        company.QS,
        company.ProjectManager,
        company.StructEng,
        company.ServiceEng
    }
    return !firstListBoxItems.Any(item => item.ID - 1 < values.Length 
        && values[item.ID - 1] != 1);
}
2
0
t3chb0t
18日 11月 2016 в 3:29
2016-11-18T15:29:26+00:00
さらに
ソース
編集
#84950711

すべての最適化とフィルタリングロジックを脇に置いています。


WPFではを使用します CollectionView

データコレクションをグループ化、並べ替え、フィルタ処理、および移動するためのビューを表します。

例えば ListBox のフィルタや並べ替えを適用します。


ListBox があなたのview-modelのプロパティにバインドされているとしましょう:


ここで、companyは次のように初期化された ICollectionView です。

public ICollectionView Companies { get; }

public CompaniesViewModel()
{
    var companies = GetCompanies();
    _companiesView = CollectionViewSource.GetDefaultView(companies);
}

次のステップは、署名付きの Filter プロパティを介してフィルタを追加することです。

public virtual Predicate Filter { get; set; }


これはそれを意味します

項目がビューに含めるのに適しているかどうかを判断するために使用されるメソッドを取得または設定します。


それでは、一度に1つの会社が受け取ることができるフィルター方式を定義しましょう

private bool CompanyFilter(object item)
{
    var company = item as Company;

   //the optimized and pretty filtering logic ;-]

    return true/false...
}

そしてフィルタを設定します。

Companies.Filter = CompanyFilter;

これまでのすべては、フィルタリングを準備するために一度だけ行います。繰り返す必要があるのは、フィルタ設定を変更し、適切な場所とタイミングで Refresh メソッドを呼び出してそれを参照することです。

  Companies.Refresh();

There is more to this which you can read on wpftutorial - How to Navigate, Group, Sort and Filter Data in WPF


先読みとしてのフィルタリング方法の設定が単純なシナリオに適用されることを付け加えたいと思います。あなたの場合は、フィルタロジックとプロパティ全体を CompanyFilter クラスにカプセル化して、そのインスタンスをビューモデルで使用するほうが良いでしょう。この方法で、あなたはより良いフィルタをテストして、他のオブジェクトを巻き込むことなしにそれがするべきであるようにそれが機能することを確かめることができます。

class CompanyFilter
{
    public bool FilterCompany(object item)
    {
        var company = item as company;

       //filtering...
    }

   //properties...

   //other filter helper methods.... but private
}
1
0
paparazzo
18日 11月 2016 в 5:31
2016-11-18T17:31:14+00:00
さらに
ソース
編集
#84950714

CleanseText(company.Prop.ToLower())を何度も呼び出す方法
そして私はもっと多くの検索ロジックをCompanayに取り込むだろう

public class Company
{
    private string name = string.Empty;
    private string cleanName = string.Empty;
    public string Name
    {
        get { return name; }
        set
        {
            if (name == value)
                return;
            name = value;
            NotifyPropertryChanged("Name");
            cleanName = CleanseText(name.ToLower());
            NotifyPropertryChanged("CleanName");
        }
    }
    public string CleanName
    {
        get { return cleanName; }
    }
    public bool MatchSearch(string searchText)
    {
        searchText = CleanseText(searchText);
        if (string.IsNullOrEmpty(searchText))
            return false;
        return (cleanName.Contains(searchText) ||
                cleanTown.Contains(searchText) ||
                cleanPostcode.Contains(searchText));
    }
}

私はMatchesFiltersをCompanyに引っ張ってフィルタのコレクションを渡すことさえします。

  • では、CompaniesListBox.ItemsSourceをバインドしないでください。 FilterCompanies()

  • メソッド内でUI要素を参照しないようにする

  • FilterCompanies()でFilteredCompaniesを新しくしてはいけません

  • FilteredCompaniesを1回正しくバインドします - 消去して追加 - それが目的です ObservableCollectionはこれを行います


0
0
質問の追加
カテゴリ
すべて
技術情報
文化・レクリエーション
生活・芸術
科学
プロフェッショナル
事業内容
ユーザー
すべて
新しい
人気
1
Денис Анненский
登録済み 2日前
2
365
登録済み 6日前
3
True Image
登録済み 6日前
4
archana agarwal
登録済み 1週間前
5
Maxim Zhilyaev
登録済み 1週間前
© de-vraag :年
ソース
codereview.stackexchange.com
ライセンス cc by-sa 3.0 帰属