0

Столкнулся с проблемой, что не понимаю как через textbox сфильтровать таблицу по столбцу фио. Не могу понять, как именно по нужному столбцу это проверять.

private void textbox1_TextChanged(object sender, TextChangedEventArgs e)

{ var tbx = sender as TextBox; if(tbx.Text!="") { clientGrid.Items[0].Where(); } }

это все что я смог. Хотелось бы узнать все же как это сделать. P.S. Таблица для фильтрации и ее textbox

Таблица для фильтрации и ее textbox.

введите сюда описание изображения

Фото с привязкой к столбцам.

    public Window1()
    {
        InitializeComponent();
        conn.FillGrid(sql, clientGrid);
    }
     string sql = "SELECT fio,bonus_card FROM client ";

public void FillGrid(string sql, DataGrid dataGrid) { using (MySqlConnection connection = new MySqlConnection(connectionString)) { connection.Open(); using (MySqlCommand cmdSel = new MySqlCommand(sql, connection)) { DataTable dt = new DataTable(); MySqlDataAdapter da = new MySqlDataAdapter(cmdSel); da.Fill(dt);

                dataGrid.ItemsSource = dt.DefaultView;

            }
            connection.Close();
        }
    }

  • 1
    Вам правильно, или абы как? Ибо сейчас у вас ерунда в коде, так не пишут в WPF. Не, ну если хотите, то вам надо переопределить данные "таблицы" (убрать старые и загрузить новые), а условие уж сами смотрите, я по этому коду не понимаю вообще о чем речь, наверно .Where(x=>x.... == tbx.Text);. Ну а если правильно, то у вас не должно быть вообще данных в контролах, вы должны разделить все на слои, как минимум данные и UI, а затем привязать их при помощи Binding механизмов. Конкретно за фильтрацию данный отвечает CollectionViewSource, которому задаете коллекцию и фильтр, а он уже выводит. – EvgeniyZ Sep 19 '22 at 16:38
  • Через Where оно то и не работает @EvgeniyZ –  Sep 19 '22 at 16:48
  • Такие штуки делаются через приязки и ICollectionView, который можное получить из CollectionViewSource. https://ru.stackoverflow.com/a/1131976/373567 здесь есть пример. – aepot Sep 19 '22 at 16:51
  • @aepot два столбца у меня привязаны как {Binding fio} и {Binding bonus_card}. –  Sep 19 '22 at 16:54
  • Пример использования ICollectionView.Filter выше. – aepot Sep 19 '22 at 16:55
  • @aepot где пример использования? –  Sep 19 '22 at 16:59
  • Через Where оно то и не работает - мне это не говорит о чем-либо. Повторю, я не понимаю ваш код и вашу задачу. Я не вижу о какой таблице идет речь, я не вижу ее в вопросе; не вижу данных, не вижу, как они поступают в эту таблицу, и так далее. Вот этот вот огрызок clientGrid.Items[0].Where(); - вообще бесполезный, ибо Where() - это получить новый список данных на основе условия, другими словами, var result = ....Where(x=>x = ...);, а у вас просто вызывается метод и бросается. причем еще с неким первым объектом, о котором нам тоже ничего не известно. И как мы должны этот костыль решать? – EvgeniyZ Sep 19 '22 at 16:59
  • @EvgeniyZ прошу прощения. Сейчас проверил вопрос и понял что фото и код не загрузился еще. –  Sep 19 '22 at 17:01
  • 1
    Вот вам пример попроще с ICollectionView: https://ru.stackoverflow.com/a/1140841/220553 Советую всеж освоить это. – EvgeniyZ Sep 19 '22 at 17:03
  • прошу прощения, но я не очень понимаю как сделать именно для DataGrid. У меня ListBox нет как в примере, который вы кинули. @EvgeniyZ –  Sep 19 '22 at 17:13
  • И в чем проблема? Привязки везде одинаковые, без разницы какой контрол используется. Также не понимаю в чем проблема с вашим "костыльным" кодом dataGrid.ItemsSource = dt.DefaultView.Where(x=>x. условие);, все (DefaultView наверно надо будет привести к классу, который будет содержать в себе свойства). – EvgeniyZ Sep 19 '22 at 17:18
  • Можно использовать DataView.RowFilter https://learn.microsoft.com/ru-ru/dotnet/api/system.data.dataview?view=net-5.0, ему можно задать фильтры, поддерживаемы DataTable. @EvgeniyZ мож напишите пример в ответ, хоть с чем-нибудь. Я бы написал, но температура под 39, котелок не варит совсем. – aepot Sep 19 '22 at 17:44
  • @aepot по пробовал через DataView.RowFilter. Почему то он берет тип char. Что не подходит для поиска DataTable dt = new DataTable(); MySqlCommand cmd = new MySqlCommand(sql,conn.conn); MySqlDataAdapter da = new MySqlDataAdapter(cmd); da.Fill(dt); clientGrid.ItemsSource = dt.AsDataView().RowFilter.Where(x=>x=tbx.Text); –  Sep 19 '22 at 17:55
  • Документацию посмотрите, ссылка выше. Там примеры кода. RowFilter - это string, что вы собрались фильтровать в строке через Where? – aepot Sep 19 '22 at 18:03
  • @aepot извиняюсь, но я ничего не нашел в примере с rowfilter. там есть таблица с его описанием и все –  Sep 19 '22 at 18:07
  • 1
    @aepot все я разобрался. Спасибо за помощь. –  Sep 19 '22 at 18:20
  • В таком случае можете опубликовать решение постом ниже, вдруг кому пригодится – aepot Sep 19 '22 at 18:24
  • хорошо чуть позже этим займусь –  Sep 19 '22 at 18:31

