using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows.Forms; namespace D2Multi { public partial class CharacterMissionsForm : Form { readonly D2Account _account; readonly List _editingCharacters; BindingList _rows; BindingSource _source; public CharacterMissionsForm(D2Account account, string accountLabel) { if (account == null) throw new ArgumentNullException(nameof(account)); _account = account; _editingCharacters = CloneCharacterList(account.Characters); InitializeComponent(); Text = "Characters — " + (accountLabel ?? account.Name ?? "Account"); BuildGrid(); } static List CloneCharacterList(IEnumerable src) { var list = new List(); if (src == null) return list; foreach (var c in src) { if (c == null) continue; list.Add(CloneCharacter(c)); } return list; } static D2Character CloneCharacter(D2Character c) { var missions = c.Missions == null ? new List() : c.Missions.Where(m => m != null).Select(m => new D2Mission { Level = m.Level, A1_Enpowered = m.A1_Enpowered, A5_Socket = m.A5_Socket, }).ToList(); return new D2Character { Name = c.Name, Class = c.Class, Missions = missions }; } void BuildGrid() { var wrapAccount = new D2Account { Characters = _editingCharacters }; _rows = new BindingList(CharacterMissionGridRow.FromAccount(wrapAccount)); _source = new BindingSource { DataSource = _rows }; dgvCharacters.AutoGenerateColumns = false; dgvCharacters.Columns.Clear(); void AddText(string prop, string header, int width) { dgvCharacters.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = prop, HeaderText = header, Width = width, }); } void AddBool(string prop, string header, string tip, int width) { var col = new DataGridViewCheckBoxColumn { DataPropertyName = prop, HeaderText = header, Width = width, ThreeState = false, }; col.ToolTipText = tip; dgvCharacters.Columns.Add(col); } AddText(nameof(CharacterMissionGridRow.Name), "Character", 140); AddText(nameof(CharacterMissionGridRow.Class), "Class", 100); const string tipDone = "Checked = reward taken (no longer available). Unchecked = still available."; AddBool(nameof(CharacterMissionGridRow.NormalA1), "Normal A1", tipDone, 85); AddBool(nameof(CharacterMissionGridRow.NormalA5), "Normal A5", tipDone, 85); AddBool(nameof(CharacterMissionGridRow.NightmareA1), "NM A1", tipDone, 70); AddBool(nameof(CharacterMissionGridRow.NightmareA5), "NM A5", tipDone, 70); AddBool(nameof(CharacterMissionGridRow.HellA1), "Hell A1", tipDone, 75); AddBool(nameof(CharacterMissionGridRow.HellA5), "Hell A5", tipDone, 75); dgvCharacters.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = nameof(CharacterMissionGridRow.MissionsAvailable), HeaderText = "Available", ReadOnly = true, Width = 80, ToolTipText = "Count of rewards not yet claimed (A1 + A5 across all difficulties).", }); dgvCharacters.DataSource = _source; } void BtnAddCharacter_Click(object sender, EventArgs e) { var c = D2CharacterMissionHelper.CreateNewCharacter(); _editingCharacters.Add(c); _rows.Add(new CharacterMissionGridRow(c)); if (_rows.Count > 0) dgvCharacters.CurrentCell = dgvCharacters.Rows[_rows.Count - 1].Cells[0]; } void BtnRemoveCharacter_Click(object sender, EventArgs e) { if (_source.Current is CharacterMissionGridRow row) { _editingCharacters.Remove(row.Character); _rows.Remove(row); } } protected override void OnFormClosing(FormClosingEventArgs e) { if (DialogResult == DialogResult.OK) { dgvCharacters.EndEdit(); _source.EndEdit(); if (_editingCharacters.Any(c => string.IsNullOrWhiteSpace(c?.Name))) { e.Cancel = true; MessageBox.Show(this, "Each character needs a name.", Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } _account.Characters = _editingCharacters; } base.OnFormClosing(e); } } }