How to Remove the Last Week Of a Calendar

2019-04-07 22:21发布

问题:

I am not sure why other people have not asked this before. But have you notice that the asp:Calendar shows an extra week at the end?

For example if the VisibleMonth is set to 2010-03-01 and FirstDayOfWeek to Sunday: It will show 6 weeks.

  1. Feb 28 to March 6
  2. March 7 to March 13
  3. March 14 to March 20
  4. March 21 to March 27
  5. March 28 to April 3
  6. April 4 to April 10

I was wondering why Microsoft shows the last Row which is entirely on April. I tried to search the net for a property but it does not seem to be existing.

The only solution that I could think of is to override the Pre_Render and check all individual date if they are still within the week of the VisibleDate. But of course that is an extreme checking since each rendering of the control shows it.

Here is my work around.

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{
    int dayOfWeek = Convert.ToInt16(e.Day.Date.DayOfWeek);
    int compensate = dayOfWeek - Convert.ToInt16(DayOfWeek.Sunday);
    DateTime WeekStart = e.Day.Date.AddDays(-1 * compensate);
    DateTime WeekEnd = WeekStart.AddDays(6);

    // If the start and end of the week does not have relevance to the current month
    if (WeekStart.Month != Calendar1.VisibleDate.Month &&
        WeekEnd .Month != Calendar1.VisibleDate.Month)
    {
        e.Cell.Text = "";
        e.Cell.Height = 0;
        e.Cell.Visible = false;
    }
}

回答1:

very nice. Works with most browsers but is fugly with Chrome 11.0.696.71 And Safari for windows (havn't tested on mac)

For some months the calendar control displays the extra week at the start of the month. (when the 1st of the month is the first day of the week.)

When you set e.cell.Visible=false it doesn't render elements . So in chrome you end up with a row <tr></tr> . Chrome renders this as a blank row. And since i don't think there's a way to set the TR element height/style via the calendar control, you end up with an ugly looking calendar that's missing it's first row for certain months.

Also setitng height to 0 does nothing when you set Visible=false. If you don't set Visible=false and just put height =0 it still doesn't render correctly in chrome. So the solution is to set height to 1

Here's my modified solution.

the onrowrender

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e){
    hideExtraWeek(sender, e, (DayOfWeek)Calendar1.FirstDayOfWeek);
}

the function

    protected void hideExtraWeek(object sender, DayRenderEventArgs e, DayOfWeek dw){
        if (dw == (DayOfWeek)7) dw = (DayOfWeek)0; // FirstDayOfweek returns 7 when set to default, But it's zero based so valid values are 0 to 6
        Boolean blnBrowserDoesntSupportsEmptyRow= Request.Browser.Browser=="Chrome" ||
                                            Request.Browser.Browser=="Safari";

        int dayOfWeek = Convert.ToInt16(e.Day.Date.DayOfWeek);
        int compensate = dayOfWeek - Convert.ToInt16(dw);
        DateTime WeekStart = e.Day.Date.AddDays(-1 * compensate);
        DateTime WeekEnd = WeekStart.AddDays(6);

        // If the start and end of the week does not have relevance to the current month
        if (WeekStart.Month==WeekEnd.Month && e.Day.IsOtherMonth){
            e.Cell.Text = "";
            e.Cell.Height = 1; // fix for chrome. Visible=false leaves a blank row even when there are no <td>s in the <tr>
            e.Cell.Visible = blnBrowserDoesntSupportsEmptyRow;
        }
    }


回答2:

If you have SelectWeekText and SelectionMode="DayWeek" or "DayWeekMonth", you'll have issues with the week selection markup still showing up for the hidden week. Here's how I did it with a little bit of JQuery.

 Sub cal_DayRender(ByVal sender As Object, ByVal e As DayRenderEventArgs)
      If HideExtraWeek(e, If(Cal.FirstDayOfWeek = WebControls.FirstDayOfWeek.Default, Threading.Thread.CurrentThread.CurrentUICulture.DateTimeFormat.FirstDayOfWeek, Cal.FirstDayOfWeek)) Then
          e.Cell.Style("display") = "none"
          e.Cell.CssClass = "hiddenWeek"
          Exit Sub
      End If
      'do other render stuff here
 End Sub

Private Function HideExtraWeek(ByVal e As DayRenderEventArgs, ByVal startOfWeekDay As Integer) As Boolean
    If e.Day.IsOtherMonth Then
        'hide empty weeks, logic credited to Robert
        Dim currDay As Integer = e.Day.Date.DayOfWeek
        Dim weekStart As DateTime = e.Day.Date.AddDays(startOfWeekDay - currDay) 'first day of the week
        Dim weekEnd As DateTime = weekStart.AddDays(6)

        Return (weekStart.Month = weekEnd.Month) 'the entire week is part of the other month
    End If
    Return False
End Function

<script type="text/javascript">
        $(document).ready(function() {
             $("td.hiddenWeek").parent().hide();
        }
</script>


回答3:

Ok i have another solution. After some tweaking. Hopefully it helps. It has slightly more functions but if your calendar gets alotof hits, it might just save a 1% of cpu :). Make sure to attach the Calendar1_VisibleMonthChanged event to OnVisibleMonthChanged

