PDA

View Full Version : SOLUTION for Ext.NET v1: Calendar with both edit window and form fully localized and customized!



Dimitris
Oct 30, 2012, 6:11 PM
Hello,

This is not a question. I've asked many questions in the forum and I thought I should give something back to help the community, instead. This solution is scattered in more than a dozen thread posts plus you will not find a CRUD sample like this with proper add/update/delete of events in database. Pay particular attention at how the ID (EventId) of the newly added database record is returned to the client so as to update the eventstore.

The following sample implements the requirements below:


CSS for the calendar groups
Overriden Event object with custom field
Fully localized event edit window
Fully localized and customized event edit form
CRUD operations PER EVENT RECORD using web service to access the database (i.e, the database is updated as soon as a new record is added / updated / deleted). NO SUBMIT BUTTON NEEDED!


The Event class:

CalendarId
EndDate
EventId (Primary Key)
IsAllDay
IsNew
Location
Notes
Reminder
StartDate
Title
Url
MyCustomField1 (extra custom field)



Default.aspx


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>

<%@ Register assembly="Ext.Net" namespace="Ext.Net" tagprefix="ext" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Ext.Net Full Custom and Local Calendar</title>
<style type="text/css">
.ext-color-4,
.ext-ie .ext-color-4-ad,
.ext-opera .ext-color-4-ad {
color: #663300;
}
.ext-cal-day-col .ext-color-4,
.ext-dd-drag-proxy .ext-color-4,
.ext-color-4-ad,
.ext-color-4-ad .ext-cal-evm,
.ext-color-4 .ext-cal-picker-icon,
.ext-color-4-x dl,
.ext-color-4-x .ext-cal-evb {
background: #663300;
}
.ext-color-4-x .ext-cal-evb,
.ext-color-4-x dl {
border-color: #7C3939;
}

.ext-color-5,
.ext-ie .ext-color-5-ad,
.ext-opera .ext-color-5-ad {
color: #660066;
}
.ext-cal-day-col .ext-color-5,
.ext-dd-drag-proxy .ext-color-5,
.ext-color-5-ad,
.ext-color-5-ad .ext-cal-evm,
.ext-color-5 .ext-cal-picker-icon,
.ext-color-5-x dl,
.ext-color-5-x .ext-cal-evb {
background: #660066;
}
.ext-color-5-x .ext-cal-evb,
.ext-color-5-x dl {
border-color: #660066;
}

.ext-color-6,
.ext-ie .ext-color-6-ad,
.ext-opera .ext-color-6-ad {
color: #7F0000;
}
.ext-cal-day-col .ext-color-6,
.ext-dd-drag-proxy .ext-color-6,
.ext-color-6-ad,
.ext-color-6-ad .ext-cal-evm,
.ext-color-6 .ext-cal-picker-icon,
.ext-color-6-x dl,
.ext-color-6-x .ext-cal-evb {
background: #7F0000;
}
.ext-color-6-x .ext-cal-evb,
.ext-color-6-x dl {
border-color: #7C3939;
}

.ext-color-7,
.ext-ie .ext-color-7-ad,
.ext-opera .ext-color-7-ad {
color: #000000;
}
.ext-cal-day-col .ext-color-7,
.ext-dd-drag-proxy .ext-color-7,
.ext-color-7-ad,
.ext-color-7-ad .ext-cal-evm,
.ext-color-7 .ext-cal-picker-icon,
.ext-color-7-x dl,
.ext-color-7-x .ext-cal-evb {
background: #000000;
}
.ext-color-7-x .ext-cal-evb,
.ext-color-7-x dl {
border-color: #7C3939;
}
</style>

<ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" />

<script src="MyEventRecord.js" type="text/javascript"></script>

