Tuesday, April 15, 2014

Talking into a DevExpress ASPxCallbackPanel from the outside!

Behold: I have taken the ASPxGridView I first showed off here and have since refined here and here made it better yet. I brought in the Measurements class I mention here and have made Default.aspx look like so:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master"
      AutoEventWireup="true" CodeBehind="Default.aspx.cs"
      Inherits="DummyDevExpressApplication._Default" %>
<%@ Register Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.ASPxEditors"
      TagPrefix="dx" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxGridView"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxTabControl"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxClasses"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxPanel"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<%@ Register TagPrefix="dx" Namespace="DevExpress.Web.ASPxCallbackPanel"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<%@ Register TagPrefix="dxe" Namespace="DevExpress.Web.ASPxEditors"
      Assembly="DevExpress.Web.v13.1, Version=13.1.9.0, Culture=neutral,
      PublicKeyToken=b88d1754d700e49a" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
   <dx:ASPxCallbackPanel ID="SolorSystemCallBackPanel"
         OnCallback="SolarSystemCallBackPanel_Callback"
         ClientInstanceName="SolorSystemCallBackPanel" HideContentOnCallback="false"
         ShowLoadingPanel="true" EnableViewState="true" runat="server" Width="700px">
      <PanelCollection>
         <dx:PanelContent ID="SolorSystemContent" runat="server">
            <dx:ASPxGridView ID="SolarSystem" ClientInstanceName="SolarSystem"
                  runat="server" Width="100%" KeyFieldName="Name"
                  AutoGenerateColumns="False">
               <SettingsBehavior AutoExpandAllGroups="True" />
               <SettingsPager PageSize="15">
               </SettingsPager>
               <Settings ShowFilterRow="True" ShowGroupPanel="True" />
               <Styles AlternatingRow-Enabled="True">
                  <AlternatingRow Enabled="True">
                  </AlternatingRow>
               </Styles>
               <Columns>
                  <dx:GridViewCommandColumn VisibleIndex="0" ButtonType="Link"
                        Caption="Edit">
                     <EditButton Visible="True">
                     </EditButton>
                     <DeleteButton Visible="True">
                     </DeleteButton>
                     <HeaderCaptionTemplate>
                        <dx:ASPxHyperLink ID="btnNew" runat="server" Text="New">
                           <ClientSideEvents Click="function (s, e) {
                                 SolarSystem.AddNewRow();}" />
                        </dx:ASPxHyperLink>
                     </HeaderCaptionTemplate>
                  </dx:GridViewCommandColumn>
                  <dx:GridViewDataTextColumn FieldName="Name" VisibleIndex="1">
                  </dx:GridViewDataTextColumn>
                  <dx:GridViewDataTextColumn
                        FieldName="ClosestAstronomicalUnitDistanceFromSun"
                        VisibleIndex="2">
                  </dx:GridViewDataTextColumn>
                  <dx:GridViewDataComboBoxColumn FieldName="PlanetType"
                        VisibleIndex="3">
                     <PropertiesComboBox DataSourceID="PlanetTypeSource"
                           ValueField="key" TextField="value" ValueType="System.String"
                           DropDownStyle="DropDown" />
                  </dx:GridViewDataComboBoxColumn>
                  <dx:GridViewDataTextColumn FieldName="MilesInDiameter"
                        VisibleIndex="4">
                  </dx:GridViewDataTextColumn>
                  <dx:GridViewDataColumn FieldName="DiscoveryDate" VisibleIndex="5">
                  </dx:GridViewDataColumn>
               </Columns>
               <SettingsPopup>
                  <EditForm Width="500" />
               </SettingsPopup>
               <SettingsPager Mode="ShowPager" PageSize="10" />
               <Settings ShowTitlePanel="true" />
               <SettingsText Title="Our Solar System" />
               <Templates>
                  <EditForm>
                     <div style="padding: 4px 4px 3px 4px">
                        <dx:ASPxPageControl runat="server" ID="PageControl" Width="100%">
                           <TabPages>
                              <dx:TabPage Text="Planet" Visible="true">
                                 <ContentCollection>
                                    <dx:ContentControl runat="server">
                                       <dx:ASPxGridViewTemplateReplacement ID="Editors"
                                             ReplacementType="EditFormEditors" runat="server">
                                       </dx:ASPxGridViewTemplateReplacement>
                                    </dx:ContentControl>
                                 </ContentCollection>
                              </dx:TabPage>
                           </TabPages>
                        </dx:ASPxPageControl>
                     </div>
                     <div style="text-align: right; padding: 2px 2px 2px 2px">
                        <dx:ASPxGridViewTemplateReplacement ID="UpdateButton"
                              ReplacementType="EditFormUpdateButton" runat="server">
                        </dx:ASPxGridViewTemplateReplacement>
                        <dx:ASPxGridViewTemplateReplacement ID="CancelButton"
                              ReplacementType="EditFormCancelButton" runat="server">
                        </dx:ASPxGridViewTemplateReplacement>
                     </div>
                  </EditForm>
               </Templates>
            </dx:ASPxGridView>
            <asp:ObjectDataSource ID="PlanetTypeSource" runat="server"
                  SelectMethod="SelectPlanetTypes"
                  TypeName="DummyDevExpressApplication.Utilities.CommonSource">
            </asp:ObjectDataSource>
         </dx:PanelContent>
       </PanelCollection>
   </dx:ASPxCallbackPanel>
   <table style="background-color:#FFFFCC; width: 700px;">
      <tr>
         <td align="center"><h3 id="TitleText">Automatically assign planet type?</h3></td>
         <td width="100"></td>
      </tr>
      <tr>
         <td align="right" valign="top">a gas giant is bigger than this many miles in
               diameter:</td>
         <td height="100">
            <dxe:ASPxListBox ID="Diameter" runat="server"
                  ClientInstanceName="DiameterClient" Height="635">
               <Items>
                  <dxe:ListEditItem Text="90000" Value="90000" />
                  <dxe:ListEditItem Text="84100" Value="84100" />
                  <dxe:ListEditItem Text="78400" Value="78400" />
                  <dxe:ListEditItem Text="72900" Value="72900" />
                  <dxe:ListEditItem Text="67600" Value="67600" />
                  <dxe:ListEditItem Text="62500" Value="62500" />
                  <dxe:ListEditItem Text="57600" Value="57600" />
                  <dxe:ListEditItem Text="52900" Value="52900" />
                  <dxe:ListEditItem Text="48400" Value="48400" />
                  <dxe:ListEditItem Text="44100" Value="44100" />
                  <dxe:ListEditItem Text="40000" Value="40000" />
                  <dxe:ListEditItem Text="36100" Value="36100" />
                  <dxe:ListEditItem Text="32400" Value="32400" />
                  <dxe:ListEditItem Text="28900" Value="28900" />
                  <dxe:ListEditItem Text="25600" Value="25600" />
                  <dxe:ListEditItem Text="22500" Value="22500" />
                  <dxe:ListEditItem Text="19600" Value="19600" />
                  <dxe:ListEditItem Text="16900" Value="16900" />
                  <dxe:ListEditItem Text="14400" Value="14400" />
                  <dxe:ListEditItem Text="12100" Value="12100" />
                  <dxe:ListEditItem Text="10000" Value="10000" Selected="True" />
                  <dxe:ListEditItem Text="8100" Value="8100" />
                  <dxe:ListEditItem Text="6400" Value="6400" />
                  <dxe:ListEditItem Text="4900" Value="4900" />
                  <dxe:ListEditItem Text="3600" Value="3600" />
                  <dxe:ListEditItem Text="2500" Value="2500" />
                  <dxe:ListEditItem Text="1600" Value="1600" />
                  <dxe:ListEditItem Text="900" Value="900" />
                  <dxe:ListEditItem Text="400" Value="400" />
                  <dxe:ListEditItem Text="100" Value="100" />
               </Items>
               <ClientSideEvents SelectedIndexChanged="function(s, e) {
                  var item = { Diameter: DiameterClient.GetSelectedItem().value, Distance:
                        DistanceClient.GetSelectedItem().value };
                  SolorSystemCallBackPanel.PerformCallback(JSON.stringify(item));
               }" />
            </dxe:ASPxListBox>
         </td>
      </tr>
      <tr>
         <td align="right" valign="top">...and inner planets are within this many AU of the
               sun:</td>
         <td height="100">
            <dxe:ASPxListBox ID="Distance" runat="server"
                  ClientInstanceName="DistanceClient" Height="635">
               <Items>
                  <dxe:ListEditItem Text="38.7" Value="38.7" />
                  <dxe:ListEditItem Text="36.163" Value="36.163" />
                  <dxe:ListEditItem Text="33.712" Value="33.712" />
                  <dxe:ListEditItem Text="31.347" Value="31.347" />
                  <dxe:ListEditItem Text="29.068" Value="29.068" />
                  <dxe:ListEditItem Text="26.875" Value="26.875" />
                  <dxe:ListEditItem Text="24.768" Value="24.768" />
                  <dxe:ListEditItem Text="22.747" Value="22.747" />
                  <dxe:ListEditItem Text="20.812" Value="20.812" />
                  <dxe:ListEditItem Text="18.963" Value="18.963" />
                  <dxe:ListEditItem Text="17.2" Value="17.2" />
                  <dxe:ListEditItem Text="15.523" Value="15.523" />
                  <dxe:ListEditItem Text="13.932" Value="13.932" />
                  <dxe:ListEditItem Text="12.427" Value="12.427" />
                  <dxe:ListEditItem Text="11.008" Value="11.008" />
                  <dxe:ListEditItem Text="10.965" Value="10.965" />
                  <dxe:ListEditItem Text="8.428" Value="8.428" />
                  <dxe:ListEditItem Text="7.267" Value="7.267" />
                  <dxe:ListEditItem Text="6.192" Value="6.192" />
                  <dxe:ListEditItem Text="5.203" Value="5.203" />
                  <dxe:ListEditItem Text="4.3" Value="4.3" />
                  <dxe:ListEditItem Text="3.483" Value="3.483" />
                  <dxe:ListEditItem Text="2.752" Value="2.752" />
                  <dxe:ListEditItem Text="2.107" Value="2.107" Selected="True" />
                  <dxe:ListEditItem Text="1.548" Value="1.548" />
                  <dxe:ListEditItem Text="1.075" Value="1.075" />
                  <dxe:ListEditItem Text="0.688" Value="0.688" />
                  <dxe:ListEditItem Text="0.387" Value="0.387" />
                  <dxe:ListEditItem Text="0.172" Value="0.172" />
                  <dxe:ListEditItem Text="0.043" Value="0.043" />
               </Items>
               <ClientSideEvents SelectedIndexChanged="function(s, e) {
                  var item = { Diameter: DiameterClient.GetSelectedItem().value, Distance:
                        DistanceClient.GetSelectedItem().value };
                  SolorSystemCallBackPanel.PerformCallback(JSON.stringify(item));
               }" />
            </dxe:ASPxListBox>
         </td>
      </tr>
   </table>
   <style type="text/css" media="all">
      #MainContent_Diameter {
         position: absolute;
         -moz-transform: rotate(90deg);
         -ms-transform: rotate(90deg);
         -o-transform: rotate(90deg);
         -webkit-transform: rotate(90deg);
         transform: rotate(90deg);
         margin-left: -260px;
         margin-top: -310px;
      }
      #MainContent_Distance {
         position: absolute;
         -moz-transform: rotate(90deg);
         -ms-transform: rotate(90deg);
         -o-transform: rotate(90deg);
         -webkit-transform: rotate(90deg);
         transform: rotate(90deg);
         margin-left: -260px;
         margin-top: -310px;
      }
   </style>
