안녕하세요 정말 오랜만에 다시 개발 글을 작성하는 것 같습니다. 🤣 이번 포스트에서는 파일 입출력(FILE I/0)을 이용해서 간단한 다이어리를 만들어 보려고 합니다. 만들게 된 계기는 교내 교육 봉사동아리 Ducami에서 중학생들을 대상으로 수업할 간단한 토이 프로젝트로 DataBase를 연동하여 다이어리를 만들려고 하였습니다.
그렇지만 처음 접하는 친구들에게는 어려울 것 같아서 파일 입출력을 이용한 SimpleDiary를 제작하였습니다. 그렇지만 이 마저도 어려울 것 같아서 지금은 ToDoList를 제작하고 있습니다. 그래서 이번 시간에는 파일 입출력을 이용하여 제작한 Simple Diary를 소개해 드리도록 하겠습니다.🙌
1. ComDef.cs ✨
사실 파일 입출력에 대해서는 알고는 있었지만 아주 간단한 것만 할 줄 알았기에 만들면서 더 배우게 되었던 것 같습니다. 안되거나 모르는 것이 있을 때마다 구글링을 통해 찾아보고 해결하였습니다.🤦♀️ UI적인 부분은 크게 신경 쓰지 않고 만들었습니다.
우선 저는 크게 두 개의 Window를 사용하였습니다. 프로젝트 생성 후 View라는 폴더를 만들고 그 안에 MainWindow와 일기를 쓰기 위한 WriteDiaryWindow를 하나 더 생성하였습니다. 그러고 나서 Common이라는 폴더를 만든 후 ComDef 클래스를 하나 만들었습니다. 그리고 이 안에 두 가지 정적 변수를 만들었습니다.
1
2
3
4
5
6
7
8
|
namespace Making_A_Diary.Common
{
public class ComDef
{
public static string file = "";
public static string folderPath = @"C:\diaryFolder";
}
}
|
cs |
첫 번째 file은 파일의 이름을 저장하기 위한 것이고, folderPath은 말 그대로 일기를 저장하기 위한 경로입니다.
2. Diary.cs ✨
두 번째 작업으로는 Model이라는 폴더를 만들고 Diary 클래스를 하나 만들었습니다. 이 클래스는 말 그대로 일기를 작성하기 위한 DiaryTitle(일기 제목) Property를 하나 가지고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
using System;
namespace Making_A_Diary.Model
{
public class Diary : ICloneable
{
public string DiaryTitle { get; set; }
public object Clone()
{
return new Diary
{
DiaryTitle = this.DiaryTitle
};
}
}
}
|
cs |
3. DiaryViewModel.cs ✨
세 번째 작업으로는 ViewModel이라는 폴더를 만들고 DiaryViewModel.cs라는 클래스를 하나 만들었습니다. 이 클래스는 일기를 작성하기 위해 폴더를 만들거나 일기를 저장, 삭제... 등 모든 기능에 대한 코드를 작성하였습니다. 먼저 필요한 Property부터 제작해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public delegate void OnWriteDiaryResultReceivedHandler();
public event OnWriteDiaryResultReceivedHandler OnWriteDiaryResultReceived;
#region Property
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private List<Diary> diaryItems = new List<Diary>();
public List<Diary> DiaryItems
{
get => diaryItems;
set
{
diaryItems = value;
NotifyPropertyChanged(nameof(DiaryItems));
}
}
private string openDiaryContent;
public string OpenDiaryContent
{
get => openDiaryContent;
set
{
openDiaryContent = value;
NotifyPropertyChanged(nameof(OpenDiaryContent));
}
}
private string writeDiaryContent;
public string WriteDiaryContent
{
get => writeDiaryContent;
set
{
writeDiaryContent = value;
NotifyPropertyChanged(nameof(WriteDiaryContent));
}
}
public ICommand DiaryOpenCommand { get; set; }
public ICommand DiaryWriteCommand { get; set; }
#endregion
public DiaryViewModel()
{
InitCommand();
}
#region Initialize
private void InitCommand()
{
DiaryOpenCommand = new DelegateCommand(OnOpenDiary);
DiaryWriteCommand = new DelegateCommand(OnDiaryWrite);
}
#endregion
#region Create Directory
public void IsDirectoryExsist()
{
DirectoryInfo directory = new DirectoryInfo(ComDef.folderPath);
if (directory.Exists == false)
{
directory.Create();
}
}
#endregion
|
cs |
제일 상단의 OnWriteDiaryResultReceived delegate&event는 일기 저장을 누른 후 그다음 과정을 전달해주기 위하여 만들었습니다. 그리고 작성한 일기의 목록을 저장하기 위한 Diary를 가지고 있는 DiaryItems 리스트, 일기의 내용을 담기 위한 OpenDiaryContent, WriteDiaryContent 프로퍼티가 있습니다. 마지막으로 Button의 Command에 바인딩하기 위한 ICommand(DiaryOpenCommand, DiaryWriteCommand) 두 가지를 만들어 주었습니다.
그리고 제일 중요한 기능에 대한 부분은 총 4가지 순서로 알려드리겠습니다. 일기를 저장하기 위한 디렉터리가 존재하는지 확인하기 위한 IsDirectoryExsist(), 일기를 열기 위한 OnOpenDiary(), 일기를 쓰기 위한 OnDiaryWrite(), 일기의 목록들을 얻어오기 위한 GetDiaryList()가 있습니다.
1. IsDirectoryExsist() 🏹
이 메서드는 크게 어려운 것 없이 이전에 만들어 두었던 ComDef.cs의 folderPath에 폴더가 존재하지 않으면 만들어주는 기능입니다.
1
2
3
4
5
6
7
8
9
10
11
|
#region Create Directory
public void IsDirectoryExsist()
{
DirectoryInfo directory = new DirectoryInfo(ComDef.folderPath);
if (directory.Exists == false)
{
directory.Create();
}
}
#endregion
|
cs |
2. OnOpenDiary() 🎢
다음은 저장된 일기를 열어서 내용을 얻어오기 위한 OnOpenDiary() 메서드입니다. OnOpenDiary() 메서드 밑의 OpenFile_FileOk 이벤트는 일기를 불러올 때 일기가 저장된 특정 디렉터리에서만 불러오도록 하기 위하여 만들었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#region Diary Open
private void OnOpenDiary()
{
try
{
OpenFileDialog openFile = new OpenFileDialog();
{
openFile.Filter = "Text File | *.txt";
openFile.FileOk += OpenFile_FileOk;
// 존재하지 않는 파일 이름을 지정할 때 대화 상자에 경고가 표시되는지를 나타내는 값을 가져오거나 설정.
openFile.CheckFileExists = true;
// 존재하지 않는 경로를 지정할 때 대화 상자에 경고가 표시되는지를 나타내는 값을 가져오거나 설정.
openFile.CheckPathExists = true;
if (openFile.ShowDialog().GetValueOrDefault())
{
ComDef.file = openFile.FileName;
using (StreamReader sr = new StreamReader(ComDef.file))
{
OpenDiaryContent = sr.ReadToEnd();
}
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
private void OpenFile_FileOk(object sender, CancelEventArgs e)
{
// File Path
var filePath = (sender as OpenFileDialog).FileName;
// FileName
var fileName = (sender as OpenFileDialog).SafeFileName;
filePath = filePath.Replace(fileName, "");
filePath = filePath.Remove(filePath.Length - 1);
if (!(filePath.Replace(fileName, "") == ComDef.folderPath))
{
MessageBox.Show("일기를 보고싶을 때 " + ComDef.folderPath + "이 위치에서 불러와 주세요.");
e.Cancel = true;
}
}
#endregion
|
cs |
3. OnDiaryWrite() 🎻
다음은 일기를 작성하고 저장할 때에 사용되는 OnDiaryWrite() 메서드입니다. OnOpenDiary() 메서드와 마찬가지로 SaveFile_FileOk()라는 메서드가 있는데 여기서 FileOk() 메서드는 일기를 저장할 때 특정 디렉터리에만 저장하기 위해서 만들었습니다. 또한 일기를 저장한 후 일기 작성을 위한 Window를 닫아주어야 합니다. 그러므로 아까 만들어 두었던 OnWriteDiaryResultReceived() delegate&event를 사용하여 MainWindow.xaml.cs로 연결해 줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
#region Diary Write
private void OnDiaryWrite()
{
if(WriteDiaryContent != null && WriteDiaryContent.Length > 0)
{
try
{
if (ComDef.file == "")
{
SaveFileDialog saveFile = new SaveFileDialog();
saveFile.Filter = "Text File | *.txt";
saveFile.FileOk += SaveFile_FileOk;
if (saveFile.ShowDialog().GetValueOrDefault())
{
ComDef.file = saveFile.FileName;
}
}
using (StreamWriter sw = new StreamWriter(ComDef.file, false, Encoding.UTF8))
{
sw.Write(writeDiaryContent);
ComDef.file = "";
}
OnWriteDiaryResultReceived?.Invoke();
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
else
{
MessageBox.Show("내용을 입력해 주세요!");
return;
}
}
private void SaveFile_FileOk(object sender, CancelEventArgs e)
{
if (!(Path.GetDirectoryName((sender as SaveFileDialog).FileName) == ComDef.folderPath))
{
MessageBox.Show("일기를 저장할 때 " + ComDef.f
|
4. GetDiaryList() 🎪
다음을 일기의 목록을 가져오기 위한 GetDiaryList() 메서드입니다. 이 메서드는 디렉터리로부터 존재하는 파일들을 불러와서 이전에 만들어 두었던 DiaryItems에 Diary를 담아줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#region GetDiaryList
public void GetDiaryList()
{
if(DiaryItems.Count > 0)
{
DiaryItems.Clear();
}
DirectoryInfo directory = new DirectoryInfo(ComDef.folderPath);
List<string> fileList = new List<string>();
foreach (FileInfo file in directory.GetFiles())
{
if (file.Extension.ToLower().CompareTo(".txt") == 0)
{
string fileNameOnly = file.Name.Substring(0, file.Name.Length - 4);
fileList.Add(fileNameOnly);
}
}
Diary diary = new Diary();
foreach(var item in fileList)
{
diary.DiaryTitle = item;
DiaryItems.Add((Diary)diary.Clone());
}
}
#endregion
|
cs |
4. MainWindow.xaml & MainWindow.xaml.cs ✨
여기까지 따라오셨다면 남은 것은 MainWindow와 WriteDiaryWindow의 UI 작업과 비하인드 코드에서 ViewModel의 기능과 Xaml의 조화를 이루어주면 모든 작업이 완료됩니다. 그렇다면 먼저 MainWindow와 WriteDiaryWindow의 간단한 UI 작업부터 해보도록 할게요. MainWindow부터 보겠습니다 🧏♂️
그전에 완성된 UI를 먼저 봅시다. 원래의 의도는 일기 쓰기 버튼과 일기 불러오기 버튼 두 가지를 배치하려고 하였습니다. 그렇지만 아래의 Diary 목록 영역에서 일기들의 목록을 나타내고 특정 일기를 눌렀을 때 하늘색 영역에 일기의 내용을 뿌려주려고 하다 보니 굳이 필요가 없게 되어서 주석처리를 하였습니다.
먼저 일기 쓰기 버튼을 클릭하게 되면 WriteDiaryWindow 가 나타나고 여기에서 일기를 쓰고 저장할 수 있습니다. 그 아래인 Diary목록 영역에서는 ListView를 이용하여 전체 일기 목록을 나타냅니다. 목록의 옆에 삭제 버튼을 두어 특정 일기를 삭제할 수 있도록 하였습니다. 마지막으로 파란색 영역에서는 특정 일기를 클릭하였을 때 그 일기의 내용을 나타내어 줍니다. 따라서 ListView에 SelectionChanged 이벤트를 달아주었습니다.
먼저 MainWindow.xaml의 UI 코드를 보도록 하겠습니다. 디자인에는 크게 신경 쓰지 않았기에 필요한 것만 작성하였습니다. Xaml 코드는 크게 볼 것 없이 일기 쓰기 버튼에 Click 이벤트를 만들어 주었고 그 아래의 Diary 목록에는 ListView를 사용하였습니다.
일기 목록을 나타내 주어야 하므로 ItemsSource에 DiaryItems를 바인딩합니다. 그리고 특정 일기를 눌렀을 때 일기 내용을 나타내 주어야 하므로 SelectionChanged 이벤트를 달아줍니다. 또한 삭제를 할 수 있어야 하므로 삭제 버튼을 만들고 클릭이벤트를 달아줍니다.
마지막으로 하늘색 영역에 일기 내용을 담을 TextBox를 만들고 OpenDiaryContent를 바인딩합니다. MainWindow의 Xaml코드는 여기까지 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
<Grid Background="#F5F7FA">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="9*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="6*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!--<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>-->
<!--<Grid Grid.Column="0" Margin="5">-->
<Grid Margin="5">
<Button Style="{StaticResource commonBtnStyle}"
Click="btn_WriteDiary_Click">
일기 쓰기
</Button>
</Grid>
<!--<Grid Grid.Column="1" Margin="5">
<Button Style="{StaticResource commonBtnStyle}"
Command="{Binding DiaryOpenCommand}">
저장된 일기 열기
</Button>
</Grid>-->
</Grid>
<Grid Grid.Row="1">
<Label Content="Diary 목록" Width="570"
HorizontalAlignment="Center" HorizontalContentAlignment="Center"
FontSize="20" FontFamily="나눔스퀘어_ac" FontWeight="SemiBold"/>
</Grid>
</Grid>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="6*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<ListView x:Name="lvDiaryList" Margin="10" ItemsSource="{Binding DiaryItems}"
SelectionChanged="lvDiaryList_SelectionChanged" Background="#5BE7C4">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<TextBlock Text="{Binding DiaryTitle}" TextWrapping="Wrap"
FontSize="20" FontFamily="나눔스퀘어_ac" VerticalAlignment="Center"/>
</Grid>
<Grid Grid.Column="1">
<Button Margin="5"
Background="Transparent"
BorderBrush="White"
FontFamily="나눔스퀘어_ac"
FontSize="20"
VerticalAlignment="Center"
Click="btn_DiaryDelete_Click">삭제하기</Button>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<Grid Grid.Row="1">
<TextBox x:Name="tbDiaryContent" Text="{Binding OpenDiaryContent}" TextWrapping="Wrap"
FontFamily="나눔스퀘어_ac" FontSize="20"
Margin="10" Background="#50C1E9"/>
</Grid>
</Grid>
</Grid>
|
cs |
다음으로는 MainWIndoow.xaml.cs(비하인드) 코드를 작성해 보도록 하겠습니다. Window 객체는 WriteDiaryWindow에서 일기 저장 후 이 윈도우를 종료시키기 위하여 만들었습니다. ICollectionView는 일기를 저장하거나 삭제 후 일기 목록의 동기화를 위해서 사용하였습니다. 나머지는 크게 어려운 부분이 없으므로 자세한 설명은 생략하도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
public partial class MainWindow : Window
{
Window window;
ICollectionView view = CollectionViewSource.GetDefaultView(App.diaryViewModel.DiaryItems);
public MainWindow()
{
InitializeComponent();
this.DataContext = App.diaryViewModel;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
App.diaryViewModel.IsDirectoryExsist();
App.diaryViewModel.GetDiaryList();
App.diaryViewModel.OnWriteDiaryResultReceived += DiaryViewModel_OnWriteDiaryResultReceived;
}
private void DiaryViewModel_OnWriteDiaryResultReceived()
{
window.Close();
App.diaryViewModel.GetDiaryList();
view.Refresh();
}
private void btn_WriteDiary_Click(object sender, RoutedEventArgs e)
{
WriteDiaryWindow writeDiaryWindow = new WriteDiaryWindow();
App.diaryViewModel.WriteDiaryContent = null;
window = writeDiaryWindow;
writeDiaryWindow.ShowDialog();
}
private void lvDiaryList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(lvDiaryList.SelectedItem != null)
{
try
{
string path = "C:\\diaryFolder\\" + (lvDiaryList.SelectedItem as Diary).DiaryTitle + ".txt";
tbDiaryContent.Text = File.ReadAllText(path);
App.diaryViewModel.OpenDiaryContent = File.ReadAllText(path);
}
catch (Exception error)
{
Debug.WriteLine(error.Message);
}
}
else
{
return;
}
}
private void btn_DiaryDelete_Click(object sender, RoutedEventArgs e)
{
if(lvDiaryList.SelectedItem == null)
{
MessageBox.Show("삭제할 목록을 선택해 주세요!");
}
else
{
try
{
string path = "C:\\diaryFolder\\" + (lvDiaryList.SelectedItem as Diary).DiaryTitle + ".txt";
if (MessageBox.Show("정말 삭제하시겠습니까?", (lvDiaryList.SelectedItem as Diary).DiaryTitle, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
File.Delete(path);
App.diaryViewModel.GetDiaryList();
view.Refresh();
}
}
catch (Exception error)
{
Debug.WriteLine(error.Message);
}
}
}
}
|
cs |
자 이제 마지막으로 WriteDiaryWindow.xaml과 WriteDiaryWindow.xaml.cs를 작성하면 모든 작업이 완료됩니다 😆 먼저 UI부터 보도록 하겠습니다. 제일 상단에는 DatePicker를 놔두어서 작성한 날짜를 저장하려고 하였으나, 굳이 필요 없을 것 같아서 더 이상의 작업은 하지 않았습니다. 그 아래에는 작성하고자 하는 일기의 내용을 저장하기 위한 TextBox를 두었습니다. 마지막으로 일기를 저장하기 위한 버튼을 두었습니다.
그렇다면 이제 코드를 한번 봐볼까요? 여기서는 일기에 작성한 내용이 필요하므로 WriteDiaryContent로 바인딩해두었고 일기 저장버튼에는 Command에 DiaryWriteCommand를 바인딩하여 아까 전에 ViewModel에서 만든 OnWriteDiary() 메서드가 동작하게 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<Grid Background="#a5dff9">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="8*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid>
<DatePicker Margin="10" FontSize="20" FontFamily="나눔스퀘어_ac"
Width="150" Height="30"/>
</Grid>
</Grid>
<Grid Grid.Row="1">
<TextBox x:Name="tbDiaryContent" Margin="10"
Text="{Binding WriteDiaryContent}" TextWrapping="Wrap"
FontFamily="나눔스퀘어_ac" FontSize="20"/>
</Grid>
<Grid Grid.Row="2">
<Button Command="{Binding DiaryWriteCommand}"
Style="{StaticResource commonBtnStyle}"
Margin="10">
일기 저장
</Button>
</Grid>
</Grid>
|
cs |
WriteDiaryWindow의 비하인드 코드는 별다른 작성없이 ViewModel의 코드를 사용할 수 있도록 DataContext만 지정해주면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public partial class WriteDiaryWindow : Window
{
public WriteDiaryWindow()
{
InitializeComponent();
Loaded += WriteDiaryWindow_Loaded;
}
private void WriteDiaryWindow_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = App.diaryViewModel;
}
}
|
cs |
이렇게 모든 작업을 끝마치게 되었습니다. 이제 실행하였을 때의 UI와 시연 영상을 통해 SimpleDiary를 보도록 하겠습니다.
위의 이미지는 SimpleDiary를 실행하였을 때의 이미지입니다. 어떤가요? UI는 정말 간단하게 구성해서 예쁘지는 않습니다. 😅 이제는 시연 영상을 보도록 하겠습니다.
이상 오늘의 포스팅은 마치도록 하겠습니다. 모든 소스코드는 아래의 제 GitHub 주소에 남겨두도록 하겠습니다. 감사합니다 😊🎉
https://github.com/KyungHoon0126/Diary
'Development > Toy Projects' 카테고리의 다른 글
[프로젝트] XKCD Comic Viewer 만들기 👀 (0) | 2020.08.20 |
---|---|
[프로젝트] 심심이(SimSimi) API를 이용한 채팅 프로그램 만들기 😀🧨 (하편) (0) | 2020.06.08 |
[프로젝트] 심심이(SimSimi) API를 이용한 채팅 프로그램 만들기 😀🧨 (상편) (3) | 2020.06.07 |
[프로젝트] 급식 데이터를 이용해 간단한 윈도우 응용 프로그램 만들어보기🔥 #3 (0) | 2020.05.14 |
[프로젝트] 데이터 모델 제작 & JsonConvert.DeserializeObject를 이용한 객체화 🔥 #2 (1) | 2020.05.12 |
댓글