using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Dtos;
using ChatworkBulkSender.UserControls;
using ChatworkBulkSender.Utils;
namespace ChatworkBulkSender.Forms
{
public partial class M_SendPatternIndividualEdit : Form, ICreatable<PatternMasterDto>, IUpdateble<PatternMasterDto>
{
public enum ButtonMode
{
Register, // 新規登録
Update // 更新
,
}
private List<int> _selectedCustomerIds = null;
// パターン名称ヘッダー
private PatternNameHeaderControl _patternNameHeader = null;
// 送信パターンマスタ編集部分
private SendContentControl _sendContent = null;
private BtnRegisterControl _btnRegister = null;
private BtnUpdateControl _btnUpdate = null;
// 初期値は、新規登録
private ButtonMode _btnMode = ButtonMode.Register;
public event EventHandler<PatternMasterDto> DataCreated;
public event EventHandler<PatternMasterDto> DataUpdated;
// 楽観的ロック用の更新日時
private DateTime? _currentUpdatedDate = null;
// 更新対象のパターンID
private int _patternId = 0;
public ButtonMode BtnMode
{
set
{
_btnMode = value;
}
}
public M_SendPatternIndividualEdit(ButtonMode buttonMode = ButtonMode.Register, PatternMasterDto sendPattern = null)
{
InitializeComponent();
this._btnMode = buttonMode;
// 更新モードの場合、楽観的ロック用のデータを保持
if (buttonMode == ButtonMode.Update && sendPattern != null)
{
_currentUpdatedDate = sendPattern.UpdatedDate;
_patternId = sendPattern.PatternId;
}
// 初期のウィンドウサイズ
this.ClientSize = new Size(1600,900);
// ウィンドウサイズの固定
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// 初期のウィンドウ表示位置
this.StartPosition = FormStartPosition.CenterScreen;
_patternNameHeader = new PatternNameHeaderControl();
_sendContent = new SendContentControl();
if (_btnMode == ButtonMode.Update)
{
_btnUpdate = new BtnUpdateControl();
_btnUpdate.Dock = DockStyle.Fill;
panel3.Controls.Add(_btnUpdate);
}
else
{
_btnRegister = new BtnRegisterControl();
_btnRegister.Dock = DockStyle.Fill;
panel3.Controls.Add(_btnRegister);
}
// レイアウトを垂直に設定
_sendContent.LayoutMode = LayoutMode.Vertical;
// Dock設定
_sendContent.Dock = DockStyle.Fill;
_patternNameHeader.Dock = DockStyle.Fill;
// パネルに追加、表示する
panel1.Controls.Add(_patternNameHeader);
panel2.Controls.Add(_sendContent);
Label label1 = _sendContent.Controls.Find("label1",false).FirstOrDefault() as Label;
label1.Width = 170;
label1.Text += "(定型文)";
}
private void M_SenderPatternIndividualEdit_Load(object sender, EventArgs e)
{
this.ActiveControl = panel1;
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.BtnUpdateClicked += BtnUpdate_Click;
_btnUpdate.BtnReturnClicked += BtnReturn_Click;
}
else
{
_btnRegister.BtnReturnClicked += BtnReturn_Click;
}
// フォントの統一設定を適用
FontSettingHelper.ApplyFontsToForm(this);
// ユーザーコントロールにも適用
FontSettingHelper.ApplyFontsToControl(_patternNameHeader);
FontSettingHelper.ApplyFontsToControl(_sendContent);
if (_btnMode == ButtonMode.Update)
{
FontSettingHelper.ApplyFontsToControl(_btnUpdate);
}
else
{
FontSettingHelper.ApplyFontsToControl(_btnRegister);
}
// TabIndexの設定(フォーカス順制御)
TabIndexHelper.SetIndividualEditScreenTabOrder(this);
}
/// <summary>
/// フォームに表示するユーザーコントロールをパネルから削除した後、明示的なDisposeで解放する。
/// </summary>
private void DisposeViewControls()
{
// パネルから削除する
panel1.Controls.Clear();
panel2.Controls.Clear();
panel3.Controls.Clear();
// 削除だけでは解放されないので、明示的に解放する
_patternNameHeader.Dispose();
_sendContent.Dispose();
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.Dispose();
}
else
{
_btnRegister.Dispose();
}
}
private void BtnRegister_Click(object sender, EventArgs e)
{
try
{
// バリデーションチェック
if (!ValidateInputs())
{
return;
}
if (_selectedCustomerIds != null)
{
_selectedCustomerIds.Clear();
}
else
{
_selectedCustomerIds = new List<int>();
}
var newPattern = GetPatternData();
if (newPattern == null) { return; }
var dao = new PatternMasterDao();
var createdId = dao.InsertWithCustomersBatch(newPattern,_selectedCustomerIds);
if (createdId == 1)
{
DataCreated?.Invoke(this, newPattern);
}
}
catch (Exception ex)
{
MessageBoxUtil.ShowErr($"{ex.Message}\n\n");
}
}
private void BtnUpdate_Click(object sender, EventArgs e)
{
try
{
// バリデーションチェック
if (!ValidateInputs())
{
return;
}
// 選択された顧客IDを取得
if (_selectedCustomerIds == null)
{
_selectedCustomerIds = new List<int>();
}
else
{
_selectedCustomerIds.Clear();
}
var dgvDestinationList = _sendContent.Controls.Find("dgvDestinationList", true).FirstOrDefault() as DataGridView;
if (dgvDestinationList != null)
{
foreach (DataGridViewRow row in dgvDestinationList.Rows)
{
if (row.Cells["送信対象"].Value != null && (bool)row.Cells["送信対象"].Value)
{
_selectedCustomerIds.Add(Convert.ToInt32(row.Cells["管理番号"].Value));
}
}
}
var updatedPattern = GetPatternData();
if (updatedPattern == null) { return; }
// パターンIDを設定
updatedPattern.PatternId = _patternId;
// 更新日時がない場合はエラー
if (_currentUpdatedDate == null)
{
MessageBox.Show("データの更新日時が取得できませんでした。", "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var dao = new PatternMasterDao();
// 更新前に最新のデータを取得して、他のユーザーが更新していないか確認
var currentDbData = dao.GetById(_patternId);
if (currentDbData != null)
{
// DateTime精度問題対策:1秒以内の差は同じとみなす
bool isUpdatedByOthers = false;
if (currentDbData.UpdatedDate.HasValue && _currentUpdatedDate.HasValue)
{
var timeDiff = Math.Abs((currentDbData.UpdatedDate.Value - _currentUpdatedDate.Value).TotalSeconds);
isUpdatedByOthers = timeDiff > 1.0; // 1秒以上の差がある場合のみ更新とみなす
}
else if (currentDbData.UpdatedDate != _currentUpdatedDate)
{
isUpdatedByOthers = true;
}
if (isUpdatedByOthers)
{
// 他のユーザーが既に更新している
MessageBox.Show(MessageBoxUtil.DB_011, "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
// 最新のデータで画面を更新
SetPatternData(currentDbData);
return;
}
}
// 選択された顧客IDを含めて更新
var newPatternId = dao.Update(updatedPattern, _currentUpdatedDate.Value, _selectedCustomerIds);
if (newPatternId == -1)
{
// 楽観的ロックエラー
MessageBox.Show(MessageBoxUtil.DB_011, "エラー",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
// 最新のデータを取得して画面を更新
var latestData = dao.GetById(_patternId);
if (latestData != null)
{
SetPatternData(latestData);
}
return;
}
if (newPatternId > 0)
{
// 更新成功後、最新のデータを取得して更新日時を更新
var latestData = dao.GetById(newPatternId);
if (latestData != null)
{
_currentUpdatedDate = latestData.UpdatedDate;
updatedPattern.UpdatedDate = latestData.UpdatedDate; // イベントで渡すデータも更新
}
// 更新成功
updatedPattern.PatternId = newPatternId;
DataUpdated?.Invoke(this, updatedPattern);
MessageBox.Show(MessageBoxUtil.DB_002, "情報",
MessageBoxButtons.OK, MessageBoxIcon.Information);
_patternId = newPatternId;
}
}
catch (Exception ex)
{
MessageBoxUtil.ShowErr($"{ex.Message}\n\n");
}
}
private void BtnReturn_Click(object sender, EventArgs e)
{
this.Close();
}
private void M_SendPatternIndividualEdit_FormClosing(object sender, FormClosingEventArgs e)
{
// 変更がない場合はメッセージを出さない
// TODO: 変更検知機能を実装する場合はここで判定
// 現時点では常にメッセージを出さない
if (false) // 将来的に変更検知を実装する場合はここを修正
{
DialogResult result = MessageBox.Show("送信パターンマスタ 個別編集画面を終了してもよろしいですか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.No)
{
e.Cancel = true;
return;
}
}
// イベント登録の解除
if (_btnMode == ButtonMode.Update)
{
_btnUpdate.BtnUpdateClicked -= BtnUpdate_Click;
_btnUpdate.BtnReturnClicked -= BtnReturn_Click;
}
else
{
_btnRegister.BtnReturnClicked -= BtnReturn_Click;
}
// 明示的に解放する
this.DisposeViewControls();
// 設定メニューを表示する
this.Owner.Show();
// 再描写を行う
this.Owner.Refresh();
}
private bool ValidateInputs()
{
// PatternNameHeaderControlのバリデーション
bool isHeaderValid = _patternNameHeader.ValidateAll();
// SendContentControlのバリデーション
bool isContentValid = _sendContent.ValidateAll();
return isHeaderValid && isContentValid;
}
/// <summary>
/// パターンデータを画面に設定する
/// </summary>
/// <param name="pattern">設定するパターンデータ</param>
public void SetPatternData(PatternMasterDto pattern)
{
if (pattern == null) return;
var patternName = _patternNameHeader.Controls.Find("txtPatternName", false).FirstOrDefault() as TextBox;
var templateTxt = _sendContent.Controls.Find("txtContents", true).FirstOrDefault() as TextBox;
var sortOrder = _patternNameHeader.Controls.Find("txtSortOrder", false).FirstOrDefault() as TextBox;
var isUnused = _patternNameHeader.Controls.Find("chkUnusedFlg", false).FirstOrDefault() as CheckBox;
if (patternName != null) patternName.Text = pattern.PatternName;
if (templateTxt != null) templateTxt.Text = pattern.TemplateText;
if (sortOrder != null) sortOrder.Text = pattern.SortOrder.ToString();
if (isUnused != null) isUnused.Checked = pattern.IsUnused;
// パターンIDも更新
_patternId = pattern.PatternId;
_currentUpdatedDate = pattern.UpdatedDate;
}
private PatternMasterDto GetPatternData()
{
var patternName =_patternNameHeader.Controls.Find("txtPatternName",false).FirstOrDefault() as TextBox;
var templateTxt = _sendContent.Controls.Find("txtContents", true).FirstOrDefault() as TextBox;
var sortOrder = _patternNameHeader.Controls.Find("txtSortOrder", false).FirstOrDefault() as TextBox;
var isUnused = _patternNameHeader.Controls.Find("chkUnusedFlg", false).FirstOrDefault() as CheckBox;
var dgvDestinationList = _sendContent.Controls.Find("dgvDestinationList", true).FirstOrDefault() as DataGridView;
var rdoAutomatic = _sendContent.Controls.Find("rdoAutomaticSelection",true).FirstOrDefault() as RadioButton;
if (dgvDestinationList != null)
{
foreach (DataGridViewRow row in dgvDestinationList.Rows)
{
if (row.Cells["送信対象"].Value != null && (bool)row.Cells["送信対象"].Value)
{
_selectedCustomerIds.Add(Convert.ToInt32(row.Cells["管理番号"].Value));
}
}
}
// 並び順のパースを安全に行う
if (!int.TryParse(sortOrder.Text, out int sortOrderValue))
{
MessageBox.Show("並び順の値が正しくありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
var newPattern = new PatternMasterDto
{
PatternName = patternName.Text,
TemplateText = templateTxt.Text,
SortOrder = sortOrderValue,
IsUnused = isUnused.Checked,
};
return newPattern;
}
}
}