</asp:Content>

 
 

I have nested my grid inside of a callback panel and I am to talk into it from the outside from two ASPxListBox controls which I have rotated ninty degrees just to be silly and to make them seem more like sliders. (ASPxTrackBar would not behave correctly so I substituted these controls.) These controls will effect the records in the grid without reposting the page.

 
 

I saw Joe Celko once suggest that data could be categorized by pieces of ranges in a talk he gave...

 
 

Building on this, I force the PlanetType of each Planet to be set by my "sliders" based upon their inputs. Planets close enough to the sun will be Inner planets. All others are either Dwarf or Gas planets depending upon how big they are. My code behind is:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Web.Script.Serialization;
using System.Web.UI;
using DevExpress.Web.ASPxClasses;
using DevExpress.Web.ASPxEditors;
using DevExpress.Web.ASPxGridView;
using DevExpress.Web.ASPxGridView.Rendering;
using DevExpress.Web.ASPxTabControl;
using DevExpress.Web.Data;
using DummyDevExpressApplication.Models;
using DummyDevExpressApplication.Utilities;
using System.Linq;
namespace DummyDevExpressApplication
{
   public partial class _Default : Page
   {
      private List<Planet> planets;
      
      protected void Page_Load(object sender, EventArgs e)
      {
         List<Planet> nullCheck = Session["planets"] as List<Planet>;
         if (nullCheck == null)
         {
            planets = PlanetFactory.GetPlanets();
            Session["planets"] = planets;
         }
         planets = Session["planets"] as List<Planet>;
         SetSolarSystem();
      }
      
