Ewl » Canonical The canonical version of EWL Read More
Clone URL:  
Pushed to 2 repositories · View In Graph Contained in tip and canonical

Restored LegacyRadioButtonGroup and LegacyFreeFormRadioList.

Changeset 8504078977de

Parent 34407856c3a8

by Profile picture of William GrossWilliam Gross

Changes to 4 files · Browse files at 8504078977de Showing diff from parent 34407856c3a8 Diff from another changeset...

 
57
58
59
60
 
61
62
63
 
123
124
125
126
127
 
 
128
129
130
 
57
58
59
 
60
61
62
63
 
123
124
125
 
 
126
127
128
129
130
@@ -57,7 +57,7 @@
  b.AddFormItems( Email.ToEmailAddressControl( false, value: user != null ? user.Email : "" ).ToFormItem( label: "Email address".ToComponents() ) );     if( includePasswordControls() ) { - var group = new RadioButtonGroup( false ); + var group = new LegacyRadioButtonGroup( false );     var keepPassword = FormItem.Create(   "", @@ -123,8 +123,8 @@
  FormItem.Create(   "Role",   SelectList.CreateDropDown( from i in availableRoles select SelectListItem.Create( i.RoleId as int?, i.Name ), user?.Role.RoleId ), - validationGetter: control => - new EwfValidation( ( pbv, validator ) => RoleId.Value = control.ValidateAndGetSelectedItemIdInPostBack( pbv, validator ) ?? default( int ) ) ) ); + validationGetter: control => new EwfValidation( + ( pbv, validator ) => RoleId.Value = control.ValidateAndGetSelectedItemIdInPostBack( pbv, validator ) ?? default( int ) ) ) );     Controls.Add( b );   }
 
1
2
 
3
 
 
 
4
5
6
 
150
151
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
 
 
1
2
3
4
5
6
7
8
9
10
 
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
 
