1 using System.Collections.Generic;
2 using System.ComponentModel;
3 using System.Diagnostics;
4 using System.Diagnostics.CodeAnalysis;
5 using System.Globalization;
6 using System.Linq;
7 using System.Web.Resources;
8 using System.Web.UI;
9 using System.Web.UI.WebControls.Expressions;
10 using System.Web.UI.WebControls;
11 
12 namespace System.Web.DynamicData {
13     /// <summary>
14     /// A templated control that automatically generates a collection of filters for a table associated with a given data source.
15     /// It is designed to work with the QueryExtender architecture and it will not render anything unless it's referenced by a
16     /// DynamicFilterExpression inside of a QueryExtender.
17     /// </summary>
18     [ParseChildren(true)]
19     [PersistChildren(false)]
20     public class QueryableFilterRepeater : Control, IFilterExpressionProvider {
21         private HttpContextBase _context;
22         private IQueryableDataSource _dataSource;
23         private List<DynamicFilter> _filters = new List<DynamicFilter>();
24         private bool _initialized = false;
25 
26         // for unit testing
27         private new HttpContextBase Context {
28             get {
29                 return _context ?? new HttpContextWrapper(HttpContext.Current);
30             }
31         }
32 
33         /// <summary>
34         /// The ID of a DynamicFilter control inside of the template that will be used configured to be a filter for a particular column.
35         /// The default value is "DynamicFilter"
36         /// </summary>
37         [
38         Category("Behavior"),
39         DefaultValue("DynamicFilter"),
40         Themeable(false),
41         IDReferenceProperty(typeof(QueryableFilterUserControl)),
42         ResourceDescription("DynamicFilterRepeater_DynamicFilterContainerId")
43         ]
44         public string DynamicFilterContainerId {
45             get {
46                 string id = ViewState["__FilterContainerId"] as string;
47                 return String.IsNullOrEmpty(id) ? "DynamicFilter" : id;
48             }
49             set {
50                 ViewState["__FilterContainerId"] = value;
51             }
52         }
53 
54         /// <summary>
55         /// The template in which the layout of each filter can be specified. Just like ItemTempalte in Repeater.
56         /// </summary>
57         [Browsable(false)]
58         [DefaultValue(null)]
59         [PersistenceMode(PersistenceMode.InnerProperty)]
60         [TemplateContainer(typeof(INamingContainer))]
61         public virtual ITemplate ItemTemplate { get; set; }
62 
QueryableFilterRepeater()63         public QueryableFilterRepeater() {
64         }
65 
66         // for unit testing
QueryableFilterRepeater(HttpContextBase context)67         internal QueryableFilterRepeater(HttpContextBase context)
68             : this() {
69             _context = context;
70         }
71 
72         [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
OnPreRender(EventArgs e)73         protected override void OnPreRender(EventArgs e) {
74             if (_filters.Count == 0) {
75                 this.Visible = false;
76             }
77             base.OnPreRender(e);
78         }
79 
80         #region IFilterExpressionProvider Members
81 
IFilterExpressionProvider.Initialize(IQueryableDataSource dataSource)82         void IFilterExpressionProvider.Initialize(IQueryableDataSource dataSource) {
83             if (dataSource == null) {
84                 throw new ArgumentNullException("dataSource");
85             }
86 
87             if (ItemTemplate == null) {
88                 return;
89             }
90 
91             _dataSource = dataSource;
92             Page.InitComplete += new EventHandler(Page_InitComplete);
93         }
94 
Page_InitComplete(object sender, EventArgs e)95         internal void Page_InitComplete(object sender, EventArgs e) {
96             if (_initialized) {
97                 return;
98             }
99 
100             Debug.Assert(_dataSource != null);
101 
102             MetaTable table = DynamicDataExtensions.GetMetaTable(_dataSource, Context);
103             int itemIndex = 0;
104             foreach (MetaColumn column in table.GetFilteredColumns()) {
105                 FilterRepeaterItem item = new FilterRepeaterItem() { DataItemIndex = itemIndex, DisplayIndex = itemIndex };
106                 itemIndex++;
107                 ItemTemplate.InstantiateIn(item);
108                 Controls.Add(item);
109 
110                 DynamicFilter filter = item.FindControl(DynamicFilterContainerId) as DynamicFilter;
111                 if (filter == null) {
112                     throw new InvalidOperationException(
113                         String.Format(CultureInfo.CurrentCulture,
114                             DynamicDataResources.FilterRepeater_CouldNotFindControlInTemplate,
115                             ID,
116                             typeof(QueryableFilterUserControl).FullName,
117                             DynamicFilterContainerId));
118                 }
119                 filter.Context = Context; // needed for unit testing
120                 filter.DataField = column.Name;
121 
122                 item.DataItem = column;
123                 item.DataBind();
124                 item.DataItem = null;
125 
126                 // Keep track of all the filters we create
127                 _filters.Add(filter);
128             }
129 
130             _filters.ForEach(f => f.Initialize(_dataSource));
131             _initialized = true;
132         }
133 
IFilterExpressionProvider.GetQueryable(IQueryable source)134         IQueryable IFilterExpressionProvider.GetQueryable(IQueryable source) {
135             foreach (DynamicFilter filter in _filters) {
136                 source = ((IFilterExpressionProvider)filter).GetQueryable(source);
137             }
138             return source;
139         }
140 
141         #endregion
142 
143         private class FilterRepeaterItem : Control, IDataItemContainer {
144             public object DataItem { get; internal set; }
145 
146             public int DataItemIndex { get; internal set; }
147 
148             public int DisplayIndex { get; internal set; }
149         }
150     }
151 }
152