Как сделать импорт файла Excel в свою коллекцию Avalonia

Всем привет, нужна помощь в импорте excel файла в коллекцию. В Excel файле должна быть одна колонка. И текст построчно должен загружаться в коллекцию в объект Text. Для открытия файла нужно использовать OpenFilePicker т.к OpenFileDialog не работает. Путь к файлу выбирает пользователь.

VM:

    public class MainWindowViewModel : ViewModelBase
    {
        private ObservableCollection<Elements> _elementsList;
    
        public ObservableCollection<Elements> ElementsList
        {
            get => _elementsList;
            set => this.RaiseAndSetIfChanged(ref _elementsList, value);
        }
    
        public ReactiveCommand<Unit, Unit> AddNewRow { get; set; }
        public ReactiveCommand<Unit, Unit> ImportFiles { get; set; }
    
        public MainWindowViewModel()
        {
            ElementsList = new ObservableCollection<Elements>(new List<Elements>());
            ElementsList.Add(new ObservableCollection<Elements>
            {
                new()
                {
                    Id = ElementsList.Count, Text = ""
                }
            });
    
            AddNewRow = ReactiveCommand.Create(() =>
            {
                ElementsList.Add(new ObservableCollection<Elements>
                {
                    new()
                    {
                        Id = ElementsList.Count, Text = ""
                    }
                });
                
            });
            ImportFiles = ReactiveCommand.Create(() =>
            {
            

var excelApp = new Microsoft.Office.Interop.Excel.Application();
        if (excelApp != null)
        {
            Workbook excelWorkbook = excelApp.Workbooks.Open(@"C:\Users\Mikhail\Desktop\test.xlsx", 0, true,
                5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true,
                1, 0);
            Worksheet excelWorksheet = (Worksheet)excelWorkbook.Sheets[1];
            Range excelRange = excelWorksheet.UsedRange;
            int rowCount = excelRange.Rows.Count;
            int colCount = excelRange.Columns.Count;
            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    Range range = (excelWorksheet.Cells[i, 1] as Range);
                    string cellValue = range.Value.ToString();
                    ElementsList.Add(new ObservableCollection<Elements>
                    {
                        new()
                        {
                            Id = ElementsList.Count, Text = cellValue
                        }
                    });
                }
            }
            excelWorkbook.Close();
            excelApp.Quit();
        }
        
            });
    
            // using var resource = AssetLoader.Open(new Uri(@"avares://AvaloniaApplication24/Assets/Testing.txt"));
            // using var reader = new StreamReader(resource);
        }
        private async void Mark()
        {
            var CreateWindow = new MainWindow();
            var topLevel = TopLevel.GetTopLevel(CreateWindow);
            // Start async operation to open the dialog.
            var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
            {
                Title = "Open Text File",
                AllowMultiple = false
            });
    
            if (files.Count >= 1)
            {
                // Open reading stream from the first file.
                await using var stream = await files[0].OpenReadAsync();
                using var streamReader = new StreamReader(stream);
                // Reads all the content of file as a text.
                
            }
        }
    }

Model:

public class Elements: ViewModelBase
{
    private int _id;
    private string _text;

    public int Id
    {
        get => _id;
        set => this.RaiseAndSetIfChanged(ref _id, value);
    }

    public string Text
    {
        get => _text;
        set => this.RaiseAndSetIfChanged(ref _text, value);
    }
}

View:

    <StackPanel>
        <Button Content="add new row" Command="{Binding AddNewRow}" />
        <Button Content="Import" Command="{Binding ImportFiles}"></Button>
        <ItemsControl ItemsSource="{Binding ElementsList}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Id}"></TextBlock>
                        <TextBox Text="{Binding Text}"></TextBox>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>

Ответы (2 шт):

Автор решения: Mikhail

У меня получилось ответить на свой вопрос. Но может есть другие способы по импорту файлов? Данный способ выглядит как-то не очень). Также если встречаются пустые ячейки, а после них текст то получаю исключение Object reference not set to an instance of an object. Хотя если ловить исключение то все хорошо.

VM:

public class MainWindowViewModel : ViewModelBase
{
    private ObservableCollection<Elements> _elementsList;

    public ObservableCollection<Elements> ElementsList
    {
        get => _elementsList;
        set => this.RaiseAndSetIfChanged(ref _elementsList, value);
    }

    public ReactiveCommand<Unit, Unit> AddNewRow { get; set; }
    public ReactiveCommand<Unit, Unit> ImportFiles { get; set; }

    public MainWindowViewModel()
    {
        ElementsList = new ObservableCollection<Elements>(new List<Elements>());
        ElementsList.Add(new ObservableCollection<Elements>
        {
            new()
            {
                Id = ElementsList.Count, Text = ""
            }
        });

        AddNewRow = ReactiveCommand.Create(() =>
        {
            ElementsList.Add(new ObservableCollection<Elements>
            {
                new()
                {
                    Id = ElementsList.Count, Text = ""
                }
            });
            
        });
        
        ImportFiles = ReactiveCommand.Create(Mark);
    }
    
    
    
