이 시스템의 핵심 자동 채움 경로다. MDataTable 조회 결과의 한 칸을 입력 컨트롤에 꽂는 배선인데,
지금까지 SheetMe 는 ComboBox 에만 안내 없는 자유 텍스트 한 줄이 있었고 나머지 7종에는 항목조차 없었다.
문제는 형식을 어겼을 때의 증상이다. 런타임 파서(ucLoadSheetBase.DataTableBinding_)는 문자열을
쪼개서 읽고, 실패하면 **빈 catch 가 예외를 삼킨다**. 로그도 없다. 디자이너에서는 정상으로 보이고
임상 화면에서만 값이 안 채워지므로 원인 추적이 어렵다. 그래서 문법 골격은 코드가 조립하고
저장 전에 검사한다.
■ 실측이 통설을 뒤집었다
레거시 전용 편집기(fmMDataTableField)는 Select 형 한 가지만 만든다:
{이름}.Select("{필터}")({행}).Item("{필드}") ← 공백 0개
그래서 이것이 '정본'이라고 알려져 있었다.
--db-dtf 진단을 새로 만들어 운영 디자인 1,271건을 센 결과는 반대였다:
Rows 형 1,624건(76%) · Select 형 268건(12%) · 미완성 "MDataTable1.Rows" 234건(11%)
Select 형만 읽었다면 기존 배선의 4분의 3을 편집기가 못 여는 상태로 출시할 뻔했다.
게다가 형태를 함부로 바꾸면 안 된다 — 보조 로더(bzDesignSheetLoader)는 .Select( 분기가 아예 없어
Rows 형만 이해한다. 그래서 **연 값의 형태를 그대로 유지**하고, 새로 만들 때만 Select 형으로 시작한다.
같은 집계에서 운영 결함도 드러났다: 같은 서식에 없는 데이터소스를 가리키는 값 20건
(S0003 은 MDataTable2 를 가리키는데 그 서식에는 MDataTable3 만 있다). 전부 조용히 실패한다.
런타임 조회가 대소문자를 구분하므로 검증에 이름 일치 검사를 넣었다.
■ 레거시보다 나은 점 3가지 (파리티 초과지만 위험 없음)
1. 기존 값을 되읽어 채운다. 레거시는 열 때마다 백지에서 시작해(InitializeSelectedData)
필드 하나만 고치려 해도 전부 다시 골라야 했다.
2. 아무것도 안 고르고 확인하는 경로를 막는다. 레거시는 5조각을 이어붙이므로 리터럴 ")(" 가 저장된다.
3. 저장 전 검사 — 필터·필드의 큰따옴표(파서가 따옴표로 쪼갠다), 이름의 마침표(첫 조각만 떼어 간다),
음수 행. 레거시는 검증이 0이다.
필드 후보를 목록으로 주지는 않았다. 레거시는 쿼리를 실제로 실행해 결과 컬럼을 쓰는데,
그 쿼리에는 테스트 환자 설정이 필요한 치환 토큰이 들어 있어 재현이 별개 작업이다.
대신 필드는 자유 입력으로 두고 그 사정을 화면에 적었다. 실제로 필드에는 SQL 별칭 표현식이
들어오기도 한다(운영 값 EMDOPRDTE||'-'||EMDOPNAME) — 식별자 화이트리스트로 막으면 안 된다.
배선을 붙인 타입은 레거시가 bzMDataTableField 편집기를 다는 8종 그대로다:
TextBox · ComboBox · CheckBox · RadioButton · DateTimePicker · MaskedTextBox · CalcBox · PictureBox.
해석 못 하는 값은 조용히 고쳐 쓰지 않는다 — 인스펙터에 "⚠ 형식을 알 수 없음: 원문" 으로 드러낸다
(운영의 미완성 값 234건이 여기 해당한다).
단위 테스트 18건 추가(표본은 전부 실측·레거시 소스 인용). 편집 스모크 4건 추가.
회귀: 테스트 142/142, 편집 스모크 187건 실패 0,
DB 왕복 1,271건 diff 0/예외 0, 종이 렌더 P062 바이트 동일.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
4.6 KiB
XML
73 lines
4.6 KiB
XML
<Window x:Class="SheetMe.Designer.Views.DataTableFieldDialogView"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
Title="데이터 필드 배선" Width="560" Height="520"
|
|
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
|
|
Style="{StaticResource ThemedWindow}">
|
|
<DockPanel Margin="14">
|
|
<TextBlock DockPanel.Dock="Top" Foreground="{DynamicResource B.Muted}" TextWrapping="Wrap" Margin="0,0,0,10"
|
|
Text="데이터소스(조회 결과)의 한 칸을 이 컨트롤에 꽂습니다. 형식이 어긋나면 런타임이 아무 경고 없이 값을 비워 두므로, 문법은 여기서 조립합니다."/>
|
|
|
|
<!-- 아래쪽 버튼 -->
|
|
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
|
|
<Button Content="배선 지우기" Padding="12,4" Click="OnClear"/>
|
|
<Button x:Name="OkButton" Content="적용" Padding="18,4" Margin="8,0,0,0"
|
|
Style="{StaticResource Primary}" Click="OnApply" IsDefault="True"/>
|
|
<Button Content="취소" Padding="18,4" Margin="8,0,0,0" IsCancel="True"/>
|
|
</StackPanel>
|
|
|
|
<!-- 조립 결과 미리보기 + 검증 메시지 -->
|
|
<Border DockPanel.Dock="Bottom" Margin="0,12,0,0" Padding="10,8" CornerRadius="6"
|
|
Background="{DynamicResource B.Input}" BorderBrush="{DynamicResource B.InputBorder}" BorderThickness="1">
|
|
<StackPanel>
|
|
<TextBlock Text="저장될 값" FontSize="10.5" Foreground="{DynamicResource B.Muted}"/>
|
|
<TextBlock x:Name="PreviewText" FontFamily="Consolas" FontSize="12" TextWrapping="Wrap" Margin="0,3,0,0"/>
|
|
<TextBlock x:Name="ProblemText" FontSize="11" TextWrapping="Wrap" Margin="0,5,0,0"
|
|
Foreground="{DynamicResource B.Danger}" Visibility="Collapsed"/>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="16"/>
|
|
<ColumnDefinition Width="*"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- 데이터소스 -->
|
|
<DockPanel Grid.Column="0">
|
|
<TextBlock DockPanel.Dock="Top" Text="데이터소스" Margin="0,0,0,4"/>
|
|
<TextBlock x:Name="NoTableHint" DockPanel.Dock="Bottom" FontSize="11" TextWrapping="Wrap"
|
|
Margin="0,6,0,0" Foreground="{DynamicResource B.Warning}" Visibility="Collapsed"
|
|
Text="이 서식에 데이터소스가 없습니다. 팔레트에서 '데이터소스'를 먼저 놓고 쿼리를 지정하세요."/>
|
|
<ListBox x:Name="TableList" SelectionChanged="OnFormChanged" FontSize="12.5"/>
|
|
</DockPanel>
|
|
|
|
<!-- 나머지 조각 -->
|
|
<StackPanel Grid.Column="2">
|
|
<TextBlock Text="형태" Margin="0,0,0,4"/>
|
|
<ComboBox x:Name="FormBox" SelectionChanged="OnFormChanged"
|
|
ToolTip="Select 형은 레거시 편집기가 만드는 형태, Rows 형은 손으로 쓰던 형태입니다. 연 값의 형태를 그대로 유지합니다.">
|
|
<ComboBoxItem Content="Select — 필터로 행을 고름"/>
|
|
<ComboBoxItem Content="Rows — 행 번호로 직접"/>
|
|
</ComboBox>
|
|
|
|
<TextBlock x:Name="FilterLabel" Text="필터" Margin="0,10,0,4"/>
|
|
<TextBox x:Name="FilterBox" TextChanged="OnFormChanged"
|
|
ToolTip="DataTable.Select 문법 (예: CODCOD='A'). 비워 두면 전체 행입니다. 큰따옴표는 쓸 수 없습니다."/>
|
|
|
|
<TextBlock Text="행 번호" Margin="0,10,0,4"/>
|
|
<TextBox x:Name="RowBox" TextChanged="OnFormChanged" ToolTip="0부터 셉니다"/>
|
|
|
|
<TextBlock Text="필드" Margin="0,10,0,4"/>
|
|
<TextBox x:Name="FieldBox" TextChanged="OnFormChanged"
|
|
ToolTip="조회 결과의 컬럼명. SQL 별칭 표현식도 됩니다 (예: A||'-'||B). 큰따옴표는 쓸 수 없습니다."/>
|
|
|
|
<TextBlock x:Name="FieldHint" FontSize="11" TextWrapping="Wrap" Margin="0,6,0,0"
|
|
Foreground="{DynamicResource B.Muted}"
|
|
Text="필드 후보는 쿼리를 실행해야 알 수 있어 목록으로 제시하지 않습니다. 데이터소스의 쿼리를 열어 컬럼명을 확인하세요."/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</DockPanel>
|
|
</Window>
|