Compare commits

2 Commits

Author SHA1 Message Date
최준영
09768ad928 [feat] WPF 회원관리 애플리케이션 구현
- .NET 8.0 WPF 프로젝트 생성
- MVVM 패턴 적용 (CommunityToolkit.Mvvm)
- 의존성 주입 구현 (Microsoft.Extensions.DependencyInjection)
- 로그인/회원관리/회원등록 화면 구현
- 테마 전환 기능 (Dark/Light)
- 다국어 지원 (한국어/영어)
- 세련된 로그인 UI 디자인
- CLAUDE.md 및 History.md 문서화

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 10:03:49 +09:00
최준영
ef2a6522d1 #2 목표 문서 Docs 삽입 2025-12-29 09:07:03 +09:00
78 changed files with 2225 additions and 2245 deletions

60
CLAUDE.md Normal file
View File

@@ -0,0 +1,60 @@
# CLAUDE.md
이 파일은 Claude Code (claude.ai/code)가 이 저장소에서 코드 작업을 할 때 참고하는 가이드입니다.
## 프로젝트 개요
EduToy는 WPF(Windows Presentation Foundation) 기초를 학습하기 위한 교육용 프로젝트입니다. "WPF 기초를 위한 토이프로젝트"라는 이름으로도 불립니다.
## 빌드 및 실행
```bash
# 프로젝트 빌드
cd src/WPFBeginner
dotnet build
# 프로젝트 실행
dotnet run
```
## 기술 스택
- **.NET 8.0** - Windows WPF 애플리케이션
- **CommunityToolkit.Mvvm 8.2.2** - MVVM 패턴 구현
- **Microsoft.Extensions.DependencyInjection 8.0.0** - 의존성 주입
## 프로젝트 아키텍처
```
src/WPFBeginner/
├── Models/ # 데이터 모델 (Member, AppSettings)
├── ViewModels/ # MVVM ViewModel (LoginViewModel, MainViewModel, RegistViewModel)
├── Views/ # XAML Window (LoginWindow, MainWindow, RegistWindow)
├── Controls/ # 재사용 가능한 UserControl (InputPanel)
├── Services/ # 비즈니스 로직 서비스 (SettingsService, MemberService)
├── Converters/ # WPF 값 변환기
└── Resources/
├── Themes/ # 테마 ResourceDictionary (DefaultTheme, LightTheme)
└── Languages/ # 다국어 리소스 (ko-KR, en-US)
```
## MVVM 패턴
- **View**: XAML만 사용, 코드 비하인드에 비즈니스 로직 없음
- **ViewModel**: CommunityToolkit.Mvvm의 `[ObservableProperty]`, `[RelayCommand]` 사용
- **DI**: App.xaml.cs에서 ServiceCollection으로 서비스 및 ViewModel 등록
## 주요 기능
- **로그인**: appsettings.json의 자격 증명으로 인증 (기본: admin/0000)
- **회원 관리**: DataGrid로 CRUD 작업
- **테마 전환**: 어두운 테마(Default) / 밝은 테마(Light)
- **다국어 지원**: 한국어(ko-KR) / 영어(en-US) 동적 전환
## 설정 파일
`appsettings.json`: 로그인 정보, 언어, 테마, 기본 회원 데이터 저장
## 언어
프로젝트 문서 및 UI는 한국어를 기본으로 사용합니다.

29
EduToy.sln Normal file
View File

@@ -0,0 +1,29 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFBeginner", "src\WPFBeginner\WPFBeginner.csproj", "{20E73DF0-D43D-8913-E28F-6960CD8A512D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{20E73DF0-D43D-8913-E28F-6960CD8A512D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20E73DF0-D43D-8913-E28F-6960CD8A512D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20E73DF0-D43D-8913-E28F-6960CD8A512D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20E73DF0-D43D-8913-E28F-6960CD8A512D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{20E73DF0-D43D-8913-E28F-6960CD8A512D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9791FF85-3B94-4645-B4A9-22D1B17C40A2}
EndGlobalSection
EndGlobal

203
History.md Normal file
View File

@@ -0,0 +1,203 @@
# 프로젝트 개발 히스토리
이 문서는 EduToy WPF 프로젝트의 개발 과정을 기록합니다.
---
## 1. 프로젝트 분석 및 CLAUDE.md 생성
### 요청
- 코드베이스 분석 및 CLAUDE.md 파일 생성
### 작업 내용
- 프로젝트 구조 탐색
- "EduToy" - WPF 교육용 프로젝트로 확인
- docs/sampleApp/WPFBeginner.exe (컴파일된 바이너리) 발견
- CLAUDE.md 파일 영문으로 최초 작성
---
## 2. 한국어 전환 및 CLAUDE.md 번역
### 요청
- 모든 대화를 한글로 진행
- CLAUDE.md를 한글로 번역
### 작업 내용
- CLAUDE.md 한국어로 번역 완료
- 프로젝트 문서화 한국어 기준으로 변경
---
## 3. WPF 애플리케이션 구현
### 요청
- PowerPoint 파일 (WPF 기초를 위한 토이프로젝트.pptx) 분석
- sampleApp 참고하여 프로그램 구현
### 기술 스택
- **.NET 8.0** Windows WPF
- **CommunityToolkit.Mvvm 8.2.2** - MVVM 패턴
- **Microsoft.Extensions.DependencyInjection 8.0.0** - DI
### 구현된 구조
```
src/WPFBeginner/
├── Models/
│ ├── Member.cs
│ └── AppSettings.cs
├── ViewModels/
│ ├── LoginViewModel.cs
│ ├── MainViewModel.cs
│ └── RegistViewModel.cs
├── Views/
│ ├── LoginWindow.xaml / .cs
│ ├── MainWindow.xaml / .cs
│ └── RegistWindow.xaml / .cs
├── Controls/
│ └── InputPanel.xaml / .cs
├── Services/
│ ├── SettingsService.cs
│ └── MemberService.cs
├── Converters/
│ └── BoolToVisibilityConverter.cs
└── Resources/
├── Themes/
│ ├── DefaultTheme.xaml
│ └── LightTheme.xaml
└── Languages/
├── ko-KR.xaml
└── en-US.xaml
```
### 주요 기능
- 로그인 (appsettings.json 자격 증명)
- 회원 관리 (DataGrid CRUD)
- 테마 전환 (Dark/Light)
- 다국어 지원 (한국어/영어)
---
## 4. 검색 영역 짤림 현상 수정
### 요청
- 창 크기 줄였을 때 검색 영역이 짤리는 현상 수정
- 엔터키로 검색 가능하도록 기능 개선
### 해결 방법
- MinWidth="700" MinHeight="400" 설정
- SearchTextBox_KeyDown 이벤트 핸들러 추가
### 수정 파일
- `MainWindow.xaml` - MinWidth/MinHeight 추가
- `MainWindow.xaml.cs` - KeyDown 이벤트 처리
```csharp
private void SearchTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && DataContext is MainViewModel viewModel)
{
viewModel.SearchCommand.Execute(null);
}
}
```
---
## 5. 로그인 화면 디자인 개선
### 요청
- 가로 스크롤 디자인 개선
- 로그인 화면을 세련되게 변경
### 적용된 디자인
- 그라데이션 배경 (#1a1a2e#16213e)
- 둥근 모서리 (CornerRadius="12")
- 그림자 효과 (DropShadowEffect)
- 로고 아이콘 (원형 그라데이션)
- 모던한 입력 필드 스타일
- 그라데이션 로그인 버튼
### 수정 파일
- `LoginWindow.xaml` - 전체 디자인 재구성
---
## 6. 로그인 화면 짤림 현상 수정
### 요청
- 로그인 화면 위아래 짤림 현상 수정
### 해결 방법
- 외부 Border에 Margin="15" 추가
- 창 크기 500x420 → 540x440으로 확대
### 수정 파일
- `LoginWindow.xaml`
```xml
<Window Height="540" Width="440">
<Border Margin="15" CornerRadius="12">
<Border.Effect>
<DropShadowEffect BlurRadius="15" ShadowDepth="0" Opacity="0.5"/>
</Border.Effect>
...
</Border>
</Window>
```
---
## 7. 테마 버튼 동작 수정
### 요청
- 테마 버튼이 동작하지 않는 문제 수정
- 로그인 화면 제외 이전 디자인 유지
### 문제 원인
- MainWindow.xaml에 하드코딩된 색상 사용
- DynamicResource 대신 직접 색상값 (#1a1a2e 등) 사용
### 해결 방법
- MainWindow.xaml을 DynamicResource 사용하도록 복원
- LoginWindow.xaml은 새 디자인 유지 (하드코딩)
### 수정 파일
- `MainWindow.xaml`
```xml
<Window Background="{DynamicResource WindowBackgroundBrush}">
<Border Background="{DynamicResource SecondaryBrush}">
<DataGrid Style="{DynamicResource DefaultDataGridStyle}">
```
### 테마 전환 구조
1. **MainWindow.xaml** - 라디오 버튼 Command 바인딩
2. **MainViewModel.cs** - SetDefaultThemeCommand, SetLightThemeCommand
3. **SettingsService.cs** - ApplyTheme() 메서드로 ResourceDictionary 교체
---
## 최종 결과
### 완료된 기능
- ✅ MVVM 패턴 기반 WPF 애플리케이션
- ✅ 의존성 주입 (DI) 구현
- ✅ 로그인/회원관리 기능
- ✅ 테마 전환 (Dark/Light)
- ✅ 다국어 지원 (한국어/영어)
- ✅ 세련된 로그인 화면 디자인
- ✅ 반응형 레이아웃 (MinWidth/MinHeight)
- ✅ 엔터키 검색 기능
### 빌드 및 실행
```bash
cd src/WPFBeginner
dotnet build
dotnet run
```
### 기본 로그인 정보
- ID: admin
- Password: 0000
---
*마지막 업데이트: 2025-12-29*

Binary file not shown.

View File

@@ -0,0 +1,75 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"WPFBeginner/1.0.0": {
"dependencies": {
"CommunityToolkit.Mvvm": "8.2.2",
"Microsoft.Extensions.DependencyInjection": "8.0.0"
},
"runtime": {
"WPFBeginner.dll": {}
}
},
"CommunityToolkit.Mvvm/8.2.2": {
"runtime": {
"lib/net6.0/CommunityToolkit.Mvvm.dll": {
"assemblyVersion": "8.2.0.0",
"fileVersion": "8.2.2.1"
}
}
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.23.53103"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.23.53103"
}
}
}
}
},
"libraries": {
"WPFBeginner/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"CommunityToolkit.Mvvm/8.2.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-r0g0k9tGYdrnz8R7T3x5UiokDffeevzK/2P/9SBL6fqLgN8B157MIi/bVUWI1KAz6ZorZrK9AdABCWUeXZZsvA==",
"path": "communitytoolkit.mvvm/8.2.2",
"hashPath": "communitytoolkit.mvvm.8.2.2.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
"path": "microsoft.extensions.dependencyinjection/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==",
"path": "microsoft.extensions.dependencyinjection.abstractions/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512"
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": true
}
}
}

View File

@@ -1,95 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Maximize Button-->
<Style x:Key="TiTleBarMaximizeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Fill="#FFB8B8B9" Height="14" Width="14" Canvas.Left="-7" Canvas.Top="5">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Normal">
<!--<Setter Property="Data" Value="F1M3,5C3,5 3,11 3,11 3,11 9,11 9,11 9,11 9,5 9,5 9,5 3,5 3,5z M5,3C5,3,5,3.375,5,3.938L5,4 6,4C8,4 10,4 10,4 10,4 10,6 10,8L10,9 10.062,9C10.625,9 11,9 11,9 11,9 11,3 11,3 11,3 5,3 5,3z M4,2C4,2 12,2 12,2 12,2 12,10 12,10 12,10 11.5,10 10.75,10L10,10 10,10.75C10,11.5 10,12 10,12 10,12 2,12 2,12 2,12 2,4 2,4 2,4 2.5,4 3.25,4L4,4 4,3.25C4,2.5,4,2,4,2z"/>-->
<Setter Property="Data" Value="F1M3,3C3,3 3,11 3,11 3,11 11,11 11,11 11,11 11,3 11,3 11,3 3,3 3,3z M2,2C2,2 12,2 12,2 12,2 12,12 12,12 12,12 2,12 2,12 2,12 2,2 2,2z" />
</DataTrigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="Data" Value="F1M3,5C3,5 3,11 3,11 3,11 9,11 9,11 9,11 9,5 9,5 9,5 3,5 3,5z M5,3C5,3 5,4 5,4 5,4 10,4 10,4 10,4 10,9 10,9 10,9 11,9 11,9 11,9 11,3 11,3 11,3 5,3 5,3z M4,2C4,2 12,2 12,2 12,2 12,10 12,10 12,10 10,10 10,10 10,10 10,12 10,12 10,12 2,12 2,12 2,12 2,4 2,4 2,4 4,4 4,4 4,4 4,2 4,2z"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF474D51"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF3D4347"/>
</Trigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Normal">
<Setter Property="ToolTip" Value="Maximize"/>
</DataTrigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="ToolTip" Value="Restore"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!--Minimize Button-->
<Style x:Key="TiTleBarMinimizeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Data="F1M2,2C2,2 12,2 12,2 12,2 12,3 12,3 12,3 2,3 2,3 2,3 2,2 2,2z" Fill="#FFB8B8B9" Height="4" Canvas.Left="0" Canvas.Top="0" Width="13"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF474D51"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF3D4347"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Close Button-->
<Style x:Key="TiTleBarCloseButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Data="F1M2,2C2,2,2.25,2,2.5,2L2.999,2 3,2 3,2.001 3.221,2.222C3.652,2.653,4.66,3.661,5.847,4.848L6.999,6 8.152,4.848C9.734,3.266,10.999,2,10.999,2L11,2.001 11,2C11,2 12,2 12,2 12,2 12,3 12,3 12,3 10.734,4.266 9.152,5.848L8,7 9.152,8.152C10.734,9.734 12,11 12,11 12,11 12,12 12,12 12,12 11,12 11,12L11,11.999 10.999,12C10.999,12,9.733,10.734,8.151,9.152L6.999,8 5.847,9.152C4.66,10.339,3.652,11.347,3.221,11.778L3,11.999 3,12 2.999,12C3,12 2,12 2,12 2,12 2,11 2,11 2,11 3.265,9.734 4.847,8.152L5.999,7 4.847,5.848C3.265,4.266,1.999,3,1.999,3L2,2.999 2,2.844C2,2.562,2,2,2,2z"
Fill="#FFB8B8B9" Height="13" Canvas.Left="0" Canvas.Top="0" Width="13"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FFF06251"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FFED3F2B"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@@ -1,55 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Colors.TitleBar.Title.Background" Color="#FF1E1E1E"/>
<SolidColorBrush x:Key="Colors.TitleBar.Title.Foreground" Color="LightGreen"/>
<SolidColorBrush x:Key="Colors.Section.HorizontalSeperator" Color="Cyan" Opacity="0.8"/>
<SolidColorBrush x:Key="Colors.Main.Background" Color="#FF1E1E1E" />
<SolidColorBrush x:Key="Colors.SettingPopup.Background" Color="#FF383838" />
<SolidColorBrush x:Key="Colors.SettingPopup.BorderBrush" Color="#FF424242" />
<SolidColorBrush x:Key="Colors.SettingPopup.Foreground" Color="White" />
<SolidColorBrush x:Key="Colors.TextBox.Background" Color="#FF383838"/>
<SolidColorBrush x:Key="Colors.TextBox.Foreground" Color="White"/>
<SolidColorBrush x:Key="Colors.TextBox.BorderBrush" Color="#FF424242"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.Normal" Color="White"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MouseOver" Color="Pink"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MousePressed" Color="Coral"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.Normal" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MouseOver" Color="Pink"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MousePressed" Color="Coral"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.Normal" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MouseOver" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MousePressed" Color="#FF1F1F1F"/>
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Normal" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Normal" Color="White" />
<!--<SolidColorBrush x:Key="Colors.DataGridButton.Background.MouseOver" Color="#FFEB530C" Opacity="0.6" />-->
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.MouseOver" StartPoint="0,0" EndPoint="0,1" Opacity="0.6">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.MouseOver" Color="Yellow" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Pressed" StartPoint="0,0" EndPoint="0,1" Opacity="0.8">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<!--<SolidColorBrush x:Key="Colors.DataGridButton.Background.Pressed" Color="#FFEB530C" Opacity="0.8" />-->
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Pressed" Color="LightSkyBlue" />
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Normal" Color="#FFB8B8B9"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Pressed" Color="#FFB8B8B9" Opacity="0.6"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MouseOver" Color="#FF474D51"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MousePressed" Color="#FF474D51"/>
</ResourceDictionary>

View File

