Загрузить данные в WPF DataGrid MVVM

#c# #wpf #xaml

#c# #wpf ( ВП ) #xaml #wpf

Вопрос:

Я уже некоторое время пытаюсь перенести свой проект в MVVM.

Есть DataTable, который я получаю из базы данных:

Основной процесс.cs:

     public static DataTable CustomersInLiinos = new DataTable();

    public static void MergedTable()
    {
        var t1 = ConnectAndRetriveDatatatableS(); // t1
        var t2 = ConnectAndRetriveDatatatableF(); // t2

        CustomersInLiinos = t1.Copy();
        CustomersInLiinos.Merge(t2);
    }
  

ViewModel.cs — Просмотр модели.cs:

     private async Task ExecuteLoadMainTableDataAsync(object commandParameter)
    {
        if (MainProcess.CheckForVPNInterface())
        {
            if (MainProcess.CustomersInLiinos != null)
            {
                this.HasProgress = true;

                IEnumerable<Item> resultItems = await LoadMainTableDataAsync();
                this.Items = new ObservableCollection<Item>(resultItems);
                EnableItemsFiltering();

                this.HasProgress = false;
            }
        }
        else
        {
            throw new VpnInterfaceException("Please, check your VPN connection!");
        }
    }
  

Внутри ViewModel.cs у меня также есть это:

     public Task<DataView> LoadMainTableDataAsync()
    {
        return Task.Run(() =>
        {
            MainProcess.MergedTable();

            return MainProcess.CustomersInLiinos.DefaultView;
        });
    }
  

В настоящее время у меня возникает ошибка, указывающая на await LoadMainTableDataAsync(); :

Описание кода серьезности Ошибка состояния подавления строки файла проекта CS0266 Не удается неявно преобразовать тип ‘System.Data.DataView’ в ‘System.Коллекции.Общий.IEnumerable<Liinos_inspector_FilterTest.Item>’. Существует явное преобразование (вам не хватает приведения?)

Я понимаю, что есть ошибка в LoadMainTableDataAsync ? Я загружаю данные в DataView и вместо этого должен загружать в IEnumerable?

Было бы проще использовать это:

     public class JoinedFandS
    {
       public string YRNRO { get; set; }
       public string HAKUNIMI { get; set; }
       public string NIMIA { get; set; }
       public string NIMIB { get; set; }
    }

    public static IEnumerable<JoinedFandS> GetMyJoinedResult()
    {
        var t1 = ConnectAndRetriveDatatatableS(); // t1
        var t2 = ConnectAndRetriveDatatatableF(); // t2

        var firstTable = ...

        var secondTable = ...

        var results = firstTable.Concat(secondTable);

        return results;
    }
  

Редактировать:

 <Window x:Class="Liinos_inspector_FilterTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:Liinos_inspector_FilterTest="clr-namespace:Liinos_inspector_FilterTest" 
        mc:Ignorable="d"
        Title="Liinos database inspector" Height="672" Width="1000" Icon="Images/logo_icon-small.jpg" Background="White" MinWidth="1000">

  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>
  

Описание кода серьезности Состояние подавления строки файла проекта
Ошибка XLS0414 Тип ‘ViewModel’ не найден. Убедитесь, что вы являетесь
не пропущена ссылка на сборку и что все сборки, на которые даны ссылки
были построены. Liinos инспектор фильтрует MainWindow.xaml 11

и

Описание кода серьезности Ошибка состояния подавления строки файла проекта XDG0008 ViewModel не поддерживается в проекте Windows Presentation Foundation (WPF). Инспектор Liinos фильтрует MainWindow.xaml 11

Что я должен использовать вместо ViewModel?

Комментарии:

1. Мы могли бы сделать и с вашим xaml. Является ли MainProcess какой-то статической глобальной переменной? (Этого следует избегать). Вам не нужно возвращать DefaultView в LoadMainTableDataAsync . Все, что вам нужно, это вернуть DataTable или, что еще лучше, создать модель и использовать ее вместо этого.

2. Это следующий вопрос. Я думаю, вы должны были задать правильный вопрос с надлежащим контекстом раньше. Для правильного ответа на этот вопрос также не хватает большого контекста. Вы должны были добавить контекст вашего предыдущего вопроса. Устранение этой ошибки приведет к нарушению решения вашего предыдущего вопроса.

3. Эй, большое вам спасибо за то, что вы начали раздачу наград за этот вопрос, чтобы почтить мой ответ дополнительными очками репутации. Я действительно ценю ваш жест. Большое вам спасибо. Это очень, очень любезно с вашей стороны.

4. @BionicCode Было много вопросов, я не знал, какой из них выбрать. Большое вам спасибо за вашего пациента и помощь! Теперь я снова немного поумнел, но еще многому нужно научиться. Я занимаюсь программированием в свободное время помимо своей обычной работы. Однако я надеюсь, что однажды я достигну такого уровня, чтобы действительно давать советы другим =))

Ответ №1:

Это несоответствие типов данных. Вы не можете нажать DataView в коллекцию. Также более практично хранить DataTable вместо DataView . Дешевле получить представление из таблицы, чем получать таблицу из представления (на случай, если вам понадобится поработать с DataTable позже).
Для исправления этой ошибки также необходимо исправить фильтрацию.

DataGrid может обрабатывать DataTable напрямую.

