Domanda

Before begin, I tested the following code on a C# project "not SharePoint related" and it works, I know a little about Sharepoint webpart development, but I don't understand why this code doesn't works in the webpart that I'm doing...

EDIT: script API added.:

<!--Load the AJAX API-->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>

I'm facing the following issue:

I'm developing a farm solution webpart that generates dynamically a Column Chart or a Pie Chart (according to selection of one of ListItem in RadioButtonList):

<asp:RadioButtonList ID="rbt_graphicType" runat="server" AutoPostBack="true" OnSelectedIndexChanged="rbt_graphicType_SelectedIndexChanged">
    <asp:ListItem Text="Meeting Type" Selected="True" /> <!--Column Chart-->
    <asp:ListItem Text="Meeting Category" /> <!--Pie Chart-->
</asp:RadioButtonList>

When I say dynamically, I mean, I build Javascript code in a method and I call it in Page_Load event.

The javascript generated will be contained in the Panel control called "pnl_data" and the chart will be in "chart_div" div element.

<asp:Panel ID="pnl_data" runat="server">
</asp:Panel>
<div id="chart_div">
</div>
<asp:Label ID="MessageLabel" runat="server" />

The code-behind is simple (in my opinion).

    /// <summary>
    /// Page_Load.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        GenerateChart();
    }

    /// <summary>
    /// SelectedIndexChanged event for call "GenerateChart" method.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void rbt_graphicType_SelectedIndexChanged(object sender, EventArgs e)
    {
        GenerateChart();
    }

    /// <summary>
    /// Method called in Page_Load.
    /// Generate a chart according to RadioButtonList selection.
    /// </summary>
    private void GenerateChart()
    {
        try
        {
            //Initialize variables.
            string built_js = ""; //The "built_js" variable will hold the js generated in the folowing lines of code.
            HtmlGenericControl html_js = new HtmlGenericControl("script");
            pnl_data.Controls.Clear();

            if (rbt_graphicType.Items[0].Selected)
            {
                //Build js code for generate Column Chart.
                built_js += "  \n  google.load('visualization', '1', { 'packages': ['columnchart'] });   \n";
                built_js += "  \n";
                built_js += "    google.setOnLoadCallback(drawChart);  \n";
                built_js += "  \n";
                built_js += "    function drawChart() {  \n";
                built_js += "        var data = google.visualization.arrayToDataTable([  \n";
                built_js += "                  ['Tipo de reunión No.1 33.5%', 'Tipo de reunión No.2 70%', 'Tipo de reunión No.3 58%', 'Tipo de reunión No.4 82%', 'Tipo de reunión No.5 44%'],   \n";
                built_js += "                  [33.5, '', '', '', ''],     \n";
                built_js += "                  ['', 70, '', '', ''],       \n";
                built_js += "                  ['', '', 58, '', ''],       \n";
                built_js += "                  ['', '', '', 82, ''],       \n";
                built_js += "                  ['', '', '', '', 44]        \n";
                built_js += "                ]);  \n";
                built_js += "   \n";
                built_js += "        var options = {   \n";
                built_js += "            width: 600,   \n";
                built_js += "            height: 300,   \n";
                built_js += "            is3D: true,   \n";
                built_js += "            title: '" + dd_salas.SelectedItem.Text + "',   \n";
                built_js += "            bar: { groupWidth: '20%' },   \n";
                built_js += "            legendFontSize: '10',   \n";
                built_js += "            min: 10,   \n";
                built_js += "            max: 100   \n";
                built_js += "        };   \n";
                built_js += "   \n";
                built_js += "        var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));   \n";
                built_js += "        chart.draw(data, options);   \n";
                built_js += "    }   \n";

                //Show text for "check" which Chart was generated.
                MessageLabel.Text = "Column Chart generated.<br/>";
            }
            if (rbt_graphicType.Items[1].Selected)
            {
                //Build js code for generate Pie Chart.
                //...

                //Show text for "check" which Chart was generated.
                MessageLabel.Text = "Pie Chart generated.<br/>";
            }

            //Add javscript code to InnerHtml attribute and built the <script> tag correctly.
            html_js.Attributes.Add("type", "text/javascript");
            html_js.InnerHtml = built_js;
            pnl_graficos.Controls.Add(html_js);
            html_js = new HtmlGenericControl("script");
        }
        catch { MessageLabel = "Error in the ''GenerateChart'' method."; }
    }