@@ -1,52 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Colors.TitleBar.Title.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="Colors.TitleBar.Title.Foreground" Color="DarkSlateBlue"/>
<SolidColorBrush x:Key="Colors.Section.HorizontalSeperator" Color="Gray" Opacity="0.8"/>
<SolidColorBrush x:Key="Colors.Main.Background" Color="#FFF0F0F0" />
<SolidColorBrush x:Key="Colors.SettingPopup.Background" Color="#FFF5F5F5" />
<SolidColorBrush x:Key="Colors.SettingPopup.BorderBrush" Color="#FFDDDDDD" />
<SolidColorBrush x:Key="Colors.SettingPopup.Foreground" Color="Black" />
<SolidColorBrush x:Key="Colors.TextBox.Background" Color="#FFF5F5F5"/>
<SolidColorBrush x:Key="Colors.TextBox.Foreground" Color="Black"/>
<SolidColorBrush x:Key="Colors.TextBox.BorderBrush" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.Normal" Color="Black"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MouseOver" Color="MediumSlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MousePressed" Color="SlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.Normal" Color="#FFEEEEEE"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MouseOver" Color="MediumSlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MousePressed" Color="SlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.Normal" Color="#FFEEEEEE"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MouseOver" Color="#FFE0E0E0"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MousePressed" Color="#FFD6D6D6"/>
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Normal" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Normal" Color="Black" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.MouseOver" StartPoint="0,0" EndPoint="0,1" Opacity="0.6">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.MouseOver" Color="DarkSlateBlue" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Pressed" StartPoint="0,0" EndPoint="0,1" Opacity="0.8">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Pressed" Color="CornflowerBlue" />
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Normal" Color="#FF6E6E70"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Pressed" Color="#FF6E6E70" Opacity="0.6"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MouseOver" Color="#FFD3D3D5"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MousePressed" Color="#FFD3D3D5"/>
</ResourceDictionary>

View File

@@ -1,36 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingImage x:Key="Images.OptionsMenu.Setting.Normal">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Normal}" Geometry="F0 M24,24z M0,0z M10.1,1.3L10.1,1.1 12.7,1.1 12.7,1.3 13,2.6 13.2,3.7C14.2,3.9,15.1,4.3,15.9,4.8L16,4.8 17.7,3.5 17.8,3.4 18.1,3.4 18.9,4.2 19.7,5 19.7,5.3 19.6,5.4 19,6.2 18.4,7.1C18.9,7.9,19.3,8.8,19.6,9.8L19.6,9.9 20.5,10 20.7,10 21.7,10.2 21.9,10.2 21.9,12.8 21.7,12.8 20.7,13 19.6,13.2C19.4,14.2,19,15.1,18.5,15.9L19.8,17.6C19.8,17.6,19.8,17.7,19.9,17.7L19.9,18 19.1,18.8 18.3,19.6 18,19.6 17.9,19.5 16,18.4C15.2,18.9,14.2,19.3,13.3,19.5L13.3,19.6 13,21.7 13,21.9 10.4,21.9 10.4,21.7 10.2,20.5 10,19.4C9.1,19.2,8.2,18.8,7.4,18.3L7,18.4 5.3,19.7 5.2,19.8 4.9,19.8 4.1,19 3.3,18.2 3.3,17.9 3.4,17.8 4.2,16.7 4.6,16 4.8,15.7C4.4,15,4,14.2,3.8,13.3L3.4,13.2 1.4,12.9 1.2,12.9 1.2,10.3 1.4,10.3 3.5,9.99999999999999 3.8,9.9C4,8.99999999999999,4.3,8.2,4.8,7.4L4.1,6.4 3.3,5.4 3.2,5.3 3.2,5 4,4.1 4.8,3.3 5.1,3.3 5.2,3.4 7,4.7 7.2,4.9C8,4.3,8.8,3.9,9.8,3.7L10,2.6 10.1,1.3z M13.9,1.3L14.1,2.8C14.7,3,15.3,3.2,15.8,3.5L17,2.6C17.1,2.5 17.4,2.3 17.7,2.2 18,2.1 18.4,2.2 18.8,2.5L19.6,3.3 20.4,4.1C20.7,4.4 20.8,4.8 20.7,5.2 20.6,5.5 20.5,5.7 20.3,5.9L19.5,7C19.8,7.6,20.1,8.2,20.3,8.8L21.7,9C21.9,9 22.2,9.1 22.4,9.2 22.7,9.4 22.9,9.7 22.9,10.2L22.9,12.6C22.9,13.1 22.7,13.4 22.4,13.6 22.2,13.8 21.9,13.8 21.7,13.8L20.3,14C20.1,14.6,19.9,15.2,19.6,15.7L20.4,16.8C20.5,16.9 20.7,17.2 20.8,17.5 20.9,17.8 20.8,18.2 20.5,18.6L19.7,19.4 18.9,20.2C18.6,20.5 18.2,20.6 17.8,20.5 17.5,20.4 17.3,20.3 17.1,20.1L15.9,19.2C15.3,19.5,14.7,19.7,14.1,19.9L13.9,21.4C13.9,21.6 13.8,21.9 13.7,22.1 13.5,22.4 13.2,22.6 12.7,22.6L10.3,22.6C9.8,22.6 9.5,22.4 9.3,22.1 9.1,21.9 9.1,21.6 9.1,21.4L8.9,19.8C8.3,19.6,7.8,19.4,7.2,19.1L5.9,20.1C5.8,20.2 5.5,20.4 5.2,20.5 4.9,20.6 4.5,20.5 4.1,20.2L3.3,19.4 2.5,18.6C2.2,18.3 2.1,17.9 2.2,17.5 2.3,17.2 2.4,17 2.6,16.8L3.6,15.4C3.4,14.9,3.2,14.5,3,14L1.2,14C1,14 0.7,13.9 0.5,13.8 0.2,13.6 0,13.3 0,12.8L0,10.4C0,9.9 0.2,9.6 0.5,9.4 0.8,9.2 1,9.2 1.2,9.2L3,8.9 3.6,7.4 2.5,6C2.4,5.8 2.2,5.6 2.2,5.3 2.1,5 2.2,4.6 2.5,4.2L3.3,3.4 4.1,2.6C4.4,2.3 4.8,2.2 5.2,2.3 5.5,2.4 5.7,2.5 5.9,2.7L7.2,3.7C7.7,3.4,8.2,3.2,8.8,3L9,1.4C9,1.2 9.1,0.9 9.2,0.7 9.5,0.3 9.8,0 10.3,0L12.7,0C13.2,0 13.5,0.2 13.7,0.5 13.8,0.8 13.9,1.1 13.9,1.3z" />
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Normal}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="3.4" RadiusY="3.4" Center="11.5,11.5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Images.OptionsMenu.Setting.Pressed">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Pressed}" Geometry="F0 M24,24z M0,0z M10.1,1.3L10.1,1.1 12.7,1.1 12.7,1.3 13,2.6 13.2,3.7C14.2,3.9,15.1,4.3,15.9,4.8L16,4.8 17.7,3.5 17.8,3.4 18.1,3.4 18.9,4.2 19.7,5 19.7,5.3 19.6,5.4 19,6.2 18.4,7.1C18.9,7.9,19.3,8.8,19.6,9.8L19.6,9.9 20.5,10 20.7,10 21.7,10.2 21.9,10.2 21.9,12.8 21.7,12.8 20.7,13 19.6,13.2C19.4,14.2,19,15.1,18.5,15.9L19.8,17.6C19.8,17.6,19.8,17.7,19.9,17.7L19.9,18 19.1,18.8 18.3,19.6 18,19.6 17.9,19.5 16,18.4C15.2,18.9,14.2,19.3,13.3,19.5L13.3,19.6 13,21.7 13,21.9 10.4,21.9 10.4,21.7 10.2,20.5 10,19.4C9.1,19.2,8.2,18.8,7.4,18.3L7,18.4 5.3,19.7 5.2,19.8 4.9,19.8 4.1,19 3.3,18.2 3.3,17.9 3.4,17.8 4.2,16.7 4.6,16 4.8,15.7C4.4,15,4,14.2,3.8,13.3L3.4,13.2 1.4,12.9 1.2,12.9 1.2,10.3 1.4,10.3 3.5,9.99999999999999 3.8,9.9C4,8.99999999999999,4.3,8.2,4.8,7.4L4.1,6.4 3.3,5.4 3.2,5.3 3.2,5 4,4.1 4.8,3.3 5.1,3.3 5.2,3.4 7,4.7 7.2,4.9C8,4.3,8.8,3.9,9.8,3.7L10,2.6 10.1,1.3z M13.9,1.3L14.1,2.8C14.7,3,15.3,3.2,15.8,3.5L17,2.6C17.1,2.5 17.4,2.3 17.7,2.2 18,2.1 18.4,2.2 18.8,2.5L19.6,3.3 20.4,4.1C20.7,4.4 20.8,4.8 20.7,5.2 20.6,5.5 20.5,5.7 20.3,5.9L19.5,7C19.8,7.6,20.1,8.2,20.3,8.8L21.7,9C21.9,9 22.2,9.1 22.4,9.2 22.7,9.4 22.9,9.7 22.9,10.2L22.9,12.6C22.9,13.1 22.7,13.4 22.4,13.6 22.2,13.8 21.9,13.8 21.7,13.8L20.3,14C20.1,14.6,19.9,15.2,19.6,15.7L20.4,16.8C20.5,16.9 20.7,17.2 20.8,17.5 20.9,17.8 20.8,18.2 20.5,18.6L19.7,19.4 18.9,20.2C18.6,20.5 18.2,20.6 17.8,20.5 17.5,20.4 17.3,20.3 17.1,20.1L15.9,19.2C15.3,19.5,14.7,19.7,14.1,19.9L13.9,21.4C13.9,21.6 13.8,21.9 13.7,22.1 13.5,22.4 13.2,22.6 12.7,22.6L10.3,22.6C9.8,22.6 9.5,22.4 9.3,22.1 9.1,21.9 9.1,21.6 9.1,21.4L8.9,19.8C8.3,19.6,7.8,19.4,7.2,19.1L5.9,20.1C5.8,20.2 5.5,20.4 5.2,20.5 4.9,20.6 4.5,20.5 4.1,20.2L3.3,19.4 2.5,18.6C2.2,18.3 2.1,17.9 2.2,17.5 2.3,17.2 2.4,17 2.6,16.8L3.6,15.4C3.4,14.9,3.2,14.5,3,14L1.2,14C1,14 0.7,13.9 0.5,13.8 0.2,13.6 0,13.3 0,12.8L0,10.4C0,9.9 0.2,9.6 0.5,9.4 0.8,9.2 1,9.2 1.2,9.2L3,8.9 3.6,7.4 2.5,6C2.4,5.8 2.2,5.6 2.2,5.3 2.1,5 2.2,4.6 2.5,4.2L3.3,3.4 4.1,2.6C4.4,2.3 4.8,2.2 5.2,2.3 5.5,2.4 5.7,2.5 5.9,2.7L7.2,3.7C7.7,3.4,8.2,3.2,8.8,3L9,1.4C9,1.2 9.1,0.9 9.2,0.7 9.5,0.3 9.8,0 10.3,0L12.7,0C13.2,0 13.5,0.2 13.7,0.5 13.8,0.8 13.9,1.1 13.9,1.3z" />
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Pressed}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="3.4" RadiusY="3.4" Center="11.5,11.5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</ResourceDictionary>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

16
src/WPFBeginner/App.xaml Normal file
View File

@@ -0,0 +1,16 @@
<Application x:Class="WPFBeginner.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFBeginner"
xmlns:converters="clr-namespace:WPFBeginner.Converters">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Themes/DefaultTheme.xaml"/>
<ResourceDictionary Source="Resources/Languages/ko-KR.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,52 @@
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using WPFBeginner.Services;
using WPFBeginner.ViewModels;
using WPFBeginner.Views;
namespace WPFBeginner;
public partial class App : Application
{
private static IServiceProvider? _serviceProvider;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var services = new ServiceCollection();
ConfigureServices(services);
_serviceProvider = services.BuildServiceProvider();
// Load settings first
var settingsService = _serviceProvider.GetRequiredService<ISettingsService>();
settingsService.Load();
// Show login window
var loginWindow = new LoginWindow();
loginWindow.Show();
}
private static void ConfigureServices(IServiceCollection services)
{
// Services
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<IMemberService, MemberService>();
// ViewModels
services.AddTransient<LoginViewModel>();
services.AddTransient<MainViewModel>();
services.AddTransient<RegistViewModel>();
}
public static T GetService<T>() where T : class
{
if (_serviceProvider == null)
{
throw new InvalidOperationException("Service provider is not initialized.");
}
return _serviceProvider.GetRequiredService<T>();
}
}

View File

