Monday, April 14, 2014

enum description attributes in C#

given something like this...

using System.ComponentModel;
namespace DummyDevExpressApplication.Models
{
   public enum PlanetType
   {
      Inner,
      [Description("Gas Giant")]
      Gas,
      Dwarf
   }
}

 
 

...one may call the following (using the System.ComponentModel and using System.Reflection namespaces) to get "Gas Giant" into a string, in this case a string called "whatever"

string whatever;
PlanetType myType = PlanetType.Gas;
FieldInfo fieldInfo = myType.GetType().GetField(myType.ToString());
if (fieldInfo != null)
{
   object[] attrs = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
   if (attrs != null && attrs.Length > 0)
   {
      whatever = ((DescriptionAttribute)attrs[0]).Description;
   }
}

Sunday, April 13, 2014

Mute and unmute audio at the Settings menu at the right navigation in Windows 8.

You will see a speaker icon there for this.

whatever.RemoveAt(13); ...is a way to remove an item by position from a list in C#.

w00t

I saw Scott Hunter kick off the Dallas Day of Dot Net convention last month.

Interesting things he mentioned:

  • Web Essentials is a Visual Studio plugin. This portrays it as the second most downloaded, and shows it providing IntelliSense for HTML tags, attribute IntelliSense for CSS, etc.
  • In the future Microsoft will distance itself from the GAC and having one install of the .NET framework for a given environment and will instead allow for different applications running at the same laptop and server to have different customizable varieties of .NET. I read onto this that some of the namespaces under System for example may become optional to include in a given project lowering the overall footprint of ASP.NET. The selective inclusion allows for the ASP.NET team to make changes for some configurations which might otherwise be breaking changes. This will speed their development.
  • AttributeRouting is a NuGet package (Install-Package AttributeRouting) which empowers a new variety of attribute-based routing for ASP.NET MVC and ASP.NET Web API.
  • A PowerShell editor for Visual Studio is to be. As I look at my notes from the talk I cannot tell if it exists already or if it is coming in the next Visual Studio.
  • There is to be a way to debug against apps deployed to Azure.
  • Browser Link is a feature for allowing edits in the browser to make their way back up into your code at Visual Studio.
  • What is Owen? If you use ASP.NET Web API or SignalR you are actually using Owen. ASP.NET is presently monolithic and the big ball of caching, session, state, etc. makes it hard to turn pieces on or off. Owen will be more componentized going forward. It came out that the decision for this (and the one in the second bullet point above) are inspired by Node.js not unlike how ASP.NET MVC was inspired by Ruby. The HttpContext currently comes with a 30K hit, but this will no longer be set in stone as your customizations may reduce this footprint. Everything will be an opt-in in Owen.
  • "Bare metal" as a term came up. I had not heard it before. I Googled it and it seems to imply a device that has not yet had an operating system installed.
  • Scott mentioned that ASP.NET is to run on both Mac and Linux! Microsoft wants ASP.NET to run everywhere to compete with Java.
  • There is to be a way to make changes in Visual Studio without stopping the debugger. Finally.

Addendum 7/27/2014: I think "Owen" is actually spelled: "Owin"

Saturday, April 12, 2014

adding events to event handlers at DevExpress ASPxGridView controls to achieve "dress up" effects

Do the event binding on the C# side as seen below. (In this example SolarSystem is a ASPxGridView.)

private void SetSolarSystem()
{
   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();
}

 
 

Um... well, I guess now we need events to go with SolarSystem_HtmlDataCellPrepared, SolarSystem_HtmlEditFormCreated, SolarSystem_RowDeleting, SolarSystem_RowInserting, SolarSystem_RowUpdating, and SolarSystem_RowValidating. I have written them here in the name of doctoring up what I originally had here and I will discuss half of these in detail. First off I am using a nullable DateTime for a planet's discovery date. If the discovery date is null I wish to denote that the planet was discovered before recorded human history. I do that like so:

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

 
 

ClosestAstronomicalUnitDistanceFromSun is a mouthful and sure does take up a lot of real estate in the form for adding and editing entries. I fix that below and I also put the name of the planet being edited into the title of the token tab on the form.

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

 
 

The RowValidating event handler provides a place to do validations. I prevent one from naming a planet a name that has already been specified.

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

 
 

The validation safeguard caused some really strange effects. Even if when a validation failed the row in the grid would get updated and even if I was also making the same validation check at RowInserting or RowUpdating. I got around the problem by making my RowInserting and RowUpdating implementations explictly write to a dummy collection and then overwriting my "real" collection with the staging grounds (if applicable). I am guessing that without this step there is some DevExpress magic to do with pointers for reference types that goes on beneath the hood which I do not yet understand which are getting in the way of my implementation.