EDIT: The values of Charts may change according to selected item from DropDownList control (the items already has values and there's no point focused on DropDownList's items values) - my bad, I didn't added this code before the answer of @Rephistorch -.

EDIT: The javascript code for generate the charts might not be elegant but it's fully functional (I tested it in C# project - as I said at the beginning of my question - ).

<asp:Label ID="lbl_selRoom" Text="Select the room" />
<asp:DropDownList ID="dd_rooms" runat="server">
   <asp:ListItem Text="Room 1" Selected="True" />
   <asp:ListItem Text="Room 2" />
   <asp:ListItem Text="Room 3" />
   <asp:ListItem Text="Room 4" />
   <asp:ListItem Text="Room 5" />
</asp:DropDownList>

The results obtained in the webpart deployed when I executed it shows only the sample text added on "MessageLabel" control and the Column Chart generated correctly (because the first element in the RadioButtonList is selected by default - I checked too putting the second element selected as default and the same situation happens - ).

I checked the console browser and there's no warning or error about js generated, however, when the selection changes, the chart code "even when the page reloads" doesn't change (still the Column Chart but no rendering, the "chart_div" element is empty).

I can share the js generated (and the C# sample code full), but the code is in spanish and I'm trying make the question the best understandable that I can.

È stato utile?

Soluzione 2

After some adjustments and using Google Chrome Console for debug at runtime the page, it shows that even when the selection of which chart generate changes, the Chart Column or Pie doesn't render at all (i.e there's no image) but the code of the chart does.