@@ -1,6 +1,6 @@
using System.Windows;
[assembly: ThemeInfo(
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)

View File

@@ -0,0 +1,22 @@
<UserControl x:Class="WPFBeginner.Controls.InputPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
mc:Ignorable="d"
x:Name="Root">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding LabelText, ElementName=Root}"
Style="{DynamicResource DefaultLabelStyle}"/>
<TextBlock Text="*"
Foreground="{DynamicResource ErrorBrush}"
Visibility="{Binding IsRequired, ElementName=Root, Converter={StaticResource BoolToVisibilityConverter}}"
Margin="2,0,0,0"
VerticalAlignment="Center"/>
</StackPanel>
<TextBox Text="{Binding Text, ElementName=Root, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{DynamicResource DefaultTextBoxStyle}"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,53 @@
using System.Windows;
using System.Windows.Controls;
namespace WPFBeginner.Controls;
public partial class InputPanel : UserControl
{
public static readonly DependencyProperty LabelTextProperty =
DependencyProperty.Register(
nameof(LabelText),
typeof(string),
typeof(InputPanel),
new PropertyMetadata(string.Empty));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(InputPanel),
new FrameworkPropertyMetadata(
string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public static readonly DependencyProperty IsRequiredProperty =
DependencyProperty.Register(
nameof(IsRequired),
typeof(bool),
typeof(InputPanel),
new PropertyMetadata(false));
public string LabelText
{
get => (string)GetValue(LabelTextProperty);
set => SetValue(LabelTextProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool IsRequired
{
get => (bool)GetValue(IsRequiredProperty);
set => SetValue(IsRequiredProperty, value);
}
public InputPanel()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,26 @@
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WPFBeginner.Converters;
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility visibility)
{
return visibility == Visibility.Visible;
}
return false;
}
}

View File

@@ -0,0 +1,15 @@
namespace WPFBeginner.Models;
public class AppSettings
{
public LoginSettings Login { get; set; } = new();
public string Language { get; set; } = "ko-KR";
public string Theme { get; set; } = "DefaultTheme";
public List<Member> DefaultMembers { get; set; } = new();
}
public class LoginSettings
{
public string User { get; set; } = "admin";
public string Password { get; set; } = "0000";
}

View File

@@ -0,0 +1,33 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace WPFBeginner.Models;
public partial class Member : ObservableObject
{
[ObservableProperty]
private string _name = string.Empty;
[ObservableProperty]
private string _call = string.Empty;
[ObservableProperty]
private string _eMail = string.Empty;
[ObservableProperty]
private string _no = string.Empty;
public Member() { }
public Member(string name, string call, string email, string no)
{
Name = name;
Call = call;
EMail = email;
No = no;
}
public Member Clone()
{
return new Member(Name, Call, EMail, No);
}
}

View File

@@ -0,0 +1,53 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<!-- Login Window -->
<system:String x:Key="Login_Title">Login</system:String>
<system:String x:Key="Login_Welcome">Welcome</system:String>
<system:String x:Key="Login_UserId">User ID</system:String>
<system:String x:Key="Login_Password">Password</system:String>
<system:String x:Key="Login_LoginButton">Login</system:String>
<system:String x:Key="Login_Error">Invalid ID or Password</system:String>
<!-- Main Window -->
<system:String x:Key="Main_Title">Member Management</system:String>
<system:String x:Key="Main_Search">Search</system:String>
<system:String x:Key="Main_SearchPlaceholder">Enter search keyword</system:String>
<system:String x:Key="Main_Add">Add</system:String>
<system:String x:Key="Main_Delete">Delete</system:String>
<system:String x:Key="Main_Reset">Reset</system:String>
<system:String x:Key="Main_Settings">Settings</system:String>
<system:String x:Key="Main_NoResults">No results found</system:String>
<!-- Main Window - DataGrid -->
<system:String x:Key="Main_Column_Name">Name</system:String>
<system:String x:Key="Main_Column_Call">Phone</system:String>
<system:String x:Key="Main_Column_Email">Email</system:String>
<system:String x:Key="Main_Column_No">Employee No.</system:String>
<!-- Main Window - Settings -->
<system:String x:Key="Settings_Language">Language</system:String>
<system:String x:Key="Settings_Korean">한국어</system:String>
<system:String x:Key="Settings_English">English</system:String>
<system:String x:Key="Settings_Theme">Theme</system:String>
<system:String x:Key="Settings_DefaultTheme">Default (Dark)</system:String>
<system:String x:Key="Settings_LightTheme">Light</system:String>
<!-- Registration Window -->
<system:String x:Key="Regist_Title">Member Registration</system:String>
<system:String x:Key="Regist_Name">Name</system:String>
<system:String x:Key="Regist_Call">Phone</system:String>
<system:String x:Key="Regist_Email">Email</system:String>
<system:String x:Key="Regist_No">Employee No.</system:String>
<system:String x:Key="Regist_Register">Register</system:String>
<system:String x:Key="Regist_Cancel">Cancel</system:String>
<system:String x:Key="Regist_NameRequired">Name is required</system:String>
<system:String x:Key="Regist_EmailRequired">Email is required</system:String>
<!-- Common -->
<system:String x:Key="Common_Close">Close</system:String>
<system:String x:Key="Common_OK">OK</system:String>
<system:String x:Key="Common_Cancel">Cancel</system:String>
</ResourceDictionary>

View File

@@ -0,0 +1,53 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<!-- Login Window -->
<system:String x:Key="Login_Title">로그인</system:String>
<system:String x:Key="Login_Welcome">환영합니다</system:String>
<system:String x:Key="Login_UserId">아이디</system:String>
<system:String x:Key="Login_Password">비밀번호</system:String>
<system:String x:Key="Login_LoginButton">로그인</system:String>
<system:String x:Key="Login_Error">아이디 또는 비밀번호가 틀렸습니다</system:String>
<!-- Main Window -->
<system:String x:Key="Main_Title">회원 관리</system:String>
<system:String x:Key="Main_Search">검색</system:String>
<system:String x:Key="Main_SearchPlaceholder">검색어를 입력하세요</system:String>
<system:String x:Key="Main_Add">추가</system:String>
<system:String x:Key="Main_Delete">삭제</system:String>
<system:String x:Key="Main_Reset">초기화</system:String>
<system:String x:Key="Main_Settings">설정</system:String>
<system:String x:Key="Main_NoResults">검색 결과가 없습니다</system:String>
<!-- Main Window - DataGrid -->
<system:String x:Key="Main_Column_Name">이름</system:String>
<system:String x:Key="Main_Column_Call">연락처</system:String>
<system:String x:Key="Main_Column_Email">이메일</system:String>
<system:String x:Key="Main_Column_No">사번</system:String>
<!-- Main Window - Settings -->
<system:String x:Key="Settings_Language">언어</system:String>
<system:String x:Key="Settings_Korean">한국어</system:String>
<system:String x:Key="Settings_English">English</system:String>
<system:String x:Key="Settings_Theme">테마</system:String>
<system:String x:Key="Settings_DefaultTheme">기본 (어두운)</system:String>
<system:String x:Key="Settings_LightTheme">밝은</system:String>
<!-- Registration Window -->
<system:String x:Key="Regist_Title">회원 등록</system:String>
<system:String x:Key="Regist_Name">이름</system:String>
<system:String x:Key="Regist_Call">연락처</system:String>
<system:String x:Key="Regist_Email">이메일</system:String>
<system:String x:Key="Regist_No">사번</system:String>
<system:String x:Key="Regist_Register">등록</system:String>
<system:String x:Key="Regist_Cancel">취소</system:String>
<system:String x:Key="Regist_NameRequired">이름은 필수입니다</system:String>
<system:String x:Key="Regist_EmailRequired">이메일은 필수입니다</system:String>
<!-- Common -->
<system:String x:Key="Common_Close">닫기</system:String>
<system:String x:Key="Common_OK">확인</system:String>
<system:String x:Key="Common_Cancel">취소</system:String>
</ResourceDictionary>

View File

@@ -0,0 +1,192 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colors -->
<Color x:Key="PrimaryColor">#2D2D30</Color>
<Color x:Key="SecondaryColor">#3E3E42</Color>
<Color x:Key="AccentColor">#007ACC</Color>
<Color x:Key="TextColor">#FFFFFF</Color>
<Color x:Key="SubTextColor">#A0A0A0</Color>
<Color x:Key="BorderColor">#555555</Color>
<Color x:Key="ErrorColor">#FF5555</Color>
<Color x:Key="SuccessColor">#55FF55</Color>
<!-- Brushes -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource SecondaryColor}"/>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="SubTextBrush" Color="{StaticResource SubTextColor}"/>
<SolidColorBrush x:Key="BorderBrush" Color="{StaticResource BorderColor}"/>
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}"/>
<SolidColorBrush x:Key="SuccessBrush" Color="{StaticResource SuccessColor}"/>
<!-- Window Background -->
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="{StaticResource PrimaryColor}"/>
<!-- Button Style -->
<Style x:Key="PrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1A8AD4"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#005A9E"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#4E4E52"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TextBox Style -->
<Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="CaretBrush" Value="{StaticResource TextBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PasswordBox Style -->
<Style x:Key="DefaultPasswordBoxStyle" TargetType="PasswordBox">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="CaretBrush" Value="{StaticResource TextBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Label Style -->
<Style x:Key="DefaultLabelStyle" TargetType="Label">
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Padding" Value="0,0,0,4"/>
</Style>
<!-- DataGrid Style -->
<Style x:Key="DefaultDataGridStyle" TargetType="DataGrid">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="RowBackground" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="AlternatingRowBackground" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="HeadersVisibility" Value="Column"/>
</Style>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="{StaticResource PrimaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="10,6"/>
<Setter Property="BorderThickness" Value="0"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#3E3E42"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- RadioButton Style -->
<Style x:Key="DefaultRadioButtonStyle" TargetType="RadioButton">
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Margin" Value="0,4"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,192 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colors -->
<Color x:Key="PrimaryColor">#FFFFFF</Color>
<Color x:Key="SecondaryColor">#F5F5F5</Color>
<Color x:Key="AccentColor">#007ACC</Color>
<Color x:Key="TextColor">#1E1E1E</Color>
<Color x:Key="SubTextColor">#666666</Color>
<Color x:Key="BorderColor">#CCCCCC</Color>
<Color x:Key="ErrorColor">#D32F2F</Color>
<Color x:Key="SuccessColor">#388E3C</Color>
<!-- Brushes -->
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}"/>
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource SecondaryColor}"/>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="SubTextBrush" Color="{StaticResource SubTextColor}"/>
<SolidColorBrush x:Key="BorderBrush" Color="{StaticResource BorderColor}"/>
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}"/>
<SolidColorBrush x:Key="SuccessBrush" Color="{StaticResource SuccessColor}"/>
<!-- Window Background -->
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="{StaticResource PrimaryColor}"/>
<!-- Button Style -->
<Style x:Key="PrimaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1A8AD4"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#005A9E"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SecondaryButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="15,8"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E0E0E0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TextBox Style -->
<Style x:Key="DefaultTextBoxStyle" TargetType="TextBox">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="CaretBrush" Value="{StaticResource TextBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- PasswordBox Style -->
<Style x:Key="DefaultPasswordBoxStyle" TargetType="PasswordBox">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="CaretBrush" Value="{StaticResource TextBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4">
<ScrollViewer x:Name="PART_ContentHost" Margin="{TemplateBinding Padding}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Label Style -->
<Style x:Key="DefaultLabelStyle" TargetType="Label">
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Padding" Value="0,0,0,4"/>
</Style>
<!-- DataGrid Style -->
<Style x:Key="DefaultDataGridStyle" TargetType="DataGrid">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="RowBackground" Value="White"/>
<Setter Property="AlternatingRowBackground" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
<Setter Property="HorizontalGridLinesBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="HeadersVisibility" Value="Column"/>
</Style>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="{StaticResource SecondaryBrush}"/>
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Padding" Value="10,8"/>
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
<Setter Property="BorderThickness" Value="0,0,1,1"/>
</Style>
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="10,6"/>
<Setter Property="BorderThickness" Value="0"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E8E8E8"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- RadioButton Style -->
<Style x:Key="DefaultRadioButtonStyle" TargetType="RadioButton">
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
<Setter Property="Margin" Value="0,4"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,61 @@
using System.Collections.ObjectModel;
using WPFBeginner.Models;
namespace WPFBeginner.Services;
public interface IMemberService
{
ObservableCollection<Member> Members { get; }
void LoadDefaultMembers(List<Member> defaultMembers);
void Add(Member member);
void Remove(Member member);
void Reset();
IEnumerable<Member> Search(string keyword);
}
public class MemberService : IMemberService
{
private readonly List<Member> _defaultMembers = new();
public ObservableCollection<Member> Members { get; } = new();
public void LoadDefaultMembers(List<Member> defaultMembers)
{
_defaultMembers.Clear();
_defaultMembers.AddRange(defaultMembers.Select(m => m.Clone()));
Reset();
}
public void Add(Member member)
{
Members.Add(member);
}
public void Remove(Member member)
{
Members.Remove(member);
}
public void Reset()
{
Members.Clear();
foreach (var member in _defaultMembers)
{
Members.Add(member.Clone());
}
}
public IEnumerable<Member> Search(string keyword)
{
if (string.IsNullOrWhiteSpace(keyword))
{
return Members;
}
var lowerKeyword = keyword.ToLower();
return Members.Where(m =>
m.Name.ToLower().Contains(lowerKeyword) ||
m.Call.ToLower().Contains(lowerKeyword) ||
m.EMail.ToLower().Contains(lowerKeyword) ||
m.No.ToLower().Contains(lowerKeyword));
}
}

View File

@@ -0,0 +1,114 @@
using System.IO;
using System.Text.Json;
using System.Windows;
using WPFBeginner.Models;
namespace WPFBeginner.Services;
public interface ISettingsService
{
AppSettings Settings { get; }
void Load();
void Save();
void SetLanguage(string language);
void SetTheme(string theme);
string CurrentLanguage { get; }
string CurrentTheme { get; }
}
public class SettingsService : ISettingsService
{
private readonly string _settingsPath;
private AppSettings _settings = new();
public AppSettings Settings => _settings;
public string CurrentLanguage => _settings.Language;
public string CurrentTheme => _settings.Theme;
public SettingsService()
{
_settingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
}
public void Load()
{
try
{
if (File.Exists(_settingsPath))
{
var json = File.ReadAllText(_settingsPath);
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
_settings = JsonSerializer.Deserialize<AppSettings>(json, options) ?? new AppSettings();
}
}
catch
{
_settings = new AppSettings();
}
ApplyLanguage(_settings.Language);
ApplyTheme(_settings.Theme);
}
public void Save()
{
try
{
var options = new JsonSerializerOptions { WriteIndented = true };
var json = JsonSerializer.Serialize(_settings, options);
File.WriteAllText(_settingsPath, json);
}
catch
{
// Ignore save errors
}
}
public void SetLanguage(string language)
{
_settings.Language = language;
ApplyLanguage(language);
Save();
}
public void SetTheme(string theme)
{
_settings.Theme = theme;
ApplyTheme(theme);
Save();
}
private void ApplyLanguage(string language)
{
var dict = new ResourceDictionary();
var langFile = language == "en-US" ? "en-US" : "ko-KR";
dict.Source = new Uri($"pack://application:,,,/Resources/Languages/{langFile}.xaml");
// Remove existing language dictionary
var existingLang = Application.Current.Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.OriginalString.Contains("/Languages/") == true);
if (existingLang != null)
{
Application.Current.Resources.MergedDictionaries.Remove(existingLang);
}
Application.Current.Resources.MergedDictionaries.Add(dict);
}
private void ApplyTheme(string theme)
{
var dict = new ResourceDictionary();
var themeFile = theme == "LightTheme" ? "LightTheme" : "DefaultTheme";
dict.Source = new Uri($"pack://application:,,,/Resources/Themes/{themeFile}.xaml");
// Remove existing theme dictionary
var existingTheme = Application.Current.Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.OriginalString.Contains("/Themes/") == true);
if (existingTheme != null)
{
Application.Current.Resources.MergedDictionaries.Remove(existingTheme);
}
Application.Current.Resources.MergedDictionaries.Add(dict);
}
}

View File

@@ -0,0 +1,62 @@
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using WPFBeginner.Services;
using WPFBeginner.Views;
namespace WPFBeginner.ViewModels;
public partial class LoginViewModel : ObservableObject
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
private string _userId = string.Empty;
[ObservableProperty]
private string _password = string.Empty;
[ObservableProperty]
private string _errorMessage = string.Empty;
[ObservableProperty]
private bool _hasError;
public string WelcomeMessage => _settingsService.CurrentLanguage == "en-US"
? "Welcome"
: "환영합니다";
public LoginViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
}
[RelayCommand]
private void Login(Window window)
{
var settings = _settingsService.Settings.Login;
if (UserId == settings.User && Password == settings.Password)
{
HasError = false;
ErrorMessage = string.Empty;
var mainWindow = new MainWindow();
mainWindow.Show();
window.Close();
}
else
{
HasError = true;
ErrorMessage = _settingsService.CurrentLanguage == "en-US"
? "Invalid ID or Password"
: "아이디 또는 비밀번호가 틀렸습니다";
}
}
[RelayCommand]
private void Close(Window window)
{
Application.Current.Shutdown();
}
}

View File

