Download Free Demos

Recurring Events

Some events that a user enters into the scheduler repeat regularly. For example, weekly meetings, driving lessons, annual fairs, anniversaries and birthdays. To handle similar cases, Scheduler provides support for recurring events.

recurring events in asp net calendar

However, this functionality isn't enabled by default, and you should extend your existing code a little bit to activate it:

  • Add 3 extra fields to database
  • Activate the 'Recurring' extension
  • Adjust the create/update/delete logic in the controller

Model changes

Let's start from the model. You should add 3 extra fields to database (as with regular events, in addition to the mandatory fields you can retrieve any extra data from the database):

  • event_length - (bigint) the time duration of an event in seconds;
  • event_pid - (int) the parent id of a series of events. Particular occurrences refer to the the series through this id;
  • rec_type - (nvarchar[64]) defines the repetition logic of a recurring event (e.g. each working day, the first Saturday of each 2 months). This field is filled in automatically. See details below.

Also note, the start_date and end_date fields change their meaning a little bit:

  • start_date - (datetime) the start date of the first event in a series in the 'yyyy-mm-dd hh:mm:ss' format
  • end_date - (datetime) the end date of the last event in a series in the 'yyyy-mm-dd 00:00:00' format

Since the start_date and end_date fields store the start and end dates of an event series (not a specific occurrence), we need to have the event_length field to store the duration of a specific occurrence.

To get detailed information on presenting recurring events in the database, please, refer to the dhtmlxScheduler documentation - 'Server-side integration'.

Sample DB structure

A basic DB table for storing recurring events can be created as in:

CREATE TABLE [Events] (
  [id]           INT           IDENTITY (1, 1) NOT NULL,
  [text]         TEXT          NULL,
  [start_date]   DATETIME      NOT NULL,
  [end_date]     DATETIME      NOT NULL,
  [event_length] BIGINT        NULL,
  [rec_type]     NVARCHAR(50)  NULL,
  [event_pid]    INT           NULL,
  PRIMARY KEY (id)
);

Controller changes

In the controller you must enable the corresponding extension:

scheduler.Extensions.Add(SchedulerExtensions.Extension.Recurring);

and provide the following logic:

  • if an event with rec_type==none was inserted - the response must have the "deleted" status;
  • if an event with rec_type!=none was updated or deleted - all records with the related event_pid must be deleted;
  • if an event with the event_pid value was deleted - it needs to be updated with rec_type==none instead of deleting.
public ActionResult Save(int? id, FormCollection actionValues)
{
    var action = new DataAction(actionValues);
 
    DHXSchedulerDataContext data = new DHXSchedulerDataContext();
 
    try
      {
         var changedEvent = (Recurring)DHXEventsHelper.Bind(typeof(Recurring), actionValues); // takes the object of an event
         bool isFinished = deleteRelated(action, changedEvent, data);
         if (!isFinished)
         {
              switch (action.Type)
              {
 
                   case DataActionTypes.Insert:
                       data.Recurrings.InsertOnSubmit(changedEvent);
                       if (changedEvent.rec_type == "none")//deletes one event from the series
                            action.Type = DataActionTypes.Delete;
                       break;
                   case DataActionTypes.Delete:
                       changedEvent = data.Recurrings.SingleOrDefault(ev => ev.id == action.SourceId);
                       data.Recurrings.DeleteOnSubmit(changedEvent);
                       break;
                   default:// "update"   
                       var eventToUpdate = data.Recurrings.SingleOrDefault(ev =>
                       ev.id == action.SourceId);
                       DHXEventsHelper.Update(eventToUpdate, changedEvent, new List() { "id" });
                       break;
              }
          }
          data.SubmitChanges();
 
          action.TargetId = changedEvent.id;
     }
     catch (Exception a)
     {
         action.Type = DataActionTypes.Error;
     }
     return (new AjaxSaveResponse(action));
}
 
// when the series is changed or deleted, we need to remove(change) all related events (events with the same 'event_pid') 
protected bool deleteRelated(DataAction action, Recurring changedEvent, DHXSchedulerDataContext context)
{
    bool finished = false;
    if ((action.Type == DataActionTypes.Delete || action.Type == DataActionTypes.Update)
            && !string.IsNullOrEmpty(changedEvent.rec_type))
    {
        context.Recurrings.DeleteAllOnSubmit(from ev in context.Recurrings
                                                  where ev.event_pid == changedEvent.id select ev);
    }
    if (action.Type == DataActionTypes.Delete
           && (changedEvent.event_pid != 0 && changedEvent.event_pid != null))
    {
        Recurring changed  = (from ev in context.Recurrings where ev.id == action.TargetId select ev)
           .Single() ;
        changed.rec_type = "none";
        finished = true;
    }
    return finished;
}

