[build] 토이 프로젝트 작업 변경
1. .gitignore 파일이 추가되어, 컴파일된 바이너리, 로그, 임시 파일 등이 Git 추적에서 제외되었습니다. 2. WPF의 MVVM 패턴을 따르는 여러 뷰(LoginWindow.xaml, MainWindow.xaml, RegistMemberWindow.xaml), 뷰 모델(LoginWindowViewModel.cs, MainWindowViewModel.cs, RegistMemberWindowViewModel.cs), 그리고 모델(Member.cs)이 추가되었습니다. 3. 리소스 및 스타일 정의를 포함한 XAML 리소스 파일(DefaultTheme.xaml, LightTheme.xaml, Buttons.xaml 등)이 추가되어 UI의 모양과 느낌을 커스터마이즈할 수 있게 되었습니다. 4. 애플리케이션 설정(AppSettings.cs)과 관련된 설정 파일과 로그인 정보(LoginInfo.cs)를 처리하는 코드가 추가되었습니다. 5. 이미지 리소스(bg_login.jpeg, btn_search_001.png, ico_pw.png, ico_user.png)가 추가되어 UI에 사용됩니다. 6. 유틸리티 및 서비스(ResourceExplorer.cs, ViewModelLocator.cs 등)를 처리하는 여러 보조 클래스들이 추가되었습니다.
This commit is contained in:
498
.gitignore
vendored
Normal file
498
.gitignore
vendored
Normal file
@@ -0,0 +1,498 @@
|
||||
# ---> C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# ---> Node
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# ---> Java
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.nar
|
||||
*.ear
|
||||
*.zip
|
||||
*.tar.gz
|
||||
*.rar
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
replay_pid*
|
||||
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]bjL/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*_h.h
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tlb
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
*.tlog
|
||||
*.metagen
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
20
src/WPFEduSolution/WPFBeginner/App.xaml
Normal file
20
src/WPFEduSolution/WPFBeginner/App.xaml
Normal file
@@ -0,0 +1,20 @@
|
||||
<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"
|
||||
StartupUri="/Views/MainWindow.xaml">
|
||||
<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>
|
||||
109
src/WPFEduSolution/WPFBeginner/App.xaml.cs
Normal file
109
src/WPFEduSolution/WPFBeginner/App.xaml.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
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; }
|
||||
|
||||
//[STAThread]
|
||||
//static void Main()
|
||||
//{
|
||||
//try
|
||||
//{
|
||||
// var application = new App();
|
||||
// application.InitializeComponent(); // For resouce load.
|
||||
// application.Run();
|
||||
//}
|
||||
//catch (AlreadyExcutedAppException ex)
|
||||
//{
|
||||
// MessageBox.Show("이미 실행중입니다. 종료 후, 다시 실행해주세요");
|
||||
//}
|
||||
//catch (LoginFailedException ex)
|
||||
//{
|
||||
// // Do not anything;
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// // log
|
||||
//}
|
||||
//}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
//Login();
|
||||
InitializeService();
|
||||
}
|
||||
|
||||
private 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 void SetTheme(string code)
|
||||
{
|
||||
if (_currentTheme != null)
|
||||
Resources.MergedDictionaries.Remove(_currentTheme);
|
||||
|
||||
_currentTheme = ResourceExplorer.GetThemeResourceDic(code);
|
||||
Resources.MergedDictionaries.Add(_currentTheme);
|
||||
}
|
||||
|
||||
private void SetLanguage(string code)
|
||||
{
|
||||
if (_currentLanguage != null)
|
||||
Resources.MergedDictionaries.Remove( _currentLanguage );
|
||||
|
||||
_currentLanguage = ResourceExplorer.GetLanguageResourceDic(code);
|
||||
Resources.MergedDictionaries.Add(_currentLanguage);
|
||||
}
|
||||
|
||||
private static void Login()
|
||||
{
|
||||
var login = new LoginWindow();
|
||||
var isSuccess = login.ShowDialog();
|
||||
|
||||
if (isSuccess != true)
|
||||
throw new LoginFailedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
10
src/WPFEduSolution/WPFBeginner/AssemblyInfo.cs
Normal file
10
src/WPFEduSolution/WPFBeginner/AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[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)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
277
src/WPFEduSolution/WPFBeginner/Behaviors/WindowBehavior.cs
Normal file
277
src/WPFEduSolution/WPFBeginner/Behaviors/WindowBehavior.cs
Normal file
@@ -0,0 +1,277 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows;
|
||||
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
75
src/WPFEduSolution/WPFBeginner/Configurations/AppSettings.cs
Normal file
75
src/WPFEduSolution/WPFBeginner/Configurations/AppSettings.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
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 = language,
|
||||
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;
|
||||
}
|
||||
}
|
||||
12
src/WPFEduSolution/WPFBeginner/Configurations/LoginInfo.cs
Normal file
12
src/WPFEduSolution/WPFBeginner/Configurations/LoginInfo.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace WPFBeginner.Configurations
|
||||
{
|
||||
public class LoginInfo
|
||||
{
|
||||
[JsonPropertyName("User")]
|
||||
public string User { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<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"
|
||||
xmlns:local="clr-namespace:WPFBeginner.Controls"
|
||||
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="{StaticResource Cultures.MainWindow.Label.Name}"/>
|
||||
<Label Grid.Row="1" Style="{StaticResource LabelTitle}" Content="{StaticResource Cultures.MainWindow.Label.Call}" />
|
||||
<Label Grid.Row="2" Style="{StaticResource LabelTitle}" Content="{StaticResource Cultures.MainWindow.Label.No}"/>
|
||||
<Label Grid.Row="3" Style="{StaticResource LabelTitle}" Content="{StaticResource 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>
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/WPFEduSolution/WPFBeginner/Converters/BaseConverter.cs
Normal file
19
src/WPFEduSolution/WPFBeginner/Converters/BaseConverter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace WPFBeginner.Converters
|
||||
{
|
||||
public abstract class BaseConverter : MarkupExtension
|
||||
{
|
||||
protected BaseConverter() { }
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPFBeginner.Exceptions
|
||||
{
|
||||
internal class AlreadyExcutedAppException : Exception
|
||||
{
|
||||
public AlreadyExcutedAppException() : base()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WPFBeginner.Exceptions
|
||||
{
|
||||
internal class LoginFailedException : Exception
|
||||
{
|
||||
public LoginFailedException() : base()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/WPFEduSolution/WPFBeginner/Models/Member.cs
Normal file
32
src/WPFEduSolution/WPFBeginner/Models/Member.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<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>
|
||||
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/bg_login.jpeg
Normal file
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/bg_login.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/ico_pw.png
Normal file
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/ico_pw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/ico_user.png
Normal file
BIN
src/WPFEduSolution/WPFBeginner/Resources/Images/ico_user.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Windows.Input;
|
||||
using System.Windows;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
62
src/WPFEduSolution/WPFBeginner/Services/ResourceExplorer.cs
Normal file
62
src/WPFEduSolution/WPFBeginner/Services/ResourceExplorer.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
15
src/WPFEduSolution/WPFBeginner/Services/ViewModelLocator.cs
Normal file
15
src/WPFEduSolution/WPFBeginner/Services/ViewModelLocator.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WPFBeginner.ViewModels;
|
||||
|
||||
namespace WPFBeginner.Services
|
||||
{
|
||||
internal class ViewModelLocator
|
||||
{
|
||||
public MainWindowViewModel MainWindowViewModel => Ioc.Default.GetService<MainWindowViewModel>();
|
||||
}
|
||||
}
|
||||
8
src/WPFEduSolution/WPFBeginner/Spec.txt
Normal file
8
src/WPFEduSolution/WPFBeginner/Spec.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
## 요구사항
|
||||
|
||||
1. 프로그램 중복실행 방지
|
||||
2. 로그인 화면
|
||||
- 파일로부터 데이터 읽어와 비교
|
||||
3.
|
||||
@@ -0,0 +1,55 @@
|
||||
<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>
|
||||
52
src/WPFEduSolution/WPFBeginner/Themes/Colors/LightTheme.xaml
Normal file
52
src/WPFEduSolution/WPFBeginner/Themes/Colors/LightTheme.xaml
Normal file
@@ -0,0 +1,52 @@
|
||||
<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>
|
||||
31
src/WPFEduSolution/WPFBeginner/Themes/Cultures/en-US.xaml
Normal file
31
src/WPFEduSolution/WPFBeginner/Themes/Cultures/en-US.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<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>
|
||||
31
src/WPFEduSolution/WPFBeginner/Themes/Cultures/ko-KR.xaml
Normal file
31
src/WPFEduSolution/WPFBeginner/Themes/Cultures/ko-KR.xaml
Normal file
@@ -0,0 +1,31 @@
|
||||
<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>
|
||||
164
src/WPFEduSolution/WPFBeginner/Themes/Styles/Buttons.xaml
Normal file
164
src/WPFEduSolution/WPFBeginner/Themes/Styles/Buttons.xaml
Normal file
@@ -0,0 +1,164 @@
|
||||
<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>
|
||||
@@ -0,0 +1,37 @@
|
||||
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() => user?.Equals(App.Config.Login.User, StringComparison.OrdinalIgnoreCase) ?? false;
|
||||
}
|
||||
}
|
||||
201
src/WPFEduSolution/WPFBeginner/ViewModels/MainWindowViewModel.cs
Normal file
201
src/WPFEduSolution/WPFBeginner/ViewModels/MainWindowViewModel.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private bool isKorean;
|
||||
public bool IsKorean
|
||||
{
|
||||
get => isKorean;
|
||||
set
|
||||
{
|
||||
SetProperty(ref isKorean, value);
|
||||
App.Config.Language = value ? "ko-KR" : "en-US";
|
||||
}
|
||||
}
|
||||
|
||||
private bool isDefaultTheme;
|
||||
public bool IsDefaultTheme
|
||||
{
|
||||
get => isDefaultTheme;
|
||||
set
|
||||
{
|
||||
SetProperty(ref isDefaultTheme, value);
|
||||
App.Config.Theme = value ? "DefaultTheme" : "LightTheme";
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
|
||||
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(() =>
|
||||
{
|
||||
var registDialog = new RegistMemberWindow();
|
||||
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();
|
||||
isKorean = App.Config.Language.Equals("ko-KR", StringComparison.OrdinalIgnoreCase);
|
||||
isDefaultTheme = App.Config.Theme.Equals("DefaultTheme", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using WPFBeginner.Services;
|
||||
using WPFBeginner.Views;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/WPFEduSolution/WPFBeginner/Views/LoginWindow.xaml
Normal file
90
src/WPFEduSolution/WPFBeginner/Views/LoginWindow.xaml
Normal file
@@ -0,0 +1,90 @@
|
||||
<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"
|
||||
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="25" RadiusY="25"/>
|
||||
</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="{StaticResource 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="{StaticResource 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>
|
||||
15
src/WPFEduSolution/WPFBeginner/Views/LoginWindow.xaml.cs
Normal file
15
src/WPFEduSolution/WPFBeginner/Views/LoginWindow.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace WPFBeginner.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// LoginWindow.xaml에 대한 상호 작용 논리
|
||||
/// </summary>
|
||||
public partial class LoginWindow : Window
|
||||
{
|
||||
public LoginWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
152
src/WPFEduSolution/WPFBeginner/Views/MainWindow.xaml
Normal file
152
src/WPFEduSolution/WPFBeginner/Views/MainWindow.xaml
Normal file
@@ -0,0 +1,152 @@
|
||||
<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"
|
||||
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="{StaticResource Cultures.MainWindow.Title}" Foreground="{StaticResource 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}"/>
|
||||
<Button Width="39" Height="27" Style="{DynamicResource TiTleBarMaximizeButton}" Command="{Binding MaximizeCommad}"/>
|
||||
<Button Width="39" Height="27" Style="{DynamicResource TiTleBarCloseButton}" Command="{Binding CloseCommand}"/>
|
||||
</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}"/>
|
||||
<RadioButton Content="English" Foreground="{DynamicResource Colors.SettingPopup.Foreground}" GroupName="Cultures" IsChecked="{Binding IsKorean, Converter={StaticResource InverseBoolConverter}}"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2">
|
||||
<RadioButton Content="Dark" Foreground="{DynamicResource Colors.SettingPopup.Foreground}" GroupName="Themes"/>
|
||||
<RadioButton Content="Light" Foreground="{DynamicResource Colors.SettingPopup.Foreground}" GroupName="Themes"/>
|
||||
</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="{StaticResource 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="{StaticResource Cultures.MainWindow.Button.Add}" Style="{DynamicResource DataGridEditButtonStyle}" Command="{Binding AddClickCommand}"/>
|
||||
<Button Content="{StaticResource Cultures.MainWindow.Button.Delete}" Style="{DynamicResource DataGridEditButtonStyle}" Command="{Binding DeleteClickCommand}"/>
|
||||
<Button Content="{StaticResource 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 Header="{DynamicResource Cultures.MainWindow.DatagGid.Column.Name}" Binding="{Binding Name}" Width="*"/>
|
||||
<DataGridTextColumn Header="{DynamicResource Cultures.MainWindow.DatagGid.Column.Call}" Binding="{Binding Call}" Width="*"/>
|
||||
<DataGridTextColumn Header="{DynamicResource Cultures.MainWindow.DatagGid.Column.No}" Binding="{Binding EmployeeNo}" Width="*"/>
|
||||
<DataGridTextColumn Header="{DynamicResource Cultures.MainWindow.DatagGid.Column.Email}" Binding="{Binding EMail}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
24
src/WPFEduSolution/WPFBeginner/Views/MainWindow.xaml.cs
Normal file
24
src/WPFEduSolution/WPFBeginner/Views/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace WPFBeginner
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/WPFEduSolution/WPFBeginner/Views/RegistMemberWindow.xaml
Normal file
46
src/WPFEduSolution/WPFBeginner/Views/RegistMemberWindow.xaml
Normal file
@@ -0,0 +1,46 @@
|
||||
<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="Regist" SizeToContent="WidthAndHeight" MinWidth="200" 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>
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace WPFBeginner.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// RegistMemberWindow.xaml에 대한 상호 작용 논리
|
||||
/// </summary>
|
||||
public partial class RegistMemberWindow : Window
|
||||
{
|
||||
public RegistMemberWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/WPFEduSolution/WPFBeginner/WPFBeginner.csproj
Normal file
46
src/WPFEduSolution/WPFBeginner/WPFBeginner.csproj
Normal file
@@ -0,0 +1,46 @@
|
||||
<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" />
|
||||
<None Remove="Spec.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Spec.txt" />
|
||||
</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>
|
||||
46
src/WPFEduSolution/WPFBeginner/appsettings.json
Normal file
46
src/WPFEduSolution/WPFBeginner/appsettings.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Login": {
|
||||
"User": "admin",
|
||||
"Password": "0000"
|
||||
},
|
||||
"Language": "ko-KR",
|
||||
"Theme": "DefaultTheme",
|
||||
"DefaultMembers": [
|
||||
{
|
||||
"Name": "최준영",
|
||||
"Call": "02-2141-7523",
|
||||
"EMail": "b17314.tdc.hanmacgroup@gmail.com",
|
||||
"No": "b17314"
|
||||
},
|
||||
{
|
||||
"Name": "엄지숙",
|
||||
"Call": "-",
|
||||
"EMail": "b23072@hanmaceng.co.kr",
|
||||
"No": "b23072"
|
||||
},
|
||||
{
|
||||
"Name": "정나래",
|
||||
"Call": "-",
|
||||
"EMail": "b23009@hanmaceng.co.kr",
|
||||
"No": "b23009"
|
||||
},
|
||||
{
|
||||
"Name": "강근아",
|
||||
"Call": "02-2141-7317",
|
||||
"EMail": "m21318.tdc.hanmacgroup@gmail.com",
|
||||
"No": "m21318"
|
||||
},
|
||||
{
|
||||
"Name": "김근형",
|
||||
"Call": "02-2141-7508",
|
||||
"EMail": "b20311.tdc.hanmacgroup@gmail.com",
|
||||
"No": "b20311"
|
||||
},
|
||||
{
|
||||
"Name": "정호진",
|
||||
"Call": "-",
|
||||
"EMail": "b23022@hanmaceng.co.kr",
|
||||
"No": "b23022"
|
||||
}
|
||||
]
|
||||
}
|
||||
25
src/WPFEduSolution/WPFEduSolution.sln
Normal file
25
src/WPFEduSolution/WPFEduSolution.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user