using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Globalization;
using System.Windows.Forms;
using ChatworkBulkSender.Utils;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Dtos;
namespace ChatworkBulkSender.UserControls
{
/// <summary>
/// 水平 or 垂直 にレイアウトを変更する
/// </summary>
public enum LayoutMode
{
Horizontal,
Vertical
}
public partial class SendContentControl : AbstractUserControl
{
// 送信対象一覧
private List<CustomerMasterDto> _senderList;
// 初期の表示設定は水平
private LayoutMode _layoutMode = LayoutMode.Horizontal;
// バリデーション用
private ErrorProvider errorProvider;
public LayoutMode LayoutMode
{
get
{
return _layoutMode;
}
set
{
_layoutMode = value;
ApplyLayout();
}
}
public SendContentControl()
{
InitializeComponent();
if (IsInDesignMode()) return;
Init();
InitializeValidation();
LoadCustomerList();
ToggleManualSettingPanel();
}
private void InitializeValidation()
{
// ErrorProviderの初期化
errorProvider = new ErrorProvider();
errorProvider.BlinkStyle = ErrorBlinkStyle.NeverBlink;
// MaxLength設定
txtContents.MaxLength = Constants.PATTERN_TEMPLATE_MAX_LENGTH;
// イベントハンドラーの登録
txtContents.TextChanged += TxtContents_TextChanged;
}
private void TxtContents_TextChanged(object sender, EventArgs e)
{
ValidateContents();
}
public bool ValidateContents()
{
if (string.IsNullOrWhiteSpace(txtContents.Text))
{
errorProvider.SetError(txtContents, "定型文を入力してください。");
return false;
}
else if (txtContents.TextLength > Constants.PATTERN_TEMPLATE_MAX_LENGTH)
{
errorProvider.SetError(txtContents,
ValidationHelperUtil.GetLengthErrorMessage("定型文", txtContents.TextLength, Constants.PATTERN_TEMPLATE_MAX_LENGTH));
return false;
}
else
{
errorProvider.SetError(txtContents, "");
return true;
}
}
public bool ValidateAll()
{
return ValidateContents();
}
/// <summary>
/// 「定期送信」「不定期送信」に応じて表示レイアウトを調整する
/// </summary>
/// <param name="sendType"></param>
public void ChangeMode(Constants.SEND_TYPE sendType)
{
if(sendType== Constants.SEND_TYPE.ADHOC_SENDING)
{
rdoAutomaticSelection.Visible = false;
rdoManualSelection.Visible = false;
pnlManualSetting.Location = new Point(this.Width - 688, 24);
pnlManualSetting.Size = new Size(685, txtContents.Height);
// 送信対象は手動設定とする
rdoManualSelection.Checked = true;
}
}
public Constants.DESTINAION_SELECT_TYPE GetTypeOfDestinationSelect()
{
if (rdoAutomaticSelection.Checked) return Constants.DESTINAION_SELECT_TYPE.AUTO;
if (rdoManualSelection.Checked) return Constants.DESTINAION_SELECT_TYPE.MANUAL;
return Constants.DESTINAION_SELECT_TYPE.NOT_SELECTED;
}
/// <summary>
/// 現在「送信対象」グリッドでチェックの付いている顧客だけを返す
/// </summary>
public List<CustomerMasterDto> GetDestinationList()
{
// 自動選択の場合
if (rdoAutomaticSelection.Checked)
{
// 送信対象一覧をそのまま返す
// ※PDF有無による自動選択はこの関数呼び出しもとで行う
return _senderList
.OrderBy(c => c.SortOrder)
.Select(c => new CustomerMasterDto(c)) // 新規インスタンスとして返す
.ToList();
}
// 手動選択の場合
if (rdoManualSelection.Checked)
{
// 送信対象一覧のうちチェックがついたデータのみ返す
return _senderList
.Where(c => c.IsSelected)
.OrderBy(c => c.SortOrder)
// 各要素を新規インスタンスにコピーしてからリスト化
.Select(c => new CustomerMasterDto(c)) // 新規インスタンスとして返す
.ToList();
}
return null;
}
public string GetContents()
{
return txtContents.Text;
}
/// <summary>
/// 送信内容をセットする
/// </summary>
/// <param name="sendContents"></param>
public void SetSendContents(string sendContents)
{
txtContents.Text = sendContents;
}
/// <summary>
/// 送信対象をセットする
/// </summary>
/// <param name="header"></param>
/// <param name="details"></param>
public void SetSendTargetOnlyFailed(List<SendHistoryDetailDto> details)
{
// 自動選択・手動選択(今回は個別にチェックを付けるため、手動選択固定とする)
rdoManualSelection.Checked = true;
ToggleManualSettingPanel();
// 送信対象選択
var failedNumbers = new HashSet<int>(
details
.Where(d => d.Success == Constants.SEND_RESULT.FAILURE)
.Select(d => d.ManagementNumber));
foreach (var cust in _senderList)
{
cust.IsSelected = failedNumbers.Contains(cust.ManagementNumber);
}
dgvDestinationList.Refresh();
}
/// <summary>
/// レイアウトを変更する
/// </summary>
///
private void ApplyLayout()
{
if (_layoutMode == LayoutMode.Vertical)
{
this.SuspendLayout();
try
{
// Anchorを設定
label2.Anchor = AnchorStyles.Top | AnchorStyles.Left;
rdoAutomaticSelection.Anchor = AnchorStyles.Top | AnchorStyles.Left;
rdoManualSelection.Anchor = AnchorStyles.Top | AnchorStyles.Left;
pnlManualSetting.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom;
// 送信内容の高さを固定
txtContents.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
txtContents.Size = new Size(this.Width - 20, 200);
// 送信対象を下に配置
int baseY = txtContents.Bottom + 10;
label2.Location = new Point(0, baseY);
label3.Anchor = AnchorStyles.Top | AnchorStyles.Left;
label6.Anchor = AnchorStyles.Top | AnchorStyles.Left;
chkSelectAll.Anchor = AnchorStyles.Top | AnchorStyles.Right;
rdoAutomaticSelection.Location = new Point(25, baseY + 30);
rdoManualSelection.Location = new Point(25, baseY + 56);
pnlManualSetting.Location = new Point(8, baseY + 82);
pnlManualSetting.Size = new Size(this.Width - 16, this.Height - baseY - 92);
dgvDestinationList.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
// データ行の横幅をDGVの横幅に合わせた後、列の比率を調整する
foreach (DataGridViewColumn column in dgvDestinationList.Columns)
{
// 顧客名の列のみ横幅を広く設定する
if (column.DataPropertyName == nameof(CustomerMasterDto.CustomerName))
{
column.Width = 400;
}
else
{
column.Width = 90;
}
}
}
finally
{
this.ResumeLayout();
}
}
}
private void Init()
{
// グリッド初期化
// 行ヘッダ非表示
dgvDestinationList.RowHeadersVisible = false;
// ユーザーによる行の高さ変更を禁止
dgvDestinationList.AllowUserToResizeRows = false;
}
/// <summary>
/// パターン情報をセットする
/// </summary>
/// <param name="patternDto"></param>
public void SetPatternData(PatternMasterDto patternDto)
{
// 定型文を設定
txtContents.Text = patternDto.TemplateText;
// 送信対象モードを設定
var mode = (Constants.DESTINAION_SELECT_TYPE)patternDto.Target;
rdoAutomaticSelection.Checked = mode == Constants.DESTINAION_SELECT_TYPE.AUTO;
rdoManualSelection.Checked = mode == Constants.DESTINAION_SELECT_TYPE.MANUAL;
// 自動モードの場合、送信対象選択グリッドのチェックをクリアする。
if (rdoAutomaticSelection.Checked)
{
if (_senderList != null)
{
foreach (var cust in _senderList)
{
cust.IsSelected = false;
}
dgvDestinationList.Refresh();
}
}
// 手動モードの場合、送信対象選択グリッドで該当データをチェックした状態とする。
if (rdoManualSelection.Checked)
{
if (_senderList != null)
{
// パターンに紐づく管理番号の集合を作成
var selectedSet = new HashSet<int>(
patternDto.Customers.Select(c => c.ManagementNumber));
// すべての行に対して IsSelected を設定
foreach (var cust in _senderList)
{
cust.IsSelected = selectedSet.Contains(cust.ManagementNumber);
}
// グリッドを再描画
dgvDestinationList.Refresh();
}
}
}
private void rdoAutomaticSelection_CheckedChanged(object sender, EventArgs e)
{
ToggleManualSettingPanel();
}
private void rdoManualSelection_CheckedChanged(object sender, EventArgs e)
{
ToggleManualSettingPanel();
}
/// <summary>
/// 「手動選択」で使用するパラメーターのEnabledを切り替える
/// </summary>
private void ToggleManualSettingPanel()
{
pnlManualSetting.Enabled = rdoManualSelection.Checked;
dgvDestinationList.Enabled = rdoManualSelection.Checked;
}
/// <summary>
/// DBより顧客情報を取得してグリッドに表示する。
/// </summary>
private void LoadCustomerList()
{
// データ取得
var dao = new CustomerMasterDao();
_senderList = dao.GetAll();
// 表示する列を定義
dgvDestinationList.AutoGenerateColumns = false;
dgvDestinationList.Columns.Clear();
dgvDestinationList.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(CustomerMasterDto.ManagementNumber),
HeaderText = "管理番号",
Width = 90,
ReadOnly = true
});
dgvDestinationList.Columns.Add(new DataGridViewTextBoxColumn
{
DataPropertyName = nameof(CustomerMasterDto.CustomerName),
HeaderText = "顧客名",
Width = 400,
ReadOnly = true
});
dgvDestinationList.Columns.Add(new DataGridViewCheckBoxColumn
{
DataPropertyName = nameof(CustomerMasterDto.IsSelected),
HeaderText = "送信対象",
Width = 90
});
// 表示(初期表示は全件)
dgvDestinationList.DataSource = _senderList;
}
/// <summary>
/// 条件に応じてフィルターした顧客情報をグリッドに表示。
/// 管理番号:前方一致
/// 顧客名:部分一致
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnFilter_Click(object sender, EventArgs e)
{
string managementNumberFilter = txtFilterManagementNumber.Text.Trim();
string customerNameFilter = txtFilterCustomerName.Text.Trim();
// カレントカルチャーの CompareInfo を取得
var cmp = CultureInfo.CurrentCulture.CompareInfo;
var filtered = _senderList.Where(c =>
// 管理番号は前方一致 → IndexOf==0
(string.IsNullOrEmpty(managementNumberFilter)
|| cmp.IndexOf(
c.ManagementNumber.ToString(), // 対象文字列
managementNumberFilter, // 検索文字列
CompareOptions.IgnoreWidth // 半角全角を無視
) == 0)
&&
// 顧客名は部分一致 → IndexOf>=0
(string.IsNullOrEmpty(customerNameFilter)
|| cmp.IndexOf(
c.CustomerName,
customerNameFilter,
CompareOptions.IgnoreWidth // 半角全角を無視
| CompareOptions.IgnoreCase // 英字の大小も無視
) >= 0)
).ToList();
dgvDestinationList.DataSource = filtered;
}
private void btnRemoveFilter_Click(object sender, EventArgs e)
{
// テキストボックスをクリア
txtFilterManagementNumber.Clear();
txtFilterCustomerName.Clear();
// 全件を再バインド
dgvDestinationList.DataSource = _senderList;
}
/// <summary>
/// 現在グリッドに表示されている内容に対してのみ、チェック状態を一括操作する。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
{
bool isChecked = chkSelectAll.Checked;
if (dgvDestinationList.DataSource is List<CustomerMasterDto> list)
{
foreach (var dto in list)
dto.IsSelected = isChecked;
dgvDestinationList.Refresh();
}
}
/// <summary>
/// 管理番号テキストボックス Enter押下でフィルター処理を実行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtFilterManagementNumber_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
btnFilter.PerformClick();
}
}
/// <summary>
/// 顧客名テキストボックス Enter押下でフィルター処理を実行
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtFilterCustomerName_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
btnFilter.PerformClick();
}
}
}
}