Getting instances of recurring events

Recurring events aren't stored in the database as individual items. The DB contains the start date, the time length and repetition of the series. Based on this information events are rendered in scheduler.

In case if you need to get an array of single events within a specific interval, you should use the List<object> GetOccurrences(IEnumerable source, DateTime from, DateTime to) method of the helper DHTMLX.Common.DHXEventsHelper class. As parameters, the method takes the collection of items, start and end dates (set the time interval you want to get instances of single events from).

var helper = new DHXEventsHelper();
var items = helper.GetOccurrences(new DHXSchedulerDataContext().Recurrings, new DateTime(2012, 11, 10),
new DateTime(2012, 12, 10));

Format details

The client side gets data from the rec_type field as a string of the following format: [type][count][count2][day][days]#[extra]

where:

  • type - the type of repetition: 'day','week','month','year'.
  • count - the interval between events in the "type" units.
  • count2 and day - defines a day of a month ( first Monday, third Friday, etc ).
  • days - a comma-separated list of affected week days.
  • extra - extra info that can be used to change presentation of recurring details. It doesn't affect the real recurring pattern and is used to set the state of the recurring editor form.
    It may take next values:
    • "no" - the event without the end date (infinite recurring)
    • {number} - the count of occurrences

Examples of the rec_type data:

  • day_3___ - each three days
  • month _2___ - each two months
  • month_1_1_2_ - second Monday of each month
  • week_2___1,5 - Monday and Friday of each second week
  • none - a special value used when you delete a single event from the series. Such 'deleted' event isn't deleted from the database, it's just marked by the 'none' value to not be rendered in Scheduler.

Excluding past days from events with the 'weekly' recurrence

By default, an event with the 'weekly' recurrence contains occurrences starting from the first day of the week when it has been created. Such a behavior can result in including past days (that are already gone) into the recurrence.

For example, the user creates an event on Friday and specifies the 'weekly' repetition on Tuesday and Thursday. The stored event will contain the current week, i.e. past Tuesday and Thursday, even though it has been created on Friday.

The library provides DHXScheduler.Config.repeat_precise property that allows excluding past days from the recurrence. Set the property to true and the start date of a recurring event will be the date of the first real occurrence, which in our example is Tuesday of the next week.

var sched = new DHXScheduler();
...
sched.Config.repeat_precise = true;

Customizing a form control for recurring events

You can redefine the layout of the recurring control.

To customize the recurring layout, you need to:

  • Change the form markup
  • Delete excess elements (e.g., the 'yearly' repeat type and all related inputs)
  • Set default values for inputs (e.g., you can set 'no end date' for recurring events and hide the block that specifies the recurrence end date)

Use-Case

Let's start with a use-case. Imagine that you need to remove the 'monthly' and 'yearly' repeat types and set the 'no end date' option for all events (i.e. remove the block that specifies the end of recurrence).

Step 1. Define the markup of a custom form and put it somewhere on the page (start with copying the default template that can be found at 'dhtmlxScheduler\sources\locale\recurring\'):

<div style="display:none">
<div class="dhx_form_repeat" id="custom_form">
  <form>
    <div>
      <select name="repeat">
        <option value="day">Daily</option>
        <option value="week">Weekly</option>
      </select>
    </div>
    <div>
      <div style="display:none;" id="dhx_repeat_day">
        <input type="hidden" name="day_type" value="d"/>
        <input type="hidden" name="day_count" value="1" />
      </div>
      <div style="display:none;" id="dhx_repeat_week">
        Repeat every week next days:<br />
        <label><input type="checkbox" name="week_day" value="1" />Monday</label>
        <label><input type="checkbox" name="week_day" value="2" />Tuesday</label>
        <label><input type="checkbox" name="week_day" value="3" />Wednesday</label>
        <label><input type="checkbox" name="week_day" value="4" />Thursday</label>
        <label><input type="checkbox" name="week_day" value="5" />Friday</label>
        <label><input type="checkbox" name="week_day" value="6" />Saturday</label>
        <label><input type="checkbox" name="week_day" value="0" />Sunday</label>
        <input type="hidden" name="week_count" value="1" />
      </div>
    </div>
    <input type="hidden" value="no" name="end">
  </form>
</div>
</div>

Step 2. Set the Form property of the LightboxRecurringBlock control to the id of your custom form:

scheduler.Lightbox.Add(new LightboxRecurringBlock("recurring", "Repeat"){
    Form = "custom_form"
});

Main Parts

We have provided the default structure of the lightbox's recurring block for different languages at the 'dhtmlxScheduler\sources\locale\recurring\' directory.

For example, see the 'dhtmlxScheduler\sources\locale \recurring\repeat_template_en.htm' file for the English locale. In general, the recurring block of the lightbox consists of 3 groups of controls:

1) Controls for choosing the recurrence type. These inputs have the name 'repeat' and one of the following values: 'daily', 'weekly', 'monthly', 'yearly'. The form must have at least one 'repeat' input with a value. You can use radio buttons, selects or set the default type in the hidden input. Consider the following examples, each of them is a valid way for selecting the type of recurrence in the form.

  • Radio buttons
