This is a migrated thread and some comments may be shown as answers.

Too much data for 1 chart

5 Answers 126 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Steve
Top achievements
Rank 1
Steve asked on 24 Feb 2012, 08:52 PM
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.

5 Answers, 1 is accepted

Sort by
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: 
  1. Add a panel
  2. Add the chart inside the panel
  3. Add a List item next to the chart (leave it absolutely empty)
  4. Bind the chart and the list to the same data-source
  5. 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. 
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?
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) : 
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. 

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.

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;
 
    }
}

Tags
General Discussions
Asked by
Steve
Top achievements
Rank 1
Answers by
Hadib Ahmabi
Top achievements
Rank 1
Ryan
Top achievements
Rank 1
Share this question
or