With this modified code, both, the 3D Column and Pie Charts now is rendering:

    /// <summary>
    /// OnPreRender.
    /// </summary>
    /// <param name="e"></param>
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        #region Sección que obtiene los valores válidos para mostrar el Pie Chart.

        DataTable data = GetRoomStadistics();

        Page.ClientScript.RegisterArrayDeclaration(
            "MyArrayData", "['RoomTypes', 'Percentaje']");

        ////Registrar las ítems dentro del arreglo de javascript "en el lado cliente".
        foreach (DataRow dataitem in data.Rows)
        {
            Page.ClientScript.RegisterArrayDeclaration("MyArrayData",
                string.Format("['{0}', {1}]",
                dataitem["RoomTypes"],
                dataitem["Percentaje"]));
        }
        data = new DataTable();

        #endregion
        GenerateCharts();
    }

    /// <summary>
    /// Page_Load.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.Page.IsPostBack)
        {
            //Get the rooms "fill DropDownList control".
            GetRooms();

            #region Code for obtain datatable with example data for generate the Pie Chart.

            DataTable data = GetRoomStadistics();

            Page.ClientScript.RegisterArrayDeclaration(
                "MyArrayData", "['RoomTypes', 'Percentaje']");

            ////Register items inside javascript array "client side".
            foreach (DataRow dataitem in data.Rows)
            {
                Page.ClientScript.RegisterArrayDeclaration("MyArrayData",
                    string.Format("['{0}', {1}]",
                    dataitem["RoomTypes"],
                    dataitem["percentaje"]));
            }
            data = new DataTable();

            #endregion
        }
        GenerateCharts();
    }

    /// <summary>
    /// Obtener las salas y agregarlos como ítems del DropDownList.
    /// </summary>
    private void GetRooms()
    {
        //Put code for add ListItems to DroDownListItem.
    }

    /// <summary>
    /// Determina el tipo de gráfica a Build.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void rbt_graphicType_SelectedIndexChanged(object sender, EventArgs e)
    {
        GenerateCharts();
    }

    /// <summary>
    /// Mauricio Arias Olave.
    /// 19/11/2014.
    /// De acuerdo a la opción seleccionada en el RadioButtonList y al a sala seleccionada, se Buildá
    /// una gráfica que indica las estadísticas de la sala seleccionada.
    /// </summary>
    private void GenerateCharts()
    {
        try
        {
            //Inicializar variables.
            string built_js = "";
            HtmlGenericControl html_js = new HtmlGenericControl("script");
            pnl_data.Controls.Clear();

            //1.0
            if (rbt_graphicType.Items[0].Selected)
            {
                #region Build Column Chart.

                //Clean string before generate javascript code.
                built_js = "";

                built_js += "  \n  google.load('visualization', '1', { 'packages': ['columnchart'] });   \n";
                built_js += "  \n";
                built_js += "    google.setOnLoadCallback(drawChart);  \n";
                built_js += "  \n";
                built_js += "    function drawChart() {  \n";
                built_js += "        var data = google.visualization.arrayToDataTable([  \n";
                built_js += "                  ['Meeting Type # 1 33.5%', 'Meeting Type # 2 70%', 'Meeting Type # 3 58%', 'Meeting Type # 4 82%', 'Meeting Type # 5 44%'],   \n";
                built_js += "                  [33.5, '', '', '', ''],     \n";
                built_js += "                  ['', 70, '', '', ''],       \n";
                built_js += "                  ['', '', 58, '', ''],       \n";
                built_js += "                  ['', '', '', 82, ''],       \n";
                built_js += "                  ['', '', '', '', 44]        \n";
                built_js += "                ]);  \n";
                built_js += "   \n";
                built_js += "        var options = {   \n";
                built_js += "            width: 600,   \n";
                built_js += "            height: 300,   \n";
                built_js += "            is3D: true,   \n";
                built_js += "            title: '" + dd_rooms.SelectedItem.Text + "',   \n";
                built_js += "            bar: { groupWidth: '20%' },   \n";
                built_js += "            legendFontSize: '10',   \n";
                built_js += "            min: 10,   \n";
                built_js += "            max: 100   \n";
                built_js += "        };   \n";
                built_js += "   \n";
                built_js += "        var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));   \n";
                built_js += "        chart.draw(data, options);   \n";
                built_js += "    }   \n";

                #endregion
                MessageLabel.Text = "Column chart!";
            }
            if (rbt_graphicType.Items[1].Selected)
            {
                #region Build Pie Chart.

                //Clean string before generate javascript code.
                built_js = "";

                built_js += "  google.load('visualization', '1', { packages: ['corechart'] });   \n";
                built_js += "      function drawVisualization() {    \n";
                built_js += " \n";
                built_js += "   /''MyArrayData'': array declarated in server side.*/   \n";
                built_js += "   var ChartData = google.visualization.arrayToDataTable(MyArrayData);   \n";
                built_js += " \n";
                built_js += "   /*Settings*/   \n";
                built_js += "   var ChartOptions = {   \n";
                built_js += "       'title': '" + dd_rooms.SelectedItem.Text + "',   \n";
                built_js += "       'titleTextStyle': { color: 'black', fontName: '\"Verdana\"', fontSize: 12 },   \n";
                built_js += "       'width': 350,   \n";
                built_js += "       'height': 350,   \n";
                built_js += "       'backgroundColor': 'white',   \n";
                built_js += "       'chartArea': { 'width': '90%', 'height': '70%' },   \n";
                built_js += "       'pieSliceText': 'percentage',   \n";
                built_js += "       'tooltip': { textStyle: { color: 'black', fontName: '\"Verdana\"', fontSize: 12} },   \n";
                built_js += "       /*'tooltip': { trigger: 'none' }, */ /*Ocultar tooltip*/   \n";
                built_js += "       'is3D': true,   \n";
                built_js += "       legend: 'bottom',   \n";
                built_js += "       /*pieStartAngle: 20*/   \n";
                built_js += "   };   \n";
                built_js += " \n";
                built_js += "   /*Draw the pie chart.*/   \n";
                built_js += "   new google.visualization.PieChart(document.getElementById('chart_div')).   \n";
                built_js += "       draw(ChartData, ChartOptions);   \n";
                built_js += "               }   \n";
                built_js += "               google.setOnLoadCallback(drawVisualization);   \n";

                #endregion
                MessageLabel.Text = "Pie chart!";
            }

            //Add <script> element to "pnl_data" element.
            html_js.Attributes.Add("type", "text/javascript");
            html_js.InnerHtml = built_js;
            pnl_data.Controls.Add(html_js);
            html_js = new HtmlGenericControl("script");
        }
        catch (Exception ex) { MessageLabel.Text += "Error: " + ex.Message; }
    }

    /// <summary>
    /// sampleTable con los indicadores de las salas.
    /// </summary>
    /// <returns>DataTable.</returns>
    private DataTable GetRoomStadistics()
    {
        DataTable sampleTable = new DataTable();

        try
        {
            DataColumn col_RoomType = new DataColumn();
            col_RoomType.ColumnName = "RoomTypes";
            col_RoomType.DataType = typeof(System.String);

            DataColumn col_RoomPercentaje = new DataColumn();
            col_RoomPercentaje.ColumnName = "Percentaje";
            col_RoomPercentaje.DataType = typeof(System.String);

            sampleTable.Columns.Add(col_RoomType);
            sampleTable.Columns.Add(col_RoomPercentaje);

            sampleTable.Rows.Add(new object[] { "Meeting Type # 1", "33.5" });
            sampleTable.Rows.Add(new object[] { "Meeting Type # 2", "70" });
            sampleTable.Rows.Add(new object[] { "Meeting Type # 3", "58" });
            sampleTable.Rows.Add(new object[] { "Meeting Type # 4", "82" });
            sampleTable.Rows.Add(new object[] { "Meeting Type # 5", "44" });

            return sampleTable;
        }
        catch { return sampleTable; }
    }

    /// <summary>
    /// SelectedIndexChanged.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void dd_rooms_SelectedIndexChanged(object sender, EventArgs e)
    {
        GenerateCharts();
    }