<script type="text/javascript">
var CompanyX = {
getCalendar: function () { return CompanyX.CalendarPanel1; },
getStore: function () { return CompanyX.EventStore1; },
getWindow: function () { return CompanyX.EventEditWindow1; },
customWindow: function () {
// event edit window localization
var win = this.getWindow();
var form = win.formPanel;
var titleItem = form.get('title');
dateRangeItem = form.get('date-range');
calendarItem = form.get('calendar');
detailsLnk = win.fbar.get(0);
saveBtn = win.fbar.get(2);
deleteBtn = win.fbar.get(3);
cancelBtn = win.fbar.get(4);
titleItem.fieldLabel = 'Περιγραφή'; // Title
dateRangeItem.fieldLabel = 'Από'; // When
dateRangeItem.toText = 'έως'; // to
dateRangeItem.allDayText = 'Ολοήμερο'; // Is all day
calendarItem.fieldLabel = 'Λόγος'; // Calendar group
detailsLnk.text = '<a href="#" id="tblink">Περισσότερα στοιχεία...</a>'; // Edit Details
saveBtn.text = 'Αποθήκευση'; // Save button
deleteBtn.text = 'Διαγραφή'; // Delete button
cancelBtn.text = 'Άκυρο'; // Cancel button

// date-range date format
win.get(0).get(1).on('render', function () {
this.startDate.format = 'd/m/Y';
this.endDate.format = 'd/m/Y';
});
},
updateTitle: function (startDt, endDt) {
var msg = '';
if (startDt.clearTime().getTime() == endDt.clearTime().getTime()) {
msg = startDt.format('F j, Y');
}
else if (startDt.getFullYear() == endDt.getFullYear()) {
if (startDt.getMonth() == endDt.getMonth()) {
msg = startDt.format('F j') + ' - ' + endDt.format('j, Y');
}
else {
msg = startDt.format('F j') + ' - ' + endDt.format('F j, Y');
}
}
else {
msg = startDt.format('F j, Y') + ' - ' + endDt.format('F j, Y');
}
this.Panel1.setTitle(msg);
},
setStartDate: function (picker, date) {
this.getCalendar().setStartDate(date);
},
rangeSelect: function (cal, dates, callback) {
this.record.show(cal, dates);
this.getWindow().on('hide', callback, cal, { single: true });
},
dayClick: function (cal, dt, allDay, el) {
this.record.show.call(this, cal, {
StartDate: dt, IsAllDay: allDay
}, el);
},
record: {
add: function (win, rec) {
win.hide();
rec.data.IsNew = false;
// save the event to database and update the event store
CompanyX.record.save(rec);
CompanyX.ShowMsg('Event ' + rec.data.Title + ' was added');
},
update: function (win, rec) {
win.hide();
rec.commit();
// update the event in the database
CompanyX.record.upd(rec);
CompanyX.ShowMsg('Event ' + rec.data.Title + ' was updated');
},
remove: function (win, rec) {
CompanyX.record.del(rec);
// remove the event from the database
this.getStore().remove(rec);
win.hide();
CompanyX.ShowMsg('Event ' + rec.data.Title + ' was deleted');
},
editFormAdd: function (win, rec) {
// called from the eventeditform EventAdd
// save the event to database and update the event store
CompanyX.record.save(rec);
},
editFormUpdate: function (win, rec) {
// called from the eventeditform EventUpdate
// update the event in the database
CompanyX.record.upd(rec);
},
editFormDelete: function (win, rec) {
// called from the eventeditform EventDelete
// delete the event from database
CompanyX.record.del(rec);
},
edit: function (win, rec) {
win.hide();
CompanyX.getCalendar().showEditForm(rec);
},
resize: function (cal, rec) {
rec.commit();
CompanyX.ShowMsg('Event ' + rec.data.Title + ' was updated');
},
move: function (cal, rec) {
rec.commit();
CompanyX.ShowMsg('Event ' + rec.data.Title + ' was moved to ' + rec.data.StartDate.format('F jS' + (rec.data.IsAllDay ? '' : ' \\a\\t g:i a')));
},
show: function (cal, rec, el) {
CompanyX.getWindow().show(rec, el);
},
save: function (rec) {
// save the event data to database
Ext.net.DirectMethod.request({
url: "RemoteService.asmx/Save",
json: true,
cleanRequest: true,
params: {
e: rec.data
},
success: function (result) {
// update record with db-assigned id
rec.data.EventId = result.EventId;
// add to store
CompanyX.getStore().add(rec);
}
});
},
upd: function (rec) {
// update event data in the database
// event data row is found in the database using EventId
Ext.net.DirectEvent.request({
url: "RemoteService.asmx/Update",
json: true,
cleanRequest: true,
extraParams: {
e: rec.data
}
});
},
del: function (rec) {
Ext.net.DirectEvent.request({
url: "RemoteService.asmx/Delete",
json: true,
cleanRequest: true,
extraParams: { e: rec.data }
});
}
}
};

