초기 커밋: SheetMe 서식생성기 (P0~P5 완료 상태)
레거시 서식생성기(VB.NET WinForms) 대체용 C#/.NET 10 WPF 디자이너. 기준선: 실DB 활성 디자인 1,271건 왕복 의미론 diff 0 / 예외 0, 단위 테스트 49/49. 이 커밋에 함께 포함된 자격증명 분리: - appsettings.json 을 __HOST__/__PASSWORD__ 플레이스홀더로 전환 - 실접속 정보는 appsettings.Development.json 으로 분리(.gitignore 제외, csproj Debug 조건부 복사라 Release 산출물에 실리지 않음) - ConfigLoader 를 환경변수 > Development > appsettings 순 레이어링으로 변경, 미치환 플레이스홀더는 '미설정'으로 간주 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,690 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using M.Framework.WPF;
|
||||
using SheetMe.Core.Catalog;
|
||||
using SheetMe.Data.Stores;
|
||||
using SheetMe.Designer.DataBusiness;
|
||||
using SheetMe.Designer.Services;
|
||||
|
||||
namespace SheetMe.Designer.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// 메인 셸 ViewModel — 멀티 문서 탭(여러 기록지 동시 편집), 상시 서식 목록 패널,
|
||||
/// 파일/DB 열기·저장, 도구(상용구/폰트). 컨트롤 클립보드는 문서 간 공유(서식 간 복사/붙여넣기).
|
||||
/// </summary>
|
||||
internal sealed class MainViewModel : ViewModelBase
|
||||
{
|
||||
#region Member Fields
|
||||
private readonly FormDesignDataBusiness dataBusiness = new();
|
||||
private readonly DialogService dialogService = new();
|
||||
private DesignerViewModel? currentDesigner;
|
||||
private string title = "SheetMe 서식생성기";
|
||||
private string statusText = "준비";
|
||||
private string sheetSearchKeyword = string.Empty;
|
||||
private bool isSheetListLoading;
|
||||
private int selectedLeftTabIndex;
|
||||
private bool isHandTool;
|
||||
private const string XmlFilter = "서식 XML (*.xml)|*.xml|모든 파일 (*.*)|*.*";
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>창 제목</summary>
|
||||
public string Title
|
||||
{
|
||||
get => title;
|
||||
set => SetProperty(ref title, value);
|
||||
}
|
||||
|
||||
/// <summary>상태바 텍스트</summary>
|
||||
public string StatusText
|
||||
{
|
||||
get => statusText;
|
||||
set => SetProperty(ref statusText, value);
|
||||
}
|
||||
|
||||
/// <summary>열린 문서(탭) 목록</summary>
|
||||
public ObservableCollection<DesignerViewModel> OpenDesigners { get; } = new();
|
||||
|
||||
/// <summary>활성 문서(선택 탭)</summary>
|
||||
public DesignerViewModel? CurrentDesigner
|
||||
{
|
||||
get => currentDesigner;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref currentDesigner, value))
|
||||
{
|
||||
Title = value is null
|
||||
? "SheetMe 서식생성기"
|
||||
: $"SheetMe 서식생성기 — {value.DisplayName}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>팔레트 컨트롤 목록(레지스트리)</summary>
|
||||
public IReadOnlyList<ControlDescriptor> PaletteItems => ControlRegistry.All;
|
||||
|
||||
/// <summary>서식 목록(상시 패널) — DB E_ShtMst</summary>
|
||||
public ObservableCollection<SheetSummary> SheetList { get; } = new();
|
||||
|
||||
/// <summary>서식 목록 검색어</summary>
|
||||
public string SheetSearchKeyword
|
||||
{
|
||||
get => sheetSearchKeyword;
|
||||
set => SetProperty(ref sheetSearchKeyword, value);
|
||||
}
|
||||
|
||||
/// <summary>서식 목록 로딩 중</summary>
|
||||
public bool IsSheetListLoading
|
||||
{
|
||||
get => isSheetListLoading;
|
||||
set => SetProperty(ref isSheetListLoading, value);
|
||||
}
|
||||
|
||||
/// <summary>DB 사용 가능 여부(서식 목록 패널 안내용)</summary>
|
||||
public bool CanUseDb => dataBusiness.CanUseDb;
|
||||
|
||||
/// <summary>좌측 패널 탭(0=서식 목록, 1=레이어, 2=도구 상자) — 서식 열면 레이어로 자동 전환([200] 관행)</summary>
|
||||
public int SelectedLeftTabIndex
|
||||
{
|
||||
get => selectedLeftTabIndex;
|
||||
set => SetProperty(ref selectedLeftTabIndex, value);
|
||||
}
|
||||
|
||||
/// <summary>손(팬) 도구 활성 — 캔버스 드래그로 화면 이동(false=선택 도구, [200] 플로팅 바 관행)</summary>
|
||||
public bool IsHandTool
|
||||
{
|
||||
get => isHandTool;
|
||||
set => SetProperty(ref isHandTool, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public MainViewModel()
|
||||
{
|
||||
LoadedCommand = new Command(async (sender, e) => await OnLoadedAsync());
|
||||
NewFileCommand = new Command((sender, e) => OnNewFile());
|
||||
OpenFileCommand = new Command((sender, e) => OnOpenFile());
|
||||
SaveFileCommand = new Command((sender, e) => OnSaveFile(saveAs: false));
|
||||
SaveAsFileCommand = new Command((sender, e) => OnSaveFile(saveAs: true));
|
||||
OpenFromDbCommand = new Command((sender, e) => OnOpenFromDb());
|
||||
SaveToDbCommand = new Command((sender, e) => OnSaveToDb());
|
||||
ExportJsonCommand = new Command((sender, e) => OnExportJson());
|
||||
ImportJsonCommand = new Command((sender, e) => OnImportJson());
|
||||
PreviewCommand = new Command((sender, e) => OnPreview());
|
||||
PrintCommand = new Command((sender, e) => OnPrint());
|
||||
RecordWordCommand = new Command((sender, e) => OnRecordWords());
|
||||
FontManagerCommand = new Command((sender, e) => OnFontManager());
|
||||
SheetHistoryCommand = new Command((sender, e) => OnSheetHistory());
|
||||
ZoomInCommand = new Command((sender, e) => CurrentDesigner?.ZoomIn());
|
||||
ZoomOutCommand = new Command((sender, e) => CurrentDesigner?.ZoomOut());
|
||||
ZoomResetCommand = new Command((sender, e) => CurrentDesigner?.ZoomReset());
|
||||
ExitCommand = new Command((sender, e) => Application.Current.Shutdown());
|
||||
SearchSheetsCommand = new Command(async (sender, e) => await LoadSheetListAsync());
|
||||
OpenSheetCommand = new Command((object param) => OnOpenSheetFromList(param as SheetSummary));
|
||||
CloseDocumentCommand = new Command((object param) => OnCloseDocument(param as DesignerViewModel));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
/// <summary>창 로드</summary>
|
||||
public ICustomCommand? LoadedCommand { get; set; }
|
||||
|
||||
/// <summary>새 서식</summary>
|
||||
public ICustomCommand? NewFileCommand { get; set; }
|
||||
|
||||
/// <summary>서식 XML 열기</summary>
|
||||
public ICustomCommand? OpenFileCommand { get; set; }
|
||||
|
||||
/// <summary>저장</summary>
|
||||
public ICustomCommand? SaveFileCommand { get; set; }
|
||||
|
||||
/// <summary>다른 이름으로 저장</summary>
|
||||
public ICustomCommand? SaveAsFileCommand { get; set; }
|
||||
|
||||
/// <summary>DB에서 서식 열기(검색 대화상자)</summary>
|
||||
public ICustomCommand? OpenFromDbCommand { get; set; }
|
||||
|
||||
/// <summary>DB에 저장(E_SdgMst 버저닝 + E_SctMst 재생성) — SaveMode 게이트</summary>
|
||||
public ICustomCommand? SaveToDbCommand { get; set; }
|
||||
|
||||
/// <summary>JSON 내보내기</summary>
|
||||
public ICustomCommand? ExportJsonCommand { get; set; }
|
||||
|
||||
/// <summary>JSON 가져오기</summary>
|
||||
public ICustomCommand? ImportJsonCommand { get; set; }
|
||||
|
||||
/// <summary>미리보기</summary>
|
||||
public ICustomCommand? PreviewCommand { get; set; }
|
||||
|
||||
/// <summary>인쇄</summary>
|
||||
public ICustomCommand? PrintCommand { get; set; }
|
||||
|
||||
/// <summary>상용구 관리</summary>
|
||||
public ICustomCommand? RecordWordCommand { get; set; }
|
||||
|
||||
/// <summary>폰트 일괄 변경</summary>
|
||||
public ICustomCommand? FontManagerCommand { get; set; }
|
||||
|
||||
/// <summary>서식 수정이력(버전 열람/복원)</summary>
|
||||
public ICustomCommand? SheetHistoryCommand { get; set; }
|
||||
|
||||
/// <summary>줌 확대</summary>
|
||||
public ICustomCommand? ZoomInCommand { get; set; }
|
||||
|
||||
/// <summary>줌 축소</summary>
|
||||
public ICustomCommand? ZoomOutCommand { get; set; }
|
||||
|
||||
/// <summary>줌 100%</summary>
|
||||
public ICustomCommand? ZoomResetCommand { get; set; }
|
||||
|
||||
/// <summary>종료</summary>
|
||||
public ICustomCommand? ExitCommand { get; set; }
|
||||
|
||||
/// <summary>서식 목록 검색</summary>
|
||||
public ICustomCommand? SearchSheetsCommand { get; set; }
|
||||
|
||||
/// <summary>서식 목록에서 열기(더블클릭)</summary>
|
||||
public ICustomCommand? OpenSheetCommand { get; set; }
|
||||
|
||||
/// <summary>문서 탭 닫기</summary>
|
||||
public ICustomCommand? CloseDocumentCommand { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Methods - 문서 탭
|
||||
/// <summary>문서를 탭으로 추가하고 활성화 — 좌측 패널은 레이어 탭으로 전환</summary>
|
||||
private void AttachDocument(DesignerViewModel designer)
|
||||
{
|
||||
OpenDesigners.Add(designer);
|
||||
CurrentDesigner = designer;
|
||||
SelectedLeftTabIndex = 1;
|
||||
}
|
||||
|
||||
private void OnCloseDocument(DesignerViewModel? designer)
|
||||
{
|
||||
designer ??= CurrentDesigner;
|
||||
if (designer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (designer.Undo.CanUndo
|
||||
&& MessageBox.Show($"'{designer.DisplayName}' 문서에 저장하지 않은 변경이 있을 수 있습니다.\n닫을까요?",
|
||||
"문서 닫기", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var index = OpenDesigners.IndexOf(designer);
|
||||
OpenDesigners.Remove(designer);
|
||||
if (CurrentDesigner == designer)
|
||||
{
|
||||
CurrentDesigner = OpenDesigners.Count > 0
|
||||
? OpenDesigners[Math.Clamp(index, 0, OpenDesigners.Count - 1)]
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>이미 열린 DB 서식이면 해당 탭 활성화 — 없으면 null</summary>
|
||||
private DesignerViewModel? FindOpenDbDocument(string shtCod)
|
||||
=> OpenDesigners.FirstOrDefault(d => d.IsFromDb && d.Document.FormId == shtCod);
|
||||
#endregion
|
||||
|
||||
#region Methods - 열기/저장
|
||||
private async Task OnLoadedAsync()
|
||||
{
|
||||
OnNewFile();
|
||||
SelectedLeftTabIndex = 0; // 시작 화면은 서식 목록 탭(초기 빈 문서로 레이어 탭 전환되는 것 되돌림)
|
||||
await LoadSheetListAsync();
|
||||
}
|
||||
|
||||
private void OnNewFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
AttachDocument(new DesignerViewModel(dataBusiness.CreateNew()));
|
||||
StatusText = "새 서식 (720×856)";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOpenFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = dialogService.ShowOpenFile(XmlFilter, "서식 XML 열기");
|
||||
if (path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var document = dataBusiness.OpenXmlFile(path);
|
||||
AttachDocument(new DesignerViewModel(document) { FilePath = path });
|
||||
StatusText = $"파일 로드: 페이지 {document.Pages.Count} · 컨트롤 {document.Pages.Sum(p => CountControls(p.Controls))}";
|
||||
ShowReadWarnings(document.Meta.ReadWarnings, "서식 열기");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"서식을 여는 중 오류가 발생했습니다.\n\n{ex}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSaveFile(bool saveAs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var path = CurrentDesigner.FilePath;
|
||||
if (saveAs || path is null)
|
||||
{
|
||||
path = dialogService.ShowSaveFile(XmlFilter, "서식 XML 저장",
|
||||
CurrentDesigner.Document.FormId + ".xml");
|
||||
if (path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dataBusiness.SaveXmlFile(CurrentDesigner.Document, path);
|
||||
CurrentDesigner.FilePath = path;
|
||||
CurrentDesigner.NotifyDisplayNameChanged();
|
||||
Title = $"SheetMe 서식생성기 — {CurrentDesigner.DisplayName}";
|
||||
StatusText = $"저장됨: {path}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"저장 중 오류가 발생했습니다.\n\n{ex}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOpenFromDb()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!EnsureDb())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var dialog = new Views.SheetOpenDialogView(keyword => dataBusiness.ListSheets(keyword))
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
};
|
||||
if (dialog.ShowDialog() != true || dialog.SelectedSheet is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
OpenDbSheet(dialog.SelectedSheet.ShtCod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"DB에서 서식을 여는 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>DB 서식 열기 공통 — 이미 열려 있으면 탭 활성화</summary>
|
||||
private void OpenDbSheet(string shtCod)
|
||||
{
|
||||
var existing = FindOpenDbDocument(shtCod);
|
||||
if (existing is not null)
|
||||
{
|
||||
CurrentDesigner = existing;
|
||||
StatusText = $"이미 열린 서식: {shtCod}";
|
||||
return;
|
||||
}
|
||||
|
||||
var document = dataBusiness.OpenFromDb(shtCod);
|
||||
if (document is null)
|
||||
{
|
||||
MessageBox.Show("활성 디자인을 찾지 못했습니다.", "DB 열기",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
AttachDocument(new DesignerViewModel(document, dataBusiness.LoadSpreadGrids(shtCod)) { IsFromDb = true });
|
||||
StatusText = $"DB 로드: {document.FormId} (SdgKey {document.Meta.SourceSdgKey}) · " +
|
||||
$"페이지 {document.Pages.Count} · 컨트롤 {document.Pages.Sum(p => CountControls(p.Controls))}";
|
||||
ShowReadWarnings(document.Meta.ReadWarnings, "DB 열기");
|
||||
}
|
||||
|
||||
private void OnSaveToDb()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!dataBusiness.CanSaveToDb)
|
||||
{
|
||||
MessageBox.Show("DB 저장이 비활성화되어 있습니다.\nappsettings.json 의 FormStore:SaveMode 를 'Db' 로 설정한 뒤 사용하세요.\n(운영 레거시 테이블에 기록되므로 명시적 활성화가 필요합니다)",
|
||||
"DB 저장", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var document = CurrentDesigner.Document;
|
||||
|
||||
// 미등록 서식이면 신규 등록 다이얼로그
|
||||
if (!dataBusiness.SheetExists(document.FormId))
|
||||
{
|
||||
var register = new Views.RegisterSheetDialogView(
|
||||
document.FormId == "NewSheet" ? string.Empty : document.FormId,
|
||||
document.Title == "새 서식" ? string.Empty : document.Title)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
};
|
||||
if (register.ShowDialog() != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
dataBusiness.RegisterSheet(register.SheetCode, register.SheetName, register.ClassCode);
|
||||
document.FormId = register.SheetCode;
|
||||
document.Title = register.SheetName;
|
||||
}
|
||||
|
||||
var confirm = MessageBox.Show(
|
||||
$"서식 [{document.FormId}] {document.Title} 을(를) DB(E_SdgMst/E_SctMst)에 저장할까요?\n\n" +
|
||||
"기존 활성 디자인은 이력(SdgDelYon='Y')으로 보존되고 새 버전이 생성됩니다.\n" +
|
||||
"(제자리 갱신 서식(ShtCneYon='Y')은 기존 버전이 갱신됩니다)",
|
||||
"DB 저장 확인", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (confirm != MessageBoxResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sdgKey = dataBusiness.SaveToDb(document);
|
||||
CurrentDesigner.IsFromDb = true;
|
||||
CurrentDesigner.NotifyDisplayNameChanged();
|
||||
StatusText = $"DB 저장 완료: {document.FormId} → SdgKey {sdgKey}";
|
||||
MessageBox.Show($"저장되었습니다. (SdgKey {sdgKey})\n레거시 뷰어/디자이너에서 열어 확인하세요.", "DB 저장",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"DB 저장 중 오류가 발생했습니다. (트랜잭션 롤백됨)\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExportJson()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var path = dialogService.ShowSaveFile("서식 JSON (*.json)|*.json", "JSON 내보내기",
|
||||
CurrentDesigner.Document.FormId + ".json");
|
||||
if (path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var json = new Core.Serialization.FormJsonSerializer().Write(CurrentDesigner.Document);
|
||||
File.WriteAllText(path, json, System.Text.Encoding.UTF8);
|
||||
StatusText = $"JSON 내보내기 완료: {path}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"JSON 내보내기 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnImportJson()
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = dialogService.ShowOpenFile("서식 JSON (*.json)|*.json", "JSON 가져오기");
|
||||
if (path is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var document = new Core.Serialization.FormJsonSerializer().Read(File.ReadAllText(path));
|
||||
if (document.FormId.Length == 0)
|
||||
{
|
||||
document.FormId = Path.GetFileNameWithoutExtension(path);
|
||||
}
|
||||
AttachDocument(new DesignerViewModel(document));
|
||||
StatusText = $"JSON 로드: 페이지 {document.Pages.Count} · 컨트롤 {document.Pages.Sum(p => CountControls(p.Controls))}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"JSON 가져오기 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods - 서식 목록 패널
|
||||
/// <summary>서식 목록 로드(비동기) — DB 미접속이면 건너뜀</summary>
|
||||
private async Task LoadSheetListAsync()
|
||||
{
|
||||
if (!dataBusiness.CanUseDb || IsSheetListLoading)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
IsSheetListLoading = true;
|
||||
var keyword = SheetSearchKeyword;
|
||||
var sheets = await Task.Run(() => dataBusiness.ListSheets(keyword));
|
||||
SheetList.Clear();
|
||||
foreach (var sheet in sheets)
|
||||
{
|
||||
SheetList.Add(sheet);
|
||||
}
|
||||
StatusText = $"서식 목록 {sheets.Count}건";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"서식 목록 조회 중 오류가 발생했습니다.\n\n{ex.Message}", "서식 목록",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsSheetListLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>서식 목록에서 열기 — 뷰 더블클릭 핸들러가 위임 호출</summary>
|
||||
public void OpenSheetFromList(SheetSummary? sheet) => OnOpenSheetFromList(sheet);
|
||||
|
||||
private void OnOpenSheetFromList(SheetSummary? sheet)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (sheet is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!sheet.HasDesign)
|
||||
{
|
||||
MessageBox.Show("선택한 서식에는 저장된 디자인이 없습니다.", "서식 열기",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
OpenDbSheet(sheet.ShtCod);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"서식을 여는 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods - 도구
|
||||
private void OnPreview()
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
new Views.PreviewWindow(CurrentDesigner)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
}.Show();
|
||||
}
|
||||
|
||||
private void OnPrint()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Services.PrintService.Print(CurrentDesigner, CurrentDesigner.Document.Title.Length > 0
|
||||
? CurrentDesigner.Document.Title
|
||||
: CurrentDesigner.Document.FormId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"인쇄 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFontManager()
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
new Views.FontManagerDialogView(CurrentDesigner)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
}.ShowDialog();
|
||||
}
|
||||
|
||||
private void OnSheetHistory()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null || !EnsureDb())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var document = CurrentDesigner.Document;
|
||||
if (document.FormId.Length == 0 || document.FormId == "NewSheet")
|
||||
{
|
||||
MessageBox.Show("수정이력은 DB에 저장된 서식에서 사용할 수 있습니다.", "서식 수정이력",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var versions = dataBusiness.ListVersions(document.FormId);
|
||||
if (versions.Count == 0)
|
||||
{
|
||||
MessageBox.Show("저장된 버전이 없습니다.", "서식 수정이력",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var dialog = new Views.SheetHistoryDialogView(document.FormId, document.Title, versions)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
};
|
||||
if (dialog.ShowDialog() != true || dialog.SelectedSdgKey is not { } sdgKey)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 열람 중인 동일 버전 탭이면 활성화
|
||||
var existing = OpenDesigners.FirstOrDefault(d => d.HistorySdgKey == sdgKey
|
||||
&& d.Document.FormId == document.FormId);
|
||||
if (existing is not null)
|
||||
{
|
||||
CurrentDesigner = existing;
|
||||
return;
|
||||
}
|
||||
|
||||
var versionDocument = dataBusiness.OpenFromDbVersion(document.FormId, sdgKey);
|
||||
if (versionDocument is null)
|
||||
{
|
||||
MessageBox.Show("해당 버전을 불러오지 못했습니다.", "서식 수정이력",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
AttachDocument(new DesignerViewModel(versionDocument, dataBusiness.LoadSpreadGrids(document.FormId))
|
||||
{
|
||||
IsFromDb = true,
|
||||
HistorySdgKey = sdgKey,
|
||||
});
|
||||
StatusText = $"이력 열람: {document.FormId} SdgKey {sdgKey} — 'DB에 저장' 시 이 내용이 새 활성 버전이 됩니다";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"수정이력 조회 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRecordWords()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CurrentDesigner is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!EnsureDb())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var document = CurrentDesigner.Document;
|
||||
if (document.FormId.Length == 0 || document.FormId == "NewSheet")
|
||||
{
|
||||
MessageBox.Show("상용구는 서식 코드 단위로 저장됩니다.\n먼저 DB에 저장(서식 등록)한 뒤 사용하세요.",
|
||||
"상용구 관리", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
new Views.RecordWordDialogView(dataBusiness.RecordWords(), document.FormId, document.Title)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
}.ShowDialog();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"상용구 관리 중 오류가 발생했습니다.\n\n{ex.Message}", "오류",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods - Private
|
||||
private bool EnsureDb()
|
||||
{
|
||||
if (dataBusiness.CanUseDb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
MessageBox.Show("DB 접속 문자열(ConnectionStrings:His)이 설정되지 않았습니다.\nappsettings.json 을 확인하세요.",
|
||||
"DB 연결", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ShowReadWarnings(List<string> warnings, string caption)
|
||||
{
|
||||
if (warnings.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var summary = string.Join("\n", warnings.Take(20));
|
||||
var more = warnings.Count > 20 ? $"\n... 외 {warnings.Count - 20}건" : string.Empty;
|
||||
MessageBox.Show($"읽기 경고 {warnings.Count}건 (미지원 컨트롤은 자리표시로 보존됩니다):\n\n{summary}{more}",
|
||||
caption, MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private static int CountControls(List<Core.Models.ControlElement> controls)
|
||||
=> controls.Sum(c => 1 + CountControls(c.Children));
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user