[C# WPF] Excel Import to Datagrid

우롱밀크티당도70·2023년 9월 21일
0

WPF

목록 보기
12/22
post-custom-banner

1. 배경

파일 탐색기에서 Excel 파일을 불러와 그 내용을 DataGrid에 보여주는 방법...
사실 구글링했을 때 방법이 안나오는건 아닌데 거의 코드 비하인드에 한 방식이기 때문에 MVVM 패턴으로 작성해봤다...근데 코드 비하인드에서의 작성이 아예 없지는 않아서 MVVM 패턴에는 위배되는 것 같다.😂


2. 개발환경

  • VisualStudio 2022 / WPF 애플리케이션(.NET Framework 4.7.2)

3. 내용

3-1. 할 일 정하기

https://velog.io/@yu_oolong/C-WPF-Behavior-InvokeCommandAction 와 같은 솔루션에서 진행한다.
id, pw를 manager로 입력하면 Menu4~6까지 보여주고 각 메뉴들은 Page4~6을 보여준다.
그 중 Page4에서 다음의 내용을 수행한다.

  • 1번에서 파일찾기 버튼을 눌러 파일탐색기에서 파일의 이름을 찾고
  • 2번에서 찾은 파일을 열어 DataGrid에 보여준다.

3-2. xaml 컨트롤 배치하기

참고로 UI는 이 분 화면을 보고 비슷하게 만들었다.

<Page x:Class="PracticeProject2.Views.Page4"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:PracticeProject2.Views"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="Page4"
      Background="White">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="100*"/>
            <RowDefinition Height="270*"/>
            <RowDefinition Height="80*"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Margin="10,0,10,0">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Label Content="파일 이름" />
                <TextBox x:Name="fileUrlTextBox" Width="450" Margin="10,0,10,0" />
                <Button Content="파일찾기"/>
            </StackPanel>
        </Grid>

        <Grid Grid.Row="1" Margin="10,0,10,0">
            <DataGrid x:Name="ExcelData">
                <DataGrid.Resources>
                    <Style TargetType="DataGridColumnHeader">
                        <Setter Property="HorizontalContentAlignment" Value="Center"/>
                    </Style>
                </DataGrid.Resources>
            </DataGrid>
        </Grid>

        <Grid Grid.Row="2" Margin="10,10,10,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200*"/>
                <ColumnDefinition Width="580*"/>
            </Grid.ColumnDefinitions>

            <Grid Grid.Column="0">
                <Button Content="들여오기" Width="100" Height="40" HorizontalAlignment="Left" />
            </Grid>

            <Grid Grid.Column="1" HorizontalAlignment="Right">
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                    <Label Content="Go to" />
                    <TextBox Width="30"/>
                    <Label Content="Page" />
                    <Button Content="Go" Margin="0,0,5,0"/>
                    <Button Content="Last Page" Margin="0,0,5,0"/>
                    <Button Content="Next Page" />
                    <Label Content="[total" />
                    <Label Content="1" />
                    <Label Content="page]" />
                    <Label Content="[current" />
                    <Label Content="1" />
                    <Label Content="page]" />
                </StackPanel>
            </Grid>
        </Grid>

    </Grid>
</Page>

3-2. 엑셀 파일만 찾을 수 있는 파일 탐색기

  1. ViewModel 폴더에 Page4ViewModel.cs를 생성한다.
  • RelayCommand, BaseViewModel 작성은 내용 참고

파일 찾기 버튼을 누르면 파일 탐색기가 열려야 한다.
파일 탐색기는 OpenFileDialog 클래스로 구현되는데 FileDialog.Filter 속성을 사용하여 확장자가 .xls, .xlsx인 엑셀 파일만 고를 수 있도록한다.

(Page4ViewModel.cs)

namespace PracticeProject2.ViewModels
{
    internal class Page4ViewModel : BaseViewModel
    {
        public ICommand SuchFileCommand { get; set; }

        string _fileUrl;

        public string FileUrl
        {
            get { return _fileUrl; }
            set
            {
                _fileUrl = value;
                OnPropertyChanged(nameof(FileUrl));
            }
        }

        public Page4ViewModel()
        {
            SuchFileCommand = new RelayCommand(SuchFile);
        }

        private void SuchFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "문서 파일 (*.xls,*xlsx)|*.xls;*.xlsx";
            if (dialog.ShowDialog() == true)
            {
                FileUrl = dialog.FileName;
            }
        }
        
    }
}
  1. Page4.xaml로 돌아와서 Button과 TextBox에 Binding하기
		<Grid Grid.Row="0" Margin="10,0,10,0">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Label Content="파일 이름" />
                <TextBox x:Name="fileUrlTextBox" Width="450" Margin="10,0,10,0" Text="{Binding FileUrl}"/>
                <Button Content="파일찾기" Command="{Binding SuchFileCommand}"/>
            </StackPanel>
        </Grid>

실행해보면 파일 찾기 버튼을 눌러 파일 탐색기를 띄웠을 때 확장자가 xls, xlsx인 파일만 보이고, 파일을 고르면 해당 파일의 이름이 TextBox에 보여진다.

3-3. Excel import to DataGrid

다음은 들여오기 버튼을 눌러 파일 탐색기에서 고른 엑셀 파일의 내용을 DataGrid에 보여주어야 한다.
Command를 작성하기 전에 솔루션용 NuGet 패키지 관리자에서 Microsoft.Office.Interop.Excel을 다운로드 받는다.

다운로드 후 추가로 참조 관리자-Com에서 Microsoft Office 16.0 Object Library를 체크>확인한다.

이후 Page4ViewModel.cs 에서 using문을 선언한다.
using Excel = Microsoft.Office.Interop.Excel

