December 2003 - Posts

Macromedia Central Failing

So, Central 1.0 has been out for quite some time (and the betas for even longer)... but, no one seems to be making any Central apps available (I see 6 total Central apps, all free samples). To the contrary, interested developers end up posting things like this in Macromedia's forums:

“Financial transactions through Verisign, Bank of America, Paycom, Authorize.net et al do not equate to 20% of sales or $20 per user per year. So it begs the question, "What else are we getting for our money". It is a valid concern. It is actually the "distribution and bringing attention to" that is in question.” [1]

“I've read the latest on the Macromedia Central License Programs today. For the individual developer turned small businessman, I consider the pricing models problematic at best“ [2]

Which is exactly the type of attitude I predicted months ago. In a world where most major companies give their frameworks away for free in order to get developers on the bandwagon and never charge any licensing costs, this whole Central pricing model is just rediculous. Developers are going to have a hard enough time trying to convience management/customers to have them develop apps for some little known app that some company is trying to push as the latest and greatest internet revolution already. Why make the barrier even bigger by telling everyone that if they ever manage to make any money have their apps, they will have to forfiet 20% of their profits to Macromedia. If Central is so cool and revolutionary, Macromedia should be able to offset development costs by increased sales in their development environments (Flex and Flash). Isn't this what Microsoft did with VS.NET? Apparently, despite all the talk coming from the PR folks at Macromedia, no one could convience management at Macromedia that Central was that revolutionary, or even revolutionary enough to take a risk with. So, why should I, the developer, put my faith in a product that Macromedia doesn't even trust?

[1] http://webforums.macromedia.com/central/messageview.cfm?catid=297&threadid=746568

[2] http://webforums.macromedia.com/central/messageview.cfm?catid=297&threadid=741520

Posted by Jesse Ezell with 13 comment(s)

Bad Interfaces

“If you have a good interface on your system, and a budget of money and time, you can work on your system. If your system has bugs or is too slow, you can improve it. But if your system has a bad interface, you basically have nothing. It won't matter if it is a work of the highest craftsmanship on the inside. If your system has a bad interface, no one will use it. So the interface or surface of the system, whether to users or other machines, is very important.” [1]

[1] Matz on Craftsmanship. http://www.artima.com/intv/craft.html

Posted by Jesse Ezell with no comments

Offshoring Not 100% Bad?

I have to agree with Anand's statement that “not everyone in India is an illitrate person coding for peanuts.“ In a project I am currently involved in, we have a dev from India who just happens to be one of the best (if not the best) experts on the jacked up PowerPoint object model. He has been working magic in ways that the American devs I have worked with on this same sort of thing could only dream.

[1] Offshoring. Anand M. http://www.dotnetindia.com/2003/12/offshoring.html

Posted by Jesse Ezell with 3 comment(s)

Longhorn and Yukon Actually Don't Share That Much Code

“Clearing up some of the mystery surrounding Microsoft's efforts, Sorensen said that products using the storage technology will not have common code, as many industry watchers have thought. Instead, individual product groups within Microsoft will each develop storage systems based on a common design specification.

Yukon and Longhorn "are two completely separate projects--different development teams, different development schedules--so there is no schedule impact. Yukon delays will have no bearing on Longhorn," Sorensen said. "There is a shared philosophy, not so much shared code." [1]

[1] The Long Road to Yukon. http://zdnet.com.com/2100-1104_2-5129900.html

Posted by Jesse Ezell with no comments

The One SQL Tool That Might Save Your Job