@@ -1,6 +1,10 @@
 using System;  using System.Collections.Generic; +using System.Collections.Immutable;  using System.Linq; +using System.Web.UI; +using System.Web.UI.WebControls; +using EnterpriseWebLibrary.EnterpriseWebFramework.DisplayLinking;  using EnterpriseWebLibrary.InputValidation;  using Humanizer;  using MoreLinq; @@ -150,4 +154,181 @@
    public EwfValidation Validation => validation;   } + + [ Obsolete( "Guaranteed through 28 Feb 2019. Use FreeFormRadioList instead." ) ] + public static class LegacyFreeFormRadioList { + /// <summary> + /// Creates a free-form radio button list. + /// </summary> + /// <param name="allowNoSelection">Pass true to cause a selected item ID with the default value (or the empty string when the item ID type is string) to + /// represent the state in which none of the radio buttons are selected. Note that this is not recommended by the Nielsen Norman Group; see + /// http://www.nngroup.com/articles/checkboxes-vs-radio-buttons/ for more information.</param> + /// <param name="selectedItemId"></param> + /// <param name="disableSingleButtonDetection">Pass true to allow just a single radio button to be displayed for this list. Use with caution, as this + /// violates the HTML specification.</param> + /// <param name="itemIdPageModificationValue"></param> + /// <param name="itemMatchPageModificationSetups"></param> + public static LegacyFreeFormRadioList<ItemIdType> Create<ItemIdType>( + bool allowNoSelection, ItemIdType selectedItemId, bool disableSingleButtonDetection = false, + PageModificationValue<ItemIdType> itemIdPageModificationValue = null, + IEnumerable<ListItemMatchPageModificationSetup<ItemIdType>> itemMatchPageModificationSetups = null ) { + return new LegacyFreeFormRadioList<ItemIdType>( + allowNoSelection, + disableSingleButtonDetection, + selectedItemId, + itemIdPageModificationValue, + itemMatchPageModificationSetups ); + } + } + + /// <summary> + /// A radio button list that allows you to arrange the buttons on the page however you wish. If you want access to the individual selection state of each + /// radio button and do not need the concept of a selected item ID for the group, use RadioButtonGroup instead. + /// </summary> + public class LegacyFreeFormRadioList<ItemIdType>: DisplayLink { + private readonly bool allowNoSelection; + private readonly FormValue<CommonCheckBox> formValue; + private readonly List<Action<PostBackValueDictionary>> displayLinkingSetInitialDisplayMethods = new List<Action<PostBackValueDictionary>>(); + private readonly List<Action> displayLinkingAddJavaScriptMethods = new List<Action>(); + private readonly List<Tuple<ItemIdType, CommonCheckBox>> itemIdsAndCheckBoxes = new List<Tuple<ItemIdType, CommonCheckBox>>(); + + internal LegacyFreeFormRadioList( + bool allowNoSelection, bool disableSingleButtonDetection, ItemIdType selectedItemId, PageModificationValue<ItemIdType> itemIdPageModificationValue, + IEnumerable<ListItemMatchPageModificationSetup<ItemIdType>> itemMatchPageModificationSetups ) { + itemMatchPageModificationSetups = itemMatchPageModificationSetups ?? ImmutableArray<ListItemMatchPageModificationSetup<ItemIdType>>.Empty; + + this.allowNoSelection = allowNoSelection; + formValue = LegacyRadioButtonGroup.GetFormValue( + allowNoSelection, + () => from i in itemIdsAndCheckBoxes select i.Item2, + () => from i in itemIdsAndCheckBoxes where EwlStatics.AreEqual( i.Item1, selectedItemId ) select i.Item2, + v => getStringId( v != null ? itemIdsAndCheckBoxes.Single( i => i.Item2 == v ).Item1 : getNoSelectionItemId() ), + rawValue => from itemIdAndCheckBox in itemIdsAndCheckBoxes + let control = (Control)itemIdAndCheckBox.Item2 + where control.IsOnPage() && getStringId( itemIdAndCheckBox.Item1 ) == rawValue + select itemIdAndCheckBox.Item2 ); + + EwfPage.Instance.AddControlTreeValidation( + () => LegacyRadioButtonGroup.ValidateControls( + allowNoSelection, + EwlStatics.AreEqual( getNoSelectionItemId(), selectedItemId ), + itemIdsAndCheckBoxes.Select( i => i.Item2 ), + disableSingleButtonDetection ) ); + + if( itemIdPageModificationValue != null ) { + formValue.AddPageModificationValue( itemIdPageModificationValue, getItemIdFromCheckBox ); + displayLinkingAddJavaScriptMethods.Add( + () => { + foreach( var pair in itemIdsAndCheckBoxes ) + pair.Item2.AddOnClickJsMethod( + itemIdPageModificationValue.GetJsModificationStatements( "'{0}'".FormatWith( pair.Item1.ObjectToString( true ) ) ) ); + } ); + } + foreach( var setup in itemMatchPageModificationSetups ) { + formValue.AddPageModificationValue( setup.PageModificationValue, checkBox => setup.ItemIds.Contains( getItemIdFromCheckBox( checkBox ) ) ); + displayLinkingAddJavaScriptMethods.Add( + () => { + foreach( var pair in itemIdsAndCheckBoxes ) + pair.Item2.AddOnClickJsMethod( + setup.PageModificationValue.GetJsModificationStatements( setup.ItemIds.Contains( pair.Item1 ) ? "true" : "false" ) ); + } ); + } + + EwfPage.Instance.AddDisplayLink( this ); + } + + public void AddDisplayLink( IEnumerable<ItemIdType> itemIds, bool controlsVisibleOnMatch, IEnumerable<WebControl> controls ) { + itemIds = itemIds.ToArray(); + controls = controls.ToArray(); + displayLinkingSetInitialDisplayMethods.Add( + formControlValues => { + var match = itemIds.Contains( GetSelectedItemIdInPostBack( formControlValues ) ); + var visible = ( controlsVisibleOnMatch && match ) || ( !controlsVisibleOnMatch && !match ); + foreach( var i in controls ) + DisplayLinkingOps.SetControlDisplay( i, visible ); + } ); + displayLinkingAddJavaScriptMethods.Add( + () => { + foreach( var pair in itemIdsAndCheckBoxes ) { + DisplayLinkingOps.AddDisplayJavaScriptToCheckBox( + pair.Item2, + itemIds.Contains( pair.Item1 ) ? controlsVisibleOnMatch : !controlsVisibleOnMatch, + controls.ToArray() ); + } + } ); + } + + /// <summary> + /// Creates an in-line radio button that is part of the list. + /// </summary> + public EwfCheckBox CreateInlineRadioButton( ItemIdType listItemId, string label = "", FormAction action = null, bool autoPostBack = false ) { + validateListItem( listItemId ); + var checkBox = + new EwfCheckBox( formValue, label, action, () => ImmutableArray<string>.Empty, listItemId: getStringId( listItemId ) ) { AutoPostBack = autoPostBack }; + itemIdsAndCheckBoxes.Add( Tuple.Create<ItemIdType, CommonCheckBox>( listItemId, checkBox ) ); + return checkBox; + } + + /// <summary> + /// Creates a block-level radio button that is part of the list. + /// </summary> + public BlockCheckBox CreateBlockRadioButton( + ItemIdType listItemId, string label = "", FormAction action = null, bool autoPostBack = false, + Func<IEnumerable<Control>> nestedControlListGetter = null ) { + validateListItem( listItemId ); + var checkBox = new BlockCheckBox( + formValue, + new BlockCheckBoxSetup( action: action, triggersActionWhenCheckedOrUnchecked: autoPostBack, nestedControlListGetter: nestedControlListGetter ), + label.ToComponents(), + () => ImmutableArray<string>.Empty, + null, + listItemId: getStringId( listItemId ) ); + itemIdsAndCheckBoxes.Add( Tuple.Create<ItemIdType, CommonCheckBox>( listItemId, checkBox ) ); + return checkBox; + } + + private void validateListItem( ItemIdType listItemId ) { + if( allowNoSelection && EwlStatics.AreEqual( listItemId, getNoSelectionItemId() ) ) + throw new ApplicationException( "You cannot create a radio button with the ID that represents no selection." ); + if( itemIdsAndCheckBoxes.Any( i => getStringId( i.Item1 ) == getStringId( listItemId ) ) ) + throw new ApplicationException( "Item IDs, when converted to strings, must be unique." ); + } + + private string getStringId( ItemIdType id ) { + return id.ObjectToString( true ); + } + + void DisplayLink.SetInitialDisplay( PostBackValueDictionary formControlValues ) { + foreach( var i in displayLinkingSetInitialDisplayMethods ) + i( formControlValues ); + } + + void DisplayLink.AddJavaScript() { + foreach( var i in displayLinkingAddJavaScriptMethods ) + i(); + } + + /// <summary> + /// Gets the selected item ID in the post back. + /// </summary> + public ItemIdType GetSelectedItemIdInPostBack( PostBackValueDictionary postBackValues ) { + return getItemIdFromCheckBox( formValue.GetValue( postBackValues ) ); + } + + private ItemIdType getItemIdFromCheckBox( CommonCheckBox checkBox ) { + var pair = itemIdsAndCheckBoxes.SingleOrDefault( i => i.Item2 == checkBox ); + return pair != null ? pair.Item1 : getNoSelectionItemId(); + } + + private ItemIdType getNoSelectionItemId() { + return EwlStatics.GetDefaultValue<ItemIdType>( true ); + } + + /// <summary> + /// Returns true if the selection changed on this post back. + /// </summary> + public bool SelectionChangedOnPostBack( PostBackValueDictionary postBackValues ) { + return formValue.ValueChangedOnPostBack( postBackValues ); + } + }  } \ No newline at end of file
 
162
163
164
165
 
166
167
168
 
227
228
229
230
 
231
232
233
 
162
163
164
 
165
166
167
168
 
227
228
229
 
230
231
232
233
@@ -162,7 +162,7 @@
  private readonly List<Tuple<IEnumerable<ItemIdType>, bool, IEnumerable<System.Web.UI.WebControls.WebControl>>> displayLinks =   new List<Tuple<IEnumerable<ItemIdType>, bool, IEnumerable<System.Web.UI.WebControls.WebControl>>>();   - private FreeFormRadioList<ItemIdType> radioList; + private LegacyFreeFormRadioList<ItemIdType> radioList;   private EwfCheckBox firstRadioButton;   private FormValue<ItemIdType> formValue;   private System.Web.UI.WebControls.WebControl selectControl; @@ -227,7 +227,7 @@
    void ControlTreeDataLoader.LoadData() {   if( useHorizontalRadioLayout.HasValue ) { - radioList = FreeFormRadioList.Create( + radioList = LegacyFreeFormRadioList.Create(   items.Any( i => !i.IsValid ),   selectedItemId,   disableSingleButtonDetection: disableSingleRadioButtonDetection.Value,
 
1
2
3
 
4
5
6
 
133
134
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
 
 
1
2
3
4
5
6
7
 
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
 
@@ -1,6 +1,7 @@
 using System;  using System.Collections.Generic;  using System.Linq; +using System.Web.UI;  using EnterpriseWebLibrary.InputValidation;    namespace EnterpriseWebLibrary.EnterpriseWebFramework { @@ -133,4 +134,134 @@
  return new FlowCheckbox( setup, CreateRadioButton( value, label, setup: setup.RadioButtonSetup, validationMethod: validationMethod ) );   }   } + + [ Obsolete( "Guaranteed through 28 Feb 2019. Use RadioButtonGroup instead." ) ] + public class LegacyRadioButtonGroup { + internal static FormValue<CommonCheckBox> GetFormValue( + bool allowsNoSelection, Func<IEnumerable<CommonCheckBox>> allCheckBoxesGetter, Func<IEnumerable<CommonCheckBox>> checkedCheckBoxesGetter, + Func<CommonCheckBox, string> stringValueSelector, Func<string, IEnumerable<CommonCheckBox>> checkedCheckBoxesInPostBackGetter ) { + return new FormValue<CommonCheckBox>( + () => checkedCheckBoxesGetter().FirstOrDefault(), + () => { + var firstCheckBoxOnPage = allCheckBoxesGetter().Select( i => (Control)i ).FirstOrDefault( i => i.IsOnPage() ); + return firstCheckBoxOnPage != null ? firstCheckBoxOnPage.UniqueID : ""; + }, + stringValueSelector, + rawValue => { + if( rawValue != null ) { + var selectedButton = checkedCheckBoxesInPostBackGetter( rawValue ).SingleOrDefault(); + return selectedButton != null + ? PostBackValueValidationResult<CommonCheckBox>.CreateValid( selectedButton ) + : PostBackValueValidationResult<CommonCheckBox>.CreateInvalid(); + } + return allowsNoSelection + ? PostBackValueValidationResult<CommonCheckBox>.CreateValid( null ) + : PostBackValueValidationResult<CommonCheckBox>.CreateInvalid(); + } ); + } + + internal static void ValidateControls( + bool allowsNoSelection, bool inNoSelectionState, IEnumerable<CommonCheckBox> checkBoxes, bool disableSingleButtonDetection ) { + Control selectedButton = null; + if( !allowsNoSelection || !inNoSelectionState ) { + var selectedButtons = checkBoxes.Where( i => i.IsChecked ).ToArray(); + if( selectedButtons.Count() != 1 ) + throw new ApplicationException( "If a radio button group is not in the no-selection state, then exactly one radio button must be selected." ); + selectedButton = selectedButtons.Single() as Control; + } + + var checkBoxesOnPage = checkBoxes.Where( i => ( i as Control ).IsOnPage() ).ToArray(); + if( checkBoxesOnPage.Any() ) { + if( selectedButton != null && !selectedButton.IsOnPage() ) + throw new ApplicationException( "The selected radio button must be on the page." ); + if( !disableSingleButtonDetection && checkBoxesOnPage.Count() < 2 ) { + const string link = "http://developers.whatwg.org/states-of-the-type-attribute.html#radio-button-state-%28type=radio%29"; + throw new ApplicationException( "A radio button group must contain more than one element; see " + link + "." ); + } + } + } + + private readonly FormValue<CommonCheckBox> formValue; + + private readonly List<Tuple<CommonCheckBox, bool, PageModificationValue<bool>>> checkBoxesAndSelectionStatesAndPageModificationValues = + new List<Tuple<CommonCheckBox, bool, PageModificationValue<bool>>>(); + + /// <summary> + /// Creates a radio button group. + /// </summary> + /// <param name="allowNoSelection">Pass true to allow the state in which none of the radio buttons are selected. Note that this is not recommended by the + /// Nielsen Norman Group; see http://www.nngroup.com/articles/checkboxes-vs-radio-buttons/ for more information.</param> + /// <param name="disableSingleButtonDetection">Pass true to allow just a single radio button to be displayed for this group. Use with caution, as this + /// violates the HTML specification.</param> + public LegacyRadioButtonGroup( bool allowNoSelection, bool disableSingleButtonDetection = false ) { + formValue = GetFormValue( + allowNoSelection, + () => from i in checkBoxesAndSelectionStatesAndPageModificationValues select i.Item1, + () => from i in checkBoxesAndSelectionStatesAndPageModificationValues where i.Item2 select i.Item1, + v => v != null ? ( (Control)v ).UniqueID : "", + rawValue => from checkBoxAndSelectionState in checkBoxesAndSelectionStatesAndPageModificationValues + let control = (Control)checkBoxAndSelectionState.Item1 + where control.IsOnPage() && control.UniqueID == rawValue + select checkBoxAndSelectionState.Item1 ); + + EwfPage.Instance.AddControlTreeValidation( + () => ValidateControls( + allowNoSelection, + checkBoxesAndSelectionStatesAndPageModificationValues.All( i => !i.Item2 ), + checkBoxesAndSelectionStatesAndPageModificationValues.Select( i => i.Item1 ), + disableSingleButtonDetection ) ); + } + + /// <summary> + /// Creates an in-line radio button that is part of the group. + /// </summary> + public EwfCheckBox CreateInlineRadioButton( + bool isSelected, string label = "", FormAction action = null, bool autoPostBack = false, PageModificationValue<bool> pageModificationValue = null ) { + EwfCheckBox checkBox = null; + checkBox = new EwfCheckBox( + formValue, + label, + action, + () => checkBoxesAndSelectionStatesAndPageModificationValues.Where( i => i.Item3 != null ) + .Select( i => i.Item3.GetJsModificationStatements( i.Item1 == checkBox ? "true" : "false" ) ) ) { AutoPostBack = autoPostBack }; + checkBoxesAndSelectionStatesAndPageModificationValues.Add( + Tuple.Create<CommonCheckBox, bool, PageModificationValue<bool>>( checkBox, isSelected, pageModificationValue ) ); + + if( pageModificationValue != null ) + formValue.AddPageModificationValue( pageModificationValue, value => value == checkBox ); + + return checkBox; + } + + /// <summary> + /// Creates a block-level radio button that is part of the group. + /// </summary> + /// <param name="isSelected"></param> + /// <param name="label"></param> + /// <param name="action"></param> + /// <param name="autoPostBack"></param> + /// <param name="pageModificationValue"></param> + /// <param name="validationMethod">The validation method. Pass null if you’re only using this control for page modification.</param> + /// <param name="nestedControlListGetter"></param> + /// <returns></returns> + public BlockCheckBox CreateBlockRadioButton( + bool isSelected, string label = "", FormAction action = null, bool autoPostBack = false, PageModificationValue<bool> pageModificationValue = null, + Action<PostBackValue<bool>, Validator> validationMethod = null, Func<IEnumerable<Control>> nestedControlListGetter = null ) { + BlockCheckBox checkBox = null; + checkBox = new BlockCheckBox( + formValue, + new BlockCheckBoxSetup( action: action, triggersActionWhenCheckedOrUnchecked: autoPostBack, nestedControlListGetter: nestedControlListGetter ), + label.ToComponents(), + () => checkBoxesAndSelectionStatesAndPageModificationValues.Where( i => i.Item3 != null ) + .Select( i => i.Item3.GetJsModificationStatements( i.Item1 == checkBox ? "true" : "false" ) ), + validationMethod ); + checkBoxesAndSelectionStatesAndPageModificationValues.Add( + Tuple.Create<CommonCheckBox, bool, PageModificationValue<bool>>( checkBox, isSelected, pageModificationValue ) ); + + if( pageModificationValue != null ) + formValue.AddPageModificationValue( pageModificationValue, value => value == checkBox ); + + return checkBox; + } + }  } \ No newline at end of file