    string _path;
    private static FilePickerFileType Excel { get; } = new("All xls")
    {
        Patterns = new[]
        {
            "*.xlsx",
            "*.xls"
        },
        MimeTypes = null,
        AppleUniformTypeIdentifiers = null,
    };
    private async void Mark()
    {
        var topLevel = TopLevel.GetTopLevel(new MainWindow());
        // Start async operation to open the dialog.
        
        
        var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
        {
            Title = "Open Excel File",
            AllowMultiple = false,
            FileTypeFilter = new[] { Excel },
            SuggestedStartLocation = await topLevel.StorageProvider.TryGetFolderFromPathAsync("C:\\")
        });
        
        
        if (files.Count >= 1)
        {
            // Open reading stream from the first file.
            await using var stream = await files[0].OpenReadAsync();
            using var streamReader = new StreamReader(stream);
            // Reads all the content of file as a text.
            _path = files[0].Path.LocalPath;
            Console.WriteLine(_path);
        }
        var excelApp = new Microsoft.Office.Interop.Excel.Application();
        if (excelApp != null)
        {
            Workbook excelWorkbook = excelApp.Workbooks.Open(Filename: _path, 0, true,
                5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true,
                1, 0);
            Worksheet excelWorksheet = (Worksheet)excelWorkbook.Sheets[1];
            Range excelRange = excelWorksheet.UsedRange;
            int rowCount = excelRange.Rows.Count;
            int colCount = excelRange.Columns.Count;
            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    Range range = (excelWorksheet.Cells[i, 1] as Range);
                    string cellValue = range.Value.ToString();
                    ElementsList.Add(new ObservableCollection<Elements>
                    {
                        new()
                        {
                            Id = ElementsList.Count, Text = cellValue
                        }
                    });
                }
            }
            excelWorkbook.Close();
            excelApp.Quit();
        }
    }
}
→ Ссылка
Автор решения: Ingvar

Это не ответ, просто вариант решения (не оптимальный, но рабочий для меня): код для Windows Forms!

//-- запрашиваем файл

using (var ofd = new OpenFileDialog() { Filter = "Файл Excel|*.XLSX;*.XLS;*.XLSM", CheckFileExists = true, CheckPathExists = true, Title = "Открыть импортируемый файл" })
{
if (ofd.ShowDialog() == DialogResult.OK && File.Exists(ofd.FileName))
                {
                    DoImport(ofd.FileName);
                }
            }

//-- кусок кода DoImport:

/// <summary>
/// Осуществление непосредственного импорта во внутренние структуры программы
/// </summary>
/// <param name="FileName">Имя файла для импорта</param>
public void DoImport(string FileName)
{
   //-- получаем экземпляр приложения Excel

   //-- Внимание! В следующих 3 х строчках кода недопустимо использовать "var", будет ошибка, только явное задание переменных!
   //-- фиг его знает, с чем это связано
   Excel.Application app = new Excel.Application();
   //-- открываем рабочий файл с диска
   Excel.Workbook wrkbook = app.Workbooks.Open(FileName); //, ReadOnly:true);
   //-- получаем активную страницу
   object sheet = wrkbook.ActiveSheet;
   var wrksht = (Excel.Worksheet)sheet;

   //-- Считываем первые пять тысяч строк (думается более и не надо, но если что можно и добавить)
   object workingRangeCells = wrksht.get_Range("A2:AC5000", Type.Missing);
   object arr = ((Excel.Range)workingRangeCells).Cells.Value2;
   var array = (Array)arr;
   app.Workbooks.Close();

   var data = new List<import>();
   //-- первую строчку пропускаем, там заголовки, которые нам не требуются
   //-- читаем подряд все данные, которые есть в EXCEL
   SplashScreenManager.Default.SetWaitFormDescription("Чтение данных...");
   Application.DoEvents();
   for (var i = 1; i <= 5001; i++)
   {
       if (array.GetValue(i, 1) == null)
           break;
       var datv = DateTime.MinValue;
       var datu = DateTime.MinValue;
       try
       {
           if (array.GetValue(i, 11) != null)
               datv = DateTime.Parse(TextCorrection.CleanTrash(array.GetValue(i, 11).ToString()));
       }
       catch (Exception)
       {
           //-- лишний код, и так понятно, что просто подавление ошибки
           // Logger.log.Log(Logger.GetErrorInfo(ex));
       }
       try
       {
           if (array.GetValue(i, 27) != null)
               datu = DateTime.Parse(TextCorrection.CleanTrash(array.GetValue(i, 27).ToString()));
       }
       catch (Exception)
       {
           //-- лишний код, и так понятно, что просто подавление ошибки
           // Logger.log.Log(Logger.GetErrorInfo(ex));
       }
       var rec = new import
       {
           v1 = TextCorrection.CleanNBSPAndTrash(array.GetValue(i, 1).ToString()).Trim(), //-- первая строка всегда проверяется на старте
           v2 = array.GetValue(i, 2) != null ? TextCorrection.CleanTrash(array.GetValue(i, 2).ToString()).Trim() : string.Empty,
           v3 = array.GetValue(i, 3) != null ? TextCorrection.CleanTrash(array.GetValue(i, 3).ToString()).Trim() : string.Empty,
           v4 = array.GetValue(i, 4) != null ? TextCorrection.CleanNBSPAndTrash(array.GetValue(i, 4).ToString()).Trim() : string.Empty,
           v5 = array.GetValue(i, 5) != null ? TextCorrection.CleanTrash(array.GetValue(i, 5).ToString()).Trim() : string.Empty,
       };
       data.Add(rec);
   }

   #region -- Служебный код завершения работы EXCEL - обнуляем все данные Excel для попытки закрыть запущенный процесс...
   //-- на самом деле убить эту гадину не так просто. ну хоть попытаемся...
   arr = null;
   array = null;
   wrksht = null;
   sheet = null;
   wrkbook = null;
   app.Quit();
   System.Runtime.InteropServices.Marshal.ReleaseComObject(app);
   app = null;
   //-- ... не удаляется, но помирает, как только выйти из программы
   #endregion
→ Ссылка