@@ -0,0 +1,165 @@
using System.Collections.ObjectModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using WPFBeginner.Models;
using WPFBeginner.Services;
using WPFBeginner.Views;
namespace WPFBeginner.ViewModels;
public partial class MainViewModel : ObservableObject
{
private readonly ISettingsService _settingsService;
private readonly IMemberService _memberService;
[ObservableProperty]
private string _searchKeyword = string.Empty;
[ObservableProperty]
private Member? _selectedMember;
[ObservableProperty]
private ObservableCollection<Member> _filteredMembers = new();
[ObservableProperty]
private bool _isKorean = true;
[ObservableProperty]
private bool _isEnglish;
[ObservableProperty]
private bool _isDefaultTheme = true;
[ObservableProperty]
private bool _isLightTheme;
[ObservableProperty]
private string _searchResultMessage = string.Empty;
[ObservableProperty]
private bool _hasSearchResult = true;
public MainViewModel(ISettingsService settingsService, IMemberService memberService)
{
_settingsService = settingsService;
_memberService = memberService;
// Load default members
_memberService.LoadDefaultMembers(_settingsService.Settings.DefaultMembers);
// Set initial language/theme based on settings
IsKorean = _settingsService.CurrentLanguage == "ko-KR";
IsEnglish = _settingsService.CurrentLanguage == "en-US";
IsDefaultTheme = _settingsService.CurrentTheme == "DefaultTheme";
IsLightTheme = _settingsService.CurrentTheme == "LightTheme";
// Initialize filtered members
RefreshFilteredMembers();
}
private void RefreshFilteredMembers()
{
FilteredMembers = new ObservableCollection<Member>(_memberService.Members);
}
[RelayCommand]
private void Search()
{
var results = _memberService.Search(SearchKeyword).ToList();
FilteredMembers = new ObservableCollection<Member>(results);
if (results.Count == 0)
{
HasSearchResult = false;
SearchResultMessage = _settingsService.CurrentLanguage == "en-US"
? "No results found"
: "검색 결과가 없습니다";
MessageBox.Show(SearchResultMessage,
_settingsService.CurrentLanguage == "en-US" ? "Search" : "검색",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
else
{
HasSearchResult = true;
SearchResultMessage = string.Empty;
}
}
[RelayCommand]
private void AddMember()
{
var registWindow = new RegistWindow();
registWindow.MemberAdded += OnMemberAdded;
registWindow.ShowDialog();
}
private void OnMemberAdded(object? sender, Member member)
{
_memberService.Add(member);
RefreshFilteredMembers();
}
[RelayCommand]
private void DeleteMember()
{
if (SelectedMember != null)
{
_memberService.Remove(SelectedMember);
RefreshFilteredMembers();
SelectedMember = null;
}
}
[RelayCommand]
private void Reset()
{
SearchKeyword = string.Empty;
_memberService.Reset();
RefreshFilteredMembers();
HasSearchResult = true;
SearchResultMessage = string.Empty;
}
[RelayCommand]
private void SetKorean()
{
IsKorean = true;
IsEnglish = false;
_settingsService.SetLanguage("ko-KR");
OnPropertyChanged(nameof(IsKorean));
OnPropertyChanged(nameof(IsEnglish));
}
[RelayCommand]
private void SetEnglish()
{
IsKorean = false;
IsEnglish = true;
_settingsService.SetLanguage("en-US");
OnPropertyChanged(nameof(IsKorean));
OnPropertyChanged(nameof(IsEnglish));
}
[RelayCommand]
private void SetDefaultTheme()
{
IsDefaultTheme = true;
IsLightTheme = false;
_settingsService.SetTheme("DefaultTheme");
OnPropertyChanged(nameof(IsDefaultTheme));
OnPropertyChanged(nameof(IsLightTheme));
}
[RelayCommand]
private void SetLightTheme()
{
IsDefaultTheme = false;
IsLightTheme = true;
_settingsService.SetTheme("LightTheme");
OnPropertyChanged(nameof(IsDefaultTheme));
OnPropertyChanged(nameof(IsLightTheme));
}
}

View File

@@ -0,0 +1,93 @@
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using WPFBeginner.Models;
using WPFBeginner.Services;
namespace WPFBeginner.ViewModels;
public partial class RegistViewModel : ObservableObject
{
private readonly ISettingsService _settingsService;
[ObservableProperty]
private string _name = string.Empty;
[ObservableProperty]
private string _call = string.Empty;
[ObservableProperty]
private string _eMail = string.Empty;
[ObservableProperty]
private string _no = string.Empty;
[ObservableProperty]
private string _errorMessage = string.Empty;
[ObservableProperty]
private bool _hasError;
public event EventHandler<Member>? MemberAdded;
public RegistViewModel(ISettingsService settingsService)
{
_settingsService = settingsService;
}
[RelayCommand]
private void Register(Window window)
{
// Validate required fields
if (string.IsNullOrWhiteSpace(Name))
{
HasError = true;
ErrorMessage = _settingsService.CurrentLanguage == "en-US"
? "Name is required"
: "이름은 필수입니다";
return;
}
if (string.IsNullOrWhiteSpace(EMail))
{
HasError = true;
ErrorMessage = _settingsService.CurrentLanguage == "en-US"
? "Email is required"
: "이메일은 필수입니다";
return;
}
HasError = false;
ErrorMessage = string.Empty;
var member = new Member(Name, Call, EMail, No);
MemberAdded?.Invoke(this, member);
window.Close();
}
[RelayCommand]
private void Cancel(Window window)
{
window.Close();
}
[RelayCommand]
private void Minimize(Window window)
{
window.WindowState = WindowState.Minimized;
}
[RelayCommand]
private void Maximize(Window window)
{
window.WindowState = window.WindowState == WindowState.Maximized
? WindowState.Normal
: WindowState.Maximized;
}
[RelayCommand]
private void Close(Window window)
{
window.Close();
}
}

View File

@@ -0,0 +1,198 @@
<Window x:Class="WPFBeginner.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
mc:Ignorable="d"
Title="{DynamicResource Login_Title}"
Height="540" Width="440"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="NoResize">
<Border Margin="15" CornerRadius="12">
<Border.Effect>
<DropShadowEffect BlurRadius="15" ShadowDepth="0" Opacity="0.5"/>
</Border.Effect>
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#1a1a2e" Offset="0"/>
<GradientStop Color="#16213e" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title Bar -->
<Grid Grid.Row="0" Background="Transparent" MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1"
Content="✕"
Width="45" Height="35"
Background="Transparent"
Foreground="#808080"
BorderThickness="0"
FontSize="14"
Cursor="Hand"
Command="{Binding CloseCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" CornerRadius="0,12,0,0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E81123"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
<!-- Content -->
<StackPanel Grid.Row="1" Margin="40,10,40,30" VerticalAlignment="Center">
<!-- Logo Icon -->
<Border Width="70" Height="70"
CornerRadius="35"
Margin="0,0,0,20"
HorizontalAlignment="Center">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="#0f3460" Offset="0"/>
<GradientStop Color="#e94560" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock Text="W"
FontSize="32"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<!-- Welcome Message -->
<TextBlock Text="{DynamicResource Login_Welcome}"
Foreground="White"
FontSize="26"
FontWeight="SemiBold"
HorizontalAlignment="Center"
Margin="0,0,0,5"/>
<TextBlock Text="WPF Beginner"
Foreground="#808080"
FontSize="13"
HorizontalAlignment="Center"
Margin="0,0,0,25"/>
<!-- User ID -->
<TextBlock Text="{DynamicResource Login_UserId}"
Foreground="#a0a0a0"
FontSize="12"
Margin="5,0,0,6"/>
<Border CornerRadius="8" Background="#0f3460" Margin="0,0,0,15">
<TextBox Text="{Binding UserId, UpdateSourceTrigger=PropertyChanged}"
Background="Transparent"
Foreground="White"
BorderThickness="0"
Padding="12,10"
FontSize="14"
CaretBrush="White"/>
</Border>
<!-- Password -->
<TextBlock Text="{DynamicResource Login_Password}"
Foreground="#a0a0a0"
FontSize="12"
Margin="5,0,0,6"/>
<Border CornerRadius="8" Background="#0f3460" Margin="0,0,0,12">
<PasswordBox x:Name="PasswordBox"
Background="Transparent"
Foreground="White"
BorderThickness="0"
Padding="12,10"
FontSize="14"
CaretBrush="White"
PasswordChanged="PasswordBox_PasswordChanged"/>
</Border>
<!-- Error Message -->
<Border Background="#3d1f2f"
CornerRadius="6"
Padding="10,6"
Margin="0,0,0,10"
Visibility="{Binding HasError, Converter={StaticResource BoolToVisibilityConverter}}">
<TextBlock Text="{Binding ErrorMessage}"
Foreground="#ff6b6b"
FontSize="12"
HorizontalAlignment="Center"/>
</Border>
<!-- Login Button -->
<Button Command="{Binding LoginCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Cursor="Hand"
Margin="0,5,0,0">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="ButtonBorder" CornerRadius="8" Padding="15,12">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#e94560" Offset="0"/>
<GradientStop Color="#0f3460" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<TextBlock Text="{DynamicResource Login_LoginButton}"
Foreground="White"
FontSize="15"
FontWeight="SemiBold"
HorizontalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonBorder" Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#ff6b6b" Offset="0"/>
<GradientStop Color="#1a5276" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonBorder" Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#c0392b" Offset="0"/>
<GradientStop Color="#0a3d62" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</Grid>
</Border>
</Window>

View File

@@ -0,0 +1,30 @@
using System.Windows;
using System.Windows.Input;
using WPFBeginner.ViewModels;
namespace WPFBeginner.Views;
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
DataContext = App.GetService<LoginViewModel>();
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
DragMove();
}
}
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
if (DataContext is LoginViewModel viewModel)
{
viewModel.Password = PasswordBox.Password;
}
}
}

View File

@@ -0,0 +1,143 @@
<Window x:Class="WPFBeginner.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
mc:Ignorable="d"
Title="{DynamicResource Main_Title}"
Height="600" Width="900"
MinWidth="700" MinHeight="400"
WindowStartupLocation="CenterScreen"
Background="{DynamicResource WindowBackgroundBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Top Bar -->
<Border Grid.Row="0" Background="{DynamicResource SecondaryBrush}" Padding="15,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Search Area -->
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBox Text="{Binding SearchKeyword, UpdateSourceTrigger=PropertyChanged}"
Style="{DynamicResource DefaultTextBoxStyle}"
Width="250"
VerticalAlignment="Center"
KeyDown="SearchTextBox_KeyDown"/>
<Button Content="{DynamicResource Main_Search}"
Style="{DynamicResource PrimaryButtonStyle}"
Command="{Binding SearchCommand}"
Margin="10,0,0,0"
Padding="20,8"/>
</StackPanel>
<!-- Action Buttons -->
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Content="{DynamicResource Main_Add}"
Style="{DynamicResource PrimaryButtonStyle}"
Command="{Binding AddMemberCommand}"
Margin="0,0,10,0"/>
<Button Content="{DynamicResource Main_Delete}"
Style="{DynamicResource SecondaryButtonStyle}"
Command="{Binding DeleteMemberCommand}"
Margin="0,0,10,0"/>
<Button Content="{DynamicResource Main_Reset}"
Style="{DynamicResource SecondaryButtonStyle}"
Command="{Binding ResetCommand}"
Margin="0,0,10,0"/>
<Button x:Name="SettingsButton"
Content="{DynamicResource Main_Settings}"
Style="{DynamicResource SecondaryButtonStyle}"
Click="SettingsButton_Click"/>
</StackPanel>
</Grid>
</Border>
<!-- Main Content -->
<Grid Grid.Row="1">
<!-- DataGrid -->
<DataGrid ItemsSource="{Binding FilteredMembers}"
SelectedItem="{Binding SelectedMember}"
Style="{DynamicResource DefaultDataGridStyle}"
AutoGenerateColumns="False"
IsReadOnly="True"
SelectionMode="Single"
Margin="15">
<DataGrid.Columns>
<DataGridTextColumn Header="{DynamicResource Main_Column_Name}"
Binding="{Binding Name}"
Width="*"/>
<DataGridTextColumn Header="{DynamicResource Main_Column_Call}"
Binding="{Binding Call}"
Width="*"/>
<DataGridTextColumn Header="{DynamicResource Main_Column_Email}"
Binding="{Binding EMail}"
Width="2*"/>
<DataGridTextColumn Header="{DynamicResource Main_Column_No}"
Binding="{Binding No}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
<!-- Settings Panel (Popup) -->
<Popup x:Name="SettingsPopup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=SettingsButton}"
StaysOpen="False"
AllowsTransparency="True">
<Border Background="{DynamicResource SecondaryBrush}"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
CornerRadius="4"
Padding="15"
Margin="5">
<Border.Effect>
<DropShadowEffect BlurRadius="10" Opacity="0.3"/>
</Border.Effect>
<StackPanel Width="200">
<!-- Language Settings -->
<TextBlock Text="{DynamicResource Settings_Language}"
Foreground="{DynamicResource TextBrush}"
FontWeight="SemiBold"
Margin="0,0,0,10"/>
<RadioButton Content="{DynamicResource Settings_Korean}"
IsChecked="{Binding IsKorean}"
Style="{DynamicResource DefaultRadioButtonStyle}"
GroupName="Language"
Command="{Binding SetKoreanCommand}"/>
<RadioButton Content="{DynamicResource Settings_English}"
IsChecked="{Binding IsEnglish}"
Style="{DynamicResource DefaultRadioButtonStyle}"
GroupName="Language"
Command="{Binding SetEnglishCommand}"/>
<Separator Margin="0,15" Background="{DynamicResource BorderBrush}"/>
<!-- Theme Settings -->
<TextBlock Text="{DynamicResource Settings_Theme}"
Foreground="{DynamicResource TextBrush}"
FontWeight="SemiBold"
Margin="0,0,0,10"/>
<RadioButton Content="{DynamicResource Settings_DefaultTheme}"
IsChecked="{Binding IsDefaultTheme}"
Style="{DynamicResource DefaultRadioButtonStyle}"
GroupName="Theme"
Command="{Binding SetDefaultThemeCommand}"/>
<RadioButton Content="{DynamicResource Settings_LightTheme}"
IsChecked="{Binding IsLightTheme}"
Style="{DynamicResource DefaultRadioButtonStyle}"
GroupName="Theme"
Command="{Binding SetLightThemeCommand}"/>
</StackPanel>
</Border>
</Popup>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,27 @@
using System.Windows;
using System.Windows.Input;
using WPFBeginner.ViewModels;
namespace WPFBeginner.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = App.GetService<MainViewModel>();
}
private void SettingsButton_Click(object sender, RoutedEventArgs e)
{
SettingsPopup.IsOpen = !SettingsPopup.IsOpen;
}
private void SearchTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && DataContext is MainViewModel viewModel)
{
viewModel.SearchCommand.Execute(null);
}
}
}

View File

@@ -0,0 +1,183 @@
<Window x:Class="WPFBeginner.Views.RegistWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:controls="clr-namespace:WPFBeginner.Controls"
mc:Ignorable="d"
Title="{DynamicResource Regist_Title}"
Height="450" Width="400"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
ResizeMode="CanResize">
<Border Background="{DynamicResource WindowBackgroundBrush}"
CornerRadius="8"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Custom Title Bar -->
<Grid Grid.Row="0" Background="{DynamicResource SecondaryBrush}" MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Regist_Title}"
Foreground="{DynamicResource TextBrush}"
VerticalAlignment="Center"
Margin="15,10"
FontSize="14"
FontWeight="SemiBold"/>
<!-- Minimize Button -->
<Button Grid.Column="1"
Content="─"
Width="40" Height="35"
Background="Transparent"
Foreground="{DynamicResource TextBrush}"
BorderThickness="0"
FontSize="12"
Command="{Binding MinimizeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#4E4E52"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<!-- Maximize Button -->
<Button Grid.Column="2"
Content="☐"
Width="40" Height="35"
Background="Transparent"
Foreground="{DynamicResource TextBrush}"
BorderThickness="0"
FontSize="12"
Command="{Binding MaximizeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#4E4E52"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<!-- Close Button -->
<Button Grid.Column="3"
Content="✕"
Width="40" Height="35"
Background="Transparent"
Foreground="{DynamicResource TextBrush}"
BorderThickness="0"
FontSize="14"
Command="{Binding CloseCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E81123"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
<!-- Content -->
<StackPanel Grid.Row="1" Margin="25,20">
<!-- Name Input -->
<controls:InputPanel LabelText="{DynamicResource Regist_Name}"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsRequired="True"
Margin="0,0,0,15"/>
<!-- Phone Input -->
<controls:InputPanel LabelText="{DynamicResource Regist_Call}"
Text="{Binding Call, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,15"/>
<!-- Email Input -->
<controls:InputPanel LabelText="{DynamicResource Regist_Email}"
Text="{Binding EMail, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsRequired="True"
Margin="0,0,0,15"/>
<!-- Employee No Input -->
<controls:InputPanel LabelText="{DynamicResource Regist_No}"
Text="{Binding No, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,10"/>
<!-- Error Message -->
<TextBlock Text="{Binding ErrorMessage}"
Foreground="{DynamicResource ErrorBrush}"
Visibility="{Binding HasError, Converter={StaticResource BoolToVisibilityConverter}}"
HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="12"/>
</StackPanel>
<!-- Buttons -->
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="25,0,25,20">
<Button Content="{DynamicResource Regist_Cancel}"
Style="{DynamicResource SecondaryButtonStyle}"
Command="{Binding CancelCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Width="80"
Margin="0,0,10,0"/>
<Button Content="{DynamicResource Regist_Register}"
Style="{DynamicResource PrimaryButtonStyle}"
Command="{Binding RegisterCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Width="80"/>
</StackPanel>
</Grid>
</Border>
</Window>

View File

@@ -0,0 +1,32 @@
using System.Windows;
using System.Windows.Input;
using WPFBeginner.Models;
using WPFBeginner.ViewModels;
namespace WPFBeginner.Views;
public partial class RegistWindow : Window
{
public event EventHandler<Member>? MemberAdded;
public RegistWindow()
{
InitializeComponent();
var viewModel = App.GetService<RegistViewModel>();
viewModel.MemberAdded += OnMemberAdded;
DataContext = viewModel;
}
private void OnMemberAdded(object? sender, Member member)
{
MemberAdded?.Invoke(this, member);
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 1)
{
DragMove();
}
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ApplicationIcon></ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -1,20 +0,0 @@
<Application x:Class="WPFBeginner.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:service="clr-namespace:WPFBeginner.Services"
xmlns:conv="clr-namespace:WPFBeginner.Converters"
Startup="Application_Startup">
<Application.Resources>
<ResourceDictionary>
<service:ViewModelLocator x:Key="ViewModelLocator"/>
<conv:InverseBoolConverter x:Key="InverseBoolConverter"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Themes/Styles/Buttons.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/Colors/DefaultTheme.xaml" />
<ResourceDictionary Source="pack://application:,,,/Themes/Cultures/ko-KR.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources/Images/DrawingImages.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,104 +0,0 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
using System.Text.Json;
using System.Windows;
using WPFBeginner.Configurations;
using WPFBeginner.Exceptions;
using WPFBeginner.Services;
using WPFBeginner.ViewModels;
using WPFBeginner.Views;
namespace WPFBeginner
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private readonly static Dictionary<string, ResourceDictionary> _languages = new Dictionary<string, ResourceDictionary>();
private static ResourceDictionary _currentLanguage;
private readonly static Dictionary<string, ResourceDictionary> _themes = new Dictionary<string, ResourceDictionary>();
private static ResourceDictionary _currentTheme;
public static AppSettings Config { get; private set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
InitializeService();
var main = new MainWindow();
App.Current.MainWindow = main;
try
{
Login();
Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
App.Current.MainWindow.Show();
}
catch (Exception ex)
{
App.Current.MainWindow.Close();
Environment.Exit(0);
}
}
private void UnhandledExceptionHandle(object e)
{
if (e is UnhandledExceptionEventArgs uee && uee.ExceptionObject is LoginFailedException)
{
}
}
private static void InitializeService()
{
Config = JsonSerializer.Deserialize<AppSettings>(File.ReadAllText("appsettings.json"));
_languages.Add("ko-KR", ResourceExplorer.GetLanguageResourceDic("ko-KR"));
_languages.Add("en-US", ResourceExplorer.GetLanguageResourceDic("en-US"));
SetLanguage(Config.Language);
_themes.Add("DefaultTheme", ResourceExplorer.GetThemeResourceDic("DefaultTheme"));
_themes.Add("LightTheme", ResourceExplorer.GetThemeResourceDic("LightTheme"));
SetTheme(Config.Theme);
Config.LanguageChanged += (s, e) => SetLanguage(e.New);
Config.ThemeChanged += (s, e) => SetTheme(e.New);
Ioc.Default.ConfigureServices(
new ServiceCollection()
.AddSingleton(new MainWindowViewModel())
.BuildServiceProvider());
}
private static void SetTheme(string code)
{
if (_currentTheme != null)
App.Current.Resources.MergedDictionaries.Remove(_currentTheme);
_currentTheme = ResourceExplorer.GetThemeResourceDic(code);
App.Current.Resources.MergedDictionaries.Add(_currentTheme);
}
private static void SetLanguage(string code)
{
if (_currentLanguage != null)
App.Current.Resources.MergedDictionaries.Remove(_currentLanguage);
_currentLanguage = ResourceExplorer.GetLanguageResourceDic(code);
App.Current.Resources.MergedDictionaries.Add(_currentLanguage);
}
private static void Login()
{
var login = new LoginWindow();
var isSuccess = login.ShowDialog();
if (isSuccess != true)
throw new LoginFailedException();
}
}
}