What happens when you load up Query Analyzer, generate a database script based off an existing database, then run that script to create an identical database? Theoretically, it creates the new database, right? Well... some times, query analyzer decides to put “USE“ statements inside the script, which means that instead of creating the new database, you will drop all the tables in the old database and then recreate them... which, of course, means that all your data will instantly dissappear (doesn't matter if it is $10 of data, or $10 million dollars of data, it will be gone). Now, assuming that your backups are all running (and are in working order), and your transaction logs contain all the information you need (which won't be the case if you are using image or text fields and haven't set explicitly told SQL to save that info), you might be able to undo the damage. But, you know what they say about assumptions...

So, if your assumption turns out to be incorrect, go get a copy of Lumigent's SQL Log Explorer. Not only is it a niftly little tool, but if you catch your error quickly and make a copy of the DB before too much new data is inserted, it can also magically restore data from dropped tables (no backups required).

Posted by Jesse Ezell with 7 comment(s)

.NET Weblogs Archive Up Again

Had a little downtime on the .NET Weblogs Archive. It is up and running again. Sorry for any missed blog reading this might have caused anyone.
Posted by Jesse Ezell with no comments

Kick @#$! Client Side Scripting Demos

At Darren's request, I have put up a little demo of the client scripting samples from last night. You can find it here:

http://www.activehead.com/demos/clientscripts/test.aspx

Posted by Jesse Ezell with 2 comment(s)

Client Side Scripts Part 2: Flex Style Animations in DHTML

Previously, I presented a framework for using client side events. Now, just to get your imaginative juices flowing, here is a sample that illustrates how you could easily implement a Flex style animation framework to add life to your UIs. Imagine being able to add rich animations to your UI with something as simple as: “<Animate Event="OnClick" ControlToModify="MyControl" PropertyToModify="Width" StartValue=“0“ EndValue=“400“ Duration=“1000“ Relative=“True“ />“. Sound interesting... read on...:

 public class AnimateClientScript : ClientScript
 {
  string controlToModify;
  public string ControlToModify
  {
   get
   {
    return controlToModify;
   }
   set
   {
    controlToModify = value;
   }
  }
  string propertyToModify;
  public string PropertyToModify
  {
   get
   {
    return propertyToModify;
   }
   set
   {
    propertyToModify = value;
   }
  }
  int duration;
  public int Duration
  {
   get
   {
    return duration;
   }
   set
   {
    duration = value;
   }
  }
  string startValue;
  public string StartValue
  {
   get
   {
    return startValue;
   }
   set
   {
    startValue = value;
   }
  }
  
  string endValue;
  public string EndValue
  {
   get
   {
    return endValue;
   }
   set
   {
    endValue = value;
   }
  }
  bool relative;
  public bool Relative
  {
   get
   {
    return relative;
   }
   set
   {
    relative = value;
   }
  }
  public override string GenerateScript()
  {
   Control c = Parent.Page.FindControl(ControlToModify);
   WebControl wc = c as WebControl;
   StringBuilder builder = new StringBuilder();
   string cID = c.ClientID.Replace(":","_");
   string property = "";
   if(propertyToModify == "Width")
   {
    property = "style.width";
   }
   builder.AppendFormat("document.all['{0}'].reps = 0;",cID);
   if(relative)
   {
    builder.AppendFormat("document.all['{0}'].initialValue = parseInt(document.all['{0}'].{1}.replace('px',''))+{2};", cID, property, startValue);
   }
   else
   {
    builder.AppendFormat("document.all['{0}'].initialValue = {1};", cID, startValue);
   }
   int timeout = duration / 50;
   builder.AppendFormat(@"document.all['{0}'].intervalID = setInterval('if(document.all[\'{0}\'].reps*{5} < {4})” + 
   @“{{ document.all[\'{0}\'].{1} = document.all[\'{0}\'].initialValue + ({2}-{3})*“ +
   @“(document.all[\'{0}\'].reps*{5})/{4}; document.all[\'{0}\'].reps++; }} else “ +
   @“{{ clearInterval(document.all[\'{0}\'].intervalID); }}', {5});", cID, property, 
   endValue, startValue, duration, timeout);
   return builder.ToString();
  }
 }

Keep in mind this sample is not intended to be complete, there are lots of little additions that would need to be made to get this fully functional (such as support for multiple concurrent animations and saving those vals back upon postback), but it does at least give you the idea of how a framework like this could really beef up your UI with zero client side script coding on the part of its consumers. Don't you wish I was on the ASP.NET team ;-).

Posted by Jesse Ezell with 1 comment(s)

Client Side Events, A Kick @#$% Solution

After reading a recent blog post by Ted Neward, I was once again reminded of why ASP.NET's class library sucks when it comes to client side events. Apparently, the ASP.NET team thinks everyone should code client side scripts in JavaScript. While that is definately one way to do it, that solution is a piece of crap, because no one actually does this. However, 90% of the time, reacting to client-side events on the client-side (what a concept...) can add a ton to your UI. Users should have to wait 3-5 seconds every time they click a button just so that you can refresh the UI a bit.

So, rather than complain about how brain-dead the ASP.NET team tends to be sometimes (and I must emphasize the sometimes part, because overall the do produce some awesome stuff), I offer a solution. Keep in mind, this was all concieved, designed, and implemented in the last hour or two, so it is just a start, but IMO, it is a pretty darn good one and sure beats the hell out of any solution I have seen so far. I will definately be integrating this approach into our apps. Feel free to extend it for yours (it could use richer design time support for starters). I've included the base classes and a sample control that allows you to dynamically the change style of any web control / control in response to client side events (upon postback, the target control will also be automatically updated to reflect the style changes in its properties, so that everything is consistent when the page refreshes).



 public class ChangeStyleClientScript : ClientScript
 { 
  public ChangeStyleClientScript()
  {
   
  }
  string controlToModify;
  public string ControlToModify
  {
   get
   {
    return controlToModify;
   }
   set
   {
    controlToModify = value;
   }
  }
  public string CssClass
  {
   get
   {
    return controlStyle.CssClass;
   }
   set
   {
    controlStyle.CssClass = value;
   }
  }
  public FontInfo Font
  {
   get
   {
    return controlStyle.Font;
   }
  }
  Style controlStyle = new Style();
  public Style ControlStyle
  {
   get
   {
    return controlStyle;
   }
  }
  public Color BackColor
  {
   get
   {
    return ControlStyle.BackColor;
   }
   set
   {
    ControlStyle.BackColor = value;
   }
  }
  public Color ForeColor
  {
   get
   {
    return ControlStyle.ForeColor;
   }
   set
   {
    ControlStyle.ForeColor = value;
   }
  }
  public Unit BorderWidth
  {
   get
   {
    return ControlStyle.BorderWidth;
   }
   set
   {
    ControlStyle.BorderWidth = value;
   }
  }
  public BorderStyle  BorderStyle
  {
   get
   {
    return ControlStyle.BorderStyle;
   }
   set
   {
    ControlStyle.BorderStyle = value;
   }
  }
  public Color BorderColor
  {
   get
   {
    return BorderColor;
   }
   set
   {
    BorderColor = value;
   }
  }
  public override string GenerateScript()
  {
   Control c = Parent.Page.FindControl(ControlToModify);   
   string ctmID = c.ClientID.Replace(":","_");
   StringBuilder script = new StringBuilder();
   WebControl wc = c as WebControl;
   if(wc == null || ControlStyle.BackColor != wc.BackColor)
   {
    script.Append(ctmID);
    script.Append(".style.backgroundColor='");
    script.Append(ColorTranslator.ToHtml(ControlStyle.BackColor));
    script.Append("';");    
   }
   if(wc == null || ControlStyle.ForeColor != wc.ForeColor)
   {
    script.Append(ctmID);
    script.Append(".style.color='");
    script.Append(ColorTranslator.ToHtml(ControlStyle.ForeColor));
    script.Append("';");    
   }
   if(wc == null || ControlStyle.BorderStyle != wc.BorderStyle)
   {
    script.Append(ctmID);
    script.Append(".style.borderStyle='");
    script.Append(ControlStyle.BorderStyle.ToString());
    script.Append("';");    
   }
   if(wc == null || ControlStyle.BorderWidth != wc.BorderWidth)
   {
    script.Append(ctmID);
    script.Append(".style.borderWidth='");
    script.Append(ControlStyle.BorderWidth.ToString());
    script.Append("';");
   }
   if(wc == null || ControlStyle.BorderColor != wc.BorderColor)
   {
    script.Append(ctmID);
    script.Append(".style.borderColor='");
    script.Append(ControlStyle.BorderColor.ToString());
    script.Append("';");    
   }
   if(wc == null || CssClass != wc.ControlStyle.CssClass)
   {
    script.Append(ctmID);
    script.Append(".className='");
    script.Append(ControlStyle.CssClass);
    script.Append("';");
   }   
   
   if(wc == null || wc.Font.Bold != Font.Bold)
   {
    script.Append(ctmID);
    script.Append(".style.fontWeight='");
    script.Append(ControlStyle.Font.Bold ? "bold" : "normal");
    script.Append("';");
   }
   if(wc == null || wc.Font.Italic != Font.Italic)
   {
    script.Append(ctmID);
    script.Append(".style.fontStyle ='");
    script.Append(ControlStyle.Font.Italic ? "italic" : "normal");
    script.Append("';");
   }
   if(wc == null || wc.Font.Underline != Font.Underline)
   {
    script.Append(ctmID);
    script.Append(".style.textDecoration ='");
    script.Append(ControlStyle.Font.Underline ? "underlined" : "none");
    script.Append("';");
    if(Font.Underline)
    {
     script.Append(ctmID);
     script.Append(".style.textUnderlinePosition='below';");
    }
   }
   if(wc == null || wc.Font.Size != Font.Size)
   {
    script.Append(ctmID);
    script.Append(".style.fontSize ='");
    script.Append(ControlStyle.Font.Size);
    script.Append("';");
   }
   if(wc == null || wc.Font.Overline != Font.Overline)
   {
    script.Append(ctmID);
    script.Append(".style.textDecoration='");
    script.Append(ControlStyle.Font.Overline ? "underlined" : "none");
    script.Append("';");
    if(Font.Overline)
    {
     script.Append(ctmID);
     script.Append(".style.textUnderlinePosition='above';");
    }
   }
   if(wc == null || wc.Font.Strikeout != Font.Strikeout)
   {
    script.Append(ctmID);
    script.Append(".style.textDecorationLineThrough='");
    script.Append(ControlStyle.Font.Overline ? "true" : "false");
    script.Append("';");
   }   
   return script.ToString();
  }
  protected override void OnExecuted()
  {
   WebControl wc = Parent.Page.FindControl(ControlToModify) as WebControl;
   if(wc != null)
   {
    wc.ControlStyle.CopyFrom(ControlStyle);
   }   
  }
 }
 public abstract class ClientScript
 {
  public event EventHandler Executed;
  Control parent;
  public Control Parent
  {
   get
   {
    return parent;
   }
   set
   {
    parent = value;
   }
  }
  ClientEvent clientEvent;
  public ClientEvent ClientEvent
  {
   get
   {
    return clientEvent;
   }
   set
   {
    clientEvent = value;
   }
  }
  public abstract string GenerateScript();
  protected virtual void OnExecuted()
  {
  }
  internal void RaiseEvent(object sender)
  {
   OnExecuted();
   if(Executed != null)
   {
    Executed(sender, EventArgs.Empty);
   }
  }
 }
 [Flags]
 public enum ClientEvent
 {
  None = 0, OnClick = 1, OnMouseOver = 2, OnMouseOut = 4
 }
 public class ClientScriptWebControl : WebControl, IPostBackDataHandler
 {
  public ClientScriptWebControl()
  {
   
  }
  ClientScript script;  
  public ClientScript Script
  {
   get
   {
    return script;
   }
   set
   {
    script = value;
    if(script != null)
    {
     script.Parent = this;
    }
   }
  }
  protected string EventFieldName
  {
   get
   {
    return "__events_"+ClientID.Replace(":","_");
   }
  }
  protected override void OnInit(EventArgs e)
  {
   base.OnInit (e);
   Page.RegisterRequiresPostBack(this);
  }
  protected override void OnPreRender(EventArgs e)
  {
   base.OnPreRender (e);   
   Page.RegisterHiddenField(EventFieldName,"");
  }
  protected void RenderScript(HtmlTextWriter writer)
  {
   if(script != null)
   {
    string scriptText = script.GenerateScript()+EventFieldName+".value=1;";
    if((script.ClientEvent & ClientEvent.OnClick) != ClientEvent.None)
    {
     writer.AddAttribute("onClick", scriptText);
    }
    if((script.ClientEvent & ClientEvent.OnMouseOver) != ClientEvent.None)
    {
     writer.AddAttribute("onMouseOver", scriptText);
    }
    if((script.ClientEvent & ClientEvent.OnMouseOut) != ClientEvent.None)
    {
     writer.AddAttribute("onMouseOut", scriptText);
    }
   }
  }
  public void RaisePostDataChangedEvent()
  {
   script.RaiseEvent(this); 
  }
  public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
  {
   if(Page.Request[EventFieldName] == "1")
   {
    RaisePostDataChangedEvent();
   }
   return false;
  }
 }
 public class ClientScriptLinkButton : ClientScriptWebControl
 {
  public string Text
  {
   get
   {
    return ViewState["Text"] as string;
   }
   set
   {
    ViewState["Text"] = value;
   }
  }
  protected override void OnPreRender(EventArgs e)
  {
   
   base.OnPreRender (e);   
  }
  public override void RenderBeginTag(HtmlTextWriter writer)
  {
   writer.AddAttribute("id", ClientID.Replace(":","_"));
   writer.AddAttribute("type", "submit");
   ControlStyle.AddAttributesToRender(writer);
   RenderScript(writer);
   writer.AddAttribute("href","#");
   writer.RenderBeginTag("a");
   writer.Write(Text);
  }
  public override void RenderEndTag(HtmlTextWriter writer)
  {
   writer.WriteEndTag("a");
  }
 }
Posted by Jesse Ezell with 2 comment(s)

Why Standards Are Better

“Signage has always offered HCI people a lot of laughs. A friend of mine recently linked to this sign at a Japanese metro (I think it's a sign on the train itself, not the station). What do you think the signs mean? I will post a comment with the answers tomorrow.“

Japanese Metro Sign

[1] Metro Sign. http://www.ok-cancel.com/archives/post/2003/12/metro_sign.html

Posted by Jesse Ezell with 3 comment(s)
More Posts Next page »