      private void SetSolarSystem()
      {
         SolarSystem.Settings.ShowGroupPanel = false;
         SolarSystem.HtmlDataCellPrepared += SolarSystem_HtmlDataCellPrepared;
         SolarSystem.HtmlEditFormCreated += SolarSystem_HtmlEditFormCreated;
         SolarSystem.RowDeleting += SolarSystem_RowDeleting;
         SolarSystem.RowInserting += SolarSystem_RowInserting;
         SolarSystem.RowUpdating += SolarSystem_RowUpdating;
         SolarSystem.RowValidating += SolarSystem_RowValidating;
         SolarSystem.SettingsEditing.Mode = GridViewEditingMode.PopupEditForm;
         SolarSystem.DataSource = Session["planets"];
         SolarSystem.DataBind();
      }
      
      private void SolarSystem_HtmlDataCellPrepared(object sender,
            ASPxGridViewTableDataCellEventArgs e)
      {
         if (e.DataColumn.FieldName == "DiscoveryDate" && e.CellValue == null)
         {
            e.Cell.Text = "prehistory";
            e.Cell.ForeColor = Color.Firebrick;
            e.Cell.Font.Size = 8;
         }
      }
      
      private void SolarSystem_HtmlEditFormCreated(object sender,
            ASPxGridViewEditFormEventArgs e)
      {
         ASPxPageControl outerWrapper =
               (ASPxPageControl)SolarSystem.FindEditFormTemplateControl("PageControl");
         GridViewEditFormTable innerWrapper =
               (GridViewEditFormTable)outerWrapper.ActiveTabPage.FindControl("DXEFT");
         LiteralControl literal =
               (LiteralControl)innerWrapper.Rows[0].Cells[2].Controls[0].Controls[0];
         literal.Text = "AU Distance";
         ASPxTextBox firstTextBox =
               (ASPxTextBox)outerWrapper.ActiveTabPage.FindControl("DXEditor1");
         outerWrapper.ActiveTabPage.Text = firstTextBox.Text;
      }
      
