using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; using UnityEngine; using UnityEngine.UI; namespace DDD.MVVM { /// /// 바인딩 타겟 인터페이스 /// UI 요소와 ViewModel 속성을 연결하는 역할 /// public interface IBindingTarget { /// /// 바인딩된 속성의 경로 /// string PropertyPath { get; } /// /// UI 요소의 값을 업데이트 /// /// 새로운 값 void UpdateValue(object value); } /// /// Text 컴포넌트에 대한 바인딩 타겟 /// public class TextBindingTarget : IBindingTarget { private readonly Text _text; public string PropertyPath { get; } public TextBindingTarget(Text text, string propertyPath) { _text = text; PropertyPath = propertyPath; } public void UpdateValue(object value) { if (_text != null) _text.text = value?.ToString() ?? string.Empty; } } /// /// Image 컴포넌트에 대한 바인딩 타겟 /// public class ImageBindingTarget : IBindingTarget { private readonly Image _image; public string PropertyPath { get; } public ImageBindingTarget(Image image, string propertyPath) { _image = image; PropertyPath = propertyPath; } public void UpdateValue(object value) { if (_image != null && value is Sprite sprite) _image.sprite = sprite; } } /// /// GameObject의 활성화 상태에 대한 바인딩 타겟 /// public class ActiveBindingTarget : IBindingTarget { private readonly GameObject _gameObject; public string PropertyPath { get; } public ActiveBindingTarget(GameObject gameObject, string propertyPath) { _gameObject = gameObject; PropertyPath = propertyPath; } public void UpdateValue(object value) { if (_gameObject != null) _gameObject.SetActive(value is bool active && active); } } /// /// Slider 컴포넌트에 대한 바인딩 타겟 /// public class SliderBindingTarget : IBindingTarget { private readonly Slider _slider; public string PropertyPath { get; } public SliderBindingTarget(Slider slider, string propertyPath) { _slider = slider; PropertyPath = propertyPath; } public void UpdateValue(object value) { if (_slider != null && value is float floatValue) _slider.value = floatValue; } } /// /// 바인딩 컨텍스트 - ViewModel과 View 간의 데이터 바인딩을 관리 /// public class BindingContext { private readonly Dictionary> _bindings = new(); private readonly Dictionary _converters = new(); private INotifyPropertyChanged _dataContext; /// /// 데이터 컨텍스트 (ViewModel) 설정 /// /// 바인딩할 ViewModel public void SetDataContext(INotifyPropertyChanged dataContext) { if (_dataContext != null) _dataContext.PropertyChanged -= OnPropertyChanged; _dataContext = dataContext; if (_dataContext != null) { _dataContext.PropertyChanged += OnPropertyChanged; RefreshAllBindings(); } } /// /// 속성 바인딩 추가 /// /// 바인딩할 속성 경로 /// 바인딩 타겟 /// 값 변환기 (선택사항) public void Bind(string propertyPath, IBindingTarget target, IValueConverter converter = null) { if (!_bindings.ContainsKey(propertyPath)) _bindings[propertyPath] = new List(); _bindings[propertyPath].Add(target); if (converter != null) _converters[propertyPath] = converter; // 즉시 초기값 설정 UpdateBinding(propertyPath); } /// /// 속성 변경 이벤트 핸들러 /// private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { UpdateBinding(e.PropertyName); } /// /// 특정 속성의 바인딩 업데이트 /// /// 업데이트할 속성 경로 private void UpdateBinding(string propertyPath) { if (!_bindings.ContainsKey(propertyPath)) return; var value = GetPropertyValue(propertyPath); // 컨버터 적용 if (_converters.TryGetValue(propertyPath, out var converter)) value = converter.Convert(value); foreach (var target in _bindings[propertyPath]) { target.UpdateValue(value); } } /// /// 속성 값 가져오기 (리플렉션 사용) /// /// 속성 경로 /// 속성 값 private object GetPropertyValue(string propertyPath) { if (_dataContext == null) return null; // 중첩 속성 지원 (예: "ItemData.Name") var properties = propertyPath.Split('.'); object current = _dataContext; foreach (var prop in properties) { if (current == null) return null; var property = current.GetType().GetProperty(prop); current = property?.GetValue(current); } return current; } /// /// 모든 바인딩 새로고침 /// private void RefreshAllBindings() { foreach (var propertyPath in _bindings.Keys) { UpdateBinding(propertyPath); } } /// /// 리소스 정리 /// public void Dispose() { if (_dataContext != null) _dataContext.PropertyChanged -= OnPropertyChanged; _bindings.Clear(); _converters.Clear(); } } /// /// 속성 경로 캐시 - 성능 최적화를 위한 리플렉션 결과 캐싱 /// public static class PropertyPathCache { private static readonly Dictionary> _cache = new(); /// /// 캐시된 PropertyInfo 가져오기 /// /// 타입 /// 속성 이름 /// PropertyInfo public static PropertyInfo GetProperty(Type type, string propertyName) { if (!_cache.ContainsKey(type)) _cache[type] = new Dictionary(); if (!_cache[type].ContainsKey(propertyName)) _cache[type][propertyName] = type.GetProperty(propertyName); return _cache[type][propertyName]; } } }