<label><input type="radio" name="repeat" value="day" />Daily</label><br />
<label><input type="radio" name="repeat" value="week"/>Weekly</label><br />
<label><input type="radio" name="repeat" value="month" />Monthly</label><br />
<label><input type="radio" name="repeat" value="year" />Yearly</label>
  • Select input, without 'Monthly' and 'Yearly' options:
<select name="repeat">
  <option value="day">Daily</option>
  <option value="week">Weekly</option>
</select>
  • Hidden input (the lightbox will create only 'Daily' series):
<input type="hidden" name="repeat" value="day" />

2) A block to configure the recurrence depending on the repeat type. For example, for the 'Daily' repeat type, the block will take the following structure:

<div class="dhx_repeat_center">
    <div style="display:none;" id="dhx_repeat_day">
        <label>
            <input class="dhx_repeat_radio" type="radio" name="day_type" value="d"/>Every
        </label>
        <input class="dhx_repeat_text" type="text" name="day_count" value="1" />day<br />
        <label>
            <input class="dhx_repeat_radio" type="radio" name="day_type" checked value="w"/>Every workday
        </label>
    </div>
...
</div>

Note, that the markup related to the specific type of recurrence can be wrapped in a div with the id in the following format "dhx_repeat_<repeat type>", e.g. "dhx_repeat_day". In this case it will be displayed only when the appropriate repeat type is selected.

3) Controls for specifying the end of recurrence. The end of recurrence is defined by an input with the 'end' name. Possible values are 'no', 'date_of_end', 'occurences_count'. Similar to the 'repeat' controls, the form must have at least one input of this type.

<div class="dhx_repeat_right">
  <label>
    <input type="radio" name="end" value="no" checked/>No end date
  </label><br />
  <label>
    <input type="radio" name="end" value="date_of_end" />After</label>
    <input type="text" name="date_of_end" />
  <br />
  <label>
    <input type="radio" name="end" value="occurences_count" />After</label>
    <input type="text" name="occurences_count" value="1" />Occurrences
</div>

The date for the 'date_of_end' mode must be defined in an input named 'date_of_end'. The same works for the 'occurences_count' mode that takes the number of occurrences from an input named 'occurences_count'.

You can remove any type or predefine it in a hidden input:

<input type="hidden" name="end" value="date_of_end" />
<input type="hidden" name="date_of_end" value="01.01.2016" />

Notes for changing the recurring block

Please, before starting to apply a custom configuration to the lightbox's recurring block, consider the following things:

  1. The 'name' attribute is hardcoded for all inputs - inputs with different names will be ignored.
  2. The 'value' attribute is fixed for all inputs except for those that imply direct input.
  3. When you specify a new form, dhtmlxScheduler doesn't use it directly and just replicates your HTML structure into the lightbox's template. It means that all event handlers or custom properties that have been attached to DOM Elements of your form from the code won't be applied to the form in the lightbox. If you want to attach an event handler, you need to either specify it as an inline HTML attribute, or attach a handler to the form when it's shown in the lightbox.

Beware, dhtmlxScheduler doesn't work with your original HTML form and just creates its copy in the lightbox's template.

For example:

  • the following line will be copied to the lightbox:
<input onclick="handler()">
  • the line below won't be copied to the lightbox:
addEventListener(node, "click", function(){...})

Was this article helpful?

Yes No