      private void SolarSystem_RowDeleting(object sender, ASPxDataDeletingEventArgs e)
      {
         UseRevampedPlanets(RevampPlanets(e.Values, new OrderedDictionary()));
         e.Cancel = true;
      }
      
      private void SolarSystem_RowInserting(object sender,
            ASPxDataInsertingEventArgs e)
      {
         UseRevampedPlanets(RevampPlanets(new OrderedDictionary(), e.NewValues));
         e.Cancel = true;
      }
      
      private void SolarSystem_RowUpdating(object sender,
            ASPxDataUpdatingEventArgs e)
      {
         UseRevampedPlanets(RevampPlanets(e.OldValues, e.NewValues));
         e.Cancel = true;
      }
      
      private void SolarSystem_RowValidating(object sender,
            ASPxDataValidationEventArgs e)
      {
         List<Planet> revamp = RevampPlanets(e.OldValues, e.NewValues);
         if (IsNamingCollision(revamp)) e.RowError = "You may not have a duplicate name.";
      }
      
      private bool IsNamingCollision(List<Planet> revamp)
      {
         return revamp.Select(x => revamp.Count(y => x.Name == y.Name))
               .Any(z => z > 1);
      }
      
      private Planet GiveNewPlanet(OrderedDictionary newValues)
      {
         return new Planet()
         {
            Name = newValues[0] as string,
            ClosestAstronomicalUnitDistanceFromSun = Convert.ToDecimal(newValues[1]),
            PlanetType = (PlanetType)Convert.ToInt32(newValues[2]),
            MilesInDiameter = Convert.ToInt32(newValues[3]),
            DiscoveryDate = newValues[4] as DateTime?
         };
      }
      
