Skip to content

Commit 13b19bd

Browse files
authored
Merge pull request #1244 from sillsdev/sp-2297
SP-2297: Prevent changing row in ContributorsListControl if not in a valid state to commit edit
2 parents 47fdb17 + 11c2ce5 commit 13b19bd

File tree

3 files changed

+84
-75
lines changed

3 files changed

+84
-75
lines changed

Palaso.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<s:Boolean x:Key="/Default/UserDictionary/Words/=parsable/@EntryIndexedValue">True</s:Boolean>
6565
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pashto/@EntryIndexedValue">True</s:Boolean>
6666
<s:Boolean x:Key="/Default/UserDictionary/Words/=Qaaa/@EntryIndexedValue">True</s:Boolean>
67+
<s:Boolean x:Key="/Default/UserDictionary/Words/=reentrant/@EntryIndexedValue">True</s:Boolean>
6768
<s:Boolean x:Key="/Default/UserDictionary/Words/=Saami/@EntryIndexedValue">True</s:Boolean>
6869
<s:Boolean x:Key="/Default/UserDictionary/Words/=SLDR/@EntryIndexedValue">True</s:Boolean>
6970
<s:Boolean x:Key="/Default/UserDictionary/Words/=subitem/@EntryIndexedValue">True</s:Boolean>

SIL.Windows.Forms/ClearShare/WinFormsUI/ContributorsListControl.cs

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Media;
88
using System.Windows.Forms;
9+
using JetBrains.Annotations;
910
using L10NSharp.UI;
1011
using SIL.Code;
1112
using SIL.Windows.Forms.Widgets.BetterGrid;
@@ -54,8 +55,6 @@ private void Initialize()
5455
{
5556
_grid.Font = SystemFonts.MenuFont;
5657

57-
// TODO: Localize column headings
58-
5958
DataGridViewColumn col = BetterGrid.CreateTextBoxColumn("name", "Name");
6059
col.Width = 150;
6160
_grid.Columns.Add(col);
@@ -74,7 +73,7 @@ private void Initialize()
7473

7574
_grid.AddRemoveRowColumn(null, null,
7675
null /* TODO: Enhance BetterGrid to be able to show tool tips in non-virtual mode */,
77-
rowIndex => DeleteRow(rowIndex));
76+
DeleteRow);
7877

7978
_grid.AllowUserToAddRows = true;
8079
_grid.AllowUserToDeleteRows = true;
@@ -88,15 +87,13 @@ private void Initialize()
8887
_grid.RowsRemoved += HandleGridRowsRemoved;
8988
_grid.ColumnHeaderMouseClick += _grid_ColumnHeaderMouseClick;
9089

91-
if (_model.ContributorsGridSettings != null)
92-
_model.ContributorsGridSettings.InitializeGrid(_grid);
90+
_model.ContributorsGridSettings?.InitializeGrid(_grid);
9391
}
9492

9593
// SP-874: Not able to open L10NSharp with Alt-Shift-click
96-
void _grid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
94+
private void _grid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
9795
{
98-
if (ColumnHeaderMouseClick != null)
99-
ColumnHeaderMouseClick(sender, e);
96+
ColumnHeaderMouseClick?.Invoke(sender, e);
10097
}
10198

10299
/// ------------------------------------------------------------------------------------
@@ -107,28 +104,20 @@ void _grid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs
107104
/// problem.
108105
/// </summary>
109106
/// ------------------------------------------------------------------------------------
110-
private new bool DesignMode
111-
{
112-
get
113-
{
114-
return (base.DesignMode || GetService(typeof(IDesignerHost)) != null) ||
115-
(LicenseManager.UsageMode == LicenseUsageMode.Designtime);
116-
}
117-
}
107+
private new bool DesignMode => base.DesignMode ||
108+
GetService(typeof(IDesignerHost)) != null ||
109+
LicenseManager.UsageMode == LicenseUsageMode.Designtime;
118110

119111
/// ------------------------------------------------------------------------------------
120-
public bool InEditMode
121-
{
122-
get { return _grid.IsCurrentRowDirty; }
123-
}
112+
[PublicAPI]
113+
public bool InEditMode => _grid.IsCurrentRowDirty;
124114

125115
/// ------------------------------------------------------------------------------------
126-
public bool InNewContributionRow
127-
{
128-
get { return (_grid.CurrentCellAddress.Y == _grid.NewRowIndex); }
129-
}
116+
[PublicAPI]
117+
public bool InNewContributionRow => _grid.CurrentCellAddress.Y == _grid.NewRowIndex;
130118

