using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace ChatworkBulkSender.Utils
{
/// <summary>
/// データ変更検知ヘルパークラス
/// </summary>
public class DataChangeDetector : IDisposable
{
private readonly Dictionary<Control, string> _initialValues = new Dictionary<Control, string>();
private readonly HashSet<Control> _monitoredControls = new HashSet<Control>();
private bool _hasChanges = false;
private bool _isLoading = true;
/// <summary>
/// データ変更があるかどうか
/// </summary>
public bool HasChanges => _hasChanges && !_isLoading;
/// <summary>
/// 変更検知を開始
/// </summary>
/// <param name="container">監視対象のコンテナコントロール</param>
public void StartMonitoring(Control container)
{
// 既存の監視を解除
StopMonitoring();
// コンテナ内のすべての入力コントロールを再帰的に取得
var controls = GetAllInputControls(container);
foreach (var control in controls)
{
// 初期値を保存
string initialValue = GetControlValue(control);
_initialValues[control] = initialValue;
_monitoredControls.Add(control);
// 変更イベントをフック
AttachChangeEvent(control);
}
_isLoading = false;
_hasChanges = false;
}
/// <summary>
/// 変更検知を停止
/// </summary>
public void StopMonitoring()
{
foreach (var control in _monitoredControls)
{
DetachChangeEvent(control);
}
_monitoredControls.Clear();
_initialValues.Clear();
_hasChanges = false;
}
/// <summary>
/// 変更状態をリセット
/// </summary>
public void ResetChanges()
{
_hasChanges = false;
// 現在の値を初期値として再設定
foreach (var control in _monitoredControls)
{
_initialValues[control] = GetControlValue(control);
}
}
/// <summary>
/// リソースの解放
/// </summary>
public void Dispose()
{
StopMonitoring();
}
/// <summary>
/// コンテナ内のすべての入力コントロールを取得
/// </summary>
private List<Control> GetAllInputControls(Control container)
{
var controls = new List<Control>();
foreach (Control control in container.Controls)
{
if (control is TextBox || control is CheckBox || control is ComboBox || control is RadioButton)
{
controls.Add(control);
}
// 子コントロールも再帰的に検索
if (control.HasChildren)
{
controls.AddRange(GetAllInputControls(control));
}
}
return controls;
}
/// <summary>
/// コントロールの現在値を取得
/// </summary>
private string GetControlValue(Control control)
{
if (control is TextBox textBox)
return textBox.Text;
else if (control is CheckBox checkBox)
return checkBox.Checked.ToString();
else if (control is ComboBox comboBox)
return comboBox.SelectedIndex.ToString();
else if (control is RadioButton radioButton)
return radioButton.Checked.ToString();
else
return string.Empty;
}
/// <summary>
/// 変更イベントをアタッチ
/// </summary>
private void AttachChangeEvent(Control control)
{
if (control is TextBox textBox)
textBox.TextChanged += Control_ValueChanged;
else if (control is CheckBox checkBox)
checkBox.CheckedChanged += Control_ValueChanged;
else if (control is ComboBox comboBox)
comboBox.SelectedIndexChanged += Control_ValueChanged;
else if (control is RadioButton radioButton)
radioButton.CheckedChanged += Control_ValueChanged;
}
/// <summary>
/// 変更イベントをデタッチ
/// </summary>
private void DetachChangeEvent(Control control)
{
if (control is TextBox textBox)
textBox.TextChanged -= Control_ValueChanged;
else if (control is CheckBox checkBox)
checkBox.CheckedChanged -= Control_ValueChanged;
else if (control is ComboBox comboBox)
comboBox.SelectedIndexChanged -= Control_ValueChanged;
else if (control is RadioButton radioButton)
radioButton.CheckedChanged -= Control_ValueChanged;
}
/// <summary>
/// コントロールの値が変更されたときのイベントハンドラ
/// </summary>
private void Control_ValueChanged(object sender, EventArgs e)
{
if (_isLoading) return;
var control = sender as Control;
if (control == null || !_monitoredControls.Contains(control)) return;
string currentValue = GetControlValue(control);
string initialValue = _initialValues.ContainsKey(control) ? _initialValues[control] : string.Empty;
// 初期値と現在値を比較
if (currentValue != initialValue)
{
_hasChanges = true;
}
else
{
// すべてのコントロールの値が初期値と同じかチェック
_hasChanges = _monitoredControls.Any(c =>
{
string currVal = GetControlValue(c);
string initVal = _initialValues.ContainsKey(c) ? _initialValues[c] : string.Empty;
return currVal != initVal;
});
}
}
}
}