View File

@@ -1,279 +0,0 @@
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace WPFBeginner.Behaviors
{
public class WindowBehavior
{
#region Win32 imports
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x80000;
private const int WS_MAXIMIZEBOX = 0x80000;
private const int WS_MINIMIZEBOX = 0x80000;
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
#endregion
private static readonly Type OwnerType = typeof(WindowBehavior);
#region HideCloseButton (attached property)
private static readonly RoutedEventHandler HideCloseButtonWhenLoadedDelegate = (sender, args) =>
{
if (sender is Window == false)
return;
var w = (Window)sender;
HideCloseButton(w);
w.Loaded -= HideCloseButtonWhenLoadedDelegate;
};
private static readonly RoutedEventHandler ShowCloseButtonWhenLoadedDelegate = (sender, args) =>
{
if (sender is Window == false)
return;
var w = (Window)sender;
ShowCloseButton(w);
w.Loaded -= ShowCloseButtonWhenLoadedDelegate;
};
public static readonly DependencyProperty HideCloseButtonProperty =
DependencyProperty.RegisterAttached("HideCloseButton", typeof(bool), OwnerType,
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideCloseButtonChangedCallback)));
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetHideCloseButton(Window obj) => (bool)obj.GetValue(HideCloseButtonProperty);
[AttachedPropertyBrowsableForType(typeof(Window))]
public static void SetHideCloseButton(Window obj, bool value) => obj.SetValue(HideCloseButtonProperty, value);
private static void HideCloseButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window == null) return;
var hideCloseButton = (bool)e.NewValue;
if (hideCloseButton && !GetIsHiddenCloseButton(window))
{
if (!window.IsLoaded)
window.Loaded += HideCloseButtonWhenLoadedDelegate;
else
HideCloseButton(window);
SetIsHiddenCloseButton(window, true);
}
else if (!hideCloseButton && GetIsHiddenCloseButton(window))
{
if (!window.IsLoaded)
window.Loaded -= ShowCloseButtonWhenLoadedDelegate;
else
ShowCloseButton(window);
SetIsHiddenCloseButton(window, false);
}
}
private static void HideCloseButton(Window w)
{
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU);
}
private static void ShowCloseButton(Window w)
{
var hwnd = new WindowInteropHelper(w).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU);
}
#endregion
#region IsHiddenCloseButton (readonly attached property)
private static readonly DependencyPropertyKey IsHiddenCloseButtonKey =
DependencyProperty.RegisterAttachedReadOnly("IsHiddenCloseButton", typeof(bool), OwnerType, new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsHiddenCloseButtonProperty = IsHiddenCloseButtonKey.DependencyProperty;
[AttachedPropertyBrowsableForType(typeof(Window))]
public static bool GetIsHiddenCloseButton(Window obj) => (bool)obj.GetValue(IsHiddenCloseButtonProperty);
private static void SetIsHiddenCloseButton(Window obj, bool value) => obj.SetValue(IsHiddenCloseButtonKey, value);
#endregion
//#region HideMinimizeButton
//private static readonly RoutedEventHandler HideMinimizeButtonWhenLoadedDelegate = (sender, args) => {
// if (sender is Window == false)
// return;
// var w = (Window)sender;
// HideMinimizeButton(w);
// w.Loaded -= HideMinimizeButtonWhenLoadedDelegate;
//};
//private static readonly RoutedEventHandler ShowMinimizeButtonWhenLoadedDelegate = (sender, args) => {
// if (sender is Window == false)
// return;
// var w = (Window)sender;
// ShowMinimizeButton(w);
// w.Loaded -= ShowMinimizeButtonWhenLoadedDelegate;
//};
//public static readonly DependencyProperty HideMinimizeButtonProperty =
// DependencyProperty.RegisterAttached("HideMinimizeButton", typeof(bool), OwnerType,
// new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideMinimizeButtonChangedCallback)));
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static bool GetHideMinimizeButton(Window obj) => (bool)obj.GetValue(HideCloseButtonProperty);
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static void SetHideMinimizeButton(Window obj, bool value) => obj.SetValue(HideCloseButtonProperty, value);
//private static void HideMinimizeButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
//{
// var window = d as Window;
// if (window == null) return;
// var hideCloseButton = (bool)e.NewValue;
// if (hideCloseButton && !GetIsHiddenMinimizeButton(window))
// {
// if (!window.IsLoaded)
// window.Loaded += HideMinimizeButtonWhenLoadedDelegate;
// else
// HideMinimizeButton(window);
// SetIsHiddenMinimizeButton(window, true);
// }
// else if (!hideCloseButton && GetIsHiddenMinimizeButton(window))
// {
// if (!window.IsLoaded)
// window.Loaded -= ShowMinimizeButtonWhenLoadedDelegate;
// else
// ShowMinimizeButton(window);
// SetIsHiddenMinimizeButton(window, false);
// }
//}
//private static void HideMinimizeButton(Window w)
//{
// var hwnd = new WindowInteropHelper(w).Handle;
// SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_MINIMIZEBOX);
//}
//private static void ShowMinimizeButton(Window w)
//{
// var hwnd = new WindowInteropHelper(w).Handle;
// SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_MINIMIZEBOX);
//}
//#endregion
//#region IsHiddenMinimizeButton (readonly attached property)
//private static readonly DependencyPropertyKey IsHiddenMinimizeButtonKey =
// DependencyProperty.RegisterAttachedReadOnly("IsHiddenMinimizeButton", typeof(bool), OwnerType, new FrameworkPropertyMetadata(false));
//public static readonly DependencyProperty IsHiddenMinimizeButtonProperty = IsHiddenMinimizeButtonKey.DependencyProperty;
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static bool GetIsHiddenMinimizeButton(Window obj) => (bool)obj.GetValue(IsHiddenMinimizeButtonProperty);
//private static void SetIsHiddenMinimizeButton(Window obj, bool value) => obj.SetValue(IsHiddenMinimizeButtonProperty, value);
//#endregion
//#region HideMaximizeButton
//private static readonly RoutedEventHandler HideMaximizeButtonWhenLoadedDelegate = (sender, args) => {
// if (sender is Window == false)
// return;
// var w = (Window)sender;
// HideMaximizeButton(w);
// w.Loaded -= HideMaximizeButtonWhenLoadedDelegate;
//};
//private static readonly RoutedEventHandler ShowMaximizeButtonWhenLoadedDelegate = (sender, args) => {
// if (sender is Window == false)
// return;
// var w = (Window)sender;
// ShowMaximizeButton(w);
// w.Loaded -= ShowMaximizeButtonWhenLoadedDelegate;
//};
//public static readonly DependencyProperty HideMaximizeButtonProperty =
// DependencyProperty.RegisterAttached("HideMaximizeButton", typeof(bool), OwnerType,
// new FrameworkPropertyMetadata(false, new PropertyChangedCallback(HideMaximizeButtonChangedCallback)));
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static bool GetHideMaximizeButton(Window obj) => (bool)obj.GetValue(HideCloseButtonProperty);
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static void SetHideMaximizeButton(Window obj, bool value) => obj.SetValue(HideCloseButtonProperty, value);
//private static void HideMaximizeButtonChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
//{
// var window = d as Window;
// if (window == null) return;
// var hideCloseButton = (bool)e.NewValue;
// if (hideCloseButton && !GetIsHiddenMaximizeButton(window))
// {
// if (!window.IsLoaded)
// window.Loaded += HideMaximizeButtonWhenLoadedDelegate;
// else
// HideMaximizeButton(window);
// SetIsHiddenMaximizeButton(window, true);
// }
// else if (!hideCloseButton && GetIsHiddenMaximizeButton(window))
// {
// if (!window.IsLoaded)
// window.Loaded -= ShowMaximizeButtonWhenLoadedDelegate;
// else
// ShowMaximizeButton(window);
// SetIsHiddenMaximizeButton(window, false);
// }
//}
//private static void HideMaximizeButton(Window w)
//{
// var hwnd = new WindowInteropHelper(w).Handle;
// SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_MINIMIZEBOX);
//}
//private static void ShowMaximizeButton(Window w)
//{
// var hwnd = new WindowInteropHelper(w).Handle;
// SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_MINIMIZEBOX);
//}
//#endregion
//#region IsHiddenMaximizeButton (readonly attached property)
//private static readonly DependencyPropertyKey IsHiddenMaximizeButtonKey =
// DependencyProperty.RegisterAttachedReadOnly("IsHiddenMaximizeButton", typeof(bool), OwnerType, new FrameworkPropertyMetadata(false));
//public static readonly DependencyProperty IsHiddenMaximizeButtonProperty = IsHiddenMaximizeButtonKey.DependencyProperty;
//[AttachedPropertyBrowsableForType(typeof(Window))]
//public static bool GetIsHiddenMaximizeButton(Window obj) => (bool)obj.GetValue(IsHiddenMaximizeButtonProperty);
//private static void SetIsHiddenMaximizeButton(Window obj, bool value) => obj.SetValue(IsHiddenMaximizeButtonProperty, value);
//#endregion
}
}

View File

@@ -1,69 +0,0 @@
using System.Text.Json.Serialization;
using WPFBeginner.Models;
namespace WPFBeginner.Configurations
{
public class AppSettings
{
public EventHandler<StringChangedEventArg> LanguageChanged;
public EventHandler<StringChangedEventArg> ThemeChanged;
[JsonPropertyName("Login")]
public LoginInfo Login { get; set; }
private string language;
[JsonPropertyName("Language")]
public string Language
{
get => language;
set
{
var eventArg = new StringChangedEventArg()
{
Old = language,
New = value
};
language = value;
if (eventArg.NotChanged)
return;
LanguageChanged?.Invoke(null, eventArg);
}
}
private string theme;
[JsonPropertyName("Theme")]
public string Theme
{
get => theme;
set
{
var eventArg = new StringChangedEventArg()
{
Old = theme,
New = value
};
theme = value;
if (eventArg.NotChanged)
return;
ThemeChanged?.Invoke(null, eventArg);
}
}
[JsonPropertyName("DefaultMembers")]
public List<Member> Members { get; set; }
}
public class StringChangedEventArg : EventArgs
{
public string Old { get; set; }
public string New { get; set; }
public bool NotChanged => Old?.Equals(New, StringComparison.OrdinalIgnoreCase) ?? true;
}
}

View File

@@ -1,12 +0,0 @@
using System.Text.Json.Serialization;
namespace WPFBeginner.Configurations
{
public class LoginInfo
{
[JsonPropertyName("User")]
public string User { get; set; }
public string Password { get; set; }
}
}

View File

@@ -1,51 +0,0 @@
<UserControl x:Class="WPFBeginner.Controls.MemberInputPanel"
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"
mc:Ignorable="d" Name="root"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="LabelTitle" TargetType="Label">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontFamily" Value="Yu Gothic UI"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Padding" Value="10 0"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.TextBox.Foreground}"/>
</Style>
<Style x:Key="InputBox" TargetType="TextBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="5 0"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.TextBox.Foreground}"/>
<Setter Property="Background" Value="{DynamicResource Colors.TextBox.Background}"/>
<Setter Property="BorderBrush" Value="#FF424242"/>
</Style>
</UserControl.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Style="{StaticResource LabelTitle}" Content="{DynamicResource Cultures.MainWindow.Label.Name}"/>
<Label Grid.Row="1" Style="{StaticResource LabelTitle}" Content="{DynamicResource Cultures.MainWindow.Label.Call}" />
<Label Grid.Row="2" Style="{StaticResource LabelTitle}" Content="{DynamicResource Cultures.MainWindow.Label.No}"/>
<Label Grid.Row="3" Style="{StaticResource LabelTitle}" Content="{DynamicResource Cultures.MainWindow.Label.Mail}"/>
<TextBox Grid.Row="0" Grid.Column="1" Style="{StaticResource InputBox}" Text="{Binding ElementName=root, Path=MemberName}"/>
<TextBox Grid.Row="1" Grid.Column="1" Style="{StaticResource InputBox}" Text="{Binding ElementName=root, Path=Call}"/>
<TextBox Grid.Row="2" Grid.Column="1" Style="{StaticResource InputBox}" Text="{Binding ElementName=root, Path=EmployeeNo}"/>
<TextBox Grid.Row="3" Grid.Column="1" Style="{StaticResource InputBox}" Text="{Binding ElementName=root, Path=Email}"/>
</Grid>
</UserControl>

View File

