diff --git a/MySolution/ToDoApp/Context/ToDoContext.cs b/MySolution/ToDoApp/Context/ToDoContext.cs
index cc61367..6d89af6 100644
--- a/MySolution/ToDoApp/Context/ToDoContext.cs
+++ b/MySolution/ToDoApp/Context/ToDoContext.cs
@@ -18,7 +18,5 @@ namespace ToDoApp.Context
optionsBuilder.UseMySQL(@"Server=peacecloud.synology.me;Port=3307;Database=Study;Uid=study;Pwd=Study1234!;");
base.OnConfiguring(optionsBuilder);
}
-
-
}
}
diff --git a/MySolution/ToDoApp/MainWindow.xaml b/MySolution/ToDoApp/MainWindow.xaml
index 277b2dc..3ce50d4 100644
--- a/MySolution/ToDoApp/MainWindow.xaml
+++ b/MySolution/ToDoApp/MainWindow.xaml
@@ -3,10 +3,87 @@
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:ToDoApp"
+ xmlns:local="clr-namespace:ToDoApp" xmlns:subcontrol="clr-namespace:ToDoApp.SubControl"
mc:Ignorable="d"
- Title="MainWindow" Height="450" Width="800">
+ Title="To Do" Height="800" Width="1000" WindowStartupLocation="CenterScreen"
+ Icon="./Resource/ToDo.png">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MySolution/ToDoApp/MainWindow.xaml.cs b/MySolution/ToDoApp/MainWindow.xaml.cs
index 6332a28..8665785 100644
--- a/MySolution/ToDoApp/MainWindow.xaml.cs
+++ b/MySolution/ToDoApp/MainWindow.xaml.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -13,8 +14,10 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ToDoApp.Context;
-using ToDoApp.Controller;
using ToDoApp.Model;
+using ToDoApp.Service;
+using ToDoApp.SubControl;
+using ToDoApp.SubWindow;
namespace ToDoApp
{
@@ -23,6 +26,9 @@ namespace ToDoApp
///
public partial class MainWindow : Window
{
+ private User _user;
+ private Item _selectedItem;
+
public MainWindow()
{
InitializeComponent();
@@ -32,13 +38,28 @@ namespace ToDoApp
MessageBox.Show("데이터베이스가 정상적이지 않아 프로그램을 종료합니다.");
Application.Current.Shutdown();
}
+
+ if (CheckLogin() != true)
+ {
+ Application.Current.Shutdown();
+ }
+
+ InitInstance();
+ }
+
+ private void InitInstance()
+ {
+ tbName.Text = _user.Name;
+ tbEmail.Text = _user.Email;
+
+ RefreshList();
}
private bool CheckDB()
{
try
{
- ToDoController.Instance.EnsureCreated();
+ DbService.Instance.EnsureCreated();
return true;
}
catch (Exception ex)
@@ -47,5 +68,123 @@ namespace ToDoApp
}
}
+
+ private bool? CheckLogin()
+ {
+ try
+ {
+ LoginWindow dlg = new LoginWindow();
+ bool? isOk = dlg.ShowDialog();
+ _user = dlg.User;
+
+ return isOk;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ private void RefreshList()
+ {
+ List- items = GetAllItems();
+ SetToDoList(items);
+ }
+
+ private List
- GetAllItems()
+ {
+ return ItemService.Instance.FindAllNotCompletedItems(_user.Id);
+ }
+
+ private void SetToDoList(List
- items)
+ {
+ List list = new List();
+ foreach (Item item in items)
+ {
+ list.Add(new ToDoListItem(item));
+ }
+
+ lbxContent.ItemsSource = list;
+ }
+
+ private void SetSelectedItem(Item item)
+ {
+ _selectedItem = item;
+
+ tbxItemName.Text = _selectedItem.Title;
+ tbItemCreatedAt.Text = _selectedItem.CreateDate.ToString("yyyy년 MM월 dd일 ddd 생성됨");
+
+ if (_selectedItem.DueDate != null)
+ dpItemDueDate.SelectedDate = _selectedItem.DueDate;
+
+ if (_selectedItem.Description != null)
+ tbxItemDescription.Text = _selectedItem.Description;
+ }
+
+ private void ClearSelectedItem()
+ {
+ _selectedItem = null;
+
+ tbxItemName.Text = "";
+ tbItemCreatedAt.Text = "";
+ dpItemDueDate.SelectedDate = null;
+ tbxItemDescription.Text = "";
+ }
+
+ private void tbxNewItemName_KeyDown(object sender, KeyEventArgs e)
+ {
+
+ }
+
+ private void tbxSearch_KeyDown(object sender, KeyEventArgs e)
+ {
+
+ }
+
+ private void btnUpdate_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void btnDelete_Click(object sender, RoutedEventArgs e)
+ {
+ if (_selectedItem == null)
+ return;
+
+ ItemService.Instance.RemoveItem(_selectedItem.Id);
+
+ ClearSelectedItem();
+ RefreshList();
+ }
+
+ private void tbxNewItem_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key != Key.Enter)
+ return;
+
+ PlaceholderTextBox textBox = sender as PlaceholderTextBox;
+ if (textBox == null || string.IsNullOrEmpty(textBox.Text))
+ return;
+
+ Item newItem = new Item()
+ {
+ UserId = _user.Id,
+ Title = textBox.Text,
+ };
+
+ ItemService.Instance.CreateItem(newItem);
+ textBox.Text = "";
+
+ RefreshList();
+ }
+
+ private void lbxContent_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ ToDoListItem toDoListItem = lbxContent.SelectedItem as ToDoListItem;
+ if (toDoListItem == null)
+ return;
+
+ SetSelectedItem(toDoListItem.Item);
+ }
}
}
diff --git a/MySolution/ToDoApp/Migrations/20231027075930_init.Designer.cs b/MySolution/ToDoApp/Migrations/20231027075930_init.Designer.cs
new file mode 100644
index 0000000..ef3072a
--- /dev/null
+++ b/MySolution/ToDoApp/Migrations/20231027075930_init.Designer.cs
@@ -0,0 +1,105 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using ToDoApp.Context;
+
+#nullable disable
+
+namespace ToDoApp.Migrations
+{
+ [DbContext(typeof(ToDoContext))]
+ [Migration("20231027075930_init")]
+ partial class init
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.13")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("ToDoApp.Model.Item", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreateDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .HasColumnType("longtext");
+
+ b.Property("DueDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsCompleted")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsToday")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Items");
+ });
+
+ modelBuilder.Entity("ToDoApp.Model.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreateDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Status")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("ToDoApp.Model.Item", b =>
+ {
+ b.HasOne("ToDoApp.Model.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/Migrations/20231027075930_init.cs b/MySolution/ToDoApp/Migrations/20231027075930_init.cs
new file mode 100644
index 0000000..387f8d4
--- /dev/null
+++ b/MySolution/ToDoApp/Migrations/20231027075930_init.cs
@@ -0,0 +1,84 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using MySql.EntityFrameworkCore.Metadata;
+
+#nullable disable
+
+namespace ToDoApp.Migrations
+{
+ ///
+ public partial class init : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterDatabase()
+ .Annotation("MySQL:Charset", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "Users",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
+ Name = table.Column(type: "varchar(255)", nullable: false),
+ Password = table.Column(type: "longtext", nullable: false),
+ Email = table.Column(type: "longtext", nullable: false),
+ CreateDate = table.Column(type: "datetime(6)", nullable: false),
+ Status = table.Column(type: "int", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Users", x => x.Id);
+ })
+ .Annotation("MySQL:Charset", "utf8mb4");
+
+ migrationBuilder.CreateTable(
+ name: "Items",
+ columns: table => new
+ {
+ Id = table.Column(type: "int", nullable: false)
+ .Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
+ UserId = table.Column(type: "int", nullable: false),
+ Title = table.Column(type: "longtext", nullable: false),
+ Description = table.Column(type: "longtext", nullable: true),
+ DueDate = table.Column(type: "datetime(6)", nullable: true),
+ IsToday = table.Column(type: "tinyint(1)", nullable: true),
+ IsCompleted = table.Column(type: "tinyint(1)", nullable: true),
+ CreateDate = table.Column(type: "datetime(6)", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Items", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Items_Users_UserId",
+ column: x => x.UserId,
+ principalTable: "Users",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ })
+ .Annotation("MySQL:Charset", "utf8mb4");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Items_UserId",
+ table: "Items",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Users_Name",
+ table: "Users",
+ column: "Name",
+ unique: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Items");
+
+ migrationBuilder.DropTable(
+ name: "Users");
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/Migrations/ToDoContextModelSnapshot.cs b/MySolution/ToDoApp/Migrations/ToDoContextModelSnapshot.cs
new file mode 100644
index 0000000..eb8b93c
--- /dev/null
+++ b/MySolution/ToDoApp/Migrations/ToDoContextModelSnapshot.cs
@@ -0,0 +1,102 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using ToDoApp.Context;
+
+#nullable disable
+
+namespace ToDoApp.Migrations
+{
+ [DbContext(typeof(ToDoContext))]
+ partial class ToDoContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.13")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("ToDoApp.Model.Item", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreateDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Description")
+ .HasColumnType("longtext");
+
+ b.Property("DueDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("IsCompleted")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("IsToday")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Items");
+ });
+
+ modelBuilder.Entity("ToDoApp.Model.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreateDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("varchar(255)");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Status")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Users");
+ });
+
+ modelBuilder.Entity("ToDoApp.Model.Item", b =>
+ {
+ b.HasOne("ToDoApp.Model.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/Model/Item.cs b/MySolution/ToDoApp/Model/Item.cs
index 530675b..4b6b932 100644
--- a/MySolution/ToDoApp/Model/Item.cs
+++ b/MySolution/ToDoApp/Model/Item.cs
@@ -17,11 +17,13 @@ namespace ToDoApp.Model
[Required]
public string Title { get; set; }
- public string Description { get; set; }
+ public string? Description { get; set; }
- public DateTime DueDate { get; set; }
+ public DateTime? DueDate { get; set; }
- public bool IsCompleted { get; set; }
+ public bool? IsToday { get; set; }
+
+ public bool? IsCompleted { get; set; }
public DateTime CreateDate { get; set; }
diff --git a/MySolution/ToDoApp/Model/User.cs b/MySolution/ToDoApp/Model/User.cs
index 86577df..5c90d3d 100644
--- a/MySolution/ToDoApp/Model/User.cs
+++ b/MySolution/ToDoApp/Model/User.cs
@@ -1,4 +1,5 @@
-using System;
+using Microsoft.EntityFrameworkCore;
+using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@@ -13,6 +14,7 @@ namespace ToDoApp.Model
Invalid
}
+ [Index(nameof(Name), IsUnique = true)]
public class User
{
public int Id { get; set; }
@@ -24,7 +26,7 @@ namespace ToDoApp.Model
public string Password { get; set; }
[EmailAddress(ErrorMessage = "Invalid email format")]
- public string Email { get; set; }
+ public string? Email { get; set; }
public DateTime CreateDate { get; set; }
diff --git a/MySolution/ToDoApp/Resource/ToDo.png b/MySolution/ToDoApp/Resource/ToDo.png
new file mode 100644
index 0000000..eb42662
Binary files /dev/null and b/MySolution/ToDoApp/Resource/ToDo.png differ
diff --git a/MySolution/ToDoApp/Service/DbService.cs b/MySolution/ToDoApp/Service/DbService.cs
new file mode 100644
index 0000000..12c4a0b
--- /dev/null
+++ b/MySolution/ToDoApp/Service/DbService.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ToDoApp.Context;
+
+namespace ToDoApp.Service
+{
+ public class DbService
+ {
+ public static DbService Instance { get; set; } = new DbService();
+
+ public void EnsureCreated()
+ {
+ using (var context = new ToDoContext())
+ {
+ context.Database.EnsureCreated();
+ }
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/Service/ItemService.cs b/MySolution/ToDoApp/Service/ItemService.cs
new file mode 100644
index 0000000..0a7f595
--- /dev/null
+++ b/MySolution/ToDoApp/Service/ItemService.cs
@@ -0,0 +1,108 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ToDoApp.Context;
+using ToDoApp.Model;
+using ToDoApp.Utility;
+
+namespace ToDoApp.Service
+{
+ public class ItemService
+ {
+ public static ItemService Instance { get; set; } = new ItemService();
+
+ public List
- FindAllItems(int userId)
+ {
+ using (var context = new ToDoContext())
+ {
+ return context.Items.Where(i => i.UserId == userId).ToList();
+ }
+ }
+
+ public List
- FindAllItems(User user)
+ {
+ using (var context = new ToDoContext())
+ {
+ return context.Items.Include(i => i.User).Where(i => i.User == user).ToList();
+ }
+ }
+
+ public List
- FindAllNotCompletedItems(int userId)
+ {
+ using (var context = new ToDoContext())
+ {
+ return context.Items.Include(i => i.User).Where(i => i.UserId == userId && i.IsCompleted != false).ToList();
+ }
+ }
+
+ public List
- FindAllNotCompletedItems(User user)
+ {
+ using (var context = new ToDoContext())
+ {
+ return context.Items.Include(i => i.User).Where(i => i.User == user && i.IsCompleted == false).ToList();
+ }
+ }
+
+ public Item? FindItem(int itemId)
+ {
+ using (var context = new ToDoContext())
+ {
+ return context.Items.FirstOrDefault(i => i.Id == itemId);
+ }
+ }
+
+ public Item? FindItem(Item item)
+ {
+ return FindItem(item.Id);
+ }
+
+ public void CreateItem(Item item)
+ {
+ using (var context = new ToDoContext())
+ {
+ item.CreateDate = DateTime.Now;
+ context.Items.Add(item);
+ context.SaveChanges();
+ }
+ }
+
+ public void UpdateItem(Item item)
+ {
+ using (var context = new ToDoContext())
+ {
+ Item? original = context.Items.Find(item.Id);
+ if (original == null)
+ return;
+
+ original.Title = item.Title;
+ original.Description = item.Description;
+ original.DueDate = item.DueDate;
+ original.IsToday = item.IsToday;
+ original.IsCompleted = item.IsCompleted;
+
+ context.SaveChanges();
+ }
+ }
+
+ public void RemoveItem(int itemId)
+ {
+ using (var context = new ToDoContext())
+ {
+ Item? item = context.Items.Find(itemId);
+ if (item == null)
+ return;
+
+ context.Remove(item);
+ context.SaveChanges();
+ }
+ }
+
+ public void RemoveItem(Item item)
+ {
+ RemoveItem(item.Id);
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/Controller/ToDoController.cs b/MySolution/ToDoApp/Service/UserService.cs
similarity index 77%
rename from MySolution/ToDoApp/Controller/ToDoController.cs
rename to MySolution/ToDoApp/Service/UserService.cs
index 52bee0b..b17a0d4 100644
--- a/MySolution/ToDoApp/Controller/ToDoController.cs
+++ b/MySolution/ToDoApp/Service/UserService.cs
@@ -7,25 +7,17 @@ using ToDoApp.Context;
using ToDoApp.Model;
using ToDoApp.Utility;
-namespace ToDoApp.Controller
+namespace ToDoApp.Service
{
- public class ToDoController
+ public class UserService
{
- public static ToDoController Instance { get; set; } = new ToDoController();
+ public static UserService Instance { get; set; } = new UserService();
- public void EnsureCreated()
+ public User? FindUser(int userId)
{
using (var context = new ToDoContext())
{
- context.Database.EnsureCreated();
- }
- }
-
- public User? FindUser(int id)
- {
- using (var context = new ToDoContext())
- {
- return context.Users.Find(id);
+ return context.Users.Find(userId);
}
}
@@ -37,7 +29,7 @@ namespace ToDoApp.Controller
}
}
- public bool LoginCheckUser(string username, string password)
+ public bool CheckUserLogin(string username, string password)
{
User? user = FindUser(username);
if (user == null)
@@ -50,17 +42,18 @@ namespace ToDoApp.Controller
{
using (var context = new ToDoContext())
{
+ user.Password = Utils.HashPassword(user.Password);
user.CreateDate = DateTime.Now;
context.Users.Add(user);
context.SaveChanges();
}
}
- public void DisableUser(int id)
+ public void DisableUser(int userId)
{
using (var context = new ToDoContext())
{
- User? user = context.Users.Find(id);
+ User? user = context.Users.Find(userId);
if (user == null)
return;
@@ -82,11 +75,11 @@ namespace ToDoApp.Controller
}
}
- public void RemoveUser(int id)
+ public void RemoveUser(int userId)
{
using (var context = new ToDoContext())
{
- User? user = context.Users.Find(id);
+ User? user = context.Users.Find(userId);
if (user == null)
return;
@@ -107,5 +100,10 @@ namespace ToDoApp.Controller
context.SaveChanges();
}
}
+
+ public void RemoveUser(User user)
+ {
+ RemoveUser(user.Id);
+ }
}
}
diff --git a/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml b/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml
new file mode 100644
index 0000000..83c863a
--- /dev/null
+++ b/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml.cs b/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml.cs
new file mode 100644
index 0000000..fd06d90
--- /dev/null
+++ b/MySolution/ToDoApp/SubControl/PlaceholderTextBox.xaml.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace ToDoApp.SubControl
+{
+ ///
+ /// PlaceholderTextBox.xaml에 대한 상호 작용 논리
+ ///
+ public partial class PlaceholderTextBox : UserControl
+ {
+ public string TPlaceholder { get; set; } = "";
+ public int TFontSize { get; set; } = 12;
+ public FontWeight TFontWeight { get; set; } = FontWeights.Normal;
+ public int THeight { get; set; } = 20;
+ public VerticalAlignment TVerticalAlignment { get; set; } = VerticalAlignment.Center;
+
+ public PlaceholderTextBox()
+ {
+ InitializeComponent();
+
+ this.DataContext = this;
+ }
+
+ public string Text
+ {
+ get { return tbxMain.Text; }
+ set { tbxMain.Text = value; }
+ }
+
+ private void tbxMain_GotFocus(object sender, RoutedEventArgs e)
+ {
+ TextBox textBox = sender as TextBox;
+ if (textBox == null)
+ return;
+
+ if (textBox.Text != this.TPlaceholder)
+ return;
+
+ textBox.Text = "";
+ textBox.Foreground = Brushes.Black;
+ }
+
+ private void tbxMain_LostFocus(object sender, RoutedEventArgs e)
+ {
+ TextBox textBox = sender as TextBox;
+ if (textBox == null)
+ return;
+
+ if (!string.IsNullOrEmpty(textBox.Text))
+ return;
+
+ textBox.Text = this.TPlaceholder;
+ textBox.Foreground = Brushes.Gray;
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/SubControl/ToDoListItem.xaml b/MySolution/ToDoApp/SubControl/ToDoListItem.xaml
new file mode 100644
index 0000000..f736f6c
--- /dev/null
+++ b/MySolution/ToDoApp/SubControl/ToDoListItem.xaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MySolution/ToDoApp/SubControl/ToDoListItem.xaml.cs b/MySolution/ToDoApp/SubControl/ToDoListItem.xaml.cs
new file mode 100644
index 0000000..4a6b084
--- /dev/null
+++ b/MySolution/ToDoApp/SubControl/ToDoListItem.xaml.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using ToDoApp.Model;
+
+namespace ToDoApp.SubControl
+{
+ ///
+ /// ToDoListItem.xaml에 대한 상호 작용 논리
+ ///
+ public partial class ToDoListItem : UserControl
+ {
+ public Item Item { get; set; }
+
+ public ToDoListItem(Item item)
+ {
+ InitializeComponent();
+
+ this.Item = item;
+ InitInstance();
+ }
+
+ private void InitInstance()
+ {
+ tbItemName.Text = this.Item.Title;
+ ckbFinish.IsChecked = this.Item.IsToday == true ? true : false;
+ }
+
+ private void miToday_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void miFinish_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void miDelete_Click(object sender, RoutedEventArgs e)
+ {
+
+ }
+
+ private void ckbFinish_Checked(object sender, RoutedEventArgs e)
+ {
+
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/SubWindow/JoinWindow.xaml b/MySolution/ToDoApp/SubWindow/JoinWindow.xaml
new file mode 100644
index 0000000..280ac50
--- /dev/null
+++ b/MySolution/ToDoApp/SubWindow/JoinWindow.xaml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MySolution/ToDoApp/SubWindow/JoinWindow.xaml.cs b/MySolution/ToDoApp/SubWindow/JoinWindow.xaml.cs
new file mode 100644
index 0000000..b672b37
--- /dev/null
+++ b/MySolution/ToDoApp/SubWindow/JoinWindow.xaml.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Mail;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using System.Xml.Linq;
+using ToDoApp.Model;
+using ToDoApp.Service;
+
+namespace ToDoApp.SubWindow
+{
+ ///
+ /// JoinWindow.xaml에 대한 상호 작용 논리
+ ///
+ public partial class JoinWindow : Window
+ {
+ public JoinWindow()
+ {
+ InitializeComponent();
+ }
+
+ private bool VerifyInfo()
+ {
+ if (!CheckName(tbName.Text))
+ return false;
+
+ if (!CheckPassword(pbPassword.Password, pbVerification.Password))
+ return false;
+
+ if (!CheckEmail(tbEmail.Text))
+ return false;
+
+ return true;
+ }
+
+ private bool CheckName(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ tbMessage.Text = "사용자 이름을 입력하세요.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+
+ User? user = UserService.Instance.FindUser(name);
+ if (user != null)
+ {
+ tbMessage.Text = "사용할 수 없는 이름입니다.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+ else
+ {
+ tbMessage.Visibility = Visibility.Collapsed;
+ return true;
+ }
+ }
+
+ private bool CheckPassword(string password, string verification)
+ {
+ if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(verification))
+ {
+ tbMessage.Text = "비밀번호를 입력하세요.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+
+ if (password != verification)
+ {
+ tbMessage.Text = "비밀번호가 일치하지 않습니다.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+ else
+ {
+ tbMessage.Visibility = Visibility.Collapsed;
+ return true;
+ }
+ }
+
+ private bool CheckEmail(string email)
+ {
+ if (string.IsNullOrEmpty(email))
+ return true;
+
+ try
+ {
+ MailAddress addr = new MailAddress(email);
+ if (addr.Address != email)
+ {
+ tbMessage.Text = "이메일 형식이 올바르지 않습니다.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+ else
+ {
+ tbMessage.Visibility = Visibility.Collapsed;
+ return true;
+ }
+ }
+ catch
+ {
+ tbMessage.Text = "이메일 형식이 올바르지 않습니다.";
+ tbMessage.Visibility = Visibility.Visible;
+ return false;
+ }
+ }
+
+
+ private void btnJoin_Click(object sender, RoutedEventArgs e)
+ {
+ if (!VerifyInfo())
+ {
+ MessageBox.Show("가입할 수 없습니다.");
+ return;
+ }
+
+ User user = new User()
+ {
+ Name = tbName.Text,
+ Email = tbEmail.Text,
+ Password = pbPassword.Password,
+ CreateDate = DateTime.Now,
+ Status = UserStatus.Valid
+ };
+
+ UserService.Instance.CreateUser(user);
+
+ MessageBox.Show("가입되었습니다.");
+ this.Close();
+ }
+
+ private void PasswordChanged(object sender, RoutedEventArgs e)
+ {
+ CheckPassword(pbPassword.Password, pbVerification.Password);
+ }
+
+ private void tbEmail_TextChanged(object sender, TextChangedEventArgs e)
+ {
+ CheckEmail(tbEmail.Text);
+ }
+
+ private void tb_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key != Key.Enter)
+ return;
+
+ btnJoin_Click(this, null);
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ tbName.Focus();
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/SubWindow/LoginWindow.xaml b/MySolution/ToDoApp/SubWindow/LoginWindow.xaml
new file mode 100644
index 0000000..b514593
--- /dev/null
+++ b/MySolution/ToDoApp/SubWindow/LoginWindow.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MySolution/ToDoApp/SubWindow/LoginWindow.xaml.cs b/MySolution/ToDoApp/SubWindow/LoginWindow.xaml.cs
new file mode 100644
index 0000000..2fea36f
--- /dev/null
+++ b/MySolution/ToDoApp/SubWindow/LoginWindow.xaml.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+using ToDoApp.Model;
+using ToDoApp.Service;
+
+namespace ToDoApp.SubWindow
+{
+ ///
+ /// LoginWindow.xaml에 대한 상호 작용 논리
+ ///
+ public partial class LoginWindow : Window
+ {
+ public User User { get; set; }
+
+ public LoginWindow()
+ {
+ InitializeComponent();
+ }
+
+ private bool CheckLogin(string name, string password)
+ {
+ if (!UserService.Instance.CheckUserLogin(name, password))
+ return false;
+ else
+ return true;
+ }
+
+ private void btnLogin_Click(object sender, RoutedEventArgs e)
+ {
+ if (!CheckLogin(tbName.Text, pbPassword.Password))
+ {
+ MessageBox.Show("로그인 정보가 올바르지 않습니다.");
+ }
+ else
+ {
+ this.User = UserService.Instance.FindUser(tbName.Text);
+
+ this.DialogResult = true;
+ this.Close();
+ }
+ }
+
+ private void btnJoin_Click(object sender, RoutedEventArgs e)
+ {
+ JoinWindow dlg = new JoinWindow();
+ dlg.ShowDialog();
+ }
+
+ private void tb_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key != Key.Enter)
+ return;
+
+ btnLogin_Click(this, null);
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ tbName.Focus();
+ }
+ }
+}
diff --git a/MySolution/ToDoApp/ToDo.ico b/MySolution/ToDoApp/ToDo.ico
new file mode 100644
index 0000000..43aa603
Binary files /dev/null and b/MySolution/ToDoApp/ToDo.ico differ
diff --git a/MySolution/ToDoApp/ToDo.png b/MySolution/ToDoApp/ToDo.png
new file mode 100644
index 0000000..eb42662
Binary files /dev/null and b/MySolution/ToDoApp/ToDo.png differ
diff --git a/MySolution/ToDoApp/ToDoApp.csproj b/MySolution/ToDoApp/ToDoApp.csproj
index 2ad5d14..1a3ec8e 100644
--- a/MySolution/ToDoApp/ToDoApp.csproj
+++ b/MySolution/ToDoApp/ToDoApp.csproj
@@ -5,8 +5,17 @@
net6.0-windows
enable
true
+ ToDo.ico
+
+
+
+
+
+
+
+
@@ -20,4 +29,10 @@
+
+
+ Always
+
+
+
diff --git a/Reaources/ToDo.ico b/Reaources/ToDo.ico
new file mode 100644
index 0000000..43aa603
Binary files /dev/null and b/Reaources/ToDo.ico differ
diff --git a/Reaources/ToDo.png b/Reaources/ToDo.png
new file mode 100644
index 0000000..eb42662
Binary files /dev/null and b/Reaources/ToDo.png differ