파일 탐색기에서 Excel 파일을 불러와 그 내용을 DataGrid에 보여주는 방법...
사실 구글링했을 때 방법이 안나오는건 아닌데 거의 코드 비하인드에 한 방식이기 때문에 MVVM 패턴으로 작성해봤다...근데 코드 비하인드에서의 작성이 아예 없지는 않아서 MVVM 패턴에는 위배되는 것 같다.😂
https://velog.io/@yu_oolong/C-WPF-Behavior-InvokeCommandAction 와 같은 솔루션에서 진행한다.
id, pw를 manager로 입력하면 Menu4~6까지 보여주고 각 메뉴들은 Page4~6을 보여준다.
그 중 Page4에서 다음의 내용을 수행한다.
참고로 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>
파일 찾기 버튼을 누르면 파일 탐색기가 열려야 한다.
파일 탐색기는 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;
}
}
}
}
<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에 보여진다.
다음은 들여오기 버튼을 눌러 파일 탐색기에서 고른 엑셀 파일의 내용을 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>