diff --git a/src/SheetMe.Core/Catalog/ControlRegistry.cs b/src/SheetMe.Core/Catalog/ControlRegistry.cs index 670bb9c..31de4e5 100644 --- a/src/SheetMe.Core/Catalog/ControlRegistry.cs +++ b/src/SheetMe.Core/Catalog/ControlRegistry.cs @@ -66,7 +66,9 @@ public static class ControlRegistry /// public static PropertyDef DataTableField => new() { - Key = "DataTableField", Label = "데이터 필드", Editor = PropEditorKind.DataTableField, + // 라벨에 '배선' 을 쓰지 않는다 — 전산실에서 배선은 랜선·전원이다. + // '값 가져오기' 는 방향이 이름에 들어 있어 형제 속성(데이터 태그)과도 구별된다. + Key = "DataTableField", Label = "값 가져오기", Editor = PropEditorKind.DataTableField, }; } diff --git a/src/SheetMe.Designer/Diagnostics/EditSmoke.cs b/src/SheetMe.Designer/Diagnostics/EditSmoke.cs index e952da2..2f77a02 100644 --- a/src/SheetMe.Designer/Diagnostics/EditSmoke.cs +++ b/src/SheetMe.Designer/Diagnostics/EditSmoke.cs @@ -2515,22 +2515,25 @@ public static class EditSmoke designer.Inspector.SelectedTab = ViewModels.Inspector.InspectorTab.Data; var dtfRow = designer.Inspector.Rows .OfType().FirstOrDefault(); - Check("배선: 전용 편집 행이 붙는다", dtfRow is not null); + Check("값 가져오기: 전용 편집 행이 붙는다", dtfRow is not null); if (dtfRow is null) { designer.Inspector.SelectedTab = previousTab; goto skipWiring; } - Check("배선: 값이 없으면 안내 문구", dtfRow.Summary == "(배선 없음)", dtfRow.Summary); + // 빈 상태 문구는 대화상자의 지우기 버튼과 글자까지 같다 — + // 그 버튼을 누르면 무엇이 되는지 미리 읽히게 하려는 것이다 + Check("값 가져오기: 값이 없으면 지우기 버튼과 같은 말을 쓴다", + dtfRow.Summary == "(가져오지 않음)", dtfRow.Summary); // 운영 다수파인 Rows 형을 읽어야 한다(1,624건, 76%) dtfRow.ValueText = $"{source.Id}.Rows(0).Item(\"ALGYON\")"; - Check("배선: Rows 형을 읽는다", dtfRow.Summary.Contains(source.Id) && dtfRow.Summary.Contains("ALGYON"), + Check("값 가져오기: 저장된 값을 사람 말로 요약한다", dtfRow.Summary.Contains(source.Id) && dtfRow.Summary.Contains("ALGYON"), dtfRow.Summary); // 해석 못 하는 값은 조용히 고치지 않고 경고로 드러낸다 dtfRow.ValueText = "MDataTable1.Rows"; - Check("배선: 미완성 값은 경고로 드러낸다", dtfRow.Summary.StartsWith("⚠"), dtfRow.Summary); + Check("값 가져오기: 미완성 값은 경고로 드러낸다", dtfRow.Summary.StartsWith("⚠"), dtfRow.Summary); dtfRow.ValueText = string.Empty; designer.Inspector.SelectedTab = previousTab; diff --git a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs index 6c20d59..2784ed2 100644 --- a/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs +++ b/src/SheetMe.Designer/ViewModels/Inspector/InspectorViewModel.cs @@ -927,7 +927,12 @@ public sealed class InspectorViewModel : ViewModelBase PropEditorKind.SqlQuery => new QueryRowViewModel(def.Label) { ControlNamesOf = ControlNames }, PropEditorKind.Mask => new MaskRowViewModel(def.Label), // 배선 대상 목록은 '지금 이 서식'의 데이터소스다 — 런타임도 같은 서식 안에서만 이름을 찾는다 - PropEditorKind.DataTableField => new DataTableFieldRowViewModel(def.Label, DataTableNames), + PropEditorKind.DataTableField => new DataTableFieldRowViewModel(def.Label, DataTableNames) + { + // 컬럼 목록과 대상 컨트롤 이름도 함께 준다 — 창이 사용자를 밖으로 내보내지 않게 한다 + OwnerLabelOf = () => designer.Selection.Primary?.Id ?? string.Empty, + ColumnsOf = DataSourceColumns, + }, _ => new TextRowViewModel(def.Label), }; // 선의 방향·굵기는 값만 바꾸는 것이 아니라 크기까지 바꾼다(ApplyLineChange). @@ -986,6 +991,36 @@ public sealed class InspectorViewModel : ViewModelBase return names; } + /// + /// 데이터소스의 조회 결과 컬럼 이름 — 못 읽으면 null(미접속·쿼리 실패). + /// + /// 값 가져오기 창이 컬럼을 목록으로 보여 줄 수 있게 하는 재료다. 전에는 그 창이 + /// "컬럼 후보는 목록으로 제시하지 않습니다" 라고 적어 두고 사용자를 밖으로 보냈는데, + /// 그 창은 모달이라 밖이 잠겨 있다 — 취소하고 쿼리를 열어 컬럼명을 외워 다시 와야 했다. + /// + /// 실행은 가 한다 — 접속·치환·캐시·실패 처리가 + /// 이미 그 한 군데에 있고, 여기서 다시 만들면 두 벌이 된다. + /// 러너가 데이터소스별로 결과를 캐시하므로 창이 여러 번 물어도 조회는 한 번이다. + /// + private IReadOnlyList? DataSourceColumns(string name) + { + try + { + var patient = Services.PatientSession.Current; + var runner = new Services.MDataTableRunner(designer.Document, + patient is null ? null + : new Services.PatientQueryVariableSource(patient, Services.UserSession.Current.UidCod)); + var result = runner.Run(name); + return result.Ok && result.Columns.Count > 0 ? result.Columns : null; + } + catch + { + // 컬럼 목록은 도움이다 — 못 읽는다고 창이 안 열려서는 안 된다. + // 창은 null 을 받으면 이유를 적고 직접 입력 칸을 남긴다. + return null; + } + } + private IReadOnlyList DataTableNames() { var names = new List(); diff --git a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs index f8df160..d6c0efa 100644 --- a/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs +++ b/src/SheetMe.Designer/ViewModels/Inspector/PropertyRows.cs @@ -968,7 +968,12 @@ public sealed class DataTableFieldRowViewModel : PropertyRowViewModel { private readonly Func> tablesOf; - /// 사람이 읽는 요약 — 배선이 없으면 안내 문구 + /// + /// 사람이 읽는 요약. '배선' 이라는 낱말을 쓰지 않는다 — + /// 전산실에서 배선은 랜선·전원이고, '배선 없음' 은 데이터소스가 없는 것처럼 읽힌다. + /// 빈 상태 문구는 대화상자의 지우기 버튼과 글자까지 같게 둔다 — + /// 그 버튼을 누르면 무엇이 되는지 미리 보이게 하려는 것이다. + /// public string Summary { get @@ -979,19 +984,40 @@ public sealed class DataTableFieldRowViewModel : PropertyRowViewModel } if (ValueText.Length == 0) { - return "(배선 없음)"; + return "(가져오지 않음)"; } var spec = SheetMe.Core.Catalog.DataTableFieldSpec.Parse(ValueText); return spec is null // 해석 못 하는 값은 원문을 그대로 보여 준다 — 조용히 고쳐 쓰지 않는다 ? $"⚠ 형식을 알 수 없음: {ValueText}" - : $"{spec.TableName} · {spec.Field}" + (spec.RowIndex == 0 ? string.Empty : $" · {spec.RowIndex}행"); + // 행 0 은 '첫 행' 이다. 그 밖은 사람이 세는 번호(0 → 1번째)로 적는다. + // 조건이 있으면 그 사실을 덧붙인다 — 조건이 화면 어디에도 안 보인 채 + // 결과를 바꾸는 상태를 남기지 않는다. + : $"{spec.TableName} · {spec.Field}" + + (spec.RowIndex == 0 ? string.Empty : $" · {spec.RowIndex + 1}번째 행") + + (spec.Form == SheetMe.Core.Catalog.DataTableFieldForm.Select && spec.Filter.Length > 0 + ? " · 조건 있음" : string.Empty); } } /// 편집기 열기 public M.Framework.WPF.ICustomCommand? EditCommand { get; set; } + /// + /// 대상 컨트롤 이름 — 창 제목에 넣는다. 모달이라 인스펙터가 가려져서, + /// 여러 칸을 연달아 채울 때 지금 어느 칸 차례인지 알 방법이 없었다. + /// + public Func? OwnerLabelOf { get; init; } + + /// + /// 데이터소스 이름 → 그 조회 결과의 컬럼 이름(없으면 null). + /// + /// 전에는 창이 '컬럼 후보는 목록으로 제시하지 않습니다' 라고 적어 두고 사용자를 밖으로 보냈는데, + /// 그 창은 모달이라 밖이 잠겨 있다 — 취소하고 쿼리를 열어 컬럼명을 외워 다시 와야 했다. + /// 실행은 이 행이 하지 않는다(호출부가 이미 만들어 둔 러너를 함수로 받는다). + /// + public Func?>? ColumnsOf { get; init; } + public DataTableFieldRowViewModel(string label, Func> tablesOf) : base(label) { this.tablesOf = tablesOf; @@ -1007,7 +1033,8 @@ public sealed class DataTableFieldRowViewModel : PropertyRowViewModel private void OnEdit() { - var dialog = new Views.DataTableFieldDialogView(ValueText, tablesOf()) + var dialog = new Views.DataTableFieldDialogView(ValueText, tablesOf(), + OwnerLabelOf?.Invoke(), ColumnsOf) { Owner = System.Windows.Application.Current.MainWindow, }; diff --git a/src/SheetMe.Designer/Views/DataTableFieldDialogView.xaml b/src/SheetMe.Designer/Views/DataTableFieldDialogView.xaml index d9482f6..faf33f7 100644 --- a/src/SheetMe.Designer/Views/DataTableFieldDialogView.xaml +++ b/src/SheetMe.Designer/Views/DataTableFieldDialogView.xaml @@ -1,29 +1,58 @@ + - + - - -