131119
/// ------------------------------------------------------------------------------------
120+
[PublicAPI]
132121
public Contribution GetCurrentContribution()
133122
{
134123
return GetContributionFromRow(_grid.CurrentCellAddress.Y);
@@ -174,14 +163,32 @@ void HandleGridMouseClick(object sender, MouseEventArgs e)
174163
return;
175164
}
176165

166+
void SelectFirstCellInClickedRow() => _grid.CurrentCell = _grid[0, hi.RowIndex];
167+
177168
// At this point we know the user clicked on a row heading. Now we
178169
// need to make sure the row they're leaving is in a valid state.
179170
if (ValidatingContributor != null && _grid.CurrentCellAddress.Y >= 0 &&
180171
_grid.CurrentCellAddress.Y < _grid.RowCount - 1)
181172
{
182-
if (_grid.CurrentCellAddress.Y == _model.Contributions.Count())
173+
if (_grid.CurrentCellAddress.Y == _model.Contributions.Count)
183174
return;
184175

176+
if (_grid.IsDirty)
177+
{
178+
try
179+
{
180+
// This actually forces the commit/validation and will fail if the
181+
// current edit has the contribution in a bogus state.
182+
SelectFirstCellInClickedRow();
183+
return;
184+
}
185+
catch
186+
{
187+
SystemSounds.Beep.Play();
188+
return;
189+
}
190+
}
191+
185192
var contribution = _model.Contributions.ElementAt(_grid.CurrentCellAddress.Y);
186193
if (!GetIsValidContribution(contribution))
187194
{
@@ -190,8 +197,7 @@ void HandleGridMouseClick(object sender, MouseEventArgs e)
190197
}
191198
}
192199

193-
// Make the first cell current in the row the user clicked.
194-
_grid.CurrentCell = _grid[0, hi.RowIndex];
200+
SelectFirstCellInClickedRow();
195201
}
196202

