I have a chart that works fine. The problem is the chart may have 7 items on the X axis or 700. If there are 7 it looks great, if there are 700 it is unreadable.
How can I print the 700 so it is readable.
The two solutions I have been looking into (I am open to other solutions) are:
1) Determine the number of elements in the result set and change the height of the report to be a multiple of the data set.
2) Duplicate the chart as many times as needed based on the results limiting each chart to 20 records.
Option 2 is probably the preferred option. I have not been able to work my way through how to do either of these.
Is there a way to do what I need to do? I don't want to filter the data, I need to show it all.
How can I print the 700 so it is readable.
The two solutions I have been looking into (I am open to other solutions) are:
1) Determine the number of elements in the result set and change the height of the report to be a multiple of the data set.
2) Duplicate the chart as many times as needed based on the results limiting each chart to 20 records.
Option 2 is probably the preferred option. I have not been able to work my way through how to do either of these.
Is there a way to do what I need to do? I don't want to filter the data, I need to show it all.
5 Answers, 1 is accepted
0
Hadib Ahmabi
Top achievements
Rank 1
answered on 27 Feb 2012, 12:34 PM
1) You can make the chart with horizontal orientation and then stretch it down. However in this case you should not have any numbers, labels, etc.. because they get stretched too and look ugly.
To do this:
To do this:
- Add a panel
- Add the chart inside the panel
- Add a List item next to the chart (leave it absolutely empty)
- Bind the chart and the list to the same data-source
- Add Top and Bottom Anchoring to the chart
Result: The list will stretch according to the records and because of the anchoring, the chart will stretch too. The height of the list will determine how much the chart will stretch per record, so you will have to adjust it properly. However this will not produce very pretty chart. It is just one possible solution.
2) Use SqlDataAdapter and get the that for the chart in the code behind. Then with while loop (until there is data) you take chunks of 20 records and add them to List<YourDataClass>, all these list you once again add to a List and you get List<List<YourDataClass>>. Then you this list to the report's data-source (this will provide you one detail section with List<YourDataClass> as DataObject for all the little lists). Bind the chart's DataSource to =ReportItem.DataObject.RawData and this you will have many chart and each chart's data-source will be List<YourDataClass> with up to 20 records.
2) Use SqlDataAdapter and get the that for the chart in the code behind. Then with while loop (until there is data) you take chunks of 20 records and add them to List<YourDataClass>, all these list you once again add to a List and you get List<List<YourDataClass>>. Then you this list to the report's data-source (this will provide you one detail section with List<YourDataClass> as DataObject for all the little lists). Bind the chart's DataSource to =ReportItem.DataObject.RawData and this you will have many chart and each chart's data-source will be List<YourDataClass> with up to 20 records.
0
Ryan
Top achievements
Rank 1
answered on 13 Mar 2012, 04:22 PM
Hadib,
I'm working the guy who posted the original message. Currently option 1 works, but it looks kinda ugly. I think we want to go with Option 2, but here's the thing. We aren't using SqlDataSource, but ObjectDataSource, as our data is coming back in the form of a list from a data provider. But, for the sake of trying to understand the reasoning behind your solution, I need to know what event on which object are we doing all this code behind? And, in the code behind, what object do we use to get access to the ReportItem.DataObject.RawData?
I'm working the guy who posted the original message. Currently option 1 works, but it looks kinda ugly. I think we want to go with Option 2, but here's the thing. We aren't using SqlDataSource, but ObjectDataSource, as our data is coming back in the form of a list from a data provider. But, for the sake of trying to understand the reasoning behind your solution, I need to know what event on which object are we doing all this code behind? And, in the code behind, what object do we use to get access to the ReportItem.DataObject.RawData?
0
Hadib Ahmabi
Top achievements
Rank 1
answered on 14 Mar 2012, 02:07 PM
Working with objects only makes things easier.
Here's my idea:
Let's say you have a List<DataClass> which holds all your data for the chart.
First you create a method that takes a piece of the whole data (for example 20 records) :
Let's say for example that you have 317 items. 20 * 16 = 320, so you need 16 charts to display these records. Then let's say that in the report_NeedDataSource you do something like that:
Then the report's data-source will be {0,1,2,...15};
This means that you will have 16 detail section and a chart in each of them.
Then you can use Bindings to set the chart's data-source. The expression will look like = GetData(ReportItem.DataObject.Item) This expression will be used at design-time, so you don't need an event for that. Look at this: http://www.telerik.com/help/reporting/expressions-bindings.html
ReportItem.DataObject.Item in the current context will be the numbers from 0 to 15 that are from the report's data-source.
This will make each chart to get portions of 20 elements from the dataList using the custom user function and passing the consequential number from the report's data-source.
Here's my idea:
Let's say you have a List<DataClass> which holds all your data for the chart.
First you create a method that takes a piece of the whole data (for example 20 records) :
public static List<DataClass> GetData(List<DataClass> dataList,
int
position,
int
legnth)
{
return
dataList.Skip(position * length).Take(length);
}
//Skip() and Take() are methods from LINQ that allow you to easily get portions of data from IEnumerable.
//If you use older .NET or do not want to use LINQ,
//you can easily implement something similar for taking certain elements from a List.
Let's say for example that you have 317 items. 20 * 16 = 320, so you need 16 charts to display these records. Then let's say that in the report_NeedDataSource you do something like that:
//here you get your data list which holds all the records
List<DataClass> data = ....
//determine how many charts you need
var iterations = (
int
)Math.Ceiling(data.Length / 20m);
var dataArray =
new
int
[iterations];
//fill a helper array
for
(
int
i = 0; i < iterations; i++)
{
dataArray[i] = i;
}
//this helper array will be the report's data-source
(sender
as
Telerik.Reporting.Processing.Report).DataSource = dataArray;
Then the report's data-source will be {0,1,2,...15};
This means that you will have 16 detail section and a chart in each of them.
Then you can use Bindings to set the chart's data-source. The expression will look like = GetData(ReportItem.DataObject.Item) This expression will be used at design-time, so you don't need an event for that. Look at this: http://www.telerik.com/help/reporting/expressions-bindings.html
ReportItem.DataObject.Item in the current context will be the numbers from 0 to 15 that are from the report's data-source.
This will make each chart to get portions of 20 elements from the dataList using the custom user function and passing the consequential number from the report's data-source.
0
Ryan
Top achievements
Rank 1
answered on 14 Mar 2012, 05:09 PM
Hadib,
I tried to understand you code, but I'm still not following. Two things are confusing me. First, your GetData static method accepts 3 parameters (Data, Position, Length) but your binding only shows 1 parameter, the current reports data. Where do I pass in the other 2 items? If I don't match up the other two parameters, it won't find the method and fail at run time.
Second thing is, I still don't fully understand how this will automatically generate multiple charts. Please understand that this report that contains the chart exists as a sub report in a higher level report, we aren't showing this report by itself.
I modified your code slightly just a little bit ago. It was my feeble-minded attempt to try to grasp the concept you are throwing at me. Please review this and let me know what I should do with the next steps because this is boggling my mind.
BTW, thanks for your help and patience. :-)
I tried to understand you code, but I'm still not following. Two things are confusing me. First, your GetData static method accepts 3 parameters (Data, Position, Length) but your binding only shows 1 parameter, the current reports data. Where do I pass in the other 2 items? If I don't match up the other two parameters, it won't find the method and fail at run time.
Second thing is, I still don't fully understand how this will automatically generate multiple charts. Please understand that this report that contains the chart exists as a sub report in a higher level report, we aren't showing this report by itself.
I modified your code slightly just a little bit ago. It was my feeble-minded attempt to try to grasp the concept you are throwing at me. Please review this and let me know what I should do with the next steps because this is boggling my mind.
public
partial
class
StorageByHostChart : Telerik.Reporting.Report
{
private
string
_ecid;
private
string
_type;
public
StorageByHostChart()
{
InitializeComponent();
}
private
void
chart1_NeedDataSource(
object
sender, EventArgs e)
{
}
private
void
StorageByHostChart_ItemDataBinding(
object
sender, EventArgs e)
{
var report = (Telerik.Reporting.Processing.Report)sender;
if
(_ecid ==
null
)
_ecid = report.Parameters[
"ecid"
].Value.ToString();
if
(_type ==
null
)
_type = report.Parameters[
"type"
].Value.ToString();
}
public
static
List<HostStorageByTypeItem_Telerik> GetData(List<HostStorageByTypeItem_Telerik> dataList,
int
position,
int
length)
{
return
dataList.Skip(position).Take(length).ToList();
}
private
void
StorageByHostChart_NeedDataSource(
object
sender, EventArgs e)
{
var itteration = 0;
var allData =
new
SPHelper().HostStorageByType_Telerik(_ecid, _type);
var dataList = GetData(allData, itteration, 5);
while
(dataList.Any())
{
itteration += 5;
(sender
as
Telerik.Reporting.Processing.Report).DataSource = dataList;
dataList = GetData(allData, itteration, 5);
}
}
}
BTW, thanks for your help and patience. :-)
0
Ryan
Top achievements
Rank 1
answered on 15 Mar 2012, 04:12 PM
Hadib,
Thanks for you help on the issue, but we fixed it ourselves. This is all done in code behind, but basically, I'm dynamically creating sub reports containing a subset of the data and passing that to that instance of the report.
I used this link to help. http://www.telerik.com/community/forums/reporting/telerik-reporting/113406-sub-reports.aspx
I ended up modifying this, and putting it in a repeater chart that dynamically creates sub-reports containing the chart. Here's a snippet for anyone else who is looking into doing the same thing.
To paginate the data source, we ended up extending the List class to do it for us by creating a PaginatedList class. Here's the code for it, for those who want it. Just change "MyNamespace" to whatever namespace you need it to be.
Thanks for you help on the issue, but we fixed it ourselves. This is all done in code behind, but basically, I'm dynamically creating sub reports containing a subset of the data and passing that to that instance of the report.
I used this link to help. http://www.telerik.com/community/forums/reporting/telerik-reporting/113406-sub-reports.aspx
I ended up modifying this, and putting it in a repeater chart that dynamically creates sub-reports containing the chart. Here's a snippet for anyone else who is looking into doing the same thing.
private
void
StorageByHostRepeater_ItemDataBinding(
object
sender, EventArgs e)
{
var report = (Telerik.Reporting.Processing.Report)sender;
if
(_ecid ==
null
)
_ecid = report.Parameters[
"ecid"
].Value.ToString();
if
(_type ==
null
)
_type = report.Parameters[
"type"
].Value.ToString();
}
private
void
AddHostStorageSubReports()
{
List<Report> detailReports =
new
List<Report>();
for
(
int
x = 0; x < data.PageCount; ++x)
{
var report =
new
StorageByHostChart();
report.DataToDisplay = data.GetCurrentList();
detailReports.Add(report);
data.CurrentPage++;
}
CombineReports(detailReports);
}
private
void
CombineReports(List<Report> detailReports)
{
Report report =
new
Report();
detail.Items.Clear();
Unit unitX = Unit.Inch(0.1);
Unit unitY = Unit.Inch(0.1);
SizeU size =
new
SizeU(Unit.Inch(1), Unit.Inch(0.5));
foreach
(Report detailReport
in
detailReports)
{
SubReport subReport;
subReport =
new
SubReport();
subReport.Location =
new
PointU(unitX, unitY);
subReport.Size = size;
unitY = unitY.Add(Unit.Inch(1));
subReport.ReportSource = detailReport;
detail.Items.Add(subReport);
}
detail.Height = Unit.Inch(detailReports.Count + 1);
}
private
void
StorageByHostRepeater_NeedDataSource(
object
sender, EventArgs e)
{
data =
new
PaginatedList<HostStorageByTypeItem_Telerik>(
new
SPHelper().HostStorageByType_Telerik(_ecid, _type), 20);
AddHostStorageSubReports();
}
To paginate the data source, we ended up extending the List class to do it for us by creating a PaginatedList class. Here's the code for it, for those who want it. Just change "MyNamespace" to whatever namespace you need it to be.
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
namespace
MyNamespace
{
public
class
PaginatedList<T> : List<T>
{
#region Properties
public
int
PageSize
{
get
{
return
_itemCountPerPage; }
set
{
if
(value >= 0)
{
_itemCountPerPage = value;
RecalculateThePageItems();
}
}
}
public
int
CurrentPage
{
get
{
return
_currentPageIndex; }
set
{
if
(value >= 0)
{
_currentPageIndex = value;
RecalculateThePageItems();
}
}
}
public
int
PageCount
{
get
{
return
Convert.ToInt32(Math.Ceiling(Convert.ToDouble(OriginalCollection.Count) / Convert.ToDouble(PageSize)));
}
}
public
List<T> GetCurrentList()
{
return
base
.ToArray().ToList();
}
public
List<T> OriginalCollection
{
get
{
return
originalCollection; }
private
set
{ originalCollection = value; }
}
#endregion
#region Constructors
public
PaginatedList(IEnumerable<T> collecton)
{
_currentPageIndex = 0;
_itemCountPerPage = 1;
originalCollection =
new
List<T>(collecton);
RecalculateThePageItems();
}
public
PaginatedList(IEnumerable<T> collecton,
int
itemCountPerPage)
{
_currentPageIndex = 0;
_itemCountPerPage = itemCountPerPage;
originalCollection =
new
List<T>(collecton);
RecalculateThePageItems();
}
public
PaginatedList(
int
itemsPerPage)
{
_currentPageIndex = 0;
_itemCountPerPage = itemsPerPage;
originalCollection =
new
List<T>();
}
public
PaginatedList()
{
_currentPageIndex = 0;
_itemCountPerPage = 1;
originalCollection =
new
List<T>();
}
#endregion
#region private
private
void
RecalculateThePageItems()
{
Clear();
int
startIndex = _currentPageIndex * _itemCountPerPage;
for
(
int
i = startIndex; i < startIndex + _itemCountPerPage; i++)
{
if
(originalCollection.Count > i)
base
.Insert(i - startIndex, originalCollection[i]);
}
}
#endregion
public
void
InsertItem(
int
index, T item)
{
int
startIndex = _currentPageIndex * _itemCountPerPage;
int
endIndex = startIndex + _itemCountPerPage;
//Check if the Index is with in the current Page then add to the collection as bellow. And add to the originalCollection also
if
((index >= startIndex) && (index < endIndex))
{
base
.Insert(index - startIndex, item);
if
(Count > _itemCountPerPage)
base
.RemoveAt(endIndex);
}
if
(index >= Count)
originalCollection.Add(item);
else
originalCollection.Insert(index, item);
}
public
void
RemoveItem(
int
index)
{
int
startIndex = _currentPageIndex * _itemCountPerPage;
int
endIndex = startIndex + _itemCountPerPage;
//Check if the Index is with in the current Page range then remove from the collection as bellow. And remove from the originalCollection also
if
((index >= startIndex) && (index < endIndex))
{
base
.RemoveAt(index - startIndex);
if
(Count <= _itemCountPerPage)
base
.Insert(endIndex - 1, originalCollection[index + 1]);
}
originalCollection.RemoveAt(index);
}
private
List<T> originalCollection;
private
int
_currentPageIndex;
private
int
_itemCountPerPage;
}
}