Introduction
License
Installation
How to use
How it works
Points of interest
Future
Update history
Introduction
These are 2 free controls, ScheduleCalendar and ScheduleGeneral, designed
to show scheduled events in the form of a table. They are simple versions of the
so-called Gantt chart. They don't have advanced features such as dependencies and
milestones, but on the other hand, they use templated databinding, so it's up to
you, the developer, what you want to show.
The controls can be used for a broad variety of applications: time tables, resource
usage planners, calendars, event schedulers, activities, reservations, sequences,
project management, etc... See the demos for some examples.
The ScheduleCalendar control for instance will turn this data:
|
StartTime
|
EndTime
|
EventDate
|
Task
|
|
9:00
|
11:00
|
3/8/2004
|
History
|
|
9:00
|
10:00
|
3/9/2004
|
Math
|
|
10:00
|
11:00
|
3/9/2004
|
Biology
|
|
11:00
|
12:00
|
3/9/2004
|
History
|
|
9:00
|
10:00
|
3/10/2004
|
Geology
|
|
10:00
|
12:00
|
3/10/2004
|
Math
|
|
9:00
|
10:00
|
3/11/2004
|
Economy
|
|
10:00
|
12:00
|
3/11/2004
|
Literature
|
|
9:00
|
12:00
|
3/12/2004
|
Sports
|
|
9:00
|
11:00
|
3/15/2004
|
History
|
|
9:00
|
10:00
|
3/16/2004
|
Math
|
|
10:00
|
11:00
|
3/16/2004
|
Biology
|
|
11:00
|
12:00
|
3/16/2004
|
History
|
|
9:00
|
10:00
|
3/17/2004
|
Geology
|
|
10:00
|
12:00
|
3/17/2004
|
Math
|
|
9:00
|
10:00
|
3/18/2004
|
Economy
|
|
10:00
|
12:00
|
3/18/2004
|
Literature
|
|
9:00
|
12:00
|
3/19/2004
|
Sports
|
into this presentation:
The other control is the ScheduleGeneral. Here you can provide
your own titles (you may think of people, resources or location names). Here's an
example with TV stations:
Automatically, overlapping events will cause extra rows or columns to be added to
the table, so that they can be shown correctly.
Overlapping items:
The controls are written in VB.NET, but they can be used in any ASP.NET application.
You may need to slightly adapt some of the sample code below to match the language
used in your application.
The controls come in two versions, one for ASP.NET 1.x (schedule.dll) and one for
ASP.NET 2.0 (schedule2.dll). The second version uses some of the new classes in
2.0 to enable new features, such as declarative databinding and smart tags. Each
version comes with its own documentation file: "documentation.chm" is for 1.0 and
"documentation2.chm" is for 2.0.
Try
the online demo
License
The controls are free and open source under the LGPL license.
Installation
Installing the controls
You can install the controls in the standard way:
- create a "bin" folder inside the application root of your website (if it's not already
there)
- copy the assembly file schedule.dll (for ASP.NET 1.x) or schedule2.dll (for ASP.NET
2.0) into the bin folder
You may want to add the controls to the toolbox of your editor (Visual Studio, Web
Matrix or C# Builder). This will allow you to set their properties in the Property
Pane. Follow the editor's procedure to add a control to the toolbox.
In order to support IntelliSense in HTML View in Visual Studio (and to avoid some
compiler warnings):
- copy the schedule.xsd file to this folder
- VS 2003:
C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml
- VS 2005:
C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\schemas\xml
- modify the
<body> tag of any new web form as follows: <body xmlns:cc1="urn:http://www.rekenwonder.com/schedule">
where cc1 is the TagPrefix of the schedule control (the one assigned in the "Register"
directive).
Installing the demos
I added three sets of demo files:
- a set of simple demo files with inline code, that can be used with Visual Studio
or Web Matrix with ASP.NET 1.x
- a Visual Studio project with codebehind for ASP.NET 1.x.
- a Visual Web Developer 2005 (or Visual Studio 2005) project with inline code for
ASP.NET 2.0
Unzip the zipfile with the demos to a subfolder of the wwwroot folder. Then, either
create a virtual directory for this folder in IIS, or copy the schedule.dll or schedule2.dll
file to the bin folder in wwwroot.
For the demo files with inline code, that's all you have to do. Just call the first
demo page through the address http://localhost/<foldername>/demo1.aspx in
your browser.
For the Visual Studio project, open Visual Studio and create a new blank solution.
Right click the new solution in the Solution Explorer, choose Add - New Project
From Web..., enter the url http://localhost/<foldername> (<foldername>
is the folder where you unzipped the demo files), and click OK. Select the project
file when prompted. You should also create a virtual directory for the demo folder.
For the Visual Web Developer 2005 project, open Visual Web Developer and choose
"Open existing Web site". Browse to the folder where the demo files are located.
Using the controls
Adding the controls to your page
There are 2 ways to add the controls to your page:
- Drag the control from the toolbox onto the page (if it was installed on the toolbox).
Be careful to select the correct type: ScheduleCalendar for a weekly
calendar schedule, ScheduleGeneral for other schedules.
- Add the code manually. Add this line to the top of your page:
<%@ Register TagPrefix="rw" Namespace="rw" Assembly="Schedule" %>
Use Assembly="Schedule2" when you want to use the ASP.NET 2.0 version.
Then, add a tag like this where you want the schedule to be displayed:
<rw:ScheduleGeneral id="Schedule1" runat="server" >
<ItemTemplate>
</ItemTemplate>
</rw:ScheduleGeneral>
Of course, for the calendar version, you use ScheduleCalendar.
If you're using code-behind, you should also add a reference to the Schedule instance
in your page class, like this:
Protected WithEvents Schedule1 As rw.ScheduleGeneral
In the code-behind page, also add this import statement:
Imports rw
Setting the control's properties
Make sure you use the appropriate settings.
The DataSource property should be either a DataSet, a
DataTable or a DataView. You can't databind to a DataReader,
since the control must be able to sort the data internally.
For each item on the schedule, the data source should provide a single record containing
a start value, an end value, and custom data (typically a description of the task
or event). For the ScheduleCalendar control when TimeFieldsContainDate=false,
you also need a date value. For the ScheduleGeneral control you also need a title
value (for display on column/row headers).
You need to set 3 properties (similar to the 2 properties DataTextField
and DataValueField that you have to set for a bound ListBox or CheckBoxList):
ScheduleCalendar:
StartTimeField: the database field (column) containing the start of
the event
EndTimeField: the database field containing the end of the event
DateField: the database field providing the dates. This field should
be of type "Date" (exception: when TimeFieldsContainDate =true this
field is ignored, see below). In a vertical layout, this field will be used for
column titles. In a horizontal layout, it will be used for row titles.
ScheduleGeneral:
DataRangeStartField: the database field (column) containing the start
of the event
DataRangeEndField: the database field containing the end of the event
TitleField: the database field providing the titles. You can think
of some useful fields for titles: people names, locations, classrooms, resources,
etc...) In a vertical layout, this field will be used for column titles. In a horizontal
layout, it will be used for row titles.
1) and 2) can be thought of as time fields, but in general, any type of sortable
field (such as an integer) will do. Only when TimeFieldsContainDate=true
for the ScheduleCalendar control, they should contain date and time information,
and when FullTimeScale=True, they should contain at least time information.
Note that there is no property to indicate which field contains the "Task" or "Event"
information. Actually, this information can be anything: a field, a combination
of multiple fields, etc... This is possible since the "Task" data is provided through
a databinding expression in the Item template. Its simplest form would be something
like this for ASP.NET 1.x:
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"taskfieldname") %>
</ItemTemplate>
and for ASP.NET 2.0:
<ItemTemplate>
<%# Eval("taskfieldname") %>
</ItemTemplate>
Optionally, you can set the ItemStyleField. When its value is not blank,
the database field with this name will be used as a css style class for the item.
This lets you set the style for each item separately from the data source.
Then, there's the FullTimeScale property. When false (the default
value), only range values occurring in the data source are shown. When true, a continuous
time scale is displayed (like the Outlook calendar). For the ScheduleGeneral control,
this requires the DataRangeStartField and DataRangeEndField
fields in the data source to be of type Date/Time.
The image below shows the the same item (Lunch from 12:30 to 13:30) in both cases:
|
FullTimeScale = false
|
12:30
|
Lunch from 12:30 to 13:30
|
|
13:30
|
|
|
|
FullTimeScale = true and TimeScaleInterval = 15
|
12:30
|
Lunch from 12:30 to 13:30
|
|
12:45
|
|
13:00
|
|
13:15
|
|
13:30
|
|
|
When FullTimeScale is true, there are 3 additional properties that
you can set:
TimeScaleInterval: an integer value giving the number of minutes for
the interval (default=60)
StartOfTimeScale: a timespan value setting the start time of the time
scale (default=8:00)
EndOfTimeScale: a timespan value setting the end time of the time scale
(default=17:00). The highest value allowed here is 24:00 (midnight), which should
be entered as "1.00:00" (one day, zero hours and minutes).
Two other important properties are IncludeEndValue and ShowValueMarks.
When IncludeEndValue is True, the row with the end value
will be included in the event. Sometimes, this will result in a more logical presentation.
However, when the items are adjacent (for instance: one event ends at 10:00 AM,
and another starts at the same time), it's better to set IncludeEndValue
to False. Otherwise, the items will be shown as overlapping. In this
case, you can also set ShowValueMarks to True, which will
add marks indicating the values, and the values will be shown in the middle of the
item. The default value is False for both.
The image below shows the the same item (Lunch from 12:30 to 13:30) in all cases:
IncludeEndvalue = False and ShowValueMarks = False
|
12:30
|
Lunch from 12:30 to 13:30
|
|
13:00
|
|
13:30
|
|
|
14:00
|
|
IncludeEndValue = True (ShowValueMarks is ignored)
|
12:30
|
Lunch from 12:30 to 13:30
|
|
13:00
|
|
13:30
|
|
14:00
|
|
IncludeEndValue = False and ShowValueMarks = True
|
12:30
|
|
|
|
|
Lunch from 12:30 to 13:30
|
|
13:00
|
|
|
|
|
13:30
|
|
|
|
|
|
14:00
|
|
|
|
|
Finally, you may want to add functionality for inserting new items when the user
clicks on an "empty slot" in the schedule. Set the EnableEmptySlotClick
property to true, and handle the OnEmptySlotClick event. Optionally,
set a tooltip for empty slots with the EmptySlotTooltip property. For
samples, see demo1 and demo2.
For the ScheduleCalendar control, you should set these additional properties:
NumberOfDays: the number of days to show at a time. The default value
is 7, making it a weekly calendar.
NumberOfRepetitions: the number of times to repeat the number of days
(the default setting is 1 repetition). This will only work in vertical layout, meaning
that the NumberOfDays is shown once on top, and repeated according to the setting
of NumberOfRepetitions.
In horizontal layout, this value is ignored, and the number of days is shown only
once.
StartDay: the day of the week to start the calendar. This property
is only used when the NumberOfDays property is set to 7. The default value is Monday.
StartDate: date to start displaying events. If this date is not on
the same day of the week as the StartDay property, the control will start displaying
on the same day prior to the chosen date. The default value is today's date. See
the demos to learn how to set today's date programmatically.
TimeFieldsContainDate: when false (the default), the DateField
property is used to extract dates for each item. This means that items must start
and end on the same day. If some of your items span midnight, you should set TimeFieldsContainDate
to true, and you should provide a full date and time in both the StartTimeField
and EndTimeField fields. In this case, the DateField property
is ignored.
The ScheduleGeneral control has two additional properties:
AutoSortTitles: When true, the titles are sorted automatically, even
when they are not sorted in the data source. When false, you may provide your own
sorting order for the titles, but make sure that the items with the same titles
are grouped together, and that for each title, the items are sorted on DataRangeStartField
first and on DataRangeEndField next. (for example: if you want to sort on a field
called "SortOrder", the DataRangeStartField is "StartTime", and the DataRangeEndField
is "EndTime", use the sorting expression "ORDER BY SortOrder ASC, StartTime ASC,
EndTime ASC") The default value for AutoSortTitles is true.
SeparateDateHeader: When true, an extra range of cells will be added
to group all events of the same date. This requires DataRangeStartField
and DataRangeEndField to be of type DateTime. The default
is False.
|
SeparateDateHeader=False
|
12/9/2004 14:00
|
Task 1
|
|
12/9/2004 15:00
|
|
12/10/2004 14:00
|
Task 2
|
|
12/10/2004 15:00
|
|
|
SeparateDateHeader= True
|
12/9/2004
|
14:00
|
Task 1
|
|
15:00
|
|
12/10/2004
|
14:00
|
Task 2
|
|
15:00
|
|
For both the ScheduleCalendar and ScheduleGeneral controls, you can easily switch
between a vertical or a horizontal layout with the Layout property,
even at run time.
Providing template content
The next step is to add the content of the templates.
You can right-click on the control to edit the templates, or you can create the
templates in HTML view. Also look at the templates in the demo pages for samples.
First of all, you should provide the
<ItemTemplate>
<%# DataBinder.Eval(Container.DataItem,"taskfieldname") %>
</ItemTemplate>
With ASP.NET 2.0, you can use:
<ItemTemplate>
<%# Eval("taskfieldname") %>
</ItemTemplate>
If you want, you can combine information from several fields in the database. You
can also add controls, images, just like you can in a Repeater or DataList control.
The other templates are for the headers. Especially in the case of a time and date
fields, you may want to format the content for proper display.
For the ScheduleGeneral control:
You just show the data:
<TitleTemplate>
<%# Container.DataItem %>
</TitleTemplate>
Typically, you could display the range header item as a short time:
<RangeHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortTimeString() %>
</RangeHeaderTemplate>
DateHeaderTemplate (only used when SeparateDateHeader
is set to true)
Typically, you could display the date header item as a short date:
<DateHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortDateString() %>
</DateHeaderTemplate>
For the ScheduleCalendar control:
You could typically display the date header as a short date:
<TitleTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortDateString() %>
</TitleTemplate>
Typically, you could display the time header item as a short time:
<RangeHeaderTemplate>
<%# Convert.ToDateTime(Container.DataItem).ToShortTimeString() %>
</RangeHeaderTemplate>
In your code, don't forget to call the DataBind() method. Without this
call, nothing will be shown.
Finally, there's the EmptyDataTemplate. Optionally, you can use this
template to provide a message to be shown when no data is found in the data source.
This template is not used by the ScheduleCalendar control when FullTimeScale=True.
New features in ASP.NET 2.0
The controls can now be bound to a DataSourceControl, just like you can with a GridView
control. Use the smart tag menu and the "Select Data Source" wizard.
The Schedule control can also be used in Master/Detail scenarios. Typically, you
can link it to a FormView or a DetailsView control, which allows for easy editing
of data items. Make sure to include a primary key field in the data source for the
Schedule control, and set the DataKeyNames property to the name of that primary
key field.
After adding the FormView or the DetailsView control, use the "Select Data Source"
wizard. Use the WHERE button to link the control to the Schedule control.
Upgrading from a version prior to 1.5
If you have projects working with a version prior to 1.5, you need to upgrade them.
The Schedule class has been removed from the assembly, from now on you'll have to
use either ScheduleCalendar or ScheduleGeneral.
If your project contains a Schedule control with DisplayMode=Calendar:
- Change the control's type from Schedule to ScheduleCalendar (just change the type
manually in the page and in the codebehind file if you use one)
- Rename the TitleTemplate into a DateTemplate (with the same content)
- Rename the RangeHeaderTemplate into a TimeTemplate (with the same content)
- Rename the TitleField property into a DateField property (with the same value)
- Rename the RangeHeaderStartField property into a StartTimeField property (with the
same value)
- Rename the RangeHeaderEndField property into an EndTimeField property (with the
same value)
- Rename the UseTitleFieldAsDate property into a TimeFieldsContainDate property (with
the opposite value, change true into false and false into true)
- Rename the TitleStyle tag to DateStyle, or remove it when there is a DateStyle tag
already
- Rename the RangeHeaderStyle tag to TimeStyle, or remove it when there is a TimeStyle
tag already
If your project contains a Schedule control with DisplayMode=General:
- Change the control's type from Schedule into ScheduleGeneral (just change the type
manually in the page and in the codebehind file if you use one)
- If some properties (such as Weeks, StartDate and UseTitleFieldAsDate) that were
not needed for DisplayMode=General were given a value anyway, remove them.
Demo pages
Download the demo pages for samples showing you how the Schedule control could be
used. In each sample, you can set the properties with the help of a form and you
can add items. These samples use an Access database as the data source.
- demo1.aspx shows a sample of a calendar. It also shows you how to delete
items with a linkbutton.
- demo2.aspx shows a sample of a task list. It also shows you how to edit and
delete items with a linkbutton.
- demo3.aspx shows a sample of a TV program schedule.
- demo4.aspx shows a sample of a calendar with items that span midnight.
Try
demo1 online
How it works
I decided to use a table for displaying all the items, instead of graphics. Graphics
can put a heavy load on the server, and most importantly: a table can have child
controls such as checkboxes and hyperlinks. By using templates, a developer has
full control about the content of the items.
I used Microsoft's sample code for the TemplatedList control as a starting point.
I created a BaseSchedule control, which contains the code that is common for ScheduleGeneral
and ScheduleCalendar.
First, all the information is extracted from the data source in order to create
lists for the row and column headers. Any double values are removed, and the remainder
is sorted in ascending order.
Next, an empty Table web control is built, with the correct number of rows and columns.
Then, the header items are added (row headers and column headers).
Finally, the scheduled items are added to the body of the table. The most difficult
part is to calculate the position and span of each item, to merge the corresponding
cells, and to check whether items don't overlap.
For the control to work correctly on postback, the control tree needs to be rebuilt
in exactly the same way (without setting any properties). To make this possible,
some information has to be stored in the control's ViewState:
- the number of rows
- the number of cells in each row
- the number of header cells in each row
- the position (row and column index) of each item
In the CreateChildControls method, this information is used to build
the control tree.
On postback, viewstate will be applied automatically to each child control, and
their state will be restored magically.
For designer support, the controls are based on Andy Smith's SimpleTemplatedControlDesigner class.
For the ASP.NET 2.0 version, I derived from the new CompositeDataBoundControl class.
I added the SelectedValue property, in order to enable master/detail scenarios with
FormView and DetailsView controls.
The designers are derived from the new DataBoundControlDesigner class, which eliminates
the need for the SimpleTemplatedControlDesigner code.
I had a lot of trouble figuring out how to do it in ASP.NET 2.0, since a lot of
documentation was still missing mid 2005.
Future
Here are some ideas for improvement:
- Add a
DeleteCommand event
- Add a
TodayStyle property (similar to the one in the Calendar control)
- Provide support for right-to-left languages
- Add dragging support for selecting a range (Outlook style)
- Support for recurring events
- Auto Format option
- Windows Forms version
If anyone decides to extend this control, or has any comments, bug reports or questions,
then it would be great to hear from you.
Points of interest
- templated control
- databound control
- control styles
- designer support
- property builders
- ASP.NET 2.0 databound control
- ASP.NET 2.0 designer action lists
Update history
All releases are fully backwards compatible, except when upgrading from a version
prior to 1.5 to version 1.6 or later. For this special case, see the
upgrading instructions above. In all other cases, your existing projects
will not stop working.
Changes in release 2.1
- On popular demand, I added the
OnEmptySlotClick event. To use it, set
the EnableEmptySlotClick property to true, and optionally, add a value
for the EmptySlotTooltip property. Handle the event in your page. See
demo1 and demo2 for samples.
- Added the documentation2.chm help file with documentation for the version for .NET
2.0.
Changes in release 2.0
- Template editing from the smart tag menu is now working in VS2005.
Changes in release 1.9
- Support for ASP.NET 2.0.
The controls now support declarative binding to ASP.NET 2.0 data source controls.
The controls can also serve as master controls in connection with a FormView or
DetailsView control. As with other ASP.NET 2.0 data controls, this enables you to
create pages without writing any code.
There is only one set of source files, with conditional compilation. Use build.bat
to build schedule.dll for ASP.NET 1.x and build2.bat to build schedule2.dll for
ASP.NET 2.0. Don't use both assemblies in the same project. If you still want to
upgrade your pages one by one, make two sets of source files with different namespaces
(e.g. rw and rw2).
- Added the
SelectedValue property.
- Added the
SelectedIndexChanged event.
Changes in release 1.6.1
- Added a new property
NumberOfDays for the ScheduleCalendar control
to allow for a number of days other than 7. The default value is still 7.
- Since the ScheduleCalendar control is not limited to a 7 day week anymore, the Weeks
property is replaced by the
NumberOfRepetitions property. Setting the
Weeks property will still work, but it will not show up in the property panel anymore.
- Added a new property
StartDay for the ScheduleCalendar control to allow
for a starting day other than Monday. This property is ignored when the NumberOfDays
is not 7. The default value is still Monday.
Changes in release 1.6
- A new property
ShowValueMarks allows for a better display of adjacent
items. See the demos and the explanation above.
- Added the possibility to add titles to the ScheduleGeneral control with no content.
This allows you to show all your titles at all times, even when they don't have
any scheduled items for the given period. In order to do this, you have to add an
extra row in your data source with a
DataRangeStartField value of NULL
for every title that you want to show up (this solution courtesy of K. B. McHugh).
- Added the
EmptyDataTemplate property. Use this template to provide
content when no rows are found in the data source (typically, this would just be
a message saying something like "No data found".
- Added a new property
ItemStyleField, which is an optional database
field providing the item styles (in the form of a css class name). If not provided,
then the ItemStyle property and the AlternatingItemStyle property will be used for
all items. This addition is courtesy of David Sanders.
- Added a new property
AutoSortTitles for the ScheduleGeneral control.
When true (the default value), the titles are sorted alphabetically by the control
according to the values in the RangeValueField (this was also the method used in
previous versions). When false, the titles are not sorted by the control, and the
order will be the one of the items in the datasource. When using this option, make
sure that the items with the same titles are grouped together in the datasource
before databinding. If the titles are not grouped, unexpected results may occur.
- Renamed the database folder in the demos to "App_Data" instead of "db", in preparation
for ASP.NET 2.0 folder naming conventions.
Changes in release 1.5
- The code now provides one base class (BaseSchedule) and 3 derived controls:
ScheduleCalendar, ScheduleGeneral and Schedule (this last one is only for backwards
compatibility). This will greatly simplify the setting of their properties and templates,
because only the relevant properties and templates are shown for each type of control.
The properties and templates are also more self-explanatory, especially for the
ScheduleCalendar control.
Upgrading issues:
Currently, you can leave your existing projects as they are, because the included
Schedule class provides the same functionality as before, but I plan to remove
this class in the future. Therefore, I suggest that you convert your existing
projects. This can be done with a few changes only. It
will take you only a minute.
- The source code is now split up in separate files for each class.
- Added property builders for ScheduleCalendar and ScheduleGeneral.
- Solved datasource property bug
- Added " " to empty cells
- Databinding errors will now cause a page error (same behavior as in other data controls),
instead of silently being suppressed
- Solved bug with databinding in header
- Solved some minor bugs
Changes in release 1.4
- Support for items that span midnight in Calendar mode (on popular demand). I added
a new property "UseTitleFieldAsDate", which should be set to False if you want to
support items that span midnight. Automatically, these items will be split into
multiple items (one for each day), and added to the schedule. I added an extra demo
page to show you how to implement this situation.
- The timescale can now go up to 24:00 hours (12:00 PM or midnight). I found out that
you can enter a 24:00 hours time span in the editor's property pane by using a dot
after the number of days ("1.00:00:00" is the same as 24 hours).
- Solved the DataSource property bug (showing an error in the property editor).
- Added Visual Studio demo files
- Solved bug with postback on header items (reported by Deraldo Messner)
- Added toolbox bitmap
- Important: when upgrading existing projects to this version, you may need to replace
the "rw" namespace with "schedule.rw".
Changes in release 1.3
- Support for designer template editing.
- Solved some bugs.
Changes in release 1.2
- Much better designer support by deriving from TemplatedControlDesigner. Almost any
change in the properties settings will now automatically be reflected in the designer.
- Solved bugs when using Schedule with no data in General Mode (reported by Beren
Longstreet).
- Added a schedule.xsd file for additional IntelliSense support in Visual Studio
Changes in release 1.1
- Made the source code compile with Option Strict On.
- Added a FullTimeScale option. Previously, only time values occurring in the data
source were used. With this option, the control will show a continuous range of
time values.
Downloads
|