197203
/// ------------------------------------------------------------------------------------
@@ -232,8 +238,7 @@ private void HandleGridRowValidating(object sender, DataGridViewCellCancelEventA
232238

233239
if (!string.IsNullOrEmpty(kvp.Key))
234240
{
235-
if (_msgWindow == null)
236-
_msgWindow = new FadingMessageWindow();
241+
_msgWindow ??= new FadingMessageWindow();
237242

238243
var dataGridViewColumn = _grid.Columns[kvp.Key];
239244
if (dataGridViewColumn != null)
@@ -324,42 +329,40 @@ protected void HandleEditingControlShowing(object sender, DataGridViewEditingCon
324329
/// ------------------------------------------------------------------------------------
325330
void HandleRoleValueChanged(object sender, EventArgs e)
326331
{
327-
if (_msgWindow != null)
328-
_msgWindow.Close();
332+
_msgWindow?.Close();
329333
}
330334

331335
/// ------------------------------------------------------------------------------------
332336
void HandleGridCellEndEdit(object sender, DataGridViewCellEventArgs e)
333337
{
334338
var ctrl = _grid.Tag as Control;
335339

340+
var txtBox = ctrl as TextBox;
336341
// SP-793: Text should match case of autocomplete list
337-
if (e.ColumnIndex == 0)
342+
if (e.ColumnIndex == 0 && txtBox != null)
338343
{
339-
var txtBox = ctrl as TextBox;
340-
if (txtBox != null)
341-
{
342-
// is the current text an exact match for the autocomplete list?
343-
var list = txtBox.AutoCompleteCustomSource.Cast<object>().ToList();
344-
var found = list.FirstOrDefault(item => String.Equals(item.ToString(), txtBox.Text, StringComparison.CurrentCulture));
344+
// is the current text an exact match for the autocomplete list?
345+
var list = txtBox.AutoCompleteCustomSource.Cast<object>().ToList();
346+
var found = list.FirstOrDefault(item =>
347+
String.Equals(item.ToString(), txtBox.Text, StringComparison.CurrentCulture));
345348

346-
if (found == null)
349+
if (found == null)
350+
{
351+
// is the current text a match except for case for the autocomplete list?
352+
found = list.FirstOrDefault(item => String.Equals(item.ToString(),
353+
txtBox.Text, StringComparison.CurrentCultureIgnoreCase));
354+
if (found != null)
347355
{
348-
// is the current text a match except for case for the autocomplete list?
349-
found = list.FirstOrDefault(item => String.Equals(item.ToString(), txtBox.Text, StringComparison.CurrentCultureIgnoreCase));
350-
if (found != null)
351-
{
352-
txtBox.Text = found.ToString();
353-
_grid.CurrentCell.Value = txtBox.Text;
354-
}
356+
txtBox.Text = found.ToString();
357+
_grid.CurrentCell.Value = txtBox.Text;
355358
}
356359
}
357360
}
358361

359-
if (ctrl is TextBox)
360-
ctrl.KeyPress -= HandleCellEditBoxKeyPress;
361-
else if (ctrl is ComboBox)
362-
((ComboBox)ctrl).SelectedIndexChanged -= HandleRoleValueChanged;
362+
if (txtBox != null)
363+
txtBox.KeyPress -= HandleCellEditBoxKeyPress;
364+
else if (ctrl is ComboBox box)
365+
box.SelectedIndexChanged -= HandleRoleValueChanged;
363366

364367
_grid.CellEndEdit -= HandleGridCellEndEdit;
365368
_grid.Tag = null;
@@ -399,15 +402,13 @@ public void SetColumnHeaderText(int columnIndex, string headerText)
399402

400403
/// <remarks>SP-874: Localize column headers</remarks>
401404
[CLSCompliant (false)]
405+
[PublicAPI]
402406
public void SetLocalizationExtender(L10NSharpExtender extender)
403407
{
404408
extender.SetLocalizingId(_grid, "ContributorsEditorGrid");
405409
}
406410

407411
/// <remarks>We need to be able to adjust the visual properties to match the hosting program</remarks>
408-
public BetterGrid Grid
409-
{
410-
get { return _grid; }
411-
}
412+
public BetterGrid Grid => _grid;
412413
}
413414
}

TestApps/SIL.Windows.Forms.TestApp/ContributorsForm.cs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace SIL.Windows.Forms.TestApp
1111
{
12-
public partial class ContributorsForm : Form
12+
public class ContributorsForm : Form
1313
{
1414
private TableLayoutPanel _tableLayout;
1515
private ContributorsListControl _contributorsControl;
@@ -36,32 +36,19 @@ public ContributorsForm()
3636
{
3737
var autoCompleter = new AutoCompleter();
3838
_model = new ContributorsListControlViewModel(autoCompleter, () => { });
39-
var dataGridView = new DataGridView();
40-
41-
_contributorsControl = new ContributorsListControl(_model);
42-
_contributorsControl.Dock = DockStyle.Fill;
43-
_contributorsControl.Location = new System.Drawing.Point(0, 0);
44-
_contributorsControl.Name = "_contributorsControl";
45-
46-
_contributorsControl.ValidatingContributor += HandleValidatingContributor;
47-
48-
// set the column header text
49-
string[] headerText =
50-
{
51-
"Name",
52-
"Role",
53-
"Date",
54-
"Comments"
55-
};
56-
57-
for (var i = 0; i < headerText.Length; i++)
58-
_contributorsControl.SetColumnHeaderText(i, headerText[i]);
5939

6040
InitializeComponent();
6141
autoCompleter.Source = _contributorNames;
6242

6343
var contribs = new ContributionCollection(new [] { new Contribution("Fred", new Role("a", "Author", "guy who writes stuff")) });
6444
_model.SetContributionList(contribs);
45+
46+
_contributorsControl.Grid.Columns["name"].AutoSizeMode =
47+
DataGridViewAutoSizeColumnMode.AllCells;
48+
_contributorsControl.Grid.Columns["role"].AutoSizeMode =
49+
DataGridViewAutoSizeColumnMode.AllCells;
50+
_contributorsControl.Grid.Columns["comments"].AutoSizeMode =
51+
DataGridViewAutoSizeColumnMode.Fill;
6552
}
6653

6754
private void InitializeComponent()
@@ -111,6 +98,26 @@ private void InitializeComponent()
11198
_contributorNames.Rows[1].Cells[0].Value = "Fred";
11299
_contributorNames.Rows[2].Cells[0].Value = "Tom";
113100

101+
_contributorsControl = new ContributorsListControl(_model)
102+
{
103+
Dock = DockStyle.Fill,
104+
Location = new System.Drawing.Point(0, 0),
105+
Name = "_contributorsControl"
106+
};
107+
_contributorsControl.ValidatingContributor += HandleValidatingContributor;
108+
109+
// set the column header text
110+
string[] headerText =
111+
{
112+
"Name",
113+
"Role",
114+
"Date",
115+
"Comments"
116+
};
117+
118+
for (var i = 0; i < headerText.Length; i++)
119+
_contributorsControl.SetColumnHeaderText(i, headerText[i]);
120+
114121
_tableLayout.Controls.Add(_contributorsControl, 0, 0);
115122
_tableLayout.SetColumnSpan(_contributorsControl, 2);
116123
_tableLayout.Controls.Add(_contributorNames, 0, 1);

0 commit comments

Comments
 (0)