var calendar_beforeRender = function (calendar, store) {
// localize event edit form
var form = calendar.get(calendar.id + '-edit');
form.titleTextAdd = 'Νέο Ραντεβού'; // form title when add
form.titleTextEdit = 'Επεξεργασία Ραντεβού'; // form title when edit

// left col
var lcol = form.get('left-col');
lcol.get(0).fieldLabel = 'Περιγραφή'; // Title
dateRangeItem = lcol.get(1);
dateRangeItem.fieldLabel = 'Από'; // When
dateRangeItem.toText = 'έως'; // to
dateRangeItem.allDayText = 'ολοήμερο'; // Is all day

// date-range date format
dateRangeItem.on('render', function () {
this.startDate.format = 'd/m/Y';
this.endDate.format = 'd/m/Y';
});

lcol.get(2).fieldLabel = 'Λόγος'; // Calendar group
lcol.get(3).fieldLabel = 'Υπενθύμιση'; // Reminder

// right col
var rcol = form.get('right-col');
rcol.get(0).fieldLabel = 'Σημειώσεις'; // Notes
rcol.get(1).fieldLabel = 'Τόπος'; // Location
rcol.get(2).fieldLabel = 'Άλλο'; // Url

// footer
form.fbar.get(0).text = 'Αποθήκευση'; // Save
form.fbar.get(1).text = 'Διαγραφή'; // Delete
form.fbar.get(2).text = 'Άκυρο'; // Cancel

// add new fields
// here a data-bound combo is added to the form
// The "customStore" store is decleared in markup and databound in code behind
var cmb = new Ext.form.ComboBox({
id: 'ComboBox1',
fieldLabel: 'CustomField',
store: store,
dataIndex: 'MyCustomField1',
mode: 'local',
displayField: 'lastname',
valueField: 'id',
anchor: '90%',
triggerAction: 'all',
selectOnFocus: true,
typeAhead: true
});

lcol.add(cmb);
};
</script>
</head>
<body>
<form id="Form1" runat="server">
<ext:ResourceManager ID="ResourceManager1" runat="server" Namespace="CompanyX" Locale="el-GR" />

<ext:Store ID="CustomStore" runat="server">
<Reader>
<ext:JsonReader IDProperty="id">
<Fields>
<ext:RecordField Name="id" Type="Int" />
<ext:RecordField Name="lastname" />
</Fields>
</ext:JsonReader>
</Reader>
</ext:Store>

<ext:Viewport ID="Viewport1" runat="server" Layout="fit">
<Items>
<ext:CalendarPanel ID="CalendarPanel1" runat="server" Height="500">
<EventStore ID="EventStore1"
runat="server"
IgnoreExtraFields="false">
</EventStore>
<GroupStore ID="GroupStore1" runat="server">
<Groups>
<ext:Group CalendarId="1" Title="Group 1" />
<ext:Group CalendarId="2" Title="Group 2" />
<ext:Group CalendarId="3" Title="Group 3" />
<ext:Group CalendarId="4" Title="Group 4" />
<ext:Group CalendarId="5" Title="Group 5" />
<ext:Group CalendarId="6" Title="Group 6" />
<ext:Group CalendarId="7" Title="Group 7" />
</Groups>
</GroupStore>
<Listeners>
<EventClick Fn="CompanyX.record.show" Scope="CompanyX" />
<DayClick Fn="CompanyX.dayClick" Scope="CompanyX" />
<RangeSelect Fn="CompanyX.rangeSelect" Scope="CompanyX" />
<EventMove Fn="CompanyX.record.move" Scope="CompanyX" />
<EventResize Fn="CompanyX.record.resize" Scope="CompanyX" />
<BeforeRender Handler="calendar_beforeRender(this, #{CustomStore});" />
<EventAdd Fn="CompanyX.record.editFormAdd" Scope="CompanyX" />
<EventUpdate Fn="CompanyX.record.editFormUpdate" Scope="CompanyX" />
<EventDelete Fn="CompanyX.record.editFormDelete" Scope="CompanyX" />
</Listeners>
</ext:CalendarPanel>
</Items>
</ext:Viewport>