my sophomoric DevExpress effort

Building on my first effort, I have refactored the markup of default.aspx as defined here to be as such:

<%@ 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" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" 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="Popup Edit Form Editing" />
      <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>
</asp:Content>

 
 

Things now look a smidge different:

 
 

My code behind has become...

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.Web.UI;
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.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();
      }
   }
}

 
 

I've made the list of planets a little longer too in the name of experimenting with pagination. I have included four dwarf planets I was not savvy to. The SettingsPager XML node in the markup breaks the content up into pages of ten. The three dwarf planets discovered since the turn of the millennium thus end up on a second page.

using System;
using System.Collections.Generic;
using DummyDevExpressApplication.Models;
namespace DummyDevExpressApplication.Utilities
{
   public static class PlanetFactory
   {
      public static List<Planet> GetPlanets()
      {
         return new List<Planet>()
         {
            new Planet()
            {
               Name = "Mercury",
               ClosestAstronomicalUnitDistanceFromSun = 0.307m,
               PlanetType = PlanetType.Inner,
               MilesInDiameter = 3032
            },
            new Planet()
            {
               Name = "Venus",
               ClosestAstronomicalUnitDistanceFromSun = 0.718m,
               PlanetType = PlanetType.Inner,
               MilesInDiameter = 7521
            },
            new Planet()
            {
               Name = "Earth",
               ClosestAstronomicalUnitDistanceFromSun = 0.98m,
               PlanetType = PlanetType.Inner,
               MilesInDiameter = 7926
            },
            new Planet()
            {
               Name = "Mars",
               ClosestAstronomicalUnitDistanceFromSun = 1.38m,
               PlanetType = PlanetType.Inner,
               MilesInDiameter = 4222
            },
            new Planet()
            {
               Name = "Ceres",
               ClosestAstronomicalUnitDistanceFromSun = 2.547m,
               PlanetType = PlanetType.Dwarf,
               MilesInDiameter = 590,
               DiscoveryDate = new DateTime(1801,1,1)
            },
            new Planet()
            {
               Name = "Jupiter",
               ClosestAstronomicalUnitDistanceFromSun = 4.95m,
               PlanetType = PlanetType.Gas,
               MilesInDiameter = 88846
            },
            new Planet()
            {
               Name = "Saturn",
               ClosestAstronomicalUnitDistanceFromSun = 9.05m,
               PlanetType = PlanetType.Gas,
               MilesInDiameter = 74898
            },
            new Planet()
            {
               Name = "Uranus",
               ClosestAstronomicalUnitDistanceFromSun = 18.4m,
               PlanetType = PlanetType.Gas,
               MilesInDiameter = 31763,
               DiscoveryDate = new DateTime(1781,3,13)
            },
            new Planet()
            {
               Name = "Neptune",
               ClosestAstronomicalUnitDistanceFromSun = 29.8m,
               PlanetType = PlanetType.Gas,
               MilesInDiameter = 30778,
               DiscoveryDate = new DateTime(1846,9,23)
            },
            new Planet()
            {
               Name = "Pluto",
               ClosestAstronomicalUnitDistanceFromSun = 29.7m,
               PlanetType = PlanetType.Dwarf,
               MilesInDiameter = 1430,
               DiscoveryDate = new DateTime(1930,2,18)
            },
            new Planet()
            {
               Name = "Haumea",
               ClosestAstronomicalUnitDistanceFromSun = 34.48m,
               PlanetType = PlanetType.Dwarf,
               MilesInDiameter = 217,
               DiscoveryDate = new DateTime(2003,3,7)
            },
            new Planet()
            {
               Name = "Makemake",
               ClosestAstronomicalUnitDistanceFromSun = 37.91m,
               PlanetType = PlanetType.Dwarf,
               MilesInDiameter = 882,
               DiscoveryDate = new DateTime(2005,3,31)
            },
            new Planet()
            {
               Name = "Eris",
               ClosestAstronomicalUnitDistanceFromSun = 38.54m,
               PlanetType = PlanetType.Dwarf,
               MilesInDiameter = 1445,
               DiscoveryDate = new DateTime(2003,10,21)
            }
         };
      }
   }
}

Yay Geek Squad

This problem was not one I could fix myself. I went to Geek Squad which is now a piece of Best Buy which has retained something of its identity and branding. I got a $200 yearly membership for three devices and I got my ability to connect to wireless networks restored. I wasn't crazy about the seven to ten day turnaround time, but then I don't really know where else I would have turned. In Googling, I saw a fix for my problem which had to do with hacking registry keys, but... oh, my gut told me I should defer to someone in the know.