1 Answers1

2

Вы идете совершенно не правильным, я даже назвал бы наверно "варварским" путем. В WPF принято первым делом использовать XAML и Привязки (Binding). Если вы это не используете, храните данные в тесной связи с UI контролами, то вы получаете очень много проблем (от трудного проектирования, до проблем с производительностью).

Покажу самый простой пример, где будет фильтрация на клиенте, с привязкой.

  1. Создадим класс, который будет работать с базой данных, назовем просто DataBase. В нем прописываем все методы получения и отправки всех нужных данных, например:

    public record User(int Id, string Name);
    

    public class DataBase { public IEnumerable<User> GetAllUsers() { DataTable table = new DataTable(); table.Columns.Add("Id", typeof(int)); table.Columns.Add("Name");

        table.Rows.Add(1, &quot;Вася&quot;);
        table.Rows.Add(2, &quot;Петя&quot;);
        table.Rows.Add(3, &quot;Коля&quot;);
    
        return table.AsEnumerable()
            .Select(x =&gt; new User(x.Field&lt;int&gt;(&quot;Id&quot;), x.Field&lt;string&gt;(&quot;Name&quot;)));
    }
    

    }

    Я здесь эмулировал класс DataTable (чтоб его...), с неким набором данных, и привел их к простой коллекции объектов User (наш простой класс, который содержит нужные свойства).

  2. Дальше создаем класс, который будет источником данных для всего приложения. Пусть зовется MainViewModel (почему такие названия, можете почитать про MVVM подход, но сейчас не о нем). В этом классе нам надо получить объект нашей базы данных, создать свойства для привязок, запросить данные из базы. Получаем примерно следующее:

    public class MainViewModel
    {
        private readonly DataBase data = new();
        public MainViewModel()
        {
            Users = new CollectionViewSource { Source = data.GetAllUsers() }.View;
        }
        public ICollectionView Users { get; }
    
    private string _searchText;
    public string SearchText
    {
        get =&gt; _searchText;
        set
        {
            _searchText = value;
            Users.Filter = x =&gt;
            {
                var user = x as User;
                return value.Length &gt; 0 ? user.Name.StartsWith(value) : true;
            };
        }
    }
    

    }

    Как видите, все просто, мы делаем объект нашей базы данных (в моем случае просто создаю, в вашем наверно надо передать через конструктор или еще что), делаем коллекцию, тип которой ICollectionView, связываем их через CollectionViewSource. Остается свойство SearchText - это простое свойство, которое будет содержать текст, вверенный пользователем в текстовое поле, в его сеттере мы задаем фильтр, некое условие, которое вернет нам true (объект надо отобразить) и false (объект не стоит отображать). Само условие простое - если у строки 0 символов, то true, если больше 0, то ищем по началу имени.

  3. Задаем DataContext окну, просто пишем в его конструкторе DataContext = new MainViewModel();, этого нам будет пока достаточно.

  4. Все, данные у нас отвязаны, как видите мы до сих пор не использовали что либо из UI, ибо нам это и не нужно, наш UI должен быть максимально отделен от данных, в этом суть WPF с его MVVM и прочими подходами. Имея все это, мы теперь можем написать XAML, например такой простой:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid Margin="5">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <TextBlock Text="Фильтровать по человеку:" />
            <TextBox
                Grid.Column="1"
                BorderThickness="0,0,0,1"
                Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
        </Grid>
        <DataGrid Grid.Row="1" ItemsSource="{Binding Users}" />
    </Grid>
    

    Тут я думаю объяснять ничего не надо, кроме всяких {Binding ...} - это привязка, через нее мы задаем свойству контрола некое место, от куда он должен забирать данные, например {Binding Users}, это public ICollectionView Users { get; } из класса, который мы указали как DataContext окну. Еще тут интересным может быть UpdateSourceTrigger=PropertyChanged - так мы задали привязки когда именно ей обновлять свойство, конкретно PropertyChanged означает "Сразу при обновлении", другими словами, привязанное свойство SearchText будет получать сразу введенные данные, а не в момент потери фокуса контрола (по умолчанию).

Все, при запуске у нас будут выведены все данные:

All Data

Ну а если мы напишем что-либо в текстовое поле, нам благополучно отфильтрует все:

Filtet Result

Вот вам и фильтрация на клиенте в WPF проекте. Но учтите, если у вас идет работа с базой, то фильтрация ее уровень, вы не должны вообще хотеть загружать на клиент всех пользователей из базы, это очень ресурсоемкая задача. Поэтому очень продумывайте такие аспекты, если у вас уже готовые данные, которые вы вывели и в них нужна фильтрация - мой пример подойдет, а если данные в базе, и на их основе надо вывести что-то конкретное, то делаете в базу конкретный запрос и выводите уже потом эти данные.

EvgeniyZ
  • 15,694