File: FieldTemplateUserControl.cs

package info (click to toggle)
mono 6.12.0.199%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,296,836 kB
  • sloc: cs: 11,181,803; xml: 2,850,076; ansic: 699,709; cpp: 123,344; perl: 59,361; javascript: 30,841; asm: 21,853; makefile: 20,405; sh: 15,009; python: 4,839; pascal: 925; sql: 859; sed: 16; php: 1
file content (585 lines) | stat: -rw-r--r-- 24,687 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Web.DynamicData.Util;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace System.Web.DynamicData {

    /// <summary>
    /// The base class for all field template user controls
    /// </summary>
    public class FieldTemplateUserControl : UserControl, IBindableControl, IFieldTemplate {

        private static RequiredAttribute s_defaultRequiredAttribute = new RequiredAttribute();
        private Dictionary<Type, bool> _ignoredModelValidationAttributes;
        private object _fieldValue;
        private DefaultValueMapping _defaultValueMapping;
        private bool _pageDataItemSet;
        private object _pageDataItem;

        public FieldTemplateUserControl() {
        }

        internal FieldTemplateUserControl(DefaultValueMapping defaultValueMapping) {
            _defaultValueMapping = defaultValueMapping;
        }

        /// <summary>
        /// The host that provides context to this field template
        /// </summary>
        [Browsable(false)]
        public IFieldTemplateHost Host { get; private set; }

        /// <summary>
        /// The formatting options that need to be applied to this field template
        /// </summary>
        [Browsable(false)]
        public IFieldFormattingOptions FormattingOptions { get; private set; }

        /// <summary>
        /// The MetaColumn that this field template is working with
        /// </summary>
        [Browsable(false)]
        public MetaColumn Column {
            get {
                return Host.Column;
            }
        }

        /// <summary>
        /// The ContainerType in which this 
        /// </summary>
        [Browsable(false)]
        public virtual ContainerType ContainerType {
            get {
                return Misc.FindContainerType(this);
            }
        }

        /// <summary>
        /// The MetaTable that this field's column belongs to
        /// </summary>
        [Browsable(false)]
        public MetaTable Table {
            get {
                return Column.Table;
            }
        }

        /// <summary>
        /// Casts the MetaColumn to a MetaForeignKeyColumn. Throws if it is not an FK column.
        /// </summary>
        [Browsable(false)]
        public MetaForeignKeyColumn ForeignKeyColumn {
            get {
                var foreignKeyColumn = Column as MetaForeignKeyColumn;
                if (foreignKeyColumn == null) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                        DynamicDataResources.FieldTemplateUserControl_ColumnIsNotFK, Column.Name));
                }
                return foreignKeyColumn;
            }
        }

        /// <summary>
        /// Casts the MetaColumn to a MetaChildrenColumn. Throws if it is not an Children column.
        /// </summary>
        [Browsable(false)]
        public MetaChildrenColumn ChildrenColumn {
            get {
                var childrenColumn = Column as MetaChildrenColumn;
                if (childrenColumn == null) {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
                        DynamicDataResources.FieldTemplateUserControl_ColumnIsNotChildren, Column.Name));
                }
                return childrenColumn;
            }
        }

        /// <summary>
        /// The mode (readonly, edit, insert) that the field template should use
        /// </summary>
        [Browsable(false)]
        public DataBoundControlMode Mode {
            get {
                return Host.Mode;
            }
        }

        /// <summary>
        /// The collection of metadata attributes that apply to this column
        /// </summary>
        [Browsable(false)]
        public System.ComponentModel.AttributeCollection MetadataAttributes {
            get {
                return Column.Attributes;
            }
        }

        /// <summary>
        /// Returns the data control that handles the field inside the field template
        /// </summary>
        [Browsable(false)]
        public virtual Control DataControl {
            get {
                return null;
            }
        }

        /// <summary>
        /// The current data object. Equivalent to Page.GetDataItem()
        /// </summary>
        [Browsable(false)]
        public virtual object Row {
            get {
                // The DataItem is normally null in insert mode, we're going to surface the DictionaryCustomTypeDescriptor if there is a
                //  a default value was specified for this column.
                if (Mode == DataBoundControlMode.Insert && DefaultValueMapping != null && DefaultValueMapping.Contains(Column)) {
                    return DefaultValueMapping.Instance;
                }

                // Used for unit testing. We can't use null since thats a valid value.
                if (_pageDataItemSet) {
                    return _pageDataItem;
                }
                return Page.GetDataItem();
            }
            internal set {
                // Only set in unit tests.
                _pageDataItem = value;
                _pageDataItemSet = true;
            }
        }

        /// <summary>
        /// The value of the Column in the current Row
        /// </summary>
        [Browsable(false)]
        public virtual object FieldValue {
            get {
                // If a field value was explicitly set, use it instead of the usual logic.
                if (_fieldValue != null)
                    return _fieldValue;

                return GetColumnValue(Column);
            }

            set {
                _fieldValue = value;
            }
        }

        /// <summary>
        /// Get the value of a specific column in the current row
        /// </summary>
        /// <param name="column"></param>
        /// <returns></returns>
        protected virtual object GetColumnValue(MetaColumn column) {           
            object row = Row;
            if (row != null) {
                return DataBinder.GetPropertyValue(row, column.Name);
            }

            // Fallback on old behavior
            if (Mode == DataBoundControlMode.Insert) {
                return column.DefaultValue;
            }

            return null;
        }

        /// <summary>
        /// Return the field value as a formatted string
        /// </summary>
        [Browsable(false)]
        public virtual string FieldValueString {
            get {
                // Get the string and preprocess it
                return FormatFieldValue(FieldValue);
            }
        }

        /// <summary>
        /// Similar to FieldValueString, but the string is to be used when the field is in edit mode
        /// </summary>
        [Browsable(false)]
        public virtual string FieldValueEditString {
            get {
                return FormattingOptions.FormatEditValue(FieldValue);
            }
        }

        /// <summary>
        /// Only applies to FK columns. Returns a URL that links to the page that displays the details
        /// of the foreign key entity. e.g. In the Product table's Category column, this produces a link
        /// that goes to the details of the category that the product is in
        /// </summary>
        protected string ForeignKeyPath {
            get {
                return ForeignKeyColumn.GetForeignKeyPath(PageAction.Details, Row);
            }
        }

        internal DefaultValueMapping DefaultValueMapping {
            get {
                if (_defaultValueMapping == null) {
                    // Ensure this only gets accessed in insert mode
                    Debug.Assert(Mode == DataBoundControlMode.Insert);
                    _defaultValueMapping = MetaTableHelper.GetDefaultValueMapping(this, Context.ToWrapper());
                }
                return _defaultValueMapping;
            }
        }

        /// <summary>
        /// Same as ForeignKeyPath, except that it allows the path part of the URL to be overriden. This is
        /// used when using pages that don't live under DynamicData/CustomPages.
        /// </summary>
        /// <param name="path">The path override</param>
        /// <returns></returns>
        protected string BuildForeignKeyPath(string path) {
            // If a path was passed in, resolved it relative to the containing page
            if (!String.IsNullOrEmpty(path)) {
                path = ResolveParentRelativePath(path);
            }

            return ForeignKeyColumn.GetForeignKeyPath(PageAction.Details, Row, path);
        }

        /// <summary>
        /// Only applies to Children columns. Returns a URL that links to the page that displays the list
        /// of children entities. e.g. In the Category table's Products column, this produces a link
        /// that goes to the list of Products that are in this Category.
        /// </summary>
        protected string ChildrenPath {
            get {
                return ChildrenColumn.GetChildrenPath(PageAction.List, Row);
            }
        }

        /// <summary>
        /// Same as ChildrenPath, except that it allows the path part of the URL to be overriden. This is
        /// used when using pages that don't live under DynamicData/CustomPages.
        /// </summary>
        /// <param name="path">The path override</param>
        /// <returns></returns>
        protected string BuildChildrenPath(string path) {
            // If a path was passed in, resolved it relative to the containing page
            if (!String.IsNullOrEmpty(path)) {
                path = ResolveParentRelativePath(path);
            }

            return ChildrenColumn.GetChildrenPath(PageAction.List, Row, path);
        }

        // Resolve a relative path based on the containing page
        private string ResolveParentRelativePath(string path) {
            if (path == null || TemplateControl == null)
                return path;

            Control parentControl = TemplateControl.Parent;
            if (parentControl == null)
                return path;

            return parentControl.ResolveUrl(path);
        }

        /// <summary>
        /// Return the field template for another column
        /// </summary>
        protected FieldTemplateUserControl FindOtherFieldTemplate(string columnName) {
            return Parent.FindFieldTemplate(columnName) as FieldTemplateUserControl;
        }

        /// <summary>
        /// Only applies to FK columns. Populate the list control with all the values from the parent table 
        /// </summary>
        /// <param name="listControl">The control to be populated</param>
        protected void PopulateListControl(ListControl listControl) {
            Type enumType;
            if (Column is MetaForeignKeyColumn) {
                Misc.FillListItemCollection(ForeignKeyColumn.ParentTable, listControl.Items);
            } else if (Column.IsEnumType(out enumType)) {
                Debug.Assert(enumType != null);
                FillEnumListControl(listControl, enumType);
            }
        }

        private void FillEnumListControl(ListControl list, Type enumType) {
            foreach (DictionaryEntry entry in Misc.GetEnumNamesAndValues(enumType)) {
                list.Items.Add(new ListItem((string)entry.Key, (string)entry.Value));
            }
        }

        /// <summary>
        /// Gets a string representation of the column's value so that it can be matched with
        /// values populated in a dropdown. This currently works for FK and Enum columns only.
        /// The method returns null for other column types.
        /// </summary>
        /// <returns></returns>
        protected string GetSelectedValueString() {
            Type enumType;
            if (Column is MetaForeignKeyColumn) {
                return ForeignKeyColumn.GetForeignKeyString(Row);
            } else if(Column.IsEnumType(out enumType)) {
                return Misc.GetUnderlyingTypeValueString(enumType, FieldValue);
            }
            return null;
        }

        /// <summary>
        /// Only applies to FK columns. This is used when saving the value of a foreign key, typically selected
        /// from a drop down.
        /// </summary>
        /// <param name="dictionary">The dictionary that contains all the new values</param>
        /// <param name="selectedValue">The value to be saved. Typically, this comes from DropDownList.SelectedValue</param>
        protected virtual void ExtractForeignKey(IDictionary dictionary, string selectedValue) {
            ForeignKeyColumn.ExtractForeignKey(dictionary, selectedValue);
        }

        /// <summary>
        /// Apply potential HTML encoding and formatting to a string that needs to be displayed
        /// </summary>
        /// <param name="fieldValue">The value that should be formatted</param>
        /// <returns>the formatted value</returns>
        public virtual string FormatFieldValue(object fieldValue) {
            return FormattingOptions.FormatValue(fieldValue);
        }

        /// <summary>
        /// Return either the input value or null based on ConvertEmptyStringToNull and NullDisplayText
        /// </summary>
        /// <param name="value">The input value</param>
        /// <returns>The converted value</returns>
        protected virtual object ConvertEditedValue(string value) {
            return FormattingOptions.ConvertEditedValue(value);
        }

        /// <summary>
        /// Set up a validator for dynamic data use. It sets the ValidationGroup on all validators,
        /// and also performs additional logic for some specific validator types. e.g. for a RangeValidator
        /// it sets the range values if they exist on the model.
        /// </summary>
        /// <param name="validator">The validator to be set up</param>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly",
            Justification = "We really want Set Up as two words")]
        protected virtual void SetUpValidator(BaseValidator validator) {
            SetUpValidator(validator, Column);
        }

        /// <summary>
        /// Set up a validator for dynamic data use. It sets the ValidationGroup on all validators,
        /// and also performs additional logic for some specific validator types. e.g. for a RangeValidator
        /// it sets the range values if they exist on the model.
        /// </summary>
        /// <param name="validator">The validator to be set up</param>
        /// <param name="column">The column for which the validator is getting set</param>
        [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly",
            Justification = "We really want Set Up as two words")]
        protected virtual void SetUpValidator(BaseValidator validator, MetaColumn column) {

            // Set the validation group to match the dynamic control
            validator.ValidationGroup = Host.ValidationGroup;

            if (validator is DynamicValidator) {
                SetUpDynamicValidator((DynamicValidator)validator, column);
            }
            else if (validator is RequiredFieldValidator) {
                SetUpRequiredFieldValidator((RequiredFieldValidator)validator, column);
            }
            else if (validator is CompareValidator) {
                SetUpCompareValidator((CompareValidator)validator, column);
            }
            else if (validator is RangeValidator) {
                SetUpRangeValidator((RangeValidator)validator, column);
            }
            else if (validator is RegularExpressionValidator) {
                SetUpRegexValidator((RegularExpressionValidator)validator, column);
            }

            validator.ToolTip = validator.ErrorMessage;
            validator.Text = "*";
        }

        private void SetUpDynamicValidator(DynamicValidator validator, MetaColumn column) {
            validator.Column = column;

            // Tell the DynamicValidator which validation attributes it should ignore (because
            // they're already handled by server side ASP.NET validator controls)
            validator.SetIgnoredModelValidationAttributes(_ignoredModelValidationAttributes);
        }

        private void SetUpRequiredFieldValidator(RequiredFieldValidator validator, MetaColumn column) {
            var requiredAttribute = column.Metadata.RequiredAttribute;
            if (requiredAttribute!= null && requiredAttribute.AllowEmptyStrings) {
                // Dev10 Bug 749744
                // If somone explicitly set AllowEmptyStrings = true then we assume that they want to
                // allow empty strings to go into a database even if the column is marked as required.
                // Since ASP.NET validators always get an empty string, this essential turns of
                // required field validation.
                IgnoreModelValidationAttribute(typeof(RequiredAttribute));
            } else if (column.IsRequired) {
                validator.Enabled = true;

                // Make sure the attribute doesn't get validated a second time by the DynamicValidator
                IgnoreModelValidationAttribute(typeof(RequiredAttribute));

                if (String.IsNullOrEmpty(validator.ErrorMessage)) {
                    string columnErrorMessage = column.RequiredErrorMessage;
                    if (String.IsNullOrEmpty(columnErrorMessage)) {
                        // generate default error message
                        validator.ErrorMessage = HttpUtility.HtmlEncode(s_defaultRequiredAttribute.FormatErrorMessage(column.DisplayName));
                    } else {
                        validator.ErrorMessage = HttpUtility.HtmlEncode(columnErrorMessage);
                    }
                }
            }
        }

        private void SetUpCompareValidator(CompareValidator validator, MetaColumn column) {
            validator.Operator = ValidationCompareOperator.DataTypeCheck;

            ValidationDataType? dataType = null;
            string errorMessage = null;
            if (column.ColumnType == typeof(DateTime)) {
                dataType = ValidationDataType.Date;
                errorMessage = String.Format(CultureInfo.CurrentCulture,
                    DynamicDataResources.FieldTemplateUserControl_CompareValidationError_Date,
                    column.DisplayName);
            } else if (column.IsInteger && column.ColumnType != typeof(long)) {
                // long is unsupported because it's larger than int
                dataType = ValidationDataType.Integer;
                errorMessage = String.Format(CultureInfo.CurrentCulture,
                    DynamicDataResources.FieldTemplateUserControl_CompareValidationError_Integer,
                    column.DisplayName);
            } else if (column.ColumnType == typeof(decimal)) {
                // 

                dataType = ValidationDataType.Double;
                errorMessage = String.Format(CultureInfo.CurrentCulture,
                    DynamicDataResources.FieldTemplateUserControl_CompareValidationError_Decimal,
                    column.DisplayName);
            } else if (column.IsFloatingPoint) {
                dataType = ValidationDataType.Double;
                errorMessage = String.Format(CultureInfo.CurrentCulture,
                    DynamicDataResources.FieldTemplateUserControl_CompareValidationError_Decimal,
                    column.DisplayName);
            }

            if (dataType != null) {
                Debug.Assert(errorMessage != null);
                validator.Enabled = true;
                validator.Type = dataType.Value;
                if (String.IsNullOrEmpty(validator.ErrorMessage)) {
                    validator.ErrorMessage = HttpUtility.HtmlEncode(errorMessage);
                }
            } else {
                // If we don't recognize the type, turn off the validator
                validator.Enabled = false;
            }
        }

        private void SetUpRangeValidator(RangeValidator validator, MetaColumn column) {
            // Nothing to do if no range was specified
            var rangeAttribute = column.Attributes.OfType<RangeAttribute>().FirstOrDefault();
            if (rangeAttribute == null)
                return;

            // Make sure the attribute doesn't get validated a second time by the DynamicValidator
            IgnoreModelValidationAttribute(rangeAttribute.GetType());

            validator.Enabled = true;

            Func<object, string> converter;
            switch (validator.Type) {
                case ValidationDataType.Integer:
                    converter = val => Convert.ToInt32(val, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);
                    break;
                case ValidationDataType.Double:
                    converter = val => Convert.ToDouble(val, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture);
                    break;
                case ValidationDataType.String:
                default:
                    converter = val => val.ToString();
                    break;
            }
            validator.MinimumValue = converter(rangeAttribute.Minimum);
            validator.MaximumValue = converter(rangeAttribute.Maximum);

            if (String.IsNullOrEmpty(validator.ErrorMessage)) {
                validator.ErrorMessage = HttpUtility.HtmlEncode(
                    StringLocalizerUtil.GetLocalizedString(rangeAttribute, column.DisplayName));
            }
        }

        private void SetUpRegexValidator(RegularExpressionValidator validator, MetaColumn column) {
            // Nothing to do if no regex was specified
            var regexAttribute = column.Attributes.OfType<RegularExpressionAttribute>().FirstOrDefault();
            if (regexAttribute == null)
                return;

            // Make sure the attribute doesn't get validated a second time by the DynamicValidator
            IgnoreModelValidationAttribute(regexAttribute.GetType());

            validator.Enabled = true;
            validator.ValidationExpression = regexAttribute.Pattern;

            if (String.IsNullOrEmpty(validator.ErrorMessage)) {
                validator.ErrorMessage = HttpUtility.HtmlEncode(
                    StringLocalizerUtil.GetLocalizedString(regexAttribute, column.DisplayName));
            }
        }

        /// <summary>
        /// This method instructs the DynamicValidator to ignore a specific type of model
        /// validation attributes. This is called when that attribute type is already being
        /// fully handled by an ASP.NET validator controls. Without this call, the validation
        /// could happen twice, resulting in a duplicated error message
        /// </summary>
        [SuppressMessage("Microsoft.Usage", "CA2301:EmbeddableTypesInContainersRule", MessageId = "_ignoredModelValidationAttributes", Justification = "The types that go into this dictionary are specifically ValidationAttribute derived types.")]
        protected void IgnoreModelValidationAttribute(Type attributeType)
        {
            // Create the dictionary on demand
            if (_ignoredModelValidationAttributes == null) {
                _ignoredModelValidationAttributes = new Dictionary<Type, bool>();
            }

            // Add the attribute type to the list
            _ignoredModelValidationAttributes[attributeType] = true;
        }

        /// <summary>
        /// Implementation of IBindableControl.ExtractValues
        /// </summary>
        /// <param name="dictionary">The dictionary that contains all the new values</param>
        protected virtual void ExtractValues(IOrderedDictionary dictionary) {
            // To nothing in the base class.  Derived field templates decide what they want to save
        }

        #region IBindableControl Members

        void IBindableControl.ExtractValues(IOrderedDictionary dictionary) {
            ExtractValues(dictionary);
        }

        #endregion

        #region IFieldTemplate Members

        void IFieldTemplate.SetHost(IFieldTemplateHost host) {
            Host = host;
            FormattingOptions = Host.FormattingOptions;
        }

        #endregion
    }
}