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];
}
}
}