      private List<Planet> RevampPlanets(OrderedDictionary oldValues,
            OrderedDictionary newValues)
      {
         List<Planet> revamp = new List<Planet>();
         if (oldValues.Count == 0) revamp.Add(GiveNewPlanet(newValues));
         foreach (Planet planet in planets)
         {
            if (oldValues.Count != 0 && planet.Name == oldValues[0] as string)
            {
               if (newValues.Count != 0) revamp.Add(GiveNewPlanet(newValues));
            } else {
               revamp.Add(planet);
            }
         }
         return revamp;
      }
      
      private void UseRevampedPlanets(List<Planet> revamp)
      {
         if (!IsNamingCollision(revamp)) planets = revamp;
         Session["planets"] = planets;
         SolarSystem.DataSource = Session["planets"];
         SolarSystem.DataBind();
         SolarSystem.CancelEdit();
      }
      
      protected void SolarSystemCallBackPanel_Callback(object sender,
            CallbackEventArgsBase e)
      {
         string json = e.Parameter;
         JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
         Measurements measurements =
               javaScriptSerializer.Deserialize<Measurements>(json);
         UpdateEverything(measurements);
      }
      
      public void UpdateEverything(Measurements measurements)
      {
         foreach (Planet planet in planets)
         {
            if (planet.ClosestAstronomicalUnitDistanceFromSun <= measurements.Distance)
            {
               planet.PlanetType = PlanetType.Inner;
            } else {
               if (planet.MilesInDiameter > measurements.Diameter)
               {
                  planet.PlanetType = PlanetType.Gas;
               } else {
                  planet.PlanetType = PlanetType.Dwarf;
               }
            }
         }
         Session["planets"] = planets;
         SolarSystem.DataSource = Session["planets"];
         SolarSystem.DataBind();
      }
   }
}

 
 

Both of my "sliders" have events at the ASP.NET markup which pass JSON into SolarSystemCallBackPanel_Callback at the code behind. Bonus: SolarSystem.Settings.ShowGroupPanel = false; ...gets rid of the "Drag a column header here to group by that column" thing. A lot of this silliness could really be better yet. The up/down arrow keys don't really work in my "sliders" as well as the clicks do. Just see this as what it is. I'm learning.

No comments:

Post a Comment