2026-01-26 14:51:28 +09:00

135 lines
4.2 KiB
C#

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<GuideStep> _steps = new List<GuideStep>();
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();
}
}
}