using System; using System.Collections.Generic; using System.Diagnostics; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; namespace VisionCommonUI.UserGuide { public class GuideManager { private readonly List _steps = new List(); private readonly UserControl _baseControl; private int _currentStep; private GuideOverlayWindow _overlay; public GuideManager(UserControl baseControl) { _baseControl = baseControl ?? throw new ArgumentNullException(nameof(baseControl)); } public void AddStep( UIElement target, string message, string imagePath = null, MessagePlacement placement = MessagePlacement.Below, double fontSize = 16) { if (target == null || !_baseControl.IsLoaded) { return; } // 위치 계산 try { var transform = target.TransformToAncestor(_baseControl); var position = transform.Transform(new Point(0, 0)); var size = target.RenderSize; if (size.Width <= 0 || size.Height <= 0) { return; // 렌더링되지 않은 컨트롤 제외 } var imageSource = string.IsNullOrEmpty(imagePath) ? null : LoadPackImage(imagePath); _steps.Add(new GuideStep { HighlightArea = new Rect(position, size), Message = message, Placement = placement, FontSize = fontSize, Imagesc = imageSource }); } catch (Exception ex) { Debug.WriteLine($"❌ GuideManager AddStep 오류: {ex.Message}"); } } public void Start() { if (_steps.Count == 0 || _baseControl == null) { return; } _overlay = new GuideOverlayWindow { WindowStartupLocation = WindowStartupLocation.Manual, Owner = Application.Current.MainWindow, Width = _baseControl.ActualWidth, Height = _baseControl.ActualHeight }; var topLeft = GetWpfRelativeScreenPosition(_baseControl); _overlay.Left = topLeft.X; _overlay.Top = topLeft.Y; _overlay.OnNextClicked += Next; _overlay.OnPrevClicked += Previous; _overlay.OnExitClicked += Exit; _overlay.Show(); ShowStep(0); } private Point GetWpfRelativeScreenPosition(Visual visual) { var point = visual.PointToScreen(new Point(0, 0)); var source = PresentationSource.FromVisual(visual); if (source?.CompositionTarget == null) { return new Point(0, 0); } Matrix transform = source.CompositionTarget.TransformFromDevice; return transform.Transform(point); } private static ImageSource LoadPackImage(string relativePath) { try { var uri = new Uri($"pack://application:,,,/VisionCommonUI;component/UserGuide/ImgSource/{relativePath}", UriKind.Absolute); return new BitmapImage(uri); } catch (Exception ex) { return null; } } private void ShowStep(int index) { if (_overlay == null || index >= _steps.Count) { return; } _currentStep = index; _overlay.Highlight(_steps[index]); } private void Next() { _currentStep++; if (_currentStep < _steps.Count) { ShowStep(_currentStep); } else { Exit(); } } private void Previous() { if (_currentStep > 0) { ShowStep(--_currentStep); } } private void Exit() { if (_overlay == null || _overlay.IsClosing) { return; } _overlay.Close(); } } }