CHashtag

[C# WPF] MVVM 패턴 csv 파일 파싱 프로그램 (ListView, Binding, Prism) 본문

C#/WPF

[C# WPF] MVVM 패턴 csv 파일 파싱 프로그램 (ListView, Binding, Prism)

HyoSeong 2020. 12. 22. 02:21
반응형

안녕하세요.

 

오늘은 csv 파일을 파싱 하는 방법에 대해 알아보겠습니다.

 

CSV 파일이란?


csv 파일 확장자는 comma-separated values라는 의미로 " , " 로 구분하는 텍스트 파일 확장자입니다.

 

 

엑셀을 이용하여 데이터를 csv파일로 저장한 뒤 데이터를 파싱 하여 보겠습니다.

 

 

해당 파일을 메모장으로 열어보면 다음과 같은 데이터가 보입니다.

 

번호,이름,사는곳,직업
1,Henry,대구,개발자
2,Tom,서울,선생님
3,Jonathan,대전,경찰
4,James,부산,무직
5,Rachel,인천,의사
6,Samuel,창원,학생
7,Peter,안동,농부
8,Clara,울진,선장
9,Harry,울산,역무원

이제 이 데이터를 파싱해 보도록 하겠습니다.

 

 

 

프로그램 제작


csv를 파싱하는 수많은 라이브러리가 있겠지만 전 오늘 Microsoft.VisualBasic.FileIO 에서 제공하는 TextFieldParser를 사용하여 파싱 후 파싱 된 데이터를 List에 보여주는 프로그램을 제작해 보겠습니다.

 

 

우선 WPF 프로젝트를 생성해줍니다. (.net framework version은 4.8로 하였습니다.)

 

 

라이브러리를 사용하려면 Microsoft.VisualBasic를 References에 추가하여야 합니다.

 

추가로 MVVM 패턴 사용을 위해 Prism 라이브러리 또한 다운로드해줍니다. (nuget에서 다운로드할 수 있습니다.)

(Prism에 대한 내용은 나중에 따로 정리하도록 하겠습니다.) 

 

 

추가하는 방법은 다음과 같습니다.

 

Add Reference...

 

VisualBasic으로 검색하여 Microsoft.VisualBasic을 추가해주면 됩니다.

 

 

 

우선 데이터를 저장할 모델을 만들어보겠습니다.

 

// User.cs
using Prism.Mvvm;

namespace ParseCSV.Models
{
    public class User : BindableBase
    {
        private int _index;
        public int Index
        {
            get => _index;
            set => SetProperty(ref _index, value);
        }

        private string _name;
        public string Name
        {
            get => _name;
            set => SetProperty(ref _name, value);
        }

        private string _location;
        public string Location
        {
            get => _location;
            set => SetProperty(ref _location, value);
        }

        private string _job;
        public string Job
        {
            get => _job;
            set => SetProperty(ref _job, value);
        }
    }
}

 

모델을 만들었으니 해당 모델을 View와 연결해줄 ViewModel 또한 만들도록 하겠습니다.

 

 

// MainWindowViewModel.cs
using Microsoft.VisualBasic.FileIO;
using Microsoft.Win32;
using ParseCSV.Models;
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;

namespace ParseCSV.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public DelegateCommand ChooseCsvFileCommand { get; private set; }

        private string _csvFilePath;
        public string CsvFilePath
        {
            get => _csvFilePath;
            set => SetProperty(ref _csvFilePath, value);
        }

        private ObservableCollection<User> _items = new ObservableCollection<User>();
        public ObservableCollection<User> Items
        {
            get => _items;
            set => SetProperty(ref _items, value);
        }

        public MainWindowViewModel()
        {
            InitializeVariable();
            InitializeCommands();
        }
        
        private void InitializeVariable()
        {
            Items = new ObservableCollection<User>();
        }

        private void InitializeCommands()
        {
            ChooseCsvFileCommand = new DelegateCommand(ChooseCsvFileCommandMethod);
        }

        private void ChooseCsvFileCommandMethod()
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "csv file (*.csv)|*.csv";
            if (openFileDialog.ShowDialog() == true)
            {
                CsvFilePath = openFileDialog.FileName;
                ParseCsvFile();
            }
        }

        private void ParseCsvFile()
        {
            if(!ValidateCsvFilePath())
            {
                return;
            }

            using (TextFieldParser parser = new TextFieldParser(CsvFilePath, Encoding.UTF8))
            {
                parser.TextFieldType = FieldType.Delimited;
                parser.SetDelimiters(",");

                // 헤더값을 List에 넣지 않기 위해 첫줄을 읽도록 한다.
                parser.ReadFields();
                while (!parser.EndOfData)
                {
                    string[] fields = parser.ReadFields();

                    User user = new User
                    {
                        Index = Int32.Parse(fields[0]),     // 번호
                        Name = fields[1],                   // 이름
                        Location = fields[2],               // 사는곳
                        Job = fields[3],                    // 직업
                    };

                    Items.Add(user);
                }
            }
        }

        /// <summary>
        /// 경로를 검증한다.
        /// true : 옳바른 경로이다.
        /// false : 부적절한 경로이다.
        /// </summary>
        private bool ValidateCsvFilePath()
        {
            if (string.IsNullOrWhiteSpace(CsvFilePath) ||
                !File.Exists(CsvFilePath))
            {
                return false;
            }
            return true;
        }
    }
}

 

이제 만든 ViewModel을 View에 연결시켜보도록 하겠습니다.

 

// MainWindow.xaml.cs
using ParseCSV.ViewModels;
using System.Windows;

namespace ParseCSV
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainWindowViewModel _viewModel;
        public MainWindow()
        {
            InitializeComponent();
            _viewModel = new MainWindowViewModel();
            this.DataContext = _viewModel;
        }
    }
}

 

MainWindow의 UI 코드(xaml)는 다음과 같습니다.

 

<!-- MainWindow.xaml -->
<Window x:Class="ParseCSV.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:local="clr-namespace:ParseCSV"
        mc:Ignorable="d"
        Title="ParseCSV" Height="450" Width="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="8*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <TextBlock Text="Csv File Path :" Margin="5" Width="80" VerticalAlignment="Center"/>
            <TextBox Cursor="Arrow" Focusable="False" Margin="5" Text="{Binding CsvFilePath}" Width="350" VerticalAlignment="Center" />
            <Button Content="파일 선택하기" Height="30" Width="100" Command="{Binding ChooseCsvFileCommand}"/>
        </StackPanel>
        <Grid Grid.Row="1">
            <ListView ItemsSource="{Binding Items}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="5">
                            <TextBlock>
                                <TextBlock.Text>
                                    <MultiBinding StringFormat="{}{0}. {1}은(는) {2}에 거주중이며 직업은 {3}입니다.">
                                        <Binding Path="Index"/>
                                        <Binding Path="Name"/>
                                        <Binding Path="Location"/>
                                        <Binding Path="Job"/>
                                    </MultiBinding>
                                </TextBlock.Text>
                            </TextBlock>
                        </Grid>
                    </DataTemplate>
                </ListView.ItemTemplate>                
            </ListView>
        </Grid>
    </Grid>
</Window>

 

해당 프로그램의 실행 결과는 다음과 같습니다.

 

 

전체 코드는 github.com/Hyo-Seong/CHashtag/tree/master/ParseCSV에서 확인하실 수 있습니다.

 

Hyo-Seong/CHashtag

https://chashtag.tistory.com/. Contribute to Hyo-Seong/CHashtag development by creating an account on GitHub.

github.com

 

 

긴 글 읽어주셔서 감사합니다.

도움이 되었길 바랍니다.

반응형