레이어 패널: 컨트롤 이름 병기 + 더블클릭으로 캔버스 이동

레이어 목록이 Text 만 보여줘서 chkOrientation1~5 처럼 문구가 같은
컨트롤들이 전부 같은 줄로 보였다. 두 줄로 나눠 아래에 Id 를 병기한다
(Text 가 없어 LayerName 이 곧 Id 인 경우는 중복이라 숨긴다).

그리고 컨트롤이 수백 개인 서식에서는 목록에서 이름을 찾아도 종이
어디에 있는지 알 수 없었다 — 더블클릭하면 해당 페이지로 전환 + 선택 +
캔버스 스크롤. 이미 전부 보이면 스크롤하지 않는다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Msystech
2026-08-12 15:08:19 +09:00
co-authored by Claude Opus 5
parent e18ad87100
commit d09bd90c8e
6 changed files with 122 additions and 9 deletions
@@ -40,16 +40,60 @@ public partial class DesignerCanvasView : UserControl
if (subscribedDesigner is not null)
{
subscribedDesigner.InlineEditRequested -= ShowInlineEditor;
subscribedDesigner.ScrollToRequested -= ScrollToControl;
}
CloseInlineEditor(commit: false);
subscribedDesigner = e.NewValue as DesignerViewModel;
if (subscribedDesigner is not null)
{
subscribedDesigner.InlineEditRequested += ShowInlineEditor;
subscribedDesigner.ScrollToRequested += ScrollToControl;
subscribedDesigner.ReportViewport(Scroll.ViewportWidth, Scroll.ViewportHeight);
}
}
/// <summary>
/// 컨트롤이 화면 가운데 오도록 스크롤 — 레이어 목록 더블클릭 경로.
///
/// 월드 좌표 → 화면 좌표 변환은 World 그리드의 LayoutTransform(줌)과 감싸는 Border 의
/// Padding 24 를 모두 반영해야 한다. 이미 보이는 컨트롤은 건드리지 않는다 —
/// 목록을 훑을 때마다 화면이 튀면 오히려 방향 감각을 잃는다.
/// </summary>
private void ScrollToControl(ControlViewModel target)
{
if (subscribedDesigner is null)
{
return;
}
// 페이지 전환·선택 변경이 레이아웃에 반영된 뒤 계산해야 오프셋이 맞는다
Dispatcher.BeginInvoke(new Action(() =>
{
var bounds = subscribedDesigner.WorldBoundsOf(target);
var zoom = subscribedDesigner.Zoom;
const double CanvasPad = 24; // DesignerCanvasView.xaml 의 Border Padding
var left = bounds.X * zoom + CanvasPad;
var top = bounds.Y * zoom + CanvasPad;
var width = bounds.Width * zoom;
var height = bounds.Height * zoom;
var visibleLeft = Scroll.HorizontalOffset;
var visibleTop = Scroll.VerticalOffset;
var visibleRight = visibleLeft + Scroll.ViewportWidth;
var visibleBottom = visibleTop + Scroll.ViewportHeight;
var fullyVisible = left >= visibleLeft && top >= visibleTop
&& left + width <= visibleRight && top + height <= visibleBottom;
if (fullyVisible)
{
return;
}
Scroll.ScrollToHorizontalOffset(left + (width / 2) - (Scroll.ViewportWidth / 2));
Scroll.ScrollToVerticalOffset(top + (height / 2) - (Scroll.ViewportHeight / 2));
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
/// <summary>뷰포트 크기를 VM 에 전달 — 화면 맞춤(ZoomFit)이 시각 트리를 몰라도 되게 한다</summary>
private void OnScrollSizeChanged(object sender, SizeChangedEventArgs e)
=> subscribedDesigner?.ReportViewport(Scroll.ViewportWidth, Scroll.ViewportHeight);
+22 -7
View File
@@ -529,8 +529,10 @@
</Border>
<!-- 레이어 목록 (활성 페이지, 그리기 순서) -->
<ListBox ItemsSource="{Binding CurrentDesigner.VisibleLayers}"
<ListBox x:Name="LayerListBox" ItemsSource="{Binding CurrentDesigner.VisibleLayers}"
SelectedItem="{Binding CurrentDesigner.SelectedLayerItem}"
MouseDoubleClick="OnLayerDoubleClick"
ToolTip="더블클릭하면 캔버스가 그 컨트롤로 이동합니다"
Margin="4,0,4,6"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
@@ -545,18 +547,31 @@
Content="{Binding HiddenIcon, Converter={StaticResource IconName}, ConverterParameter=13}"/>
<ContentControl DockPanel.Dock="Left" Margin="2,0,6,0" VerticalAlignment="Center" IsTabStop="False"
Content="{Binding Type, Converter={StaticResource TypeToIcon}, ConverterParameter=14|B.Accent}"/>
<TextBlock Text="{Binding LayerName}" FontSize="12" VerticalAlignment="Center"
TextTrimming="CharacterEllipsis" ToolTip="{Binding Id}">
<TextBlock.Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<!--
문구와 이름을 두 줄로. 컨트롤 이름(Id)은 외부 참조 키라
(액션 대상·입력 파라미터가 이름으로 서로를 가리키고 CalcBox 수식도 이름으로 합산한다)
배선하려면 목록에서 보여야 하는데, 248px 폭에 나란히 두면 양쪽 다 잘려
'txtKinPhone…' 처럼 구분이 안 되는 꼬리가 남는다. 한 줄 더 쓰는 편이 낫다.
표시 문구가 없어 이름이 곧 문구인 행에서는 둘째 줄이 뜨지 않는다.
-->
<StackPanel VerticalAlignment="Center">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHiddenFlag}" Value="True">
<Setter Property="Opacity" Value="0.55"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel.Style>
<TextBlock Text="{Binding LayerName}" FontSize="12"
TextTrimming="CharacterEllipsis" ToolTip="{Binding LayerName}"/>
<TextBlock Text="{Binding LayerId}" FontSize="10.5" Margin="0,1,0,0"
Foreground="{DynamicResource B.Muted}"
TextTrimming="CharacterEllipsis"
ToolTip="컨트롤 이름 — 다른 속성이 이 이름으로 참조합니다"
Visibility="{Binding HasLayerId, Converter={StaticResource BoolToVisibility}}"/>
</StackPanel>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
@@ -38,6 +38,20 @@ public partial class MainView : Window
}
}
/// <summary>
/// 레이어 더블클릭 → 캔버스를 그 컨트롤로 이동.
/// 컨트롤이 수백 개인 서식에서는 목록에서 이름을 찾아도 종이 어디에 있는지 알 수 없었다.
/// (ListBox 더블클릭은 InputBinding 을 지원하지 않아 코드비하인드로 위임한다 — 서식 목록과 같은 이유)
/// </summary>
private void OnLayerDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (DataContext is MainViewModel { CurrentDesigner: { } designer }
&& LayerListBox.SelectedItem is ViewModels.Controls.ControlViewModel target)
{
designer.RevealControl(target);
}
}
private void OnThemeToggleClick(object sender, RoutedEventArgs e)
{
ThemeManager.Toggle();