Обратите внимание, что вы должны await возвращать значение LoadMainTableDataAsync . В противном случае этот метод вернул бы результат преждевременно (поскольку результат вычисляется в фоновом потоке). Я уверен, что эта часть вашего кода даже не компилируется. Возможно, это просто пример кода.

В этом примере также настраивается фильтрация на основе новой структуры данных. Для фильтрации DataTable.DataView вы должны использовать DataView.RowFilter свойство (см. справку в разделе Синтаксис выражения). Вам необходимо настроить фактическую логику фильтра в соответствии с вашими требованиями:

ViewModel.cs

 class ViewModel : INotifyPropertyChanged
{
  public ICommand LoadMainTableDataCommand => new RelayCommand(async param => ExecuteLoadMainTableDataAsync());

  private DataTable mainDataTable;   
  public DataTable MainDataTable 
  {
    get => this.mainDataTable;
    set 
    { 
      this.mainDataTable = value; 
      OnPropertyChanged();  
      
      // Set the DataTable filter expression
      EnableRowFiltering();
    }
  }

  // Binding source for the first name TextBox
  private string firstNameSearchKey;   
  public string FirstNameSearchKey
  {
    get => this.firstNameSearchKey;
    set 
    { 
      this.firstNameSearchKey = value; 
      OnPropertyChanged();  
      
      // Refresh the DataTable filter expression
      EnableRowFiltering();
    }
  }

  // Binding source for the last name TextBox
  private string lastNameSearchKey;   
  public string LastNameSearchKey
  {
    get => this.lastNameSearchKey;
    set 
    { 
      this.lastNameSearchKey = value; 
      OnPropertyChanged();  
      
      // Refresh the DataTable filter expression
      EnableRowFiltering();
    }
  }

  private bool hasProgress;   
  public bool HasProgress
  {
    get => this.hasProgress;
    set 
    { 
      this.hasProgress = value; 
      OnPropertyChanged();
    }
  }

  public void EnableRowFiltering()
  {
    // The filter assumes a column 'FirstName' and a column 'LastName' in the DataView. 
    // The filter expression mimics string.StartsWith.
    this.MainDataTable.DefaultView.RowFilter = 
      $"FirstName LIKE '{this.FirstNameSearchKey}*' "   
      $"OR LastName LIKE '{this.LastNameSearchKey}*'";
  }

  private async Task ExecuteLoadMainTableDataAsync()
  {
    if (MainProcess.CheckForVPNInterface())
    {
      if (MainProcess.Customers != null)
      {
        this.HasProgress = true;

        this.MainDataTable = await LoadMainTableDataAsync();

        this.HasProgress = false;
      }
    }
    else
    {
      throw new VpnInterfaceException("Please, check your VPN connection!");
    }
  } 

  public async Task<DataTable> LoadMainTableDataAsync()
  {
    return await Task.Run(() =>
    {
      MainProcess.MergedTable();

      return MainProcess.CustomersInLiinos;
    });
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
  

MainWindow.xaml

 <Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
  </Window.Resources>
 
  <StackPanel>
    <ProgressBar IsIndeterminate="True"
                 Visibility="{Binding HasProgress, Converter={StaticResource BooleanToVisibilityConverter}}" />

    <Button Command="{Binding LoadMainTableDataCommand}" 
            Content="Load Data" />

    <TextBox Text="{Binding FirstNameSearchKey}" />
    <TextBox Text="{Binding LastNameSearchKey}" />
    <DataGrid ItemsSource="{Binding MainDataTable}" />
  <StackPanel>
</Window>
  

Комментарии:

1. Еще раз спасибо! Я думаю, что я довольно близок к чему-то, что, надеюсь, сработает, и я смогу продолжить свое обучение. В настоящее время у меня осталось две ошибки, this.MainDataTable.DataView.RowFilter говорящие DataTable' does not contain a definition for 'DataView' and no accessible extension method 'DataView' accepting a first argument of type 'DataTable' could be found , и new RelayCommand(ExecuteLoadMainTableDataAsync) я предполагаю, что для этого мне нужен новый класс. Я создал его, но, вероятно, сделал что-то не так.

2. Добро пожаловать. Я использовал неправильное (несуществующее) свойство. Вместо этого должно быть DefaultView . Я обновил пример. Для RelayCommand вы можете скопировать эту реализацию из Microsoft и использовать new RealyCommand(async param => await ExecuteLoadMainTableDataAsync()) . Или , в качестве альтернативы , добавьте перегрузку конструктора , которая принимает Func<object, Task> .

3. Похоже, это тоже можно удалить object commandParameter ? Теперь ошибок нет. Я постараюсь проверить.

4. Можете проверить, находится ли модель представления, которая определяет команду, в DataContext вашем представлении? Как вы устанавливаете DataContext?

5. Я обновил свой ответ. Я думаю, проблема в том, что MainDataTable свойство меняется, но поскольку автоматическое свойство не вызывало INotifyPropertyChanged.PropertyChanged событие, DataGrid привязка никогда не обновляется. Теперь я превратил свойство в свойство с уведомлением об изменении. Теперь все должно работать. В ходе этого вопроса было переработано так много вещей, что некоторые ошибки возникли из-за того, что я забыл настроить определенный код. Я прошу прощения за это. Дайте мне знать, если все еще есть какие-либо проблемы, но их не должно быть.