@@ -1,52 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace WPFBeginner.Controls
{
/// <summary>
/// MemberInputPanel.xaml에 대한 상호 작용 논리
/// </summary>
public partial class MemberInputPanel : UserControl
{
public static readonly DependencyProperty MemberNameProperty =
DependencyProperty.Register(nameof(MemberName), typeof(string), typeof(MemberInputPanel), new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public string MemberName
{
get => (string)GetValue(MemberNameProperty);
set => SetValue(MemberNameProperty, value);
}
public static readonly DependencyProperty EmployeeNoProperty =
DependencyProperty.Register(nameof(EmployeeNo), typeof(string), typeof(MemberInputPanel), new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public string EmployeeNo
{
get => (string)GetValue(EmployeeNoProperty);
set => SetValue(EmployeeNoProperty, value);
}
public static readonly DependencyProperty CallProperty =
DependencyProperty.Register(nameof(Call), typeof(string), typeof(MemberInputPanel), new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public string Call
{
get => (string)GetValue(CallProperty);
set => SetValue(CallProperty, value);
}
public static readonly DependencyProperty EmailProperty =
DependencyProperty.Register(nameof(Email), typeof(string), typeof(MemberInputPanel), new FrameworkPropertyMetadata() { BindsTwoWayByDefault = true });
public string Email
{
get => (string)GetValue(EmailProperty);
set => SetValue(EmailProperty, value);
}
public MemberInputPanel()
{
InitializeComponent();
}
}
}

View File

@@ -1,14 +0,0 @@
using System.Windows.Markup;
namespace WPFBeginner.Converters
{
public abstract class BaseConverter : MarkupExtension
{
protected BaseConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}

View File

@@ -1,20 +0,0 @@
using System.Windows.Data;
namespace WPFBeginner.Converters
{
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBoolConverter : BaseConverter, IValueConverter
{
public InverseBoolConverter() { }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
}
}

View File

@@ -1,10 +0,0 @@
namespace WPFBeginner.Exceptions
{
internal class LoginFailedException : Exception
{
public LoginFailedException() : base()
{
}
}
}

View File

@@ -1,27 +0,0 @@
using System.Text.Json.Serialization;
namespace WPFBeginner.Models
{
public class Member
{
[JsonPropertyName("Name")]
public string Name { get; set; }
[JsonPropertyName("Call")]
public string Call { get; set; }
[JsonPropertyName("EMail")]
public string EMail { get; set; }
[JsonPropertyName("No")]
public string EmployeeNo { get; set; }
public static Member Create(string name, string call, string email, string employeeNo)
{
return new Member()
{
Name = name,
Call = call,
EMail = email,
EmployeeNo = employeeNo
};
}
}
}

View File

@@ -1,36 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DrawingImage x:Key="Images.OptionsMenu.Setting.Normal">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Normal}" Geometry="F0 M24,24z M0,0z M10.1,1.3L10.1,1.1 12.7,1.1 12.7,1.3 13,2.6 13.2,3.7C14.2,3.9,15.1,4.3,15.9,4.8L16,4.8 17.7,3.5 17.8,3.4 18.1,3.4 18.9,4.2 19.7,5 19.7,5.3 19.6,5.4 19,6.2 18.4,7.1C18.9,7.9,19.3,8.8,19.6,9.8L19.6,9.9 20.5,10 20.7,10 21.7,10.2 21.9,10.2 21.9,12.8 21.7,12.8 20.7,13 19.6,13.2C19.4,14.2,19,15.1,18.5,15.9L19.8,17.6C19.8,17.6,19.8,17.7,19.9,17.7L19.9,18 19.1,18.8 18.3,19.6 18,19.6 17.9,19.5 16,18.4C15.2,18.9,14.2,19.3,13.3,19.5L13.3,19.6 13,21.7 13,21.9 10.4,21.9 10.4,21.7 10.2,20.5 10,19.4C9.1,19.2,8.2,18.8,7.4,18.3L7,18.4 5.3,19.7 5.2,19.8 4.9,19.8 4.1,19 3.3,18.2 3.3,17.9 3.4,17.8 4.2,16.7 4.6,16 4.8,15.7C4.4,15,4,14.2,3.8,13.3L3.4,13.2 1.4,12.9 1.2,12.9 1.2,10.3 1.4,10.3 3.5,9.99999999999999 3.8,9.9C4,8.99999999999999,4.3,8.2,4.8,7.4L4.1,6.4 3.3,5.4 3.2,5.3 3.2,5 4,4.1 4.8,3.3 5.1,3.3 5.2,3.4 7,4.7 7.2,4.9C8,4.3,8.8,3.9,9.8,3.7L10,2.6 10.1,1.3z M13.9,1.3L14.1,2.8C14.7,3,15.3,3.2,15.8,3.5L17,2.6C17.1,2.5 17.4,2.3 17.7,2.2 18,2.1 18.4,2.2 18.8,2.5L19.6,3.3 20.4,4.1C20.7,4.4 20.8,4.8 20.7,5.2 20.6,5.5 20.5,5.7 20.3,5.9L19.5,7C19.8,7.6,20.1,8.2,20.3,8.8L21.7,9C21.9,9 22.2,9.1 22.4,9.2 22.7,9.4 22.9,9.7 22.9,10.2L22.9,12.6C22.9,13.1 22.7,13.4 22.4,13.6 22.2,13.8 21.9,13.8 21.7,13.8L20.3,14C20.1,14.6,19.9,15.2,19.6,15.7L20.4,16.8C20.5,16.9 20.7,17.2 20.8,17.5 20.9,17.8 20.8,18.2 20.5,18.6L19.7,19.4 18.9,20.2C18.6,20.5 18.2,20.6 17.8,20.5 17.5,20.4 17.3,20.3 17.1,20.1L15.9,19.2C15.3,19.5,14.7,19.7,14.1,19.9L13.9,21.4C13.9,21.6 13.8,21.9 13.7,22.1 13.5,22.4 13.2,22.6 12.7,22.6L10.3,22.6C9.8,22.6 9.5,22.4 9.3,22.1 9.1,21.9 9.1,21.6 9.1,21.4L8.9,19.8C8.3,19.6,7.8,19.4,7.2,19.1L5.9,20.1C5.8,20.2 5.5,20.4 5.2,20.5 4.9,20.6 4.5,20.5 4.1,20.2L3.3,19.4 2.5,18.6C2.2,18.3 2.1,17.9 2.2,17.5 2.3,17.2 2.4,17 2.6,16.8L3.6,15.4C3.4,14.9,3.2,14.5,3,14L1.2,14C1,14 0.7,13.9 0.5,13.8 0.2,13.6 0,13.3 0,12.8L0,10.4C0,9.9 0.2,9.6 0.5,9.4 0.8,9.2 1,9.2 1.2,9.2L3,8.9 3.6,7.4 2.5,6C2.4,5.8 2.2,5.6 2.2,5.3 2.1,5 2.2,4.6 2.5,4.2L3.3,3.4 4.1,2.6C4.4,2.3 4.8,2.2 5.2,2.3 5.5,2.4 5.7,2.5 5.9,2.7L7.2,3.7C7.7,3.4,8.2,3.2,8.8,3L9,1.4C9,1.2 9.1,0.9 9.2,0.7 9.5,0.3 9.8,0 10.3,0L12.7,0C13.2,0 13.5,0.2 13.7,0.5 13.8,0.8 13.9,1.1 13.9,1.3z" />
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Normal}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="3.4" RadiusY="3.4" Center="11.5,11.5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="Images.OptionsMenu.Setting.Pressed">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Pressed}" Geometry="F0 M24,24z M0,0z M10.1,1.3L10.1,1.1 12.7,1.1 12.7,1.3 13,2.6 13.2,3.7C14.2,3.9,15.1,4.3,15.9,4.8L16,4.8 17.7,3.5 17.8,3.4 18.1,3.4 18.9,4.2 19.7,5 19.7,5.3 19.6,5.4 19,6.2 18.4,7.1C18.9,7.9,19.3,8.8,19.6,9.8L19.6,9.9 20.5,10 20.7,10 21.7,10.2 21.9,10.2 21.9,12.8 21.7,12.8 20.7,13 19.6,13.2C19.4,14.2,19,15.1,18.5,15.9L19.8,17.6C19.8,17.6,19.8,17.7,19.9,17.7L19.9,18 19.1,18.8 18.3,19.6 18,19.6 17.9,19.5 16,18.4C15.2,18.9,14.2,19.3,13.3,19.5L13.3,19.6 13,21.7 13,21.9 10.4,21.9 10.4,21.7 10.2,20.5 10,19.4C9.1,19.2,8.2,18.8,7.4,18.3L7,18.4 5.3,19.7 5.2,19.8 4.9,19.8 4.1,19 3.3,18.2 3.3,17.9 3.4,17.8 4.2,16.7 4.6,16 4.8,15.7C4.4,15,4,14.2,3.8,13.3L3.4,13.2 1.4,12.9 1.2,12.9 1.2,10.3 1.4,10.3 3.5,9.99999999999999 3.8,9.9C4,8.99999999999999,4.3,8.2,4.8,7.4L4.1,6.4 3.3,5.4 3.2,5.3 3.2,5 4,4.1 4.8,3.3 5.1,3.3 5.2,3.4 7,4.7 7.2,4.9C8,4.3,8.8,3.9,9.8,3.7L10,2.6 10.1,1.3z M13.9,1.3L14.1,2.8C14.7,3,15.3,3.2,15.8,3.5L17,2.6C17.1,2.5 17.4,2.3 17.7,2.2 18,2.1 18.4,2.2 18.8,2.5L19.6,3.3 20.4,4.1C20.7,4.4 20.8,4.8 20.7,5.2 20.6,5.5 20.5,5.7 20.3,5.9L19.5,7C19.8,7.6,20.1,8.2,20.3,8.8L21.7,9C21.9,9 22.2,9.1 22.4,9.2 22.7,9.4 22.9,9.7 22.9,10.2L22.9,12.6C22.9,13.1 22.7,13.4 22.4,13.6 22.2,13.8 21.9,13.8 21.7,13.8L20.3,14C20.1,14.6,19.9,15.2,19.6,15.7L20.4,16.8C20.5,16.9 20.7,17.2 20.8,17.5 20.9,17.8 20.8,18.2 20.5,18.6L19.7,19.4 18.9,20.2C18.6,20.5 18.2,20.6 17.8,20.5 17.5,20.4 17.3,20.3 17.1,20.1L15.9,19.2C15.3,19.5,14.7,19.7,14.1,19.9L13.9,21.4C13.9,21.6 13.8,21.9 13.7,22.1 13.5,22.4 13.2,22.6 12.7,22.6L10.3,22.6C9.8,22.6 9.5,22.4 9.3,22.1 9.1,21.9 9.1,21.6 9.1,21.4L8.9,19.8C8.3,19.6,7.8,19.4,7.2,19.1L5.9,20.1C5.8,20.2 5.5,20.4 5.2,20.5 4.9,20.6 4.5,20.5 4.1,20.2L3.3,19.4 2.5,18.6C2.2,18.3 2.1,17.9 2.2,17.5 2.3,17.2 2.4,17 2.6,16.8L3.6,15.4C3.4,14.9,3.2,14.5,3,14L1.2,14C1,14 0.7,13.9 0.5,13.8 0.2,13.6 0,13.3 0,12.8L0,10.4C0,9.9 0.2,9.6 0.5,9.4 0.8,9.2 1,9.2 1.2,9.2L3,8.9 3.6,7.4 2.5,6C2.4,5.8 2.2,5.6 2.2,5.3 2.1,5 2.2,4.6 2.5,4.2L3.3,3.4 4.1,2.6C4.4,2.3 4.8,2.2 5.2,2.3 5.5,2.4 5.7,2.5 5.9,2.7L7.2,3.7C7.7,3.4,8.2,3.2,8.8,3L9,1.4C9,1.2 9.1,0.9 9.2,0.7 9.5,0.3 9.8,0 10.3,0L12.7,0C13.2,0 13.5,0.2 13.7,0.5 13.8,0.8 13.9,1.1 13.9,1.3z" />
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="{DynamicResource Colors.Images.OptionsMenu.Setting.Pressed}" Thickness="1" StartLineCap="Flat" EndLineCap="Flat" LineJoin="Miter" />
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="3.4" RadiusY="3.4" Center="11.5,11.5" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</ResourceDictionary>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,43 +0,0 @@
using System.Windows;
namespace WPFBeginner.Resources
{
public class SharedResourceDictionary : ResourceDictionary
{
private static Dictionary<Uri, WeakReference> _cache = new Dictionary<Uri, WeakReference>();
private Uri source;
public new Uri Source
{
get => source;
set
{
source = value;
if (!_cache.ContainsKey(source))
{
AddToCache();
}
else
{
WeakReference weakReference = _cache[source];
if (weakReference != null && weakReference.IsAlive)
MergedDictionaries.Add((ResourceDictionary)weakReference.Target);
else
AddToCache();
}
}
}
private void AddToCache()
{
base.Source = source;
if (_cache.ContainsKey(source))
_cache.Remove(source);
_cache.Add(source, new WeakReference(this, false));
}
}
}

View File

@@ -1,52 +0,0 @@
using System.Windows;
using System.Windows.Input;
namespace WPFBeginner.Services
{
public class EnterKeyTraversal
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(EnterKeyTraversal), new UIPropertyMetadata(false, IsEnabledChanged));
public static bool GetIsEnabled(DependencyObject obj) => (bool)obj.GetValue(IsEnabledProperty);
public static void SetIsEnabled(DependencyObject obj, bool value) => obj.SetValue(IsEnabledProperty, value);
static void PreviewKeyDown(object sender, KeyEventArgs e)
{
var uiElement = e.OriginalSource as FrameworkElement;
if (e.Key == Key.Enter)
{
e.Handled = true;
uiElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
private static void Unloaded(object sender, RoutedEventArgs e)
{
var uiElement = sender as FrameworkElement;
if (uiElement == null)
return;
uiElement.Unloaded -= Unloaded;
uiElement.PreviewKeyDown -= PreviewKeyDown;
}
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ue = d as FrameworkElement;
if (ue == null) return;
if ((bool)e.NewValue)
{
ue.Unloaded += Unloaded;
ue.PreviewKeyDown += PreviewKeyDown;
}
else
{
ue.PreviewKeyDown -= PreviewKeyDown;
}
}
}
}

View File

@@ -1,95 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace WPFBeginner.Services
{
public static class PasswordBoxAssistant
{
#region BoundPassword Property
public static readonly DependencyProperty BoundPassword =
DependencyProperty.RegisterAttached(nameof(BoundPassword), typeof(string), typeof(PasswordBoxAssistant), new PropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static string GetBoundPassword(DependencyObject dp) => (string)dp.GetValue(BoundPassword);
public static void SetBoundPassword(DependencyObject dp, string value) => dp.SetValue(BoundPassword, value);
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = d as PasswordBox;
// only handle this event when the property is attached to a PasswordBox
// and when the BindPassword attached property has been set to true
if (d == null || !GetEnableBindingPassword(d))
return;
// avoid recursive updating by ignoring the box's changed event
box.PasswordChanged -= HandlePasswordChanged;
string newPassword = (string)e.NewValue;
if (!GetUpdatingPassword(box))
box.Password = newPassword;
box.PasswordChanged += HandlePasswordChanged;
}
#endregion
#region EnableBindingPassword
public static readonly DependencyProperty EnableBindingPassword =
DependencyProperty.RegisterAttached(nameof(EnableBindingPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnEnableBindingPasswordChanged));
public static bool GetEnableBindingPassword(DependencyObject dp) => (bool)dp.GetValue(EnableBindingPassword);
public static void SetEnableBindingPassword(DependencyObject dp, bool value) => dp.SetValue(EnableBindingPassword, value);
private static void OnEnableBindingPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
// when the BindPassword attached property is set on a PasswordBox,
// start listening to its PasswordChanged event
var box = dp as PasswordBox;
if (box == null)
return;
bool wasBound = (bool)(e.OldValue);
bool needToBind = (bool)(e.NewValue);
if (wasBound)
box.PasswordChanged -= HandlePasswordChanged;
if (needToBind)
box.PasswordChanged += HandlePasswordChanged;
}
#endregion
#region UpdatingPassword
#endregion
private static readonly DependencyProperty UpdatingPassword =
DependencyProperty.RegisterAttached(nameof(UpdatingPassword), typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false));
private static bool GetUpdatingPassword(DependencyObject dp) => (bool)dp.GetValue(UpdatingPassword);
private static void SetUpdatingPassword(DependencyObject dp, bool value) => dp.SetValue(UpdatingPassword, value);
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
var box = sender as PasswordBox;
// set a flag to indicate that we're updating the password
SetUpdatingPassword(box, true);
// push the new password into the BoundPassword property
SetBoundPassword(box, box.Password);
SetUpdatingPassword(box, false);
}
}
}

View File

@@ -1,54 +0,0 @@
using System.Windows;
namespace WPFBeginner.Services
{
internal static class ResourceExplorer
{
public static string GetStringResource(string key)
{
var cultureCode = App.Config.Language;
var rd = App.Current.Resources.MergedDictionaries.FirstOrDefault(i => i.Source.AbsolutePath.EndsWith($"{cultureCode}.xaml"));
if (rd == null)
return null;
return rd[key] as string;
}
public static void ChangeXamlResource(string oldRes, string newRes)
{
var found = App.Current.Resources.MergedDictionaries.Select((item, i) => new { Item = item, Index = i }).FirstOrDefault(i => i.Item.Source.AbsoluteUri.Equals($"{oldRes}", StringComparison.OrdinalIgnoreCase));
if (found == null)
return;
var newResource = new ResourceDictionary()
{
Source = new Uri(newRes, UriKind.RelativeOrAbsolute)
};
App.Current.Resources.MergedDictionaries.Remove(found.Item);
App.Current.Resources.MergedDictionaries.Add(newResource);
}
public static ResourceDictionary GetLanguageResourceDic(string name)
{
var foundName = $"pack://application:,,,/Themes/Cultures/{name}.xaml";
//var foundName = $"/WPFBeginner;component/Themes/Cultures/{name}.xaml";
var found = App.Current.Resources.MergedDictionaries.Select((item, i) => new { Item = item, Index = i }).FirstOrDefault(i => i.Item.Source.AbsoluteUri.Equals(foundName, StringComparison.OrdinalIgnoreCase));
if (found == null)
return new ResourceDictionary() { Source = new Uri(foundName, UriKind.RelativeOrAbsolute) };
return found.Item;
}
public static ResourceDictionary GetThemeResourceDic(string name)
{
var foundName = $"pack://application:,,,/Themes/Colors/{name}.xaml";
//var foundName = $"/WPFBeginner;component/Themes/Colors/{name}.xaml";
var found = App.Current.Resources.MergedDictionaries.Select((item, i) => new { Item = item, Index = i }).FirstOrDefault(i => i.Item.Source.AbsoluteUri.Equals(foundName, StringComparison.OrdinalIgnoreCase));
if (found == null)
return new ResourceDictionary() { Source = new Uri(foundName, UriKind.RelativeOrAbsolute) };
return found.Item;
}
}
}

View File

@@ -1,10 +0,0 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using WPFBeginner.ViewModels;
namespace WPFBeginner.Services
{
internal class ViewModelLocator
{
public MainWindowViewModel MainWindowViewModel => Ioc.Default.GetService<MainWindowViewModel>();
}
}

View File

