|
//------------------------------------------------------------------------------
// <copyright file="RepeatInfo.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls {
using System;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// <para>Defines the information used to render a list of items using
/// a <see cref='System.Web.UI.WebControls.Repeater'/>.</para>
/// </devdoc>
public sealed class RepeatInfo {
private RepeatDirection repeatDirection;
private RepeatLayout repeatLayout;
private int repeatColumns;
private string caption;
private TableCaptionAlign captionAlign;
private bool useAccessibleHeader;
private bool outerTableImplied;
private bool enableLegacyRendering;
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.RepeatInfo'/> class. This class is not
/// inheritable.</para>
/// </devdoc>
public RepeatInfo() {
repeatDirection = RepeatDirection.Vertical;
repeatLayout = RepeatLayout.Table;
repeatColumns = 0;
outerTableImplied = false;
}
public string Caption {
get {
return (caption == null) ? String.Empty : caption;
}
set {
caption = value;
}
}
public TableCaptionAlign CaptionAlign {
get {
return captionAlign;
}
set {
if ((value < TableCaptionAlign.NotSet) ||
(value > TableCaptionAlign.Right)) {
throw new ArgumentOutOfRangeException("value");
}
captionAlign = value;
}
}
// DevDiv 33149: A backward compat. switch for Everett rendering
internal bool EnableLegacyRendering {
get {
return enableLegacyRendering;
}
set {
enableLegacyRendering = value;
}
}
private bool IsListLayout {
get {
return
(RepeatLayout == RepeatLayout.UnorderedList) ||
(RepeatLayout == RepeatLayout.OrderedList);
}
}
/// <devdoc>
/// Indicates whether an outer table is implied
/// for the items.
/// </devdoc>
public bool OuterTableImplied {
get {
return outerTableImplied;
}
set {
outerTableImplied = value;
}
}
/// <devdoc>
/// <para> Indicates the column count of items.</para>
/// </devdoc>
public int RepeatColumns {
get {
return repeatColumns;
}
set {
repeatColumns = value;
}
}
/// <devdoc>
/// <para>Indicates the direction of flow of items.</para>
/// </devdoc>
public RepeatDirection RepeatDirection {
get {
return repeatDirection;
}
set {
if (value < RepeatDirection.Horizontal || value > RepeatDirection.Vertical) {
throw new ArgumentOutOfRangeException("value");
}
repeatDirection = value;
}
}
/// <devdoc>
/// Indicates the layout of items.
/// </devdoc>
public RepeatLayout RepeatLayout {
get {
return repeatLayout;
}
set {
EnumerationRangeValidationUtil.ValidateRepeatLayout(value);
repeatLayout = value;
}
}
public bool UseAccessibleHeader {
get {
return useAccessibleHeader;
}
set {
useAccessibleHeader = value;
}
}
/// <devdoc>
/// </devdoc>
private void RenderHorizontalRepeater(HtmlTextWriter writer, IRepeatInfoUser user, Style controlStyle, WebControl baseControl) {
Debug.Assert(outerTableImplied == false, "Cannot use outer implied table with Horizontal layout");
int itemCount = user.RepeatedItemCount;
int totalColumns = repeatColumns;
int currentColumn = 0;
if (totalColumns == 0) {
// 0 implies a complete horizontal repetition without any
// column count constraints
totalColumns = itemCount;
}
WebControl outerControl = null;
bool tableLayout = false;
switch (repeatLayout) {
case RepeatLayout.Table:
outerControl = new Table();
if (Caption.Length != 0) {
((Table)outerControl).Caption = Caption;
((Table)outerControl).CaptionAlign = CaptionAlign;
}
tableLayout = true;
break;
case RepeatLayout.Flow:
outerControl = new WebControl(HtmlTextWriterTag.Span);
break;
}
bool separators = user.HasSeparators;
// use ClientID (and not ID) since we want to render out the fully qualified client id
// even though this outer control will not be parented to the control hierarchy
outerControl.ID = baseControl.ClientID;
outerControl.CopyBaseAttributes(baseControl);
outerControl.ApplyStyle(controlStyle);
outerControl.RenderBeginTag(writer);
if (user.HasHeader) {
if (tableLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// add attributes to render for TD/TH
if ((totalColumns != 1) || separators) {
int columnSpan = totalColumns;
if (separators)
columnSpan += totalColumns;
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnSpan.ToString(NumberFormatInfo.InvariantInfo));
}
if (useAccessibleHeader) {
writer.AddAttribute(HtmlTextWriterAttribute.Scope, "col");
}
// add style attributes to render for TD/TH
Style style = user.GetItemStyle(ListItemType.Header, -1);
if (style != null) {
style.AddAttributesToRender(writer);
}
// render begin tag
if (useAccessibleHeader) {
writer.RenderBeginTag(HtmlTextWriterTag.Th);
}
else {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
}
user.RenderItem(ListItemType.Header, -1, this, writer);
if (tableLayout) {
// render end tags TD/TH and TR
writer.RenderEndTag();
writer.RenderEndTag();
}
else {
if (totalColumns < itemCount) {
// we have multiple rows, so have a break between the header and first row.
if (EnableLegacyRendering) {
writer.WriteObsoleteBreak();
}
else {
writer.WriteBreak();
}
}
}
}
for (int i = 0; i < itemCount; i++) {
if (tableLayout && (currentColumn == 0)) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
if (tableLayout) {
// add style attributes to render for TD
Style style = user.GetItemStyle(ListItemType.Item, i);
if (style != null)
style.AddAttributesToRender(writer);
// render begin tag for TD
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
user.RenderItem(ListItemType.Item, i, this, writer);
if (tableLayout) {
// render end tag for TD
writer.RenderEndTag();
}
if (separators && (i != (itemCount - 1))) {
if (tableLayout) {
Style style = user.GetItemStyle(ListItemType.Separator, i);
if (style != null)
style.AddAttributesToRender(writer);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
user.RenderItem(ListItemType.Separator, i, this, writer);
if (tableLayout)
writer.RenderEndTag();
}
currentColumn++;
// on the last line, fill in the rest of the empty spots with <td/>s.
// If there were separators, we need twice as many plus one to accomodate for
// the last item not having a separator after it.
if (tableLayout && i == itemCount - 1) {
int unfilledColumns = totalColumns - currentColumn;
if (separators == true) {
int unfilledColumnsWithSeparators = (unfilledColumns * 2) + 1;
if (unfilledColumnsWithSeparators > unfilledColumns) {
unfilledColumns = unfilledColumnsWithSeparators;
}
}
for (int k = 0; k < unfilledColumns; k++) {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.RenderEndTag();
}
}
if ((currentColumn == totalColumns) || (i == itemCount - 1)) {
if (tableLayout) {
// End tag for TR
writer.RenderEndTag();
}
else {
// write out the <br> after rows when there are multiple rows
if (totalColumns < itemCount) {
if (EnableLegacyRendering) {
writer.WriteObsoleteBreak();
}
else {
writer.WriteBreak();
}
}
}
currentColumn = 0;
}
}
if (user.HasFooter) {
if (tableLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
if ((totalColumns != 1) || separators) {
// add attributes to render for TD
int columnSpan = totalColumns;
if (separators)
columnSpan += totalColumns;
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnSpan.ToString(NumberFormatInfo.InvariantInfo));
}
// add style attributes to render for TD
Style style = user.GetItemStyle(ListItemType.Footer, -1);
if (style != null)
style.AddAttributesToRender(writer);
// render begin tag for TD
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
user.RenderItem(ListItemType.Footer, -1, this, writer);
if (tableLayout) {
// render end tag for TR and TD
writer.RenderEndTag();
writer.RenderEndTag();
}
}
outerControl.RenderEndTag(writer);
}
/// <devdoc>
/// <para>Renders the Repeater with the specified
/// information.</para>
/// </devdoc>
public void RenderRepeater(HtmlTextWriter writer, IRepeatInfoUser user, Style controlStyle, WebControl baseControl) {
if (IsListLayout) {
if (user.HasFooter || user.HasHeader || user.HasSeparators) {
throw new InvalidOperationException(SR.GetString(SR.RepeatInfo_ListLayoutDoesNotSupportHeaderFooterSeparator));
}
if (RepeatDirection != RepeatDirection.Vertical) {
throw new InvalidOperationException(SR.GetString(SR.RepeatInfo_ListLayoutOnlySupportsVerticalLayout));
}
if ((RepeatColumns != 0) && (RepeatColumns != 1)) {
throw new InvalidOperationException(SR.GetString(SR.RepeatInfo_ListLayoutDoesNotSupportMultipleColumn));
}
if (OuterTableImplied) {
throw new InvalidOperationException(SR.GetString(SR.RepeatInfo_ListLayoutDoesNotSupportImpliedOuterTable));
}
}
if (repeatDirection == RepeatDirection.Vertical) {
RenderVerticalRepeater(writer, user, controlStyle, baseControl);
}
else {
RenderHorizontalRepeater(writer, user, controlStyle, baseControl);
}
}
/// <devdoc>
/// </devdoc>
private void RenderVerticalRepeater(HtmlTextWriter writer, IRepeatInfoUser user, Style controlStyle, WebControl baseControl) {
int itemCount = user.RepeatedItemCount;
int totalColumns;
int totalRows;
int filledColumns;
// List Layout Constraint --> Columns = 0 or 1
if ((repeatColumns == 0) || (repeatColumns == 1)) {
// A RepeatColumns of 0 implies a completely vertical repetition in
// a single column. This is same as repeatColumns of 1.
totalColumns = 1;
filledColumns = 1;
totalRows = itemCount;
}
else {
totalColumns = repeatColumns;
totalRows = (int)((itemCount + repeatColumns - 1) / repeatColumns);
if ((totalRows == 0) && (itemCount != 0)) {
// if repeatColumns is a huge number like Int32.MaxValue, then the
// calculation above essentially reduces down to 0
totalRows = 1;
}
filledColumns = itemCount % totalColumns;
if (filledColumns == 0) {
filledColumns = totalColumns;
}
}
WebControl outerControl = null;
bool tableLayout = false;
// List Layout Constraint --> OuterTableImplied = false
// List Layout Constraint --> tableLayout = false
if (!outerTableImplied) {
switch (repeatLayout) {
case RepeatLayout.Table:
outerControl = new Table();
if (Caption.Length != 0) {
((Table)outerControl).Caption = Caption;
((Table)outerControl).CaptionAlign = CaptionAlign;
}
tableLayout = true;
break;
case RepeatLayout.Flow:
outerControl = new WebControl(HtmlTextWriterTag.Span);
break;
case RepeatLayout.UnorderedList:
outerControl = new WebControl(HtmlTextWriterTag.Ul);
break;
case RepeatLayout.OrderedList:
outerControl = new WebControl(HtmlTextWriterTag.Ol);
break;
}
}
bool separators = user.HasSeparators;
// List Layout Constraint --> separators = false
if (outerControl != null) {
// use ClientID (and not ID) since we want to render out the fully qualified client id
// even though this outer control will not be parented to the control hierarchy
outerControl.ID = baseControl.ClientID;
outerControl.CopyBaseAttributes(baseControl);
outerControl.ApplyStyle(controlStyle);
outerControl.RenderBeginTag(writer);
}
// List Layout Constraint --> HasHeader = false
if (user.HasHeader) {
if (tableLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// add attributes to render for TH
if (totalColumns != 1) {
int columnSpan = totalColumns;
if (separators)
columnSpan += totalColumns;
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnSpan.ToString(NumberFormatInfo.InvariantInfo));
}
if (useAccessibleHeader) {
writer.AddAttribute(HtmlTextWriterAttribute.Scope, "col");
}
// add style attributes to render for TD/TH
Style style = user.GetItemStyle(ListItemType.Header, -1);
if (style != null) {
style.AddAttributesToRender(writer);
}
// render begin tag for TD/TH
if (useAccessibleHeader) {
writer.RenderBeginTag(HtmlTextWriterTag.Th);
}
else {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
}
user.RenderItem(ListItemType.Header, -1, this, writer);
if (tableLayout) {
// render end tags TD/TH and TR
writer.RenderEndTag();
writer.RenderEndTag();
}
else if (!outerTableImplied) {
if (EnableLegacyRendering) {
writer.WriteObsoleteBreak();
}
else {
writer.WriteBreak();
}
}
}
int itemCounter = 0;
for (int currentRow = 0; currentRow < totalRows; currentRow++) {
// List Layout Constraint --> tableLayout = false
if (tableLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
int itemIndex = currentRow;
for (int currentCol = 0; currentCol < totalColumns; currentCol++) {
if (itemCounter >= itemCount) {
// done rendering all items, so break out of the loop now...
// we might end up here, in unfilled columns attempting to re-render items that
// have already been rendered on the next column in a prior row.
break;
}
if (currentCol != 0) {
itemIndex += totalRows;
// if the previous column (currentColumn - 1) was not a filled column, i.e.,
// it had one less item (the maximum possible), then subtract 1 from the item index.
if ((currentCol - 1) >= filledColumns) {
itemIndex--;
}
}
if (itemIndex >= itemCount)
continue;
itemCounter++;
// List Layout Constraint --> tableLayout = false
if (tableLayout) {
// add style attributes to render for TD
Style style = user.GetItemStyle(ListItemType.Item, itemIndex);
if (style != null)
style.AddAttributesToRender(writer);
// render begin tag for TD
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
if (IsListLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Li);
}
user.RenderItem(ListItemType.Item, itemIndex, this, writer);
if (IsListLayout) {
writer.RenderEndTag();
writer.WriteLine();
}
// List Layout Constraint --> tableLayout = false
if (tableLayout) {
// render end tag for TD
writer.RenderEndTag();
}
// List Layout Constraint --> separators = false
if (separators) {
if (itemIndex != (itemCount - 1)) {
if (totalColumns == 1) {
if (tableLayout) {
writer.RenderEndTag();
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
}
else {
if (!outerTableImplied) {
if (EnableLegacyRendering) {
writer.WriteObsoleteBreak();
}
else {
writer.WriteBreak();
}
}
}
}
if (tableLayout) {
Style style = user.GetItemStyle(ListItemType.Separator, itemIndex);
if (style != null)
style.AddAttributesToRender(writer);
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
if (itemIndex < itemCount)
user.RenderItem(ListItemType.Separator, itemIndex, this, writer);
if (tableLayout)
writer.RenderEndTag();
}
else {
// if we're on the last filled line and separators are specified, add another <td/>
// to accomodate for the lack of a separator on the last item. If there's only one
// column, though, separators will get their own row anyways.
if (tableLayout && totalColumns > 1) {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.RenderEndTag();
}
}
}
}
// on the last line, fill in the remaining empty slots with <td/>s. We need twice as many
// if there were separators.
// List Layout Constraint --> tableLayout = false
if (tableLayout) {
if (currentRow == totalRows - 1) {
int unfilledColumns = totalColumns - filledColumns;
if (separators) {
int unfilledColumnsWithSeparators = unfilledColumns * 2;
if (unfilledColumnsWithSeparators >= unfilledColumns) {
unfilledColumns = unfilledColumnsWithSeparators;
}
}
if (unfilledColumns != 0) {
for (int i = 0; i < unfilledColumns; i++) {
writer.RenderBeginTag(HtmlTextWriterTag.Td);
writer.RenderEndTag();
}
}
}
writer.RenderEndTag();
}
else {
if (((currentRow != totalRows - 1) || user.HasFooter) &&
(!outerTableImplied) && (!IsListLayout)) {
if (EnableLegacyRendering) {
writer.WriteObsoleteBreak();
}
else {
writer.WriteBreak();
}
}
}
}
// List Layout Constraint --> HasFooter = false
if (user.HasFooter) {
if (tableLayout) {
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
// add attributes to render for TD
if (totalColumns != 1) {
int columnSpan = totalColumns;
if (separators)
columnSpan += totalColumns;
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnSpan.ToString(NumberFormatInfo.InvariantInfo));
}
// add style attributes to render for TD
Style style = user.GetItemStyle(ListItemType.Footer, -1);
if (style != null)
style.AddAttributesToRender(writer);
// render begin tag for TD
writer.RenderBeginTag(HtmlTextWriterTag.Td);
}
user.RenderItem(ListItemType.Footer, -1, this, writer);
if (tableLayout) {
// render end tag for TR and TD
writer.RenderEndTag();
writer.RenderEndTag();
}
}
if (outerControl != null)
outerControl.RenderEndTag(writer);
}
}
}
|