<ext:EventEditWindow
ID="EventEditWindow1"
runat="server"
Hidden="true"
GroupStoreID="GroupStore1"
TitleTextAdd="New Event"
TitleTextEdit="Edit Event">
<Listeners>
<EventAdd Fn="CompanyX.record.add" Scope="CompanyX" />
<EventUpdate Fn="CompanyX.record.update" Scope="CompanyX" />
<EditDetails Fn="CompanyX.record.edit" Scope="CompanyX" />
<EventDelete Fn="CompanyX.record.remove" Scope="CompanyX" />
<Render Fn="CompanyX.customWindow" Scope="CompanyX" />
</Listeners>
</ext:EventEditWindow>

</form>
</body>
</html>


MyEventRecord.js


Ext.calendar.EventMappings = {
EventId: {
name: 'EventId',
mapping: 'id',
type: 'int'
},
CalendarId: {
name: 'CalendarId',
mapping: 'cid',
type: 'int'
},
Title: {
name: 'Title',
mapping: 'title',
type: 'string'
},
StartDate: {
name: 'StartDate',
mapping: 'start',
type: 'date',
dateFormat: 'c'
},
EndDate: {
name: 'EndDate',
mapping: 'end',
type: 'date',
dateFormat: 'c'
},
Location: {
name: 'Location',
mapping: 'loc',
type: 'string'
},
Notes: {
name: 'Notes',
mapping: 'notes',
type: 'string'
},
Url: {
name: 'Url',
mapping: 'url',
type: 'string'
},
IsAllDay: {
name: 'IsAllDay',
mapping: 'ad',
type: 'boolean'
},
Reminder: {
name: 'Reminder',
mapping: 'rem',
type: 'string'
},
IsNew: {
name: 'IsNew',
mapping: 'n',
type: 'boolean'
},
MyCustomField1: {
name: 'MyCustomField1',
mapping: 'MyCustomField1',
type: 'int'
}
};

(function () {
var M = Ext.calendar.EventMappings;

Ext.calendar.EventRecord = Ext.data.Record.create([
M.EventId,
M.CalendarId,
M.Title,
M.StartDate,
M.EndDate,
M.Location,
M.Notes,
M.Url,
M.IsAllDay,
M.Reminder,
M.IsNew,
M.MyCustomField1
]);

Ext.calendar.EventRecord.reconfigure = function () {
Ext.calendar.EventRecord = Ext.data.Record.create([
M.EventId,
M.CalendarId,
M.Title,
M.StartDate,
M.EndDate,
M.Location,
M.Notes,
M.Url,
M.IsAllDay,
M.Reminder,
M.IsNew,
M.MyCustomField1
]);
};
})();


Default.aspx.cs


using Ext.Net;

namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
[DirectMethod(Namespace = "CompanyX")]
public void ShowMsg(string msg)
{
X.Msg.Notify("Message", msg).Show();
}

protected void Page_Load(object sender, EventArgs e)
{
// config event store with standard plus custom fields
this.CalendarPanel1.EventStore.AddStandardFields() ;

// custom fields must also be declared in MyEventRecord.js
this.CalendarPanel1.EventStore.Reader[0].Fields.Add(new RecordField("MyCustomField1", RecordFieldType.Int));

if (!X.IsAjaxRequest)
{

// load combo with sample custom data
List<CustomField> data = new List<CustomField> {
new CustomField
{
id = 1,
lastname = "value 1"
},
new CustomField
{
id = 2,
lastname = "value 2"
}
};
CustomStore.DataSource = data;
CustomStore.DataBind();


// bind events to event store
RemoteService service = new RemoteService();
CalendarPanel1.EventStore.DataSource = service.GetEvents(null, null);
CalendarPanel1.EventStore.DataBind();
}
}
}
}


RemoteService.asmx


namespace WebApplication1
{
/// <summary>
/// Summary description for RemoteService
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class RemoteService : System.Web.Services.WebService
{
[WebMethod]
public IEnumerable<Event> GetEvents(DateTime? start, DateTime? end)
{
DateTime now = DateTime.Now;

return new List<Event>
{
new Event
{
EventId = 1001,
CalendarId = 1,
Title = "Vacation",
StartDate = now.AddDays(-20).AddHours(10),
EndDate = now.AddDays(-10).AddHours(15),
IsAllDay = false,
Notes = "Have fun",
MyCustomField1 = 1
}
};
}

[WebMethod]
public Event Save(Event e)
{
// save to database
// ...

// update EventId with real db-assigned id
// e.g e.EventId = 1;
return e;
}


[WebMethod]
public void Update(Event e)
{
// update database
// ...
}

[WebMethod]
public void Delete(Event e)
{
// delete from database
// ...
}
}
}