SuchFile() 메소드에서 파일 탐색기를 열어 가져온 파일의 이름을 FileUrl에 초기화했다.
ImportFile() 메소드는 DataTable을 생성하여 SuchFile() 메소드에서 초기화한 FileUrl의 엑셀 파일의 내용으로부터 데이터를 저장하고, Page4에 배치된 x:Name이 ExcelData인 DataGrid에 보여준다.

불러올 엑셀 파일의 내용은 이렇다.

(Page4ViewModel.cs)

using Microsoft.Win32;
using PracticeProject2.Commands;
using PracticeProject2.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
using Excel = Microsoft.Office.Interop.Excel;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Data;
using System.Diagnostics.Eventing.Reader;

namespace PracticeProject2.ViewModels
{
    internal class Page4ViewModel : BaseViewModel
    {
        public ICommand SuchFileCommand { get; set; }
        public ICommand ImportFileCommand { get; set; }

        string _fileUrl;

        public string FileUrl
        {
            get { return _fileUrl; }
            set
            {
                _fileUrl = value;
                OnPropertyChanged(nameof(FileUrl));
            }
        }

        public Page4ViewModel()
        {
            SuchFileCommand = new RelayCommand(SuchFile);
            ImportFileCommand = new RelayCommand(ImportFile);
        }

        private void SuchFile()
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "문서 파일 (*.xls,*xlsx)|*.xls;*.xlsx";
            if (dialog.ShowDialog() == true)
            {
                FileUrl = dialog.FileName;
            }
        }

        private void ImportFile()
        {
            Page4 page4 = Page4.GetInstance();
            
            DataTable dt = new DataTable(); //엑셀 데이터를 위한 컨테이너
            DataRow dr;

            int row = 0;
            int column = 0;

            Excel.Application excelApp = null;
            Excel.Workbook workbook = null;
            Excel.Worksheet worksheet = null;

            try
            {
                excelApp = new Excel.Application();                             //엑셀 어플리케이션 생성
                workbook = excelApp.Workbooks.Open(FileUrl);                    //워크북 열기
                worksheet = workbook.Worksheets.get_Item(1) as Excel.Worksheet; //엑셀 첫 번째 워크시트 가져오기

                Excel.Range range = worksheet.UsedRange;                        //사용중인 셀 범위 가져오기
                
                //첫 행을 제목으로
                for (column = 1; column <= range.Columns.Count; column++)
                {
                    //header = (range.Cells[1, column] as Excel.Range).Value2.ToString();
                    string str = (string)(range.Cells[1, column] as Excel.Range).Value2;
                    dt.Columns.Add(str, typeof(string));
                }

                //내용 데이터 가져오기
                int rowCounter;
                for (row = 2; row <= range.Rows.Count; row++)
                {
                    dr = dt.NewRow();
                    rowCounter = 0;
                    for (column = 1; column <= range.Columns.Count; column++)
                    {
                        if (range.Cells[row, column] != null && range.Cells[row, column].Value2 != null)
                        {
                            dr[rowCounter] = range.Cells[row, column].Value2.ToString();
                        }
                        else
                        {
                            dr[row] = "";
                        }
                        rowCounter++;
                    }
                    dt.Rows.Add(dr);
                }

                page4.ExcelData.ItemsSource = dt.DefaultView;

                workbook.Close();
                excelApp.Quit();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

        }
    }
}

이때 Page4의 GetInstance() 메소드는 Page4.xaml.cs에서 작성해주어야 한다.
GetInstance는 싱글톤 패턴으로 작성한 Page4의 객체이다.

(+ 2023.09.26 날짜 형식 추가)
네 번째 컬럼이 '날짜'여서 if(column=4) 일때...
셀의 내용(string)->double로 형 변환하여 날짜 형식으로 행에 추가한다.

				//내용 데이터 가져오기
                int rowCounter; //This variable is used for row index number
                for (row = 2; row <= range.Rows.Count; row++)
                {
                    dr = dt.NewRow(); //assign new row to DataTable
                    rowCounter = 0;
                    for (column = 1; column <= range.Columns.Count; column++) //Loop for available column of excel data
                    {
                        //check if cell is empty
                        if (range.Cells[row, column] != null && range.Cells[row, column].Value2 != null)
                        {
                            if (column == 4) //string to double 날짜 형식 변환
                            {
                                double val = Double.Parse(range.Cells[row, column].Value2.ToString());
                                DateTime conv = DateTime.FromOADate(val);
                                dr[rowCounter] = conv.ToString("yyyy/MM/dd");
                            }
                            else
                            {
                                dr[rowCounter] = range.Cells[row, column].Value2.ToString();
                            }
                        }
                        else
                        {
                            dr[row] = "";
                        }
                        rowCounter++;
                    }
                    dt.Rows.Add(dr);
                }

그럼 이런식으로...날짜 컬럼의 내용이 다섯자리 숫자가 아니라 보통 날짜 형식으로 나온다.

(Page4.xaml.cs)

namespace PracticeProject2.Views
{
    /// <summary>
    /// Page4.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class Page4 : Page
    {
        private static Page4 _Instance = null;

        public static Page4 GetInstance()
        {
            if (_Instance == null || !_Instance.IsLoaded)
            {
                _Instance = new Page4();
            }

            return _Instance;
        }

        public Page4()
        {
            InitializeComponent();

            _Instance = this;
        }
    }
}

위의 내용까지 코드 작성을 마쳤다면 Page4.xaml에서 들여오기 버튼에 Command를 Binding 한다.

(Page4.xaml)

	<Grid Grid.Column="0">
		<Button Content="들여오기" Width="100" Height="40" HorizontalAlignment="Left" Command="{Binding ImportFileCommand}"/>
	</Grid>

4. 결과


5. 참조

profile
안뇽하세용
post-custom-banner

0개의 댓글