이제 지금까지 만든 Paint 프로그램에 파일 기능을 추가해 보겠습니다.
여기서는 두가지 파일에 대해 알아보겠습니다. 하나는 bmp, gif, jpg등 이미지 파일이고, 다른 하나는 일반 데이터 저장 파일입니다.
이미지 파일로 저장하기 위해 파일 메뉴에 Export를 추가합니다.
그런 다음 Export를 선택하여 생성된 함수에서 다음과 같이 입력하면 이미지 파일로 저장이 가능하게 됩니다.
private void exportToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "Image Files(*.bmp;*.gif;*.jpg)|*.bmp;*.gif;*.jpg";
if (dlg.ShowDialog() != DialogResult.OK)
return;
Bitmap bitmap = new Bitmap(imageSize.Width, imageSize.Height);
this.objList.Draw(Graphics.FromImage(bitmap));
bitmap.Save(dlg.FileName);
}
이미지 저장은 아주 간단합니다.
Bitmap bitmap = new Bitmap(imageSize.Width, imageSize.Height);
이미지 크기에 맞추어 생성합니다.
this.objList.Draw(Graphics.FromImage(bitmap));
지난시간 설명한 그리는 루틴입니다. 넘어가는 인자에 이미지로부터 Graphics를 만들어 넘겨주게 되면 이미지에 그림을 그리게됩니다.
bitmap.Save(dlg.FileName);
파일로 저장합니다. bmp, gif, jpg등 모든 이미지 파일이 지원됩니다.
이제 일반 데이터로 저장을 해보겠습니다.
저장 메뉴와 새이름으로 저장 메뉴을 선택했을 때 실행되는 함수를 만들어 다음과 같이 Save()와 SaveAs 함수를 호출하도록 구성합니다.
void Save()
{
if (string.IsNullOrEmpty(this.strFileName))
{
SaveAs();
return;
}
SaveReal(this.strFileName);
}
void SaveAs()
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "*.dat|*.dat";
if (dlg.ShowDialog() != DialogResult.OK)
return;
SaveReal(dlg.FileName);
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
Save();
}
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveAs();
}
Save함수는 파일이름이 설정(열기 및 저장 후 설정함)되지 않았으면 SaveAs를 호출하도록 하고 설정되어 있으면 SaveReal(이후 설명)함수를 호출하도록 구성하였습니다.
SaveAs함수는 무조건 저장 대화 상자를 띄워 파일이름을 얻도록 하여 SaveReal을 호출하도록 구성하였습니다.
이제 실제 저장하는 SaveReal함수에 대해 알아보자
void SaveReal(string strFileName)
{
Stream stream = null;
try
{
stream = new FileStream(strFileName, FileMode.OpenOrCreate);
BinaryWriter file = new BinaryWriter(stream, Encoding.Default);
this.objList.Write(file);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return;
}
finally
{
if (stream != null)
stream.Close();
}
this.strFileName = strFileName;
this.bModified = false;
}
Stream stream = new FileStream(strFileName, FileMode.OpenOrCreate);
먼저 파일 스트림을 얻습니다. FileMode는 OpenOrCreate로 설정합니다.
BinaryWriter file = new BinaryWriter(stream, Encoding.Default);
Stream으로부터 실제 파일을 쓰기 위한 BinaryWriter를 얻습니다.
this.objList.Write(file);
objList의 실제 파일에 내용을 저장하는 루틴을 구현(이후에 설명)한 함수 Write를 호출합니다.
stream.Close();
Stream을 닫습니다.
나머지 부분은 모두 예외처리 및 기타 기능(파일 이름 설정 및 수정 Flag를 fasle로 설정)을 위해 입력한 항목입니다.
이제 실제 파일 내용을 저장하기 위한 함수를 구현해보겠습니다. ObjList.cs에 다음과 같이 함수를 추가합니다.
public void Write(BinaryWriter file)
{
int nCount = this.objList.Count;
DrawObj obj;
string strType;
file.Write(this.strVersion);
file.Write(pBuffer);
file.Write(nCount);
for (int i = 0; i < nCount; i++)
{
obj = (DrawObj)this.objList[i];
strType = obj.GetType().Name;
file.Write(strType);
obj.Write(file);
}
}
첫 번째로 버전(strVersion)을 저장합니다. 향후 파일이 변경되면 이 버전 정보로 과거파일을 읽을 수 있도록 구성합니다.
pBuffer를 저장합니다. pBuffer는 생성자에서 전체 데이터를 0으로 지정합니다.
string strVersion = "1.0";
const int BUFFER_SIZE = 30;
Byte[] pBuffer = new Byte[BUFFER_SIZE];
public ObjList()
{
for (int i = 0; i < BUFFER_SIZE; i++)
{
pBuffer[i] = 0;
}
}
향후 일부 내용이 추가될 경우를 대비하여 버전 변경없이 사용할 수 있도록 구성하기 위해서 일부 버퍼을 두는 것이 좋습니다.
strType = obj.GetType().Name;
strType에 클래스 이름이 넘어 옵니다. 읽을 때 그리기 종류를 구분하기 위해 저장합니다.
이제 DrawObj.cs에 저장 루틴을 구현해 보겠습니다.
public void Write(BinaryWriter file)
{
file.Write(rectPos.Left);
file.Write(rectPos.Top);
file.Write(rectPos.Width);
file.Write(rectPos.Height);
int nColor = this.colorLine.ToArgb();
file.Write(nColor);
nColor = this.colorFace.ToArgb();
file.Write(nColor);
file.Write(this.pBuffer);
}
구성을 보면 각 클래스에 필요한 Write를 만들고 그 함수를 호출하여 구성하도록 합니다.
만약 Class별로 저장 내용이 다르다면, Write함수를 Virtual로 선언하고 개별 Class에서 override하여 작성(Draw함수 참조)하여 구성합니다.
이제 읽기 함수를 구현해 보겠습니다.
void LoadReal(string strFileName)
{
Stream stream = null;
try
{
stream = new FileStream(strFileName, FileMode.Open);
BinaryReader file = new BinaryReader(stream, Encoding.Default);
this.objList.Read(file);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return;
}
finally
{
if (stream != null)
stream.Close();
}
this.strFileName = strFileName;
this.bModified = false;
Invalidate();
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "*.dat|*.dat";
if (dlg.ShowDialog() != DialogResult.OK)
return;
LoadReal(dlg.FileName);
}
읽기 함수는 구조적인 면에서 쓰기와 비슷하므로 세부적으로 설명하지 않을 것입니다.
ObjList.cs에 다음과 같이 함수를 추가합니다.
public void Read(BinaryReader file)
{
DrawObj obj;
string strType;
this.objList.Clear();
string strVersion = file.ReadString();
if (strVersion != this.strVersion)
{
MessageBox.Show("파일 Format Error");
return;
}
file.Read(this.pBuffer, 0, BUFFER_SIZE);
int nCount = file.ReadInt32();
for (int i = 0; i < nCount; i++)
{
strType = file.ReadString();
if (string.IsNullOrEmpty(strType) == true)
break;
if (strType == "DrawLine")
obj = new DrawLine();
else if (strType == "DrawRect")
obj = new DrawRect();
else if (strType == "DrawEllipse")
obj = new DrawEllipse();
else
break;
obj.Read(file);
Add(obj);
}
}
DrawObj.cs에 다음과 같이 함수를 추가합니다.
public void Read(BinaryReader file)
{
int nLeft = file.ReadInt32();
int nTop = file.ReadInt32();
int nWidth = file.ReadInt32();
int nHeight = file.ReadInt32();
this.rectPos = new Rectangle(nLeft, nTop, nWidth, nHeight);
int nColor = file.ReadInt32();
this.colorLine = Color.FromArgb(nColor);
nColor = file.ReadInt32();
this.colorFace = Color.FromArgb(nColor);
file.Read(this.pBuffer, 0, BUFFER_SIZE);
}
예제 프로그램 다운로드
'C#' 카테고리의 다른 글
C#11. DLL과 웹서비스 (0) | 2022.03.08 |
---|---|
C#10. MDI 및 인쇄하기 (0) | 2022.03.08 |
C#08. 메모리, 마우스 및 스크롤 (0) | 2022.03.08 |
C#07. 대화상자 (0) | 2022.03.08 |
C#06. GDI+ 및 깜박임 문제 해결 (0) | 2022.03.08 |