Event.cs


namespace WebApplication1
{
public class Event
{
public int EventId { get; set; }
public int CalendarId { get; set; }
public DateTime EndDate { get; set; }
public DateTime StartDate { get; set; }
public bool IsAllDay { get; set; }
public bool IsNew { get; set; }
public string Location { get; set; }
public string Notes { get; set; }
public string Reminder { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public int MyCustomField1 { get; set; }
}
}


CustomField.cs


namespace WebApplication1
{
public class CustomField
{
public int id { get; set; }
public string lastname { get; set; }
}
}


Screenshots show the localized and customized edit window and form.
4991
4992

Screenshot shows the execution stack when user clicks the Add button in the EventEditWindow
Notice how the client side record is updated with the EventId value
4995


Hope you like it. I've spent two weeks to make things work. Enjoy.


Cheers,
Dimitris

geoffrey.mcgill
Oct 30, 2012, 6:18 PM
Hi Dimitris,

Thanks for sharing this sample. We REALLY appreciate you giving back to the community.

Would you be able to add a screen shot of this running? Sorry for the extra work when you've already put in a lot, but I'm sure the screen capture would help others understand the context of what's happening in the sample.

We'll review your sample and offer any feedback.

Dimitris
Nov 29, 2012, 6:38 AM
It is my understanding that some people have had difficulty with the original sample code. Let me remind you this is about tweaking/extending the calendar control to suit custom data fields, event CRUD operations and full localization. I recommend you already have your calendar set up using the official ext.net example and use this code if it does not fit your needs.

Nevertheless, I've now changed the original sample above to one that is fully working as is. I've removed any database related code/references and have introduces stub methods and local sample data instead. Just copy and paste the code into a new Visual Studio ASP.NET Web Application Project and give it an immediate run.

Hope this helps

Daniil
Dec 07, 2012, 11:24 AM
Hi Dimitris,

Cool! Thank you very much for sharing, the corrections to get the example running locally and the screenshots!

I reviewed the code. It appears to be good. The great job!

I am moving the thread to the "Examples and Extras" forum.

Also I added "Ext.NET v1" in the title.

Dimitris
Feb 16, 2013, 3:34 AM
Here's a wee addition to the customization solution provided above: as people have noticed, in the event edit form, startTime and endTime combobox items retain their AM/PM format even after the field format has been changed, for example:

this.startTime.format = 'H:i';

After investigating into the javascript code I found out both fields fill a store with the time values. So, here is a possible solution to the problem:


// daterange date format
dateRangeItem.on('render', function () {
this.startDate.format = 'd/m/Y';
this.startTime.format = 'H:i';
this.endDate.format = 'd/m/Y';
this.endTime.format = 'H:i';

var localTimes = ['13:00', '14:00'];
this.endTime.getStore().removeAll();
this.endTime.getStore().loadData(localTimes);
});


Another nice thing you might find helpful is the customization of the form layout. By default, the form uses 'column' layout. If the form contents do not fill well in your web page width then simply change the layout, for example:


var form = calendar.get(calendar.id + '-edit');
form.titleTextAdd = '...';
form.titleTextEdit = '...';
form.layout = "form";


That's all folks.

Daniil
Feb 18, 2013, 3:36 AM
Thank you for sharing, Dimitris!

alonisoft
Feb 22, 2013, 6:10 PM
every calendar in this days should be available for us where ever we need it.
and the best choice for most users is GMAIL calendar.

is it difficult ?

Daniil
Sep 30, 2013, 5:22 AM
Hi @alonisoft,

I think your question is a good candidate for a new thread.

Exponentious
Jan 29, 2014, 6:01 PM
Didn't need all the extensions or custom stuff, but I struggle with UI admittedly. The styles at top helped me greatly.

hujq
Dec 01, 2014, 3:04 PM
hi @ Mimisss!

I have tried my project using EXT.NET 2.2 by your supplied example code, but it failed! so please give me a example like this using ext.net 2.2 SOLUTION for Ext.NET v2: Calendar with both edit window and form fully localized and customized!
thanks a lot!

Dimitris
Dec 02, 2014, 6:09 AM
As the thread title states this solution applies only to version 1.x.
The calendar control differs a lot in version 2.x so it is normal (or I'd rather say expected) the sample code fails to compile.