- Implementation of the Main Functionality;
- Creation of Date and Time Filter;
- Signing Rent Boxes with Rent Duration Period;
Part I. Implementation of the Main Functionality
Let’s create a simple application with a calendar that has a vertical car arrangement and a search field to filter the available cars by price and type.a) Initialization
As the first step, we create a new ASP.NET MVC 3 Web Application with the Razor view engine.
For initialization add the .dll library reference and scripts from the DHTMLX Scheduler .NET package to your project folder. If you need help with that, please, refer to the items 2-3 of our ASP.NET simple event calendar tutorial.
b) Database and Model Creation
Right-click on your project name in the Solution Explorer and add a new ASP.NET folder App_Data. Now create a new SQL Server Database and name it MyScheduler.mdf
To set up the application database create 3 tables – Type,Car and Order – and add the required columns:
The ‘Type’ table should only have the car id and the title values:
The ‘Order’ table is used to enable order placement with a car rental form. Therefore it should contain description, time period, pick up/drop off locations and car id columns:
When the tables are completed, create a data model LINQ to SQL Classes. Name it Rental and drag the newly created tables onto the LINQ to SQL designer surface.
To achieve this, change Parent Property Access from Public to Internal for each association:
1
2
3
4
5
6
7
8
9
10
11
12
13
| using System;using System.Collections.Generic;using System.Linq;using System.Web;using DHTMLX.Scheduler;namespace Rental.Models{ public class ViewModel { public DHXScheduler Scheduler { get; set; } public int CategoryCount { get; set; } } |
1
2
3
4
5
| public class FormState { public string Type { get; set; } public string Price { get; set; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <!DOCTYPE html><html><head> <title>@ViewBag.Title</title> <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script></head><body> <div class="page"> <div id="header"> </div> <div id="main"> @RenderBody() <div id="footer"> </div> </div> </div></body></html> |
The full code at this stage should look as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
| @{ ViewBag.Title = "Car Rental Service";}@model Rental.Models.ViewModel @Html.Raw(Model.Scheduler.GenerateCSS())@Html.Raw(Model.Scheduler.GenerateJS()) <style type="text/css"> /*override some scheduler css*/ #scheduler_here .dhx_cal_tab { display:none; } .dhx_cal_header div div { border:none; } .dhx_cal_data { background-color:#fff; } .dhx_cal_event_line { background-image:none; background-color:#FFDD71; font-size:13px; line-height:@((Model.Scheduler.Views[0] as DHTMLX.Scheduler.Controls.TimelineView).EventDy)px; } .dhx_matrix_scell { max-width:@((Model.Scheduler.Views[0] as DHTMLX.Scheduler.Controls.TimelineView).Dx)px; } .dhx_cal_event_line .dhx_event_resize { background-image: none; } </style> <script> //define template for timeline units function def_template(){ scheduler.templates['@((Model.Scheduler.Views[0]).Name)_scale_label']= function(key, label, obj){ return "<div style=\"width:100%\">\ <img src=\""+obj.link+"\" alt=\""+label+"\"></img><br/>\ <div class=\"car_brand\">"+label+"</div><div class=\"car_price\">$"+obj.price+"</div></div>"; }; scheduler.templates.event_bar_text = function (start, end, event) { return "Rented"; }; } </script><div style="height:750px"><div class="message" > @ViewBag.Message </div> <div style="float:left; width:230px;height:100%;"> @using (Html.BeginForm("Index", "Home", FormMethod.Post)) { <div class="search_form"> <div class="form_head"> <span class="rent_title">Rent a Car</span><br /> <span class="rent_small">with DHTMLX Scheduler .NET</span><br /> <div class="hd_line"></div> </div> <div class="controls"> <div> @Html.Label("Type", "Type:")<br /> @Html.DropDownList("Type") </div> <div> @Html.Label("Price", "Price:")<br /> @Html.DropDownList("Price") </div> <div> <input type="submit" id="submit" value="Search" /> </div> </div> </div> } </div> <div style="float:left; width:950px;height:100%;"> @if (!string.IsNullOrEmpty((string)ViewData["Message"] )) { <script> dhtmlx.message("@ViewData["Message"]"); </script> } @{ //calculate height of calendar container int rowHeigth = (Model.Scheduler.Views[0] as DHTMLX.Scheduler.Controls.TimelineView).Dy; int headerHeight = 45; int showRows = 7; int actualHeight = rowHeigth * Model.CategoryCount + headerHeight; int maxHeight = showRows * rowHeigth + headerHeight; } <div style="height:@(actualHeight < maxHeight ? actualHeight : maxHeight)px;"> @Html.Raw(Model.Scheduler.GenerateHTML()) </div> </div> <div class="clear"></div> <script> //after created/edited rent order - navigate calendar to its start date scheduler.attachEvent("onEventSave", function (id, data, is_new_event) { if (data.start_date) { scheduler.setCurrentView(new Date(data.start_date)); } return true; }); </script></div> |
This time we render the Scheduler in an unusual way to override some default css styles after loading and before Scheduler initialization. Instead of using Scheduler.Render () we did in the following way:
At first, we loaded the required css and js:
1
2
3
| @Html.Raw(Model.Scheduler.GenerateCSS()) @Html.Raw(Model.Scheduler.GenerateJS()) |
1
| @Html.Raw(Model.Scheduler.GenerateHTML()) |
The Scheduler initializes in the container of the set height. The number of cars (and consequently, the actual calendar height) can be different. It depends on the search form output. We’ve chosen the default height of 7 lines. If there are < 7 lines, the calendar height is equal to the number of lines. If there are > 7 lines in the calendar, scrolling is enabled while the calendar height is set to the default 7 lines.
1
2
3
4
5
6
7
8
| //calculate height of calendar container int rowHeigth = (Model.Scheduler.Views[0] as DHTMLX.Scheduler.Controls.TimelineView).Dy; int headerHeight = 45; int showRows = 7; int actualHeight = rowHeigth * Model.CategoryCount + headerHeight; int maxHeight = headerHeight + showRows * rowHeigth; }<div style="height:@(actualHeight < maxHeight ? actualHeight : maxHeight)px;"> |
1
2
3
4
5
6
7
8
9
10
| scheduler.templates['@((Model.Scheduler.Views[0]).Name)_scale_label']= function(key, label, obj){ return "<div style=\"width:100%\">\ <img src=\""+obj.link+"\" alt=\""+label+"\"></img><br/>\ <div class=\"car_brand\">"+label+"</div><div class=\"car_price\">$"+obj.price+"</div></div>"; }; scheduler.templates.event_bar_text = function (start, end, event) { return "Rented"; }; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
| body{ background: #5c87b2; background-image: url("./background_car_rent.png"); font-size: 75%; font-family:Arial,sans-serif; margin: 0; padding: 0; color: #696969;}.hd_line{ height:1px; background-image: url("./line.png"); width:100%; margin:10px 0 20px;}.rent_title{ color: #d4f4fc; font-size:18px;}.rent_small{ color: #d4e6fc; font-size:10px;} .search_form { width:173px; height:350px; padding:15px 20px; background-repeat:repeat-x; background-image: url("./bg_form.png");} .message{ text-align:right; color:White; font-size:18px; height:24px; padding:5px;}.car_brand, .car_price{ border-left:none !important; overflow:hidden;}.car_price{ color:Gray; width:100%; font-size:10px; margin-top:2px; text-align:center;}.car_brand{ white-space:nowrap; margin-top:-4px; color:Black; font-weight:bold;} .search_form input{ height:18px;}.search_form select{ height:19px; vertical-align:top;}.search_form option{ vertical-align:middle;}.search_form select, .search_form input{ font-size:11px; width:170px; padding: 0; margin: 5px 0;} #submit{ width: 170px; height: 23px; border: 0 none; background-color: #EFEFEF; line-height: 23px; margin-top: 15px; font-weight:bold;}.search_form { border:none; color:#eee;} .search_form .controls > div{ margin-top:9px;} /* PRIMARY LAYOUT ELEMENTS ----------------------------------------------------------*/ /* you can specify a greater or lesser percentage for thepage width. Or, you can specify an exact pixel width. */.page{ width: 90%; margin-left: auto; margin-right: auto;} #main{ padding: 30px 30px 15px 30px; _height: 1px; /* only IE6 applies CSS properties starting with an underscore */} .clear{ clear: both;} .page{ width:1200px;} #main{ padding:0;}.minical_container{ position:absolute; width:200px;} |
The next stage is to create a controller. Right-click on the Controllers folder in the Solution Explorer and create HomeController.cs.
The full code should look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
| using System.Linq;using System.Web;using System.Web.Mvc;using System.Collections;using System.Collections.Generic;using DHTMLX.Scheduler;using DHTMLX.Scheduler.Controls;using DHTMLX.Scheduler.Data;using DHTMLX.Common;using Rental.Models;namespace Rental.Controllers{ public class HomeController : Controller { public ActionResult Index(FormState state) { var scheduler = new DHXScheduler(this); scheduler.Extensions.Add(SchedulerExtensions.Extension.Collision); scheduler.Extensions.Add(SchedulerExtensions.Extension.Minical); //call custom template initialization scheduler.BeforeInit.Add("def_template();"); scheduler.Config.time_step = 60; //set row height scheduler.XY.bar_height = 76; scheduler.InitialValues.Add("text", ""); var context = new RentalDataContext(); //selecting cars according to form values var cars = _SelectCars(context, state); //if no cars found - show message and load default set if (cars.Count() == 0) { ViewData["Message"] = "Nothing was found on your request"; cars = _SelectCars(context);//select default set of events } //create custom details form var printableList = cars.Select(c => new { key = c.id, label = c.Brand, price = c.Price, link = c.Photo }); _ConfigureLightbox(scheduler, printableList); //load cars to the timeline view _ConfigureViews(scheduler, printableList); //assign ViewData values _UpdateViewData(scheduler, context, state); //data loading/saving settings scheduler.PreventCache(); scheduler.LoadData = true; scheduler.EnableDataprocessor = true; //collect model var model = new ViewModel(); model.Scheduler = scheduler; model.CategoryCount = cars.Count(); return View(model); } protected void _UpdateViewData(DHXScheduler scheduler, RentalDataContext context, FormState state) { ViewData["Price"] = _CreatePriceSelect(scheduler, state.Price); ViewData["Type"] = _CreateTypeSelect(context.Types, state.Type); } private List<SelectListItem> _CreateTypeSelect(IEnumerable<Models.Type> types, string selected) { var typesList = new List<SelectListItem>() { new SelectListItem(){Value = "", Text = "Any"} }; foreach (var type in types) { var item = new SelectListItem() { Value = type.id.ToString(), Text = string.Format("{0}: {1} cars", type.title, type.Cars.Count) }; if (item.Value == selected) item.Selected = true; typesList.Add(item); } return typesList; } private List<SelectListItem> _CreatePriceSelect(DHXScheduler scheduler, string selected) { var priceRanges = new string[] { "50-80", "80-120", "120-150" }; var prices = new List<SelectListItem>(){ new SelectListItem(){Value = "", Text = "Any"} }; foreach (var pr in priceRanges) { var item = new SelectListItem() { Value = pr, Text = string.Format("${0}", pr) }; if (pr == selected) item.Selected = true; prices.Add(item); } return prices; } protected IQueryable<Car> _SelectCars(RentalDataContext context) { return _SelectCars(context, null); } protected IQueryable<Car> _SelectCars(RentalDataContext context, FormState state) { IQueryable<Car> cars = from car in context.Cars select car; if (state == null) return cars; string _type = state.Type; string _price = state.Price; //filter by car type if (!string.IsNullOrEmpty(_type)) { int type = context.Types.First().id; if (!string.IsNullOrEmpty(_type)) { int.TryParse(_type, out type); } cars = cars.Where(c => c.TypeId == type); } //filter by price if (!string.IsNullOrEmpty(_price)) { var price = _price.Split('-'); int low = int.Parse(price[0]); int top = int.Parse(price[1]); cars = cars.Where(c => c.Price <= top && c.Price >= low); } return cars; } protected void _ConfigureViews(DHXScheduler scheduler, IEnumerable cars) { var units = new TimelineView("Orders", "car_id"); units.X_Step = 2; units.X_Length = 12; units.X_Size = 12; //width of the first column units.Dx = 149; //row height units.Dy = 76; //rent boxes height units.EventDy = units.Dy - 5; units.AddOptions(cars); units.RenderMode = TimelineView.RenderModes.Bar; scheduler.Views.Clear(); scheduler.Views.Add(units); scheduler.InitialView = scheduler.Views[0].Name; } protected void _ConfigureLightbox(DHXScheduler scheduler, IEnumerable cars) { scheduler.Lightbox.Add(new LightboxText("text", "Contact details") { Height = 42, Focus = true }); scheduler.Lightbox.Add(new LightboxText("description", "Note") { Height = 63 }); var select = new LightboxSelect("car_id", "Car Brand"); scheduler.Lightbox.Add(select); scheduler.Lightbox.Add(new LightboxText("pick_location", "Pick up location") { Height = 21 }); scheduler.Lightbox.Add(new LightboxText("drop_location", "Drop off location") { Height = 21 }); select.AddOptions(cars); scheduler.Lightbox.Add(new LightboxTime("time", "Time period")); } public ContentResult Data() { return new SchedulerAjaxData((new RentalDataContext()).Orders); } public ContentResult Save(int? id, FormCollection actionValues) { var action = new DataAction(actionValues); RentalDataContext data = new RentalDataContext(); try { var changedEvent = (Order)DHXEventsHelper.Bind(typeof(Order), actionValues); switch (action.Type) { case DataActionTypes.Insert: data.Orders.InsertOnSubmit(changedEvent); break; case DataActionTypes.Delete: changedEvent = data.Orders.SingleOrDefault(ev => ev.id == action.SourceId); data.Orders.DeleteOnSubmit(changedEvent); break; default:// "update" var eventToUpdate = data.Orders.SingleOrDefault(ev => ev.id == action.SourceId); DHXEventsHelper.Update(eventToUpdate, changedEvent, new List<string>() { "id" }); break; } data.SubmitChanges(); action.TargetId = changedEvent.id; } catch { action.Type = DataActionTypes.Error; } return (new AjaxSaveResponse(action)); } }} |
First of all, add ‘Collision extension’ to avoid multiple orders of one and the same car for the same period. At this step we also connect a mini-calendar that will be later required:
1
2
3
4
5
6
| public ActionResult Index(FormState state) { var scheduler = new DHXScheduler(this); scheduler.Extensions.Add(SchedulerExtensions.Extension.Collision); scheduler.Extensions.Add(SchedulerExtensions.Extension.Minical); |
1
2
3
4
5
6
7
8
| protected IQueryable<Car> _SelectCars(RentalDataContext context, FormState state) { IQueryable<Car> cars = from car in context.Cars select car; if (state == null) return cars; string _type = state.Type; string _price = state.Price; |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| //filter by car type if (!string.IsNullOrEmpty(_type)) { int type = context.Types.First().id; if (!string.IsNullOrEmpty(_type)) { int.TryParse(_type, out type); } cars = cars.Where(c => c.TypeId == type); }//filter by price if (!string.IsNullOrEmpty(_price)) { var price = _price.Split('-'); int low = int.Parse(price[0]); int top = int.Parse(price[1]); cars = cars.Where(c => c.Price <= top && c.Price >= low); } return cars; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| var units = new TimelineView("Orders", "car_id"); units.X_Step = 2; units.X_Length = 12; units.X_Size = 12; //width of the first column units.Dx = 149; //row height units.Dy = 76; //rent boxes height units.EventDy = units.Dy - 5; units.AddOptions(cars); units.RenderMode = TimelineView.RenderModes.Bar; scheduler.Views.Clear(); scheduler.Views.Add(units); scheduler.InitialView = scheduler.Views[0].Name; |
1
2
3
4
5
| protected void _UpdateViewData(DHXScheduler scheduler, RentalDataContext context, FormState state) { ViewData["Price"] = _CreatePriceSelect(scheduler , state.Price); ViewData["Type"] = _CreateTypeSelect(context.Types, state.Type); } |
1
2
3
4
5
6
7
8
9
10
11
12
| protected void _ConfigureLightbox(DHXScheduler scheduler, IEnumerable cars) { scheduler.Lightbox.Add(new LightboxText("text", "Contact details") { Height = 42, Focus = true }); scheduler.Lightbox.Add(new LightboxText("description", "Note") { Height = 63 }); var select = new LightboxSelect("car_id", "Car Brand"); scheduler.Lightbox.Add(select); scheduler.Lightbox.Add(new LightboxText("pick_location", "Pick up location") { Height = 21 }); scheduler.Lightbox.Add(new LightboxText("drop_location", "Drop off location") { Height = 21 }); select.AddOptions(cars); scheduler.Lightbox.Add(new LightboxTime("time", "Time period")); } |
Part II. Creation of Date and Time Filter
1
2
3
4
5
6
7
8
9
10
11
12
| <div> <span>Pick Up Date:</span><br /> @Html.TextBox("DateFrom") <img src="@Url.Content("~/Content/calendar.gif")" class="date_calendar" onclick="show_minical('DateFrom');"/> @Html.DropDownList("TimeFrom") </div> <div> <span>Drop Off Date:</span><br /> @Html.TextBox("DateTo") <img src="@Url.Content("~/Content/calendar.gif")" class="date_calendar" onclick="show_minical('DateTo');"/> @Html.DropDownList("TimeTo")</div> |
1
2
3
4
| <div class="check_dates"> <span>Only available: </span> @Html.CheckBox("DateFilter", true) </div> |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| .check_dates{ line-height:14px; margin-bottom:-10px; margin-top:0 !important;}.check_dates > input{ width:20px; vertical-align:middle;} #DateFrom, #DateTo{ width:80px;}#TimeFrom, #TimeTo{ width:60px;}.date_calendar{ cursor:pointer; height:18px; left:-2px; position:relative; vertical-align:baseline; top:1px; width:18px;} |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| /*Date picker behavior*/scheduler.pickerDateFormat = "%m/%d/%Y"; function remove_minical() { if (scheduler._calendar) { scheduler.destroyCalendar(scheduler._calendar); $("#minical_cont").remove(); scheduler._calendar = null; }}function show_minical(id) { //if mini calendar already shown - destroy it if (scheduler._calendar) { remove_minical(); } //create container for the mini calendar var position = $("#" + id).position(); var div = $("<div></div>") .attr('id', 'minical_cont').attr('class', 'minical_container') .css('left', position.left).css('top', position.top); $('body').append(div); //create minicalendar scheduler._calendar = scheduler.renderCalendar({ container: "minical_cont", date: scheduler._date, navigation: true, handler: function (date, calendar) { //on click - put selected value to the input $("#" + id).val(scheduler.date.date_to_str(scheduler.pickerDateFormat)(date)); //and remove calendar remove_minical(); //check if 'To' date is later than 'From' date if (!areDatesCorrect()) { showWarning(); } } }); function areDatesCorrect() { var from = $("#DateFrom").val(), to = $("#DateTo").val(); if (from && to) { //function to convert string to date object var converter = scheduler.date.str_to_date(scheduler.pickerDateFormat); from = converter(from); to = converter(to); if (from && to) {//if converted successfully if (from.getTime() > to.getTime()) //return false only if start date is later than end date, other cases are valid return false; } } return true; } function showWarning() { $("#DateTo").val(""); dhtmlx.message("Pick up date must be before Drop off date!"); }} |
1
| <script src="@Url.Content("~/Scripts/scripts.js")" type="text/javascript"></script> |
a) Model
Update ViewModel.cs by adding properties for the new controls to the FormState:
1
2
3
4
5
6
7
8
9
10
| public class FormState{ public string DateFrom { get; set; } public string DateTo { get; set; } public string TimeFrom { get; set; } public string TimeTo { get; set; } public string Type { get; set; } public string Price { get; set; } public bool DateFilter { get; set; }} |
Date is selected from the two inputs – date picker and ‘select time’ drop-down list. We add a function to HomeController.cs to parse the date to the DataTime object.
1
2
3
4
5
6
7
| private DateTime _ParseDate(string date, string time) { var datestr = string.Format("{0} {1}", date, time); DateTime result = new DateTime(); DateTime.TryParse(datestr, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out result); return result; } |
1
2
3
4
5
6
7
8
9
| public ActionResult Index(FormState state) {…if (_ParseDate(state.DateFrom, state.TimeFrom) != default(DateTime)) { scheduler.InitialDate = _ParseDate(state.DateFrom, state.TimeFrom); }… } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| protected IQueryable<Car> _SelectCars(RentalDataContext context) {…var _from = default(DateTime); var _to = default(DateTime); //try to parse time range if (!string.IsNullOrEmpty(state.DateFrom)) { _from = _ParseDate(state.DateFrom, state.TimeFrom); _to = _ParseDate(state.DateTo, state.TimeTo); if (_from.CompareTo(default(DateTime)) != 0 && _to.CompareTo(default(DateTime)) == 0)//only start date set { _to = _from.AddHours(1); } } if (state.DateFilter && _from != default(DateTime) && _to != default(DateTime)) { //select cars, which are available in specified time range cars = from car in cars where car.Orders.Count == 0 || car.Orders.Where(o => o.end_date > _from && o.start_date < _to).Count() == 0 select car ; } return cars; } |
1
2
3
4
5
6
7
8
9
10
| protected void _UpdateViewData(DHXScheduler scheduler, RentalDataContext context, FormState state) { ViewData["Price"] = _CreatePriceSelect(scheduler , state.Price); ViewData["Type"] = _CreateTypeSelect(context.Types, state.Type) ; ViewData["DateFrom"] = state.DateFrom; ViewData["DateTo"] = state.DateTo; ViewData["TimeFrom"] = _CreateTimeSelect(scheduler, state.TimeFrom); ViewData["TimeTo"] = _CreateTimeSelect(scheduler, state.TimeTo); ViewData["DateFilter"] = state.DateFilter; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
| using System;using System.Collections.Generic;using System.Collections;using System.Linq;using System.Web;using System.Web.Mvc;using DHTMLX.Scheduler;using DHTMLX.Scheduler.Controls;using DHTMLX.Scheduler.Data;using DHTMLX.Common;using Rental.Models;namespace Rental.Controllers{ public class HomeController : Controller { public ActionResult Index(FormState state) { var scheduler = new DHXScheduler(this); scheduler.Extensions.Add(SchedulerExtensions.Extension.Collision); scheduler.Extensions.Add(SchedulerExtensions.Extension.Minical); //call custom template initialization scheduler.BeforeInit.Add("def_template();"); scheduler.Config.time_step = 60; //set row height scheduler.XY.bar_height = 76; scheduler.InitialValues.Add("text", ""); //if 'Pick Up Date' selected - make it initial date for calendar if (_ParseDate(state.DateFrom, state.TimeFrom) != default(DateTime)) { scheduler.InitialDate = _ParseDate(state.DateFrom, state.TimeFrom); } var context = new RentalDataContext(); //selecting cars according to form values var cars = _SelectCars(context, state); //if no cars found - show message and load default set if (cars.Count() == 0) { ViewData["Message"] = "Nothing was found on your request"; cars = _SelectCars(context);//select default set of events } //create custom details form var printableList = cars.Select(c => new { key = c.id, label = c.Brand, price = c.Price, link = c.Photo }); _ConfigureLightbox(scheduler, printableList); //load cars to the timeline view _ConfigureViews(scheduler, printableList); //assign ViewData values _UpdateViewData(scheduler, context, state); //data loading/saving settings scheduler.PreventCache(); scheduler.LoadData = true; scheduler.EnableDataprocessor = true; //collect model var model = new ViewModel(); model.Scheduler = scheduler; model.CategoryCount = cars.Count(); return View(model); } protected void _UpdateViewData(DHXScheduler scheduler, RentalDataContext context, FormState state) { ViewData["Price"] = _CreatePriceSelect(scheduler, state.Price); ViewData["Type"] = _CreateTypeSelect(context.Types, state.Type); ViewData["DateFrom"] = state.DateFrom; ViewData["DateTo"] = state.DateTo; ViewData["TimeFrom"] = _CreateTimeSelect(scheduler, state.TimeFrom); ViewData["TimeTo"] = _CreateTimeSelect(scheduler, state.TimeTo); ViewData["DateFilter"] = state.DateFilter; } private List<SelectListItem> _CreateTypeSelect(IEnumerable<Models.Type> types, string selected) { var typesList = new List<SelectListItem>() { new SelectListItem(){Value = "", Text = "Any"} }; foreach (var type in types) { var item = new SelectListItem() { Value = type.id.ToString(), Text = string.Format("{0}: {1} cars", type.title, type.Cars.Count) }; if (item.Value == selected) item.Selected = true; typesList.Add(item); } return typesList; } private List<SelectListItem> _CreatePriceSelect(DHXScheduler scheduler, string selected) { var priceRanges = new string[] { "50-80", "80-120", "120-150" }; var prices = new List<SelectListItem>(){ new SelectListItem(){Value = "", Text = "Any"} }; foreach (var pr in priceRanges) { var item = new SelectListItem() { Value = pr, Text = string.Format("${0}", pr) }; if (pr == selected) item.Selected = true; prices.Add(item); } return prices; } private List<SelectListItem> _CreateTimeSelect(DHXScheduler scheduler, string selected) { var opts = new List<SelectListItem>(); for (var i = scheduler.Config.first_hour; i < scheduler.Config.last_hour; i++) { var value = string.Format("{0}:00", i < 10 ? "0" + i.ToString() : i.ToString()); var item = new SelectListItem() { Text = value, Value = value }; if (value == selected) item.Selected = true; opts.Add(item); } return opts; } protected IQueryable<Car> _SelectCars(RentalDataContext context) { return _SelectCars(context, null); } private DateTime _ParseDate(string date, string time) { var datestr = string.Format("{0} {1}", date, time); DateTime result = new DateTime(); DateTime.TryParse(datestr, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out result); return result; } protected IQueryable<Car> _SelectCars(RentalDataContext context, FormState state) { IQueryable<Car> cars = from car in context.Cars select car; if (state == null) return cars; string _type = state.Type; string _price = state.Price; var _from = default(DateTime); var _to = default(DateTime); //try to parse time range if (!string.IsNullOrEmpty(state.DateFrom)) { _from = _ParseDate(state.DateFrom, state.TimeFrom); _to = _ParseDate(state.DateTo, state.TimeTo); if (_from.CompareTo(default(DateTime)) != 0 && _to.CompareTo(default(DateTime)) == 0)//only start date set { _to = _from.AddHours(1); } } //filter by car type if (!string.IsNullOrEmpty(_type)) { int type = context.Types.First().id; if (!string.IsNullOrEmpty(_type)) { int.TryParse(_type, out type); } cars = cars.Where(c => c.TypeId == type); } //filter by price if (!string.IsNullOrEmpty(_price)) { var price = _price.Split('-'); int low = int.Parse(price[0]); int top = int.Parse(price[1]); cars = cars.Where(c => c.Price <= top && c.Price >= low); } if (state.DateFilter && _from != default(DateTime) && _to != default(DateTime)) { //select cars, which are available in specified time range cars = from car in cars where car.Orders.Count == 0 || car.Orders.Where(o => o.end_date > _from && o.start_date < _to).Count() == 0 select car; } return cars; } protected void _ConfigureViews(DHXScheduler scheduler, IEnumerable cars) { var units = new TimelineView("Orders", "car_id"); units.X_Step = 2; units.X_Length = 12; units.X_Size = 12; //width of the first column units.Dx = 149; //row height units.Dy = 76; //order bar height units.EventDy = units.Dy - 5; units.AddOptions(cars); units.RenderMode = TimelineView.RenderModes.Bar; scheduler.Views.Clear(); scheduler.Views.Add(units); scheduler.InitialView = scheduler.Views[0].Name; } protected void _ConfigureLightbox(DHXScheduler scheduler, IEnumerable cars) { scheduler.Lightbox.Add(new LightboxText("text", "Contact details") { Height = 42, Focus = true }); scheduler.Lightbox.Add(new LightboxText("description", "Note") { Height = 63 }); var select = new LightboxSelect("car_id", "Car Brand"); scheduler.Lightbox.Add(select); scheduler.Lightbox.Add(new LightboxText("pick_location", "Pick up location") { Height = 21 }); scheduler.Lightbox.Add(new LightboxText("drop_location", "Drop off location") { Height = 21 }); select.AddOptions(cars); scheduler.Lightbox.Add(new LightboxTime("time", "Time period")); } public ContentResult Data() { return new SchedulerAjaxData((new RentalDataContext()).Orders); } public ContentResult Save(int? id, FormCollection actionValues) { var action = new DataAction(actionValues); RentalDataContext data = new RentalDataContext(); try { var changedEvent = (Order)DHXEventsHelper.Bind(typeof(Order), actionValues); switch (action.Type) { case DataActionTypes.Insert: data.Orders.InsertOnSubmit(changedEvent); break; case DataActionTypes.Delete: changedEvent = data.Orders.SingleOrDefault(ev => ev.id == action.SourceId); data.Orders.DeleteOnSubmit(changedEvent); break; default:// "update" var eventToUpdate = data.Orders.SingleOrDefault(ev => ev.id == action.SourceId); DHXEventsHelper.Update(eventToUpdate, changedEvent, new List<string>() { "id" }); break; } data.SubmitChanges(); action.TargetId = changedEvent.id; } catch { action.Type = DataActionTypes.Error; } return (new AjaxSaveResponse(action)); } }} |
Part III. Signing rent boxes with rent duration period
Customize the template to show the rent duration period in the rent box (e.g. “Rented for 4 hours”). Add the required attributes to scripts.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| //set displayed text var durations = { day: 24 * 60 * 60 * 1000, hour: 60 * 60 * 1000}; var get_formatted_duration = function (start, end) { var diff = end - start; var days = Math.floor(diff / durations.day); diff -= days * durations.day; var hours = Math.floor(diff / durations.hour); diff -= hours * durations.hour; var results = []; if (days) results.push(days + " days"); if (hours) results.push(hours + " hours"); return results.join(", ");}; |
1
2
3
| scheduler.templates.event_bar_text = function (start, end, event) { var text = "Rented"; return text + " for " + get_formatted_duration(start, end); |
No comments:
Post a Comment
Thanks for your valuable comment...........
Md. Mominul Islam