A close view to the code, I'm not using any SPLisItem, SPQuery and/or similar SharePoint class for obtain data (I omitted it because this isn't part of the main scope of my question).

There's a short description about which steps I follow for get this results:

  1. Call GenerateChart() method in (OnPreRender, PageLoad and SelectedIndexChanged) methods.
  2. In Page_Load method, the call to GenerateChart() method must be outside of condtional IsPostBack.

I don't know if this is the best approach for generate charts dynamically but anyone can modify this code for your purpose.

Also, in Internet Explorer, the 3D Column Chart doesn't render. I know that columnchart is deprecated but it works if:

  1. Open F12 Developer Tool.
  2. Change Document Mode that stays standard to Internet Explorer 7 Standard.

I think that there's some cleaner way for get similar or betters results, but, for the scope of my question this is an accepted one.

Altri suggerimenti

Instead of using Google's google.setOnLoadCallback(drawChart); event in your javascript, try using _spBodyOnLoadFunctionNames.push("drawChart");.

My theory is that by the time Google's API has loaded, your page's DOM has not been fully built, and so there is no "chart_div" to access.

Sidenote: I would recommend using the StringBuilder class instead of all of that concatenation. It has nothing to do with your problem, but doing an AppendLine("...") is easier than putting \n everywhere. Feel free to ignore this if you want.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a sharepoint.stackexchange
scroll top