@@ -1,55 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Colors.TitleBar.Title.Background" Color="#FF1E1E1E"/>
<SolidColorBrush x:Key="Colors.TitleBar.Title.Foreground" Color="LightGreen"/>
<SolidColorBrush x:Key="Colors.Section.HorizontalSeperator" Color="Cyan" Opacity="0.8"/>
<SolidColorBrush x:Key="Colors.Main.Background" Color="#FF1E1E1E" />
<SolidColorBrush x:Key="Colors.SettingPopup.Background" Color="#FF383838" />
<SolidColorBrush x:Key="Colors.SettingPopup.BorderBrush" Color="#FF424242" />
<SolidColorBrush x:Key="Colors.SettingPopup.Foreground" Color="White" />
<SolidColorBrush x:Key="Colors.TextBox.Background" Color="#FF383838"/>
<SolidColorBrush x:Key="Colors.TextBox.Foreground" Color="White"/>
<SolidColorBrush x:Key="Colors.TextBox.BorderBrush" Color="#FF424242"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.Normal" Color="White"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MouseOver" Color="Pink"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MousePressed" Color="Coral"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.Normal" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MouseOver" Color="Pink"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MousePressed" Color="Coral"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.Normal" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MouseOver" Color="#FF1F1F1F"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MousePressed" Color="#FF1F1F1F"/>
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Normal" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Normal" Color="White" />
<!--<SolidColorBrush x:Key="Colors.DataGridButton.Background.MouseOver" Color="#FFEB530C" Opacity="0.6" />-->
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.MouseOver" StartPoint="0,0" EndPoint="0,1" Opacity="0.6">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.MouseOver" Color="Yellow" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Pressed" StartPoint="0,0" EndPoint="0,1" Opacity="0.8">
<GradientStop Color="#38301f" Offset="0.0"/>
<GradientStop Color="#4a3c2b" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="0.5"/>
<GradientStop Color="#342e1d" Offset="1.0"/>
</LinearGradientBrush>
<!--<SolidColorBrush x:Key="Colors.DataGridButton.Background.Pressed" Color="#FFEB530C" Opacity="0.8" />-->
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Pressed" Color="LightSkyBlue" />
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Normal" Color="#FFB8B8B9"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Pressed" Color="#FFB8B8B9" Opacity="0.6"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MouseOver" Color="#FF474D51"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MousePressed" Color="#FF474D51"/>
</ResourceDictionary>

View File

@@ -1,52 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Colors.TitleBar.Title.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="Colors.TitleBar.Title.Foreground" Color="DarkSlateBlue"/>
<SolidColorBrush x:Key="Colors.Section.HorizontalSeperator" Color="Gray" Opacity="0.8"/>
<SolidColorBrush x:Key="Colors.Main.Background" Color="#FFF0F0F0" />
<SolidColorBrush x:Key="Colors.SettingPopup.Background" Color="#FFF5F5F5" />
<SolidColorBrush x:Key="Colors.SettingPopup.BorderBrush" Color="#FFDDDDDD" />
<SolidColorBrush x:Key="Colors.SettingPopup.Foreground" Color="Black" />
<SolidColorBrush x:Key="Colors.TextBox.Background" Color="#FFF5F5F5"/>
<SolidColorBrush x:Key="Colors.TextBox.Foreground" Color="Black"/>
<SolidColorBrush x:Key="Colors.TextBox.BorderBrush" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.Normal" Color="Black"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MouseOver" Color="MediumSlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.Foreground.MousePressed" Color="SlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.Normal" Color="#FFEEEEEE"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MouseOver" Color="MediumSlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.BorderBrush.MousePressed" Color="SlateBlue"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.Normal" Color="#FFEEEEEE"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MouseOver" Color="#FFE0E0E0"/>
<SolidColorBrush x:Key="Colors.SearchButton.Background.MousePressed" Color="#FFD6D6D6"/>
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Normal" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Normal" Color="Black" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.MouseOver" StartPoint="0,0" EndPoint="0,1" Opacity="0.6">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.MouseOver" Color="DarkSlateBlue" />
<LinearGradientBrush x:Key="Colors.DataGridButton.Background.Pressed" StartPoint="0,0" EndPoint="0,1" Opacity="0.8">
<GradientStop Color="#E3EAF3" Offset="0.0"/>
<GradientStop Color="#D4DFEA" Offset="0.5"/>
<GradientStop Color="#C8D4E1" Offset="0.5"/>
<GradientStop Color="#BFCCE0" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="Colors.DataGridButton.Foreground.Pressed" Color="CornflowerBlue" />
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Normal" Color="#FF6E6E70"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Pressed" Color="#FF6E6E70" Opacity="0.6"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MouseOver" Color="#FFD3D3D5"/>
<SolidColorBrush x:Key="Colors.Images.OptionsMenu.Setting.Background.MousePressed" Color="#FFD3D3D5"/>
</ResourceDictionary>

View File

@@ -1,31 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<sys:String x:Key="Cultures.LoginWindow.ErrorMessage.DuplicatedExecute">Duplicated execution.</sys:String>
<sys:String x:Key="Cultures.LoginWindow.ErrorMessage.InvalidUser">Please check your ID and password again.</sys:String>
<sys:String x:Key="Cultures.LoginWindow.LoginButtonText">Login</sys:String>
<sys:String x:Key="Cultures.LoginWindow.Inform">Please enter your ID and password.</sys:String>
<sys:String x:Key="Cultures.MainWindow.Title">Contact Management Program</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Name">Name:</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Call">Contact</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.No">Employee Number:</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Mail">Email:</sys:String>
<sys:String x:Key="Cultures.MainWindow.ErrorMessage.SearchConditionEmpty">No search condition provided. Please enter a search condition.</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Add">Add</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Search">Search</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Delete">Delete</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Init">Reset</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Name">Name</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Call">Contact</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.No">Employee Number</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Email">Email</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.RegistButton">Register</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.CancelButton">Cancel</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.ErrorMessage.FillMustValue">Name or employee number is required.</sys:String>
</ResourceDictionary>

View File

@@ -1,31 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<sys:String x:Key="Cultures.LoginWindow.ErrorMessage.DuplicatedExecute">중복된 실행입니다.</sys:String>
<sys:String x:Key="Cultures.LoginWindow.ErrorMessage.InvalidUser">아이디와 비번을 다시 한번 확인해주세요</sys:String>
<sys:String x:Key="Cultures.LoginWindow.LoginButtonText">로그인</sys:String>
<sys:String x:Key="Cultures.LoginWindow.Inform">아이디와 비번을 입력해주세요</sys:String>
<sys:String x:Key="Cultures.MainWindow.Title">연락처 관리 프로그램</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Name">이름:</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Call">연락처</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.No">사번:</sys:String>
<sys:String x:Key="Cultures.MainWindow.Label.Mail">이메일:</sys:String>
<sys:String x:Key="Cultures.MainWindow.ErrorMessage.SearchConditionEmpty">검색 조건이 없습니다. 검색 조건을 입력해주세요</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Add">추가</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Search">검 색</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Delete">삭제</sys:String>
<sys:String x:Key="Cultures.MainWindow.Button.Init">초기화</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Name">이름</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Call">연락처</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.No">사번</sys:String>
<sys:String x:Key="Cultures.MainWindow.DatagGid.Column.Email">이메일</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.RegistButton">등록</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.CancelButton">취소</sys:String>
<sys:String x:Key="Cultures.RegistMemberWindow.ErrorMessage.FillMustValue">이름 또는 사번은 반드시 필요합니다.</sys:String>
</ResourceDictionary>

View File

@@ -1,164 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Maximize Button-->
<Style x:Key="TiTleBarMaximizeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Fill="#FFB8B8B9" Height="14" Width="14" Canvas.Left="-7" Canvas.Top="5">
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Normal">
<!--<Setter Property="Data" Value="F1M3,5C3,5 3,11 3,11 3,11 9,11 9,11 9,11 9,5 9,5 9,5 3,5 3,5z M5,3C5,3,5,3.375,5,3.938L5,4 6,4C8,4 10,4 10,4 10,4 10,6 10,8L10,9 10.062,9C10.625,9 11,9 11,9 11,9 11,3 11,3 11,3 5,3 5,3z M4,2C4,2 12,2 12,2 12,2 12,10 12,10 12,10 11.5,10 10.75,10L10,10 10,10.75C10,11.5 10,12 10,12 10,12 2,12 2,12 2,12 2,4 2,4 2,4 2.5,4 3.25,4L4,4 4,3.25C4,2.5,4,2,4,2z"/>-->
<Setter Property="Data" Value="F1M3,3C3,3 3,11 3,11 3,11 11,11 11,11 11,11 11,3 11,3 11,3 3,3 3,3z M2,2C2,2 12,2 12,2 12,2 12,12 12,12 12,12 2,12 2,12 2,12 2,2 2,2z" />
</DataTrigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="Data" Value="F1M3,5C3,5 3,11 3,11 3,11 9,11 9,11 9,11 9,5 9,5 9,5 3,5 3,5z M5,3C5,3 5,4 5,4 5,4 10,4 10,4 10,4 10,9 10,9 10,9 11,9 11,9 11,9 11,3 11,3 11,3 5,3 5,3z M4,2C4,2 12,2 12,2 12,2 12,10 12,10 12,10 10,10 10,10 10,10 10,12 10,12 10,12 2,12 2,12 2,12 2,4 2,4 2,4 4,4 4,4 4,4 4,2 4,2z"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF474D51"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF3D4347"/>
</Trigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Normal">
<Setter Property="ToolTip" Value="Maximize"/>
</DataTrigger>
<DataTrigger Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" Value="Maximized">
<Setter Property="ToolTip" Value="Restore"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!--Minimize Button-->
<Style x:Key="TiTleBarMinimizeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Data="F1M2,2C2,2 12,2 12,2 12,2 12,3 12,3 12,3 2,3 2,3 2,3 2,2 2,2z" Fill="#FFB8B8B9" Height="4" Canvas.Left="0" Canvas.Top="0" Width="13"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF474D51"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF3D4347"/>
</Trigger>
</Style.Triggers>
</Style>
<!--Close Button-->
<Style x:Key="TiTleBarCloseButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Focusable" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<Path Data="F1M2,2C2,2,2.25,2,2.5,2L2.999,2 3,2 3,2.001 3.221,2.222C3.652,2.653,4.66,3.661,5.847,4.848L6.999,6 8.152,4.848C9.734,3.266,10.999,2,10.999,2L11,2.001 11,2C11,2 12,2 12,2 12,2 12,3 12,3 12,3 10.734,4.266 9.152,5.848L8,7 9.152,8.152C10.734,9.734 12,11 12,11 12,11 12,12 12,12 12,12 11,12 11,12L11,11.999 10.999,12C10.999,12,9.733,10.734,8.151,9.152L6.999,8 5.847,9.152C4.66,10.339,3.652,11.347,3.221,11.778L3,11.999 3,12 2.999,12C3,12 2,12 2,12 2,12 2,11 2,11 2,11 3.265,9.734 4.847,8.152L5.999,7 4.847,5.848C3.265,4.266,1.999,3,1.999,3L2,2.999 2,2.844C2,2.562,2,2,2,2z"
Fill="#FFB8B8B9" Height="13" Canvas.Left="0" Canvas.Top="0" Width="13"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FFF06251"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FFED3F2B"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="SettingButtonStyle" TargetType="ToggleButton">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Focusable" Value="False"/>
<Setter Property="Height" Value="22" />
<Setter Property="Width" Value="22" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="border" Padding="{TemplateBinding Padding}">
<Viewbox HorizontalAlignment="Center">
<Image Grid.Column="0" x:Name="img" Stretch="Fill" Source="{DynamicResource Images.OptionsMenu.Setting.Normal}" />
</Viewbox>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource Colors.Images.OptionsMenu.Setting.Background.MouseOver}" />
<Setter TargetName="img" Property="Source" Value="{DynamicResource Images.OptionsMenu.Setting.Pressed}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource Colors.Images.OptionsMenu.Setting.Background.MousePressed}"/>
<Setter TargetName="img" Property="Source" Value="{DynamicResource Images.OptionsMenu.Setting.Pressed}"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource Colors.Images.OptionsMenu.Setting.Background.MousePressed}"/>
<Setter TargetName="img" Property="Source" Value="{DynamicResource Images.OptionsMenu.Setting.Pressed}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.3" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DataGridEditButtonStyle" TargetType="Button">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="VerticalAlignment" Value="Bottom"/>
<Setter Property="Width" Value="56"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Margin" Value="3 0"/>
<Setter Property="Background" Value="{DynamicResource Colors.DataGridButton.Background.Normal}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.DataGridButton.Foreground.Normal}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="10" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource Colors.DataGridButton.Background.MouseOver}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.DataGridButton.Foreground.MouseOver}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource Colors.DataGridButton.Background.Pressed}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.DataGridButton.Foreground.Pressed}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.3" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -1,37 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows;
using System.Windows.Input;
using WPFBeginner.Services;
namespace WPFBeginner.ViewModels
{
public partial class LoginWindowViewModel : ObservableObject
{
[ObservableProperty]
private string user;
[ObservableProperty]
private string password;
[ObservableProperty]
private string message;
public ICommand LoginClickCommand => new RelayCommand<Window>((window) =>
{
if (CheckID() && CheckPassword())
window.DialogResult = true;
else
Message = ResourceExplorer.GetStringResource("Cultures.LoginWindow.ErrorMessage.InvalidUser");
});
public ICommand CloseClickCommand => new RelayCommand<Window>((window) =>
{
window.DialogResult = false;
});
private bool CheckID() => user?.Equals(App.Config.Login.User, StringComparison.OrdinalIgnoreCase) ?? false;
private bool CheckPassword() => password?.Equals(App.Config.Login.Password, StringComparison.OrdinalIgnoreCase) ?? false;
}
}

View File

@@ -1,217 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using WPFBeginner.Models;
using WPFBeginner.Services;
using WPFBeginner.Views;
namespace WPFBeginner.ViewModels
{
public partial class MainWindowViewModel : ObservableRecipient
{
private ObservableCollection<Member> members;
public ObservableCollection<Member> Members
{
get
{
if (filterMemebers != null)
return filterMemebers;
return members;
}
set => SetProperty(ref members, value);
}
private ObservableCollection<Member> filterMemebers;
[ObservableProperty]
private Member selectedMember;
private Member searchModel;
public string MemberName
{
get => searchModel?.Name;
set
{
if (searchModel == null)
searchModel = new Member();
searchModel.Name = value;
OnPropertyChanged();
}
}
public string Call
{
get => searchModel?.Call;
set
{
if (searchModel == null)
searchModel = new Member();
searchModel.Call = value;
OnPropertyChanged();
}
}
public string EmployeeNo
{
get => searchModel?.EmployeeNo;
set
{
if (searchModel == null)
searchModel = new Member();
searchModel.EmployeeNo = value;
OnPropertyChanged();
}
}
public string EMail
{
get => searchModel.EMail;
set
{
if (searchModel == null)
searchModel = new Member();
searchModel.EMail = value;
OnPropertyChanged();
}
}
public ICommand SearchClickCommand => new RelayCommand(() =>
{
var errorMessage = ValidateSearchValues();
if (errorMessage != null)
{
MessageBox.Show(errorMessage);
filterMemebers = null;
return;
}
IEnumerable<Member> result = Members;
if (!string.IsNullOrWhiteSpace(MemberName))
result = result.Where(i => i.Name?.Contains(MemberName) ?? false);
if (!string.IsNullOrWhiteSpace(EMail))
result = result.Where(i => i.EMail?.Contains(EMail) ?? false);
if (!string.IsNullOrWhiteSpace(Call))
result = result.Where(i => i.Call?.Contains(Call) ?? false);
if (!string.IsNullOrWhiteSpace(EmployeeNo))
result = result.Where(i => i.EmployeeNo?.Contains(EmployeeNo) ?? false);
if (result == null)
filterMemebers = new ObservableCollection<Member>();
else
filterMemebers = new ObservableCollection<Member>(result);
OnPropertyChanged(nameof(Members));
});
public ICommand CloseCommand => new RelayCommand<Window>(window => window.Close());
public ICommand MinimizeCommand => new RelayCommand<Window>(window => window.WindowState = WindowState.Minimized);
public ICommand MaximizeCommad => new RelayCommand<Window>(window =>
{
switch (window.WindowState)
{
case WindowState.Maximized:
window.WindowState = WindowState.Normal;
break;
case WindowState.Normal:
window.WindowState = WindowState.Maximized;
break;
}
});
public bool IsKorean => App.Config.Language.Equals("ko-KR", StringComparison.OrdinalIgnoreCase);
public bool IsEnglish => App.Config.Language.Equals("en-US", StringComparison.OrdinalIgnoreCase);
public ICommand LangueChangeCommand => new RelayCommand<string>(code =>
{
App.Config.Language = code;
OnPropertyChanged(nameof(IsKorean));
OnPropertyChanged(nameof(IsEnglish));
});
public bool IsDefaultTheme => App.Config.Theme.Equals("DefaultTheme", StringComparison.OrdinalIgnoreCase);
public bool IsLightTheme => App.Config.Theme.Equals("LightTheme", StringComparison.OrdinalIgnoreCase);
public ICommand ThemeChangeCommand => new RelayCommand<string>(code =>
{
App.Config.Theme = code;
OnPropertyChanged(nameof(IsDefaultTheme));
OnPropertyChanged(nameof(IsLightTheme));
});
private string ValidateSearchValues()
{
if (string.IsNullOrWhiteSpace(MemberName)
&& string.IsNullOrWhiteSpace(EmployeeNo)
&& string.IsNullOrWhiteSpace(Call)
&& string.IsNullOrWhiteSpace(EMail))
return ResourceExplorer.GetStringResource("Cultures.MainWindow.ErrorMessage.SearchConditionEmpty");
return null;
}
public ICommand DeleteClickCommand => new RelayCommand(() =>
{
if (SelectedMember == null)
return;
Members.Remove(SelectedMember);
});
public ICommand InitClickCommand => new RelayCommand(() =>
{
filterMemebers = null;
searchModel = new Member();
OnPropertyChanged(nameof(MemberName));
OnPropertyChanged(nameof(EmployeeNo));
OnPropertyChanged(nameof(EMail));
OnPropertyChanged(nameof(Call));
OnPropertyChanged(nameof(Members));
});
public ICommand AddClickCommand => new RelayCommand<Window>((parentWindow) =>
{
var registDialog = new RegistMemberWindow();
registDialog.Owner = parentWindow;
var result = registDialog.ShowDialog();
if (result == true)
{
var dx = registDialog.DataContext as RegistMemberWindowViewModel;
if (dx == null)
return;
Members.Add(Member.Create(dx.MemberName, dx.Call, dx.Email, dx.EmployeeNo));
}
});
public MainWindowViewModel()
{
CreateDefaultMember();
searchModel = new Member();
}
private void CreateDefaultMember()
{
members = new ObservableCollection<Member>();
foreach (var member in App.Config.Members)
members.Add(Member.Create(member.Name, member.Call, member.EMail, member.EmployeeNo));
OnPropertyChanged(nameof(Members));
}
}
}

View File

@@ -1,49 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Windows;
using System.Windows.Input;
using WPFBeginner.Services;
namespace WPFBeginner.ViewModels
{
public partial class RegistMemberWindowViewModel : ObservableRecipient
{
[ObservableProperty]
private string memberName;
[ObservableProperty]
private string call;
[ObservableProperty]
private string employeeNo;
[ObservableProperty]
private string email;
public ICommand RegistClickCommand => new RelayCommand<Window>((window) =>
{
var result = Validate();
if (result != null)
MessageBox.Show(result);
else
window.DialogResult = true;
});
public ICommand CancelClickCommand => new RelayCommand<Window>((window) =>
{
window.DialogResult = false;
});
private string Validate()
{
if (string.IsNullOrWhiteSpace(MemberName) || string.IsNullOrWhiteSpace(EmployeeNo))
return ResourceExplorer.GetStringResource("Cultures.RegistMemberWindow.ErrorMessage.FillMustValue");
return null;
}
}
}

View File

@@ -1,90 +0,0 @@
<Window x:Class="WPFBeginner.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:vm="clr-namespace:WPFBeginner.ViewModels"
xmlns:sv="clr-namespace:WPFBeginner.Services"
Background="Transparent" AllowsTransparency="True"
WindowStyle="None" Name="root" WindowStartupLocation="CenterScreen"
mc:Ignorable="d" Height="436" Width="720">
<Window.DataContext>
<vm:LoginWindowViewModel/>
</Window.DataContext>
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="{Binding ElementName=root, Path=ActualHeight}" />
</WindowChrome.WindowChrome>
<Window.Resources>
<Style x:Key="LoginButtonStyle" TargetType="Button">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="15" Background="{TemplateBinding Background}">
<Label HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}"
Content="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Window.Clip>
<RectangleGeometry Rect="0,0,720,436" RadiusX="15" RadiusY="15"/>
</Window.Clip>
<Border Background="Transparent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="36"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Source="/Resources/Images/bg_login.jpeg" Stretch="Fill" Grid.RowSpan="2"/>
<Grid Grid.Row="0">
<StackPanel Orientation="Horizontal" Margin="0,0,17,0" Height="29" HorizontalAlignment="Right" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True">
<Button Width="46" Height="29" Style="{DynamicResource TiTleBarCloseButton}" Command="{Binding CloseClickCommand}" CommandParameter="{Binding ElementName=root}"
IsTabStop="True" TabIndex="4"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<Label Content="WPF toy project" FontFamily="Noto Sans CJK KR Medium" FontSize="48" Foreground="White" Margin="0,93,0,0" Width="Auto" Height="Auto" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Label Content="For beginner" FontFamily="Noto Sans CJK KR Medium" FontSize="24" Foreground="White" Margin="389,60,0,0" Width="Auto" Height="Auto" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Border CornerRadius="5" Margin="150,179,150,32" Background="#4CFFFFFF" WindowChrome.IsHitTestVisibleInChrome="True">
<Grid FocusManager.FocusedElement="{Binding ElementName=TB_User}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="1.2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.ColumnSpan="2" Content="{DynamicResource Cultures.LoginWindow.Inform}" Margin="15,0" FontSize="24" HorizontalAlignment="Left" VerticalAlignment="Center" FontWeight="Bold" FontFamily="Noto Sans CJK KR Medium" Foreground="Black" />
<Image Grid.Row="1" Grid.Column="0" Source="/Resources/Images/ico_user.png" Margin="10"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="TB_User" Text="{Binding User}" Margin="5,5,15,0" FontSize="14"
Background="#7FFFFFFF" VerticalContentAlignment="Center" Padding="10,0,0,0"
IsTabStop="True" TabIndex="1" Focusable="True" sv:EnterKeyTraversal.IsEnabled="True"/>
<Image Grid.Row="2" Grid.Column="0" Source="/Resources/Images/ico_pw.png" Margin="10"/>
<PasswordBox Grid.Row="2" Grid.Column="1" sv:PasswordBoxAssistant.EnableBindingPassword="True" sv:EnterKeyTraversal.IsEnabled="True"
sv:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="5,5,15,0"
FontSize="14" Background="#7FFFFFFF" VerticalContentAlignment="Center" Padding="10,0,0,0" IsTabStop="True" TabIndex="2"/>
<Grid Grid.Row="3" Grid.ColumnSpan="2">
<Button Style="{StaticResource LoginButtonStyle}" Content="{DynamicResource Cultures.LoginWindow.LoginButtonText}" Margin="100,5" Background="#FFFF7575" FontSize="14" Foreground="White" FontWeight="Bold"
Command="{Binding LoginClickCommand}" CommandParameter="{Binding ElementName=root}" IsTabStop="True" TabIndex="3"/>
</Grid>
</Grid>
</Border>
<Label Content="{Binding Message}" FontFamily="Noto Sans CJK KR Medium" FontSize="11" Foreground="Yellow"
Margin="159,369,0,0" Width="Auto" Height="Auto" HorizontalAlignment="Left" VerticalAlignment="Top" d:Content="ddd"/>
</Grid>
</Grid>
</Border>
</Window>

View File

@@ -1,15 +0,0 @@
using System.Windows;
namespace WPFBeginner.Views
{
/// <summary>
/// LoginWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
}
}

View File

@@ -1,172 +0,0 @@
<Window x:Class="WPFBeginner.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:uc="clr-namespace:WPFBeginner.Controls"
WindowStyle="None" ResizeMode="CanResize" Name="root"
mc:Ignorable="d" Title="MainWindow" Height="500" Width="400" Padding="20">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="{Binding ElementName=titleBar, Path=ActualHeight}" ResizeBorderThickness="0 5 0 5"/>
</WindowChrome.WindowChrome>
<Window.DataContext>
<Binding Source="{StaticResource ViewModelLocator}" Path="MainWindowViewModel"/>
</Window.DataContext>
<Window.Resources>
<Style x:Key="SearchButton" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource Colors.SearchButton.Background.Normal}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Colors.SearchButton.BorderBrush.Normal}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.SearchButton.Foreground.Normal}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource Colors.SearchButton.Background.MouseOver}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Colors.SearchButton.BorderBrush.MouseOver}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.SearchButton.Foreground.MouseOver}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource Colors.SearchButton.Background.MousePressed}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Colors.SearchButton.BorderBrush.MousePressed}"/>
<Setter Property="Foreground" Value="{DynamicResource Colors.SearchButton.Foreground.MousePressed}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="130"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--Title Bar-->
<Grid x:Name="titleBar" Background="{DynamicResource Colors.TitleBar.Title.Background}" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource Cultures.MainWindow.Title}" Foreground="{DynamicResource Colors.TitleBar.Title.Foreground}"
FontSize="14" FontFamily="NanumSquareOTF_ac Bold" VerticalContentAlignment="Center" Padding="10 0"/>
<Grid Grid.Column="2">
<StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True" VerticalAlignment="Top">
<ToggleButton Width="22" Height="22" Style="{DynamicResource SettingButtonStyle}" Name="bt_settings"/>
<Button Width="39" Height="27" Style="{DynamicResource TiTleBarMinimizeButton}" Command="{Binding MinimizeCommand}" CommandParameter="{Binding ElementName=root}"/>
<Button Width="39" Height="27" Style="{DynamicResource TiTleBarMaximizeButton}" Command="{Binding MaximizeCommad}" CommandParameter="{Binding ElementName=root}"/>
<Button Width="39" Height="27" Style="{DynamicResource TiTleBarCloseButton}" Command="{Binding CloseCommand}" CommandParameter="{Binding ElementName=root}"/>
</StackPanel>
<Popup PlacementTarget="{Binding ElementName=bt_settings}" Placement="Bottom" IsOpen="{Binding ElementName=bt_settings, Path=IsChecked}" StaysOpen="False">
<Border Background="{DynamicResource Colors.SettingPopup.Background}" BorderThickness="1" BorderBrush="{DynamicResource Colors.SettingPopup.BorderBrush}" >
<StackPanel Orientation="Vertical" Width="100">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel>
<RadioButton Content="한글판" Foreground="{DynamicResource Colors.SettingPopup.Foreground}"
GroupName="Cultures" IsChecked="{Binding IsKorean, Mode=OneWay}" Command="{Binding LangueChangeCommand}" CommandParameter="ko-KR"/>
<RadioButton Content="English" Foreground="{DynamicResource Colors.SettingPopup.Foreground}"
GroupName="Cultures" IsChecked="{Binding IsEnglish, Mode=OneWay}" Command="{Binding LangueChangeCommand}" CommandParameter="en-US"/>
</StackPanel>
<StackPanel Grid.Row="2">
<RadioButton Content="Default" Foreground="{DynamicResource Colors.SettingPopup.Foreground}" GroupName="Themes"
IsChecked="{Binding IsDefaultTheme, Mode=OneWay}" Command="{Binding ThemeChangeCommand}" CommandParameter="DefaultTheme"/>
<RadioButton Content="Light" Foreground="{DynamicResource Colors.SettingPopup.Foreground}" GroupName="Themes"
IsChecked="{Binding IsLightTheme, Mode=OneWay}" Command="{Binding ThemeChangeCommand}" CommandParameter="LightTheme"/>
</StackPanel>
</Grid>
</StackPanel>
</Border>
</Popup>
</Grid>
</Grid>
<Border HorizontalAlignment="Stretch" VerticalAlignment="Bottom" BorderThickness="0.5" BorderBrush="{DynamicResource Colors.Section.HorizontalSeperator}"/>
<!-- Filter Box -->
<Border Grid.Row="1" Padding="0 10" HorizontalAlignment="Right" Width="400" Background="{DynamicResource Colors.Main.Background}">
<Grid HorizontalAlignment="Right" Width="400">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<uc:MemberInputPanel MemberName="{Binding MemberName}" Call="{Binding Call}"
EmployeeNo="{Binding EmployeeNo}" Email="{Binding Email}"/>
<Grid Grid.RowSpan="4" Grid.Column="1">
<Button Margin="3" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="2" Style="{StaticResource SearchButton}"
Command="{Binding SearchClickCommand}">
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Image Source="/Resources/Images/btn_search_001.png" Stretch="UniformToFill"/>
<Label Content="{DynamicResource Cultures.MainWindow.Button.Search}"
Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}"
HorizontalAlignment="Center" FontFamily="Yu Gothic UI" FontWeight="Bold" FontSize="12" Margin="0" Padding="0"/>
</StackPanel>
</Button>
</Grid>
</Grid>
</Border>
<Border Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" BorderThickness="0.5" BorderBrush="{DynamicResource Colors.Section.HorizontalSeperator}"/>
<!-- Search Result -->
<Grid Grid.Row="3" Background="{DynamicResource Colors.Main.Background}">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="10 5">
<Button Content="{DynamicResource Cultures.MainWindow.Button.Add}" Style="{DynamicResource DataGridEditButtonStyle}" Command="{Binding AddClickCommand}" CommandParameter="{Binding ElementName=root}"/>
<Button Content="{DynamicResource Cultures.MainWindow.Button.Delete}" Style="{DynamicResource DataGridEditButtonStyle}" Command="{Binding DeleteClickCommand}"/>
<Button Content="{DynamicResource Cultures.MainWindow.Button.Init}" Style="{DynamicResource DataGridEditButtonStyle}" Command="{Binding InitClickCommand}"/>
</StackPanel>
</Grid>
<DataGrid Grid.Row="1" Margin="5" ItemsSource="{Binding Members}" SelectedItem="{Binding SelectedMember}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Width="*">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource Cultures.MainWindow.DatagGid.Column.Name}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Call}" Width="*">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource Cultures.MainWindow.DatagGid.Column.Call}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding EmployeeNo}" Width="*">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource Cultures.MainWindow.DatagGid.Column.No}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding EMail}" Width="*">
<DataGridTextColumn.Header>
<TextBlock Text="{DynamicResource Cultures.MainWindow.DatagGid.Column.Email}" />
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Grid>
</Window>

View File

@@ -1,15 +0,0 @@
using System.Windows;
namespace WPFBeginner
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@@ -1,46 +0,0 @@
<Window x:Class="WPFBeginner.Views.RegistMemberWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:uc="clr-namespace:WPFBeginner.Controls"
xmlns:bh="clr-namespace:WPFBeginner.Behaviors"
WindowStyle="SingleBorderWindow"
xmlns:vm ="clr-namespace:WPFBeginner.ViewModels"
mc:Ignorable="d" WindowStartupLocation="CenterOwner" Name="root"
Title="" SizeToContent="WidthAndHeight" MinWidth="350" MinHeight="200" d:DesignHeight="200" d:DesignWidth="200"
bh:WindowBehavior.HideCloseButton="True">
<Window.DataContext>
<vm:RegistMemberWindowViewModel />
</Window.DataContext>
<Border Background="{DynamicResource Colors.Main.Background}" Padding="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<uc:MemberInputPanel x:Name="memberPanel"
MemberName="{Binding MemberName}"
Call="{Binding Call}"
EmployeeNo="{Binding EmployeeNo}"
Email="{Binding Email}"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Content="{DynamicResource Cultures.RegistMemberWindow.RegistButton}" HorizontalAlignment="Stretch" Width="Auto"
Command="{Binding RegistClickCommand}" CommandParameter="{Binding ElementName=root}"/>
<Button Grid.Column="2" Content="{DynamicResource Cultures.RegistMemberWindow.CancelButton}" HorizontalAlignment="Stretch" Width="Auto"
Command="{Binding CancelClickCommand}" CommandParameter="{Binding ElementName=root}"/>
</Grid>
</Grid>
</Border>
</Window>

View File

@@ -1,15 +0,0 @@
using System.Windows;
namespace WPFBeginner.Views
{
/// <summary>
/// RegistMemberWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class RegistMemberWindow : Window
{
public RegistMemberWindow()
{
InitializeComponent();
}
}
}

View File

@@ -1,44 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<StartupObject>WPFBeginner.App</StartupObject>
</PropertyGroup>
<ItemGroup>
<None Remove="appsettings.json" />
<None Remove="Resources\Images\bg_login.jpeg" />
<None Remove="Resources\Images\btn_search_001.png" />
<None Remove="Resources\Images\ico_pw.png" />
<None Remove="Resources\Images\ico_user.png" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Images\bg_login.jpeg" />
<Resource Include="Resources\Images\btn_search_001.png" />
<Resource Include="Resources\Images\ico_pw.png" />
<Resource Include="Resources\Images\ico_user.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Update="App.xaml">
<Generator>MSBuild:Compile</Generator>
</ApplicationDefinition>
</ItemGroup>
</Project>

View File

@@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34607.119
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPFBeginner", "WPFBeginner\WPFBeginner.csproj", "{8A5A2302-7A11-434B-8C98-D586BDC85E4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A5A2302-7A11-434B-8C98-D586BDC85E4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A5A2302-7A11-434B-8C98-D586BDC85E4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A5A2302-7A11-434B-8C98-D586BDC85E4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A5A2302-7A11-434B-8C98-D586BDC85E4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {65B27DF8-6AA9-4BC1-964B-51069FA5B003}
EndGlobalSection
EndGlobal