private DateTime fromDate;
private DateTime toDate;
private Boolean blnBrowserDoesntSupportsEmptyRow = Request.Browser.Browser=="Chrome" ||
                                                       Request.Browser.Browser=="Safari";

    protected void Page_Load(object sender, EventArgs e){
        if (!Page.IsPostBack){
            setFromToDates(new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1));
        }
    }

    protected void Calendar1_VisibleMonthChanged(object sender, MonthChangedEventArgs e){
        setFromToDates(e.NewDate);
    }

    protected void setFromToDates(DateTime monthStart){

        DayOfWeek dw = (DayOfWeek)Calendar1.FirstDayOfWeek;
        if (dw == (DayOfWeek)7) dw = (DayOfWeek)0;


        if (monthStart.DayOfWeek == dw){
            this.fromDate = monthStart;// if 1st day of calendar is also 1st of the month. just set as is
        }else{
            int dayOfWeek = Convert.ToInt16(monthStart.DayOfWeek);
            dayOfWeek = dayOfWeek == 0 ? 7 : dayOfWeek;
            int compensate = dayOfWeek - Convert.ToInt16(dw);
            this.fromDate = monthStart.AddDays(-1 * compensate);// set FromDate to the beggning day of the calendar. I.e may start from e.g. 25th of the previous month
        }

        this.toDate = monthStart.AddMonths(1);
        if (this.toDate.DayOfWeek != dw)
        {
            int dayOfWeek = Convert.ToInt16(this.toDate.DayOfWeek);
            dayOfWeek = dayOfWeek == 0 ? 7 : dayOfWeek;
            int compensate = dayOfWeek - Convert.ToInt16(dw);
            this.toDate=this.toDate.AddDays(7-compensate);
        }
    }








    protected void Calendar1_DayRender(object sender, DayRenderEventArgs e){
        // hide extra week
        hideExtraWeek(sender, e);
    }

    // returns weather or not the given day is hidden
    protected Boolean hideExtraWeek(object sender, DayRenderEventArgs e){
            Boolean isVisibleDay = e.Day.Date >= this.fromDate && e.Day.Date < this.toDate;

            // If the start and end of the week does not have relevance to the current month
            if (!isVisibleDay){
                e.Cell.Text = "";
                e.Cell.Height = 1; // fix for chrome. Visible=false leaves a blank row even when there are no <td>s in the <tr>
                e.Cell.Visible = blnBrowserDoesntSupportsEmptyRow;
             }
        return !isVisibleDay;
    }


回答4:

<asp:Calendar ID="Calendar1" runat="server" CellPadding="1" CellSpacing="0"
 Width="600px" FirstDayOfWeek="Monday" BorderColor="#a1a1a1"
BorderWidth="1" BorderStyle="None" ShowGridLines="True" ShowDescriptionAsToolTip="True"
NextPrevStyle-CssClass=""
Height="500px" OnDayRender="Calendar1_DayRender" 
SelectionMode="None"  NextMonthText="&amp;gt;&amp;gt;" PrevMonthText="&amp;lt;&amp;lt;">
<OtherMonthDayStyle BorderStyle="None" />
</asp:Calendar>

bool weekstart = false;
int[] longmonths = new int[] { 1, 3, 5, 7, 8, 10, 12 };// 31 days in a Month
int[] shortmonths = new int[] { 4, 6, 9, 11 };// 30 days in a Month

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
{

   if (e.Day.IsOtherMonth)
   {
        e.Cell.Text = String.Empty;
        e.Cell.Height = 0;
        if (e.Day.Date.DayOfWeek == DayOfWeek.Monday )//Monday is FirstDayOfWeek
        {
          if (shortmonths.Contains(e.Day.Date.Month) && e.Day.Date.Day == 24)
          {
             weekstart = true;
          }
          else if (longmonths.Contains(e.Day.Date.Month) && e.Day.Date.Day == 25)
          {
             weekstart = true;
          }
        }
        if (weekstart)
        {
           e.Cell.Visible = false;
        }
   }
   else if (!e.Day.IsOtherMonth)
   {
      weekstart = false;
   }

}


回答5:

Here is the VB code I use. It will remove empty rows at both the top and bottom when necessary. It uses a private variable. This method does not seem to have any issues in Chrome or Safari.

Private _hideEmptyWeek As Boolean

Protected Sub Calendar1_DayRender(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DayRenderEventArgs) Handles Calendar1.DayRender

    If e.Day.IsOtherMonth Then

        '' Hide first week if empty
        If e.Day.Date = e.Day.Date.AddDays(-e.Day.Date.Day + 1).AddMonths(1).AddDays(-7) Then ' If this date is a full week before next month
            _hideEmptyWeek = True
        End If


        '' Hide last week if empty
        If e.Day.Date.DayOfWeek = DayOfWeek.Sunday And e.Day.Date.Day < 7 Then ' If this is the first Sunday of next month
            _hideEmptyWeek = True
        End If


        '' Hide cell if we are in an empty week
        If _hideEmptyWeek = True Then
            e.Cell.Visible = False
        End If
    Else
        _hideEmptyWeek = False
    End If
End Sub