33 changed files with 3160 additions and 299 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
@ -0,0 +1,295 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Text.RegularExpressions; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Housing.Services |
|||
{ |
|||
// 1. DatabaseSettings
|
|||
public class DatabaseSettings |
|||
{ |
|||
public string ConnectionString { get; set; } = string.Empty; |
|||
|
|||
public static DatabaseSettings Load(string path) |
|||
{ |
|||
var settings = new DatabaseSettings(); |
|||
try |
|||
{ |
|||
if (File.Exists(path)) |
|||
{ |
|||
var lines = File.ReadAllLines(path); |
|||
foreach (var line in lines) |
|||
{ |
|||
var trimmed = line.Trim(); |
|||
if (trimmed.StartsWith("ConnectionString", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
var parts = trimmed.Split(new[] { '=' }, 2); |
|||
if (parts.Length == 2) |
|||
{ |
|||
settings.ConnectionString = parts[1].Trim(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// 로드 실패 시 무시
|
|||
} |
|||
return settings; |
|||
} |
|||
} |
|||
|
|||
// 2. LoginSettings
|
|||
public class LoginSettings |
|||
{ |
|||
public bool OfflinePreview { get; set; } = true; // 기본값은 오프라인 테스트 편의를 위해 true
|
|||
|
|||
public static LoginSettings Load(string path) |
|||
{ |
|||
var settings = new LoginSettings(); |
|||
try |
|||
{ |
|||
if (File.Exists(path)) |
|||
{ |
|||
var lines = File.ReadAllLines(path); |
|||
foreach (var line in lines) |
|||
{ |
|||
var trimmed = line.Trim(); |
|||
if (trimmed.StartsWith("OfflinePreview", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
var parts = trimmed.Split(new[] { '=' }, 2); |
|||
if (parts.Length == 2) |
|||
{ |
|||
if (bool.TryParse(parts[1].Trim(), out var val)) |
|||
{ |
|||
settings.OfflinePreview = val; |
|||
} |
|||
else if (int.TryParse(parts[1].Trim(), out var intVal)) |
|||
{ |
|||
settings.OfflinePreview = intVal != 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// 로드 실패 시 무시
|
|||
} |
|||
return settings; |
|||
} |
|||
} |
|||
|
|||
// 3. LoginHistoryStore
|
|||
public class LoginHistoryStore |
|||
{ |
|||
private readonly string _filePath; |
|||
private readonly Dictionary<string, List<string>> _history = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); |
|||
|
|||
private LoginHistoryStore(string filePath) |
|||
{ |
|||
_filePath = filePath; |
|||
} |
|||
|
|||
public static LoginHistoryStore Load(string path) |
|||
{ |
|||
var store = new LoginHistoryStore(path); |
|||
store.LoadFromFile(); |
|||
return store; |
|||
} |
|||
|
|||
public List<string> GetValues(string fieldName) |
|||
{ |
|||
if (_history.TryGetValue(fieldName, out var list)) |
|||
{ |
|||
return new List<string>(list); |
|||
} |
|||
return new List<string>(); |
|||
} |
|||
|
|||
public void Remember(string fieldName, string value) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) return; |
|||
value = value.Trim(); |
|||
|
|||
if (!_history.TryGetValue(fieldName, out var list)) |
|||
{ |
|||
list = new List<string>(); |
|||
_history[fieldName] = list; |
|||
} |
|||
|
|||
// 중복 제거 및 맨 앞으로 이동
|
|||
list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)); |
|||
list.Insert(0, value); |
|||
|
|||
// 최대 5개 이력 유지
|
|||
if (list.Count > 5) |
|||
{ |
|||
list.RemoveRange(5, list.Count - 5); |
|||
} |
|||
} |
|||
|
|||
public bool Forget(string fieldName, string value) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(value)) return false; |
|||
value = value.Trim(); |
|||
|
|||
if (_history.TryGetValue(fieldName, out var list)) |
|||
{ |
|||
bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0; |
|||
return removed; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public void Save() |
|||
{ |
|||
try |
|||
{ |
|||
var dir = Path.GetDirectoryName(_filePath); |
|||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) |
|||
{ |
|||
Directory.CreateDirectory(dir); |
|||
} |
|||
|
|||
// 초간단 JSON 형식으로 직렬화 (의존성 최소화)
|
|||
using (var sw = new StreamWriter(_filePath)) |
|||
{ |
|||
sw.WriteLine("{"); |
|||
var keys = new List<string>(_history.Keys); |
|||
for (int i = 0; i < keys.Count; i++) |
|||
{ |
|||
var key = keys[i]; |
|||
var values = _history[key]; |
|||
var arrayItems = new List<string>(); |
|||
foreach (var val in values) |
|||
{ |
|||
// 따옴표 escape
|
|||
var escaped = val.Replace("\"", "\\\""); |
|||
arrayItems.Add($"\"{escaped}\""); |
|||
} |
|||
var line = $" \"{key}\": [{string.Join(", ", arrayItems)}]"; |
|||
if (i < keys.Count - 1) |
|||
{ |
|||
line += ","; |
|||
} |
|||
sw.WriteLine(line); |
|||
} |
|||
sw.WriteLine("}"); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// 저장 오류 시 무시
|
|||
} |
|||
} |
|||
|
|||
private void LoadFromFile() |
|||
{ |
|||
try |
|||
{ |
|||
if (!File.Exists(_filePath)) return; |
|||
|
|||
var text = File.ReadAllText(_filePath); |
|||
// 정규식이나 아주 심플한 파싱으로 키와 배열 파싱
|
|||
// 형식 예: "Maker": ["MOBI", "TEST"]
|
|||
var matches = Regex.Matches(text, @"""([^""]+)""\s*:\s*\[([^\]]*)\]"); |
|||
foreach (Match match in matches) |
|||
{ |
|||
var key = match.Groups[1].Value; |
|||
var arrayContent = match.Groups[2].Value; |
|||
var valueMatches = Regex.Matches(arrayContent, @"""([^""]*)"""); |
|||
|
|||
var list = new List<string>(); |
|||
foreach (Match valMatch in valueMatches) |
|||
{ |
|||
list.Add(valMatch.Groups[1].Value); |
|||
} |
|||
_history[key] = list; |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// 로드 오류 시 무시
|
|||
} |
|||
} |
|||
} |
|||
|
|||
// 4. LoginAccountRepository
|
|||
public class LoginAccountRepository |
|||
{ |
|||
private readonly DatabaseSettings _dbSettings; |
|||
private readonly LoginSettings _loginSettings; |
|||
|
|||
public LoginAccountRepository(DatabaseSettings dbSettings, LoginSettings loginSettings) |
|||
{ |
|||
_dbSettings = dbSettings; |
|||
_loginSettings = loginSettings; |
|||
} |
|||
|
|||
public async Task<bool> ExistsAsync(string operatorId, string password) |
|||
{ |
|||
// 오프라인 모드인 경우 기본 성공
|
|||
if (_loginSettings.OfflinePreview) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// DB 커넥션 스트링이 비어있으면 기본값 확인
|
|||
if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString)) |
|||
{ |
|||
return IsDefaultAccount(operatorId, password); |
|||
} |
|||
|
|||
try |
|||
{ |
|||
// 실제 DB에서 계정을 조회해보기 위한 시도
|
|||
using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString)) |
|||
{ |
|||
await conn.OpenAsync(); |
|||
|
|||
// 우선 Users 또는 Accounts와 같이 로그인 테이블명이 뭔지 모를 수 있으므로
|
|||
// 쿼리 실패 시 default 계정 검증으로 fallback 하도록 구성
|
|||
const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw"; |
|||
using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn)) |
|||
{ |
|||
cmd.Parameters.AddWithValue("@Id", operatorId); |
|||
cmd.Parameters.AddWithValue("@Pw", password); |
|||
|
|||
var count = (int)await cmd.ExecuteScalarAsync(); |
|||
return count > 0; |
|||
} |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// DB 연결이나 쿼리 오류 발생 시, 테스트 편의를 위해 test/test 혹은 admin/admin 등 디폴트 계정을 제공
|
|||
return IsDefaultAccount(operatorId, password); |
|||
} |
|||
} |
|||
|
|||
private bool IsDefaultAccount(string operatorId, string password) |
|||
{ |
|||
// 기본 개발/테스트용 계정 처리
|
|||
if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) && |
|||
string.Equals(password, "test", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return true; |
|||
} |
|||
if (string.Equals(operatorId, "admin", StringComparison.OrdinalIgnoreCase) && |
|||
string.Equals(password, "admin", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return true; |
|||
} |
|||
if (string.Equals(operatorId, "su", StringComparison.OrdinalIgnoreCase) && |
|||
string.Equals(password, "su", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -1,71 +1,335 @@ |
|||
#nullable enable |
|||
using System; |
|||
using System.IO; |
|||
using System.Windows; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Threading; |
|||
using Housing.Services; |
|||
|
|||
namespace Housing.Login |
|||
{ |
|||
/* |
|||
* Usage in App.xaml.cs or Program.cs: |
|||
* |
|||
* var loginWindow = new Housing.Login.StartupLoginWindow(); |
|||
* if (loginWindow.ShowDialog() != true) |
|||
* { |
|||
* return; |
|||
* } |
|||
* |
|||
* StartupLoginWindowResult loginResult = loginWindow.Result; |
|||
*/ |
|||
|
|||
public sealed class StartupLoginWindowResult |
|||
{ |
|||
public string Maker { get; set; } = string.Empty; |
|||
public string Model { get; set; } = string.Empty; |
|||
public string ColorCode { get; set; } = string.Empty; |
|||
public string Operator { get; set; } = string.Empty; |
|||
public string Password { get; set; } = string.Empty; |
|||
public string LineNo { get; set; } = string.Empty; |
|||
public string LotNo { get; set; } = string.Empty; |
|||
public string JigNo { get; set; } = string.Empty; |
|||
} |
|||
|
|||
public partial class StartupLoginWindow : Window |
|||
{ |
|||
public StartupLoginWindowResult Result { get; private set; } = new StartupLoginWindowResult(); |
|||
|
|||
public StartupLoginWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
PasswordBox.Password = "test"; |
|||
public sealed class StartupLoginWindowResult |
|||
{ |
|||
public string Maker { get; set; } = string.Empty; |
|||
public string Model { get; set; } = string.Empty; |
|||
public string Variant1 { get; set; } = string.Empty; |
|||
public string Variant2 { get; set; } = string.Empty; |
|||
public string Operator { get; set; } = string.Empty; |
|||
public string Password { get; set; } = string.Empty; |
|||
public string LineNo { get; set; } = string.Empty; |
|||
public string LotNo { get; set; } = string.Empty; |
|||
public string JigNo { get; set; } = string.Empty; |
|||
} |
|||
|
|||
public partial class StartupLoginWindow : Window |
|||
{ |
|||
private const string OperatorHistoryFieldName = "Operator"; |
|||
private const string SuppressedOperatorHistoryValue = "su"; |
|||
private readonly LoginHistoryStore _loginHistoryStore; |
|||
private Popup? _openHistoryPopup; |
|||
|
|||
public StartupLoginWindowResult Result { get; private set; } = new(); |
|||
|
|||
public StartupLoginWindow() |
|||
{ |
|||
_loginHistoryStore = LoginHistoryStore.Load(GetLoginHistoryPath()); |
|||
RemoveSuppressedOperatorHistory(); |
|||
InitializeComponent(); |
|||
AttachLoginHistoryMenus(); |
|||
this.PasswordBox.GotKeyboardFocus += (_, _) => CloseHistoryPopup(); |
|||
LoginButton.GotKeyboardFocus += (_, _) => CloseHistoryPopup(); |
|||
OperatorTextBox.Focus(); |
|||
} |
|||
|
|||
private async void LogInButton_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
CloseHistoryPopup(); |
|||
|
|||
if (!TryReadRequiredFields(out var loginResult)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (sender is Button button) |
|||
{ |
|||
button.IsEnabled = false; |
|||
} |
|||
|
|||
private void LogInButton_Click(object sender, RoutedEventArgs e) |
|||
try |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(OperatorTextBox.Text)) |
|||
var loginSettings = LoginSettings.Load(GetDatabaseIniPath()); |
|||
if (loginSettings.OfflinePreview) |
|||
{ |
|||
MessageBox.Show(this, "Please enter Operator.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
OperatorTextBox.Focus(); |
|||
Result = loginResult; |
|||
SaveLoginHistory(loginResult); |
|||
DialogResult = true; |
|||
Close(); |
|||
return; |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(PasswordBox.Password)) |
|||
var repository = new LoginAccountRepository( |
|||
DatabaseSettings.Load(GetDatabaseIniPath()), |
|||
loginSettings); |
|||
|
|||
if (!await repository.ExistsAsync(loginResult.Operator, loginResult.Password)) |
|||
{ |
|||
MessageBox.Show(this, "Please enter Password.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
PasswordBox.Focus(); |
|||
MessageBox.Show(this, "DB에 등록된 ID/PW가 아닙니다.", "Login", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
this.PasswordBox.Clear(); |
|||
this.PasswordBox.Focus(); |
|||
return; |
|||
} |
|||
|
|||
Result = new StartupLoginWindowResult |
|||
Result = loginResult; |
|||
SaveLoginHistory(loginResult); |
|||
DialogResult = true; |
|||
Close(); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
MessageBox.Show(this, $"로그인 DB 확인 실패: {ex.Message}", "Login", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
OperatorTextBox.Focus(); |
|||
OperatorTextBox.SelectAll(); |
|||
} |
|||
finally |
|||
{ |
|||
if (sender is Button loginButton) |
|||
{ |
|||
loginButton.IsEnabled = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private bool TryReadRequiredFields(out StartupLoginWindowResult result) |
|||
{ |
|||
result = new StartupLoginWindowResult(); |
|||
|
|||
if (!TryReadTextBox(MakerTextBox, "Maker", out var maker) || |
|||
!TryReadTextBox(ModelTextBox, "Model", out var model) || |
|||
!TryReadTextBox(Variant1TextBox, "Variant 1", out var variant1) || |
|||
!TryReadTextBox(Variant2TextBox, "Variant 2", out var variant2) || |
|||
!TryReadTextBox(OperatorTextBox, "ID", out var loginId) || |
|||
!TryReadPasswordBox(this.PasswordBox, "PW", out var password) || |
|||
!TryReadTextBox(LineNoTextBox, "Line", out var lineNo) || |
|||
!TryReadTextBox(LotNoTextBox, "Lot No", out var lotNo) || |
|||
!TryReadTextBox(JigNoTextBox, "Jig No", out var jigNo)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
result = new StartupLoginWindowResult |
|||
{ |
|||
Maker = maker, |
|||
Model = model, |
|||
Variant1 = variant1, |
|||
Variant2 = variant2, |
|||
Operator = loginId, |
|||
Password = password, |
|||
LineNo = lineNo, |
|||
LotNo = lotNo, |
|||
JigNo = jigNo |
|||
}; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private void AttachLoginHistoryMenus() |
|||
{ |
|||
AttachHistoryMenu(MakerTextBox, "Maker", ModelTextBox); |
|||
AttachHistoryMenu(ModelTextBox, "Model", Variant1TextBox); |
|||
AttachHistoryMenu(Variant1TextBox, "Variant1", Variant2TextBox); |
|||
AttachHistoryMenu(Variant2TextBox, "Variant2", OperatorTextBox); |
|||
AttachHistoryMenu(OperatorTextBox, OperatorHistoryFieldName, this.PasswordBox); |
|||
AttachHistoryMenu(LineNoTextBox, "LineNo", LotNoTextBox); |
|||
AttachHistoryMenu(LotNoTextBox, "LotNo", JigNoTextBox); |
|||
AttachHistoryMenu(JigNoTextBox, "JigNo", LoginButton); |
|||
} |
|||
|
|||
private void AttachHistoryMenu(TextBox textBox, string fieldName, Control nextFocusControl) |
|||
{ |
|||
textBox.GotKeyboardFocus += (_, _) => ShowHistoryMenu(textBox, fieldName, nextFocusControl); |
|||
textBox.PreviewMouseLeftButtonDown += (_, _) => |
|||
{ |
|||
if (textBox.IsKeyboardFocusWithin) |
|||
{ |
|||
ShowHistoryMenu(textBox, fieldName, nextFocusControl); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
private void ShowHistoryMenu(TextBox textBox, string fieldName, Control nextFocusControl) |
|||
{ |
|||
var values = _loginHistoryStore.GetValues(fieldName); |
|||
if (values.Count == 0) |
|||
{ |
|||
CloseHistoryPopup(); |
|||
return; |
|||
} |
|||
|
|||
CloseHistoryPopup(); |
|||
|
|||
var menuWidth = Math.Max(160, textBox.ActualWidth); |
|||
var stackPanel = new StackPanel(); |
|||
var popup = new Popup |
|||
{ |
|||
PlacementTarget = textBox, |
|||
Placement = PlacementMode.Bottom, |
|||
HorizontalOffset = 0, |
|||
StaysOpen = true, |
|||
AllowsTransparency = true, |
|||
Focusable = false |
|||
}; |
|||
|
|||
foreach (var value in values) |
|||
{ |
|||
var item = new Button |
|||
{ |
|||
Content = value, |
|||
MinWidth = menuWidth, |
|||
Padding = new Thickness(10, 6, 10, 6), |
|||
BorderThickness = new Thickness(0), |
|||
Background = textBox.Background, |
|||
Foreground = textBox.Foreground, |
|||
HorizontalContentAlignment = HorizontalAlignment.Left |
|||
}; |
|||
item.Click += (_, _) => |
|||
{ |
|||
Maker = MakerTextBox.Text.Trim(), |
|||
Model = ModelTextBox.Text.Trim(), |
|||
ColorCode = ColorCodeTextBox.Text.Trim(), |
|||
Operator = OperatorTextBox.Text.Trim(), |
|||
Password = PasswordBox.Password, |
|||
LineNo = LineNoTextBox.Text.Trim(), |
|||
LotNo = LotNoTextBox.Text.Trim(), |
|||
JigNo = JigNoTextBox.Text.Trim() |
|||
textBox.Text = value; |
|||
textBox.CaretIndex = textBox.Text.Length; |
|||
CloseHistoryPopup(); |
|||
FocusControl(nextFocusControl); |
|||
}; |
|||
stackPanel.Children.Add(item); |
|||
} |
|||
|
|||
DialogResult = true; |
|||
Close(); |
|||
popup.Child = new Border |
|||
{ |
|||
Width = menuWidth, |
|||
MaxHeight = 180, |
|||
Background = textBox.Background, |
|||
BorderBrush = textBox.BorderBrush, |
|||
BorderThickness = new Thickness(1), |
|||
Child = new ScrollViewer |
|||
{ |
|||
MaxHeight = 180, |
|||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto, |
|||
Content = stackPanel |
|||
} |
|||
}; |
|||
|
|||
_openHistoryPopup = popup; |
|||
popup.IsOpen = true; |
|||
} |
|||
|
|||
private void CloseHistoryPopup() |
|||
{ |
|||
if (_openHistoryPopup is not null) |
|||
{ |
|||
_openHistoryPopup.IsOpen = false; |
|||
_openHistoryPopup = null; |
|||
} |
|||
} |
|||
|
|||
private void FocusControl(Control control) |
|||
{ |
|||
Dispatcher.BeginInvoke(new Action(() => |
|||
{ |
|||
control.Focus(); |
|||
|
|||
if (control is TextBox textBox) |
|||
{ |
|||
textBox.CaretIndex = textBox.Text.Length; |
|||
} |
|||
else if (control is PasswordBox passwordBox) |
|||
{ |
|||
passwordBox.Focus(); |
|||
} |
|||
}), DispatcherPriority.Input); |
|||
} |
|||
|
|||
private void SaveLoginHistory(StartupLoginWindowResult result) |
|||
{ |
|||
_loginHistoryStore.Remember("Maker", result.Maker); |
|||
_loginHistoryStore.Remember("Model", result.Model); |
|||
_loginHistoryStore.Remember("Variant1", result.Variant1); |
|||
_loginHistoryStore.Remember("Variant2", result.Variant2); |
|||
RememberOperatorHistory(result.Operator); |
|||
_loginHistoryStore.Remember("LineNo", result.LineNo); |
|||
_loginHistoryStore.Remember("LotNo", result.LotNo); |
|||
_loginHistoryStore.Remember("JigNo", result.JigNo); |
|||
_loginHistoryStore.Save(); |
|||
} |
|||
|
|||
private void RememberOperatorHistory(string operatorId) |
|||
{ |
|||
if (IsSuppressedOperatorHistoryValue(operatorId)) |
|||
{ |
|||
_loginHistoryStore.Forget(OperatorHistoryFieldName, operatorId); |
|||
return; |
|||
} |
|||
|
|||
_loginHistoryStore.Remember(OperatorHistoryFieldName, operatorId); |
|||
} |
|||
|
|||
private void RemoveSuppressedOperatorHistory() |
|||
{ |
|||
try |
|||
{ |
|||
if (_loginHistoryStore.Forget(OperatorHistoryFieldName, SuppressedOperatorHistoryValue)) |
|||
{ |
|||
_loginHistoryStore.Save(); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// Login history cleanup is optional and should not block the login window.
|
|||
} |
|||
} |
|||
|
|||
private static bool IsSuppressedOperatorHistoryValue(string value) |
|||
{ |
|||
return string.Equals(value.Trim(), SuppressedOperatorHistoryValue, StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
private bool TryReadTextBox(TextBox textBox, string fieldName, out string value) |
|||
{ |
|||
value = textBox.Text.Trim(); |
|||
if (!string.IsNullOrWhiteSpace(value)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
MessageBox.Show(this, $"{fieldName}을(를) 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
textBox.Focus(); |
|||
textBox.SelectAll(); |
|||
return false; |
|||
} |
|||
|
|||
private bool TryReadPasswordBox(PasswordBox passwordBox, string fieldName, out string value) |
|||
{ |
|||
value = passwordBox.Password; |
|||
if (!string.IsNullOrWhiteSpace(value)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
MessageBox.Show(this, $"{fieldName}을(를) 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
passwordBox.Focus(); |
|||
passwordBox.SelectAll(); |
|||
return false; |
|||
} |
|||
|
|||
private static string GetDatabaseIniPath() |
|||
{ |
|||
return Path.Combine(AppContext.BaseDirectory, "Database.ini"); |
|||
} |
|||
|
|||
private static string GetLoginHistoryPath() |
|||
{ |
|||
return Path.Combine( |
|||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), |
|||
"Housing", |
|||
"LoginHistory.json"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1 +1 @@ |
|||
ec6dd6e1649354a88820f29fa99f449377f51e4d5e8d8707b3c7326a682c80e1 |
|||
786424e28f57daf62f6180d8da5730790c336df832876ad7e4dec61838b897e5 |
|||
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Loading…
Reference in new issue