I’ve just released the 1.3.1 version of my Community Server 2007 addon pack.

Small incremental release because I only added a small new component, AllWeblogPostsList, which doesn’t uses paging and has a few internal optimizations to retrieve all blog posts from a given blog (the one in which you place it).

I made it because I wanted to have a full one-page post archive without additional uneeded stuff for one of our blogs.

Using it is fairly easy (read the included txt), just like the normal blog post archive from CS2007 (and allowing any formatting you want):

<KartonesNet:AllWeblogPostsList id="EntryItems" Runat="server">
   
<HeaderTemplate><ul></HeaderTemplate>
   
<ItemTemplate>
       
<div>
           
<CSBlog:WeblogPostData ID="WeblogPostData" runat="server" Property="Subject" LinkTo="Post" Tag="li" />
        </
div>

   
</ItemTemplate>
   
<FooterTemplate></ul></FooterTemplate>
</KartonesNet:AllWeblogPostsList>

 

You can see a live example here.

 

I’m taking small vacations in a few days so more small things that I have in mind related to Kartones.Net and Community Server 2007 will have to wait. Until then, may the force of C# be with you!

Posted by Kartones | with no comments

Since my shift of job, I’m “living” on a multi-platform environment (in fact, I am part of the minority of Windows users), and a lot of people has iPhones. Add to it some friends who have them too, and it’s impossible to avoid looking and chatting about them. So this post is some sort of personal comparison and thoughts about iPhone and Windows Mobile.

I’ve been using Windows Mobile since I think 8 years (the old HPs with 4 colors and a terrible battery), so I have some knowledge of their evolution.

Windows Mobile has always been very similar. Apart from higher resolution "cousins” (which haven’t extended too much to the market, maybe because of the higher battery consumption, maybe because a lot of applications don’t support non standard 320x240 or 240x320 resolutions), most core changes habe been related either to internal aspects (less hangs/errors, better multithreading, lower battery consumption, smaller WM cores…) or to integrate Windows Mobile with phones (but then, not so good, because Smartphones are just non-touch screens and fewer buttons, and at least with WM5.0 Visual Studio had yet problems differentiating them).

So we can say that Windows Mobile evolves very slowly, except on from the developers perspective, because since .NET Compact Framework appeared, creating apps has been simplified and extended a lot (currently we have .NET CF versions 2.0 and 3.5, although are not comparable to normal .NET Framework ones).
And with P/Invoke we always have the option to access all the APIs non available from managed code.

But Windows Mobile has a really big burden that Microsoft seems to avoid constantly: Usability.

Using a smartphone is far away from using a low-cost Nokia, and miles away from using an iPhone. And the PDAs, phone or not, can’t be properly used without the pen, no matter how much effort companies like HTC put into creating custom UI; they are just applications over the Windows GUI and you end having to use it for any non-trivial task.

But then came the iPhone and revolution arised. A semi-perfect UI, impressive look&feel, fast responses, very decent battery, big buttons and on-screen keyboard to avoid needing a pen…

So my first question is: Why Microsoft doesn’t improves drastically the UI of Windows Mobile? After all those years it should have been a logical advance. Now, it is a requisite.

Windows Mobile 6.5 has indeed improved it, but both internet articles and friends told me that it is “the Windows Vista of PDAs”: Pretty but slow and a resource hog. So either you buy a new non-cheap phone with a fast "cpu, or forget about it. Do we have to wait now until WM7.0 to “fix” resource usage?

Apple is improving iPhone OS quite vastly, adding tons of new APIs and radical unpresent functionalities (like push application messages). Why MS don’t does something similar, at least with the things it most needs?

The iPhone has hardware accelerated graphics built-in, and a growing list of impressive games. Windows Mobile has had touch screens since they appeared, and until the Nintendo DS came out, we didn’t had more than one or two games that implemented radically different ways of interacting with a pen on a touch screen.
Some PDAs even had a now defunct NVidia 3D accelerator. And yet, I only play two games or ScummVM from my PDA, because the rest of games suck.

Why so much resource, innovation (at the past) and time wasted for nothing?

The iPhone has some serious lacks, like not supporting background applications. Windows Mobile has always had them, and it is so easy to develop one. And yet, Appe launches a “patch” (push messages) and I’m sure it will make the iPhone shine even more.

I am tired of phone company spam over SMS, so I have in mind developing an SMS-spam filter (a background application that checks new SMS as they arrive and using a black-list deletes them automatically). That kind of application can’t be done for the iPhone, unless Apple decides to do it (or you jailbreak it, because I’ve seen that kind of apps on a unlocked iPhone).

We have Windows Mobile emulators, APIs, third party components, native DLLs, ActiveSync APIs, PocketOutlook, IRDA, GPRS, 3G, Wireless, Bluetooth, SMS, MMS, push mail… tons of options at our disposal. And all of them lightly used.

Just look at the browsers… iPhones Safari is almost perfect, it only lacks Flash support, but renders webpages perfectly, allows zoom and works like a charm. Is almost equal as using the desktop Safari.
Windows Mobile 6 has a crappy “Mobile Internet Explorer” that sucks (in fact works like Internet Explorer 3.0, doesn’t even supports a decent javascript or CSS 2.0), so we have to either rely on Opera Mobile (which works fine, but needs a lot of memory and is slow and not as usable as Safari) or wish that Mobile Firefox comes out soon. Why doesn’t Microsoft improves once and for all Mobile Internet Explorer? (And no, Pocket IE6 is not much of an improvement, it is only good compared with the current PocketIE).

Apple has some dictatorial restrictions: App store apps having to be approved, SDK only for Mac and limited (but growing a lot in new APIs), no option of other browsers or applications already present at the iPhone OS… But it works! At least what they provide is really good, enough for the majority of the users!

I love having the option to develop my own applications for the devices I use, I like developing in C# for my HTC Touch. But the truth is that if in Spain Telefonica didn’t had the monopoly of iPhone distribution, I would probably switch from Windows Mobile to an iPhone, even not being able to develop my apps (or maybe yes, if Mac OS and the SDK works inside a VM…).

 

Palm is already dead because of their lack of innovation, and Windows Mobile took its market.
Blackberrys are widely extended for corporate use because they won’t be really nice, but they are usable and good for email.
If Microsoft doesn’t “awakes” and starts focusing on really improving Windows Mobile, they will lose their market share and user base and keep their PDA-Phones only for their employees and friends.

And from my developer perspective, that would be a pity. Windows Mobile isn’t bad, it is just too desktop-like and needs to become a real phone-oriented OS. But Microsoft doesn’t seems to understand that, and is slowly going to the same path Palm went: slow and delayed improvement (not evolution). While iPhone is becoming a revolution.

 

Note: Here it is a very similar post to what I think of iPhone, but more deep as the author actually owns an iPhone and not has only played with them as I.

This weekend, in my spare time, I’ve finally achieved one thing I wanted since some time: Fully automating the Site Stats of Kartones.Net.

Previously, I had to manually update the page with the current data each month, plus exporting some Google Analytics reports in PDF. As computers are supposed to exist to make our lives easier (although for developers is more of the opposite actualy :), I decided to use the Google Analytics Data Export API (which as usual uses GData for retrieval of the information) and ASP.NET Chart Controls to create an automated and live stats page.

As I had in mind publishing the class under my KartonesNet CS2007 Addon Pack, I’ve made some things in the code specifically to make it more customizable by anyone. But let’s get into how I’ve made it and how it works…

 

Without entering into details of how GData works, as usual working with it when being authenticated we will make http request to specific urls and retrieve XML contents (in which we will have a list of <entry> elements). The requests have this format:

https://www.google.com/analytics/feeds/data?ids=ga:_ProfileID_&dimensions=_Dimensions_&metrics=_Metrics_&sort=_OrderBy_&start-date=_StartDate_&end-date=_EndDate_&prettyprint=true&max-results=_MaxResults_

As we can see, the first parameter is a Profile ID, obtained after authentication. My API supports only ClientLogin (user & password, as usual *), but there are another two methods. After authentication, we will receive an authorization token, which we will need to always specify in the headers of our Http Requests:

request.Headers.Add("Authorization: GoogleLogin auth=" + sessionToken);

The profile ID represents the “site” we want to get data about. If you don’t know the profile ID of your site, or only have one, don’t bother with it, if you call the API Login() method without ProfileID, it will get the first available one.

Login with the API can’t be easier:

GoogleAnalyticsAPI googleAnalytics = new GoogleAnalyticsAPI();
bool
loginSuccess = googleAnalytics.Login(user, password)
;
if
(loginSuccess)
{
    ...

 

After authentication, we can make GData requests to gather data, by specifying dimensions and metrics. As the documentation is pretty self-explaining I won’t enter into details, just mentioning that it is important to check the valid combinations of them because you can have errors with given combinations.

I have recreated for Kartones.Net some basic reports, like visits/pageviews, visitor OS, browser and language config, or a ‘Top 20 content pages’. There is a lot of room for imagination here, specially if you perform operations with the returned data.

The API calls are very simple:

public string GADataCall(string[] Dimensions, string[] Metrics, string OrderBy);

I have not made enumerations for dimensions/metrics just in case Google extends it (and because they are a lot and I only needed a few for my reports), and the OrderBy parameter can be set to null/string.Empty if you don’t want ordering.

Note: To specify a descending order, prepend a minus to the field, e.g.: -ga:pageviews

A few important things about the API calls to gather data:

  • The date range of data to request is defined with the StartingDate and EndingDate properties of the GoogleAnalyticsAPI class. If not specified, they will set to retrieve from the past month up to yesterday (same day).
  • The number of results is specified too with the property MaxResults. By default is 50.
  • If running under ASP.NET, call results are cached automatically for the number of seconds specified in CacheExpirationTime (default: 300 secs).
  • And most important, returned data from this call is not XML, but a formatted string. ¿How do we format that string?…

One thing I wanted is to allow customization of the results while keeping them small and light. My scenario was the web, but even in there I needed more than only “table formatting” with <tr> and <td>, so I created this small interface:

namespace KartonesNet.APIs
{
   
/// <summary>
    /// Interface for implementing a Google Analytics data formatter
    /// </summary>
   
public interface
IGoogleAnalyticsDataFormatter
    {
       
/// <summary>
        /// Tag to inject before each element
        /// </summary>
        /// <returns>Tag</returns>
       
string PreElementTag()
;

       
/// <summary>
        /// Tag to inject before each field of an element
        /// </summary>
        /// <returns>Tag</returns>
       
string PreFieldTag()
;

       
/// <summary>
        /// Tag to inject after each field of an element
        /// </summary>
        /// <returns>Tag</returns>
       
string PostFieldTag()
;

       
/// <summary>
        /// Tag to inject after each element
        /// </summary>
        /// <returns>Tag</returns>
       
string PostElementTag()
;
   
}
}

Very simple and customizable (you can simply return string.Empty on those values you don’t want to use). I provide three DataFormatters with the API:

  • GoogleAnalyticsCommaDataFormatter: A simple concatenation of fields via commas. Very useful for manipulating later data (with a simple string.split(‘,’))
  • GoogleAnalyticsTableDataFormatter: Simple table formatter, adds <tr><td></td></tr> structure. Note: It adds Community Server css classes too, so I recommend you to create another one without custom css classes.
  • GoogleAnalyticsTableWithLinkDataFormatter: The same as the previous one, but converting the first field of each data element/entry into a HREF tag (for the Top 20 content pages) and prepending the host url (‘http://Kartones.net’) to it.

This is an example call that uses all stuff (not much anyway):

googleAnalytics.DataFormatter = new GoogleAnalyticsTableDataFormatter();
googleAnalytics.MaxResults = 12
;
osGAData =
googleAnalytics.GADataCall(
       
new string[] { "ga:operatingSystem"
},
       
new string[] { "ga:pageviews"
},
       
"-ga:pageviews");

 

And not much else to add… Plugging the gathered data into the ASP.NET Charting Control as usual took more time for visual setup than for logic itself but was really easy, and as it has caching too, the resulting stats page loads pretty fast on subsequent calls (and not slow on the first one).

 

Here is a screenshot of how all this looks once finished (if you want to see it live, check the stats page):

The new Kartones.Net site stats

All that remains is grabbing the component and making your own reports!

I hope the API proves useful, feel free to drop a comment with suggestions, problems or just thoughts about it :)

I’ve just updated my Community Server 2007 addon pack to add support for Slideshare presentations. The name of the component has changed to from GDocsSlideshowAddon to SlideshowsAddon so remember to update your CommunityServer.config file reflecting the change.

Here is a sample embedded presentation:

Posted by Kartones | with no comments

Sadly, the end of a lot of small to medium sized blogs is the same: left dead.

The problems or circumstances for what this happens can be varied:

  • Loss of enthusiasm: As with everything new, at first there is a peak of hype in which we only think about ‘our blog’, ‘what to post about’, etcetera.  But when that peak lowers, you have to start thinking about spending time as if it were a small job.
  • Goal-oriented blog: Some blogs don’t appear to share knowledge. Some blogs have ‘hidden’ goals, like looking cool for a job we want (at a company we know takes into account having a blog). Those blogs end usually dead when the goal is achieved (or left for impossible).
  • Loss of the original focus of the blog: A few blogs suddenly change the topics they talk about to something very different, lose their readers and end dead. IMHO it is better to open another blog for the new topics instead of switching too radically. Same applies with the language, I’ve left reading two blogs because they switched from english to german.
  • No time for blogging: Blogging takes time, specially if you plan to do it on a regular basis. Thinking small code examples, building diagrams, searching for images, writing the text… You might have personal or professional changes that take out the free time you had before. For example I use Windows Live Writer to write down ideas or post drafts, so if I’m inspired one day, I can write down all the ideas before they fade out, and then write the full posts whenever I have time.
  • Too much corporate blogging: The biggest example is MSDN blogs. Some of their blogs have awesome posts, but other blogs are dead since months or even years. Again, having a blog requires time, so you must be willing to write on it, instead of doing so just for work.
  • Zombie blogs: This is my personal denomination for those blogs who have a low and prolonged inconsistent posting rate. Blogs that for example Google Reader suggests as ‘most obscure’ (probably dead) but that, from time to time (usually a few months) post something. Only people well known and with a solid user base can relax so much without losing their readers.

 

As a small conclusion, blogging is for fun and sharing what we know, but you need to spend time on it.

Posted by Kartones | with no comments
Filed under: ,

Time for another review, PasswordsPro. PasswordsPro is a “passwords safe” tool, similar to another application I use, Flexwallet/eWallet. It allows storing sensitive passwords (like website or email account ones) in an encrypted file.

screenshot

The interface is very simple: We choose a passwords file, enter it’s master password, and then manage the list of passwords and secure notes (more on this later). There is no categorization so with a huge list of passwords it might get a bit messy, but nothing we can’t control by adding more info to the names.

screenshot

The info for each password is the typical as we can see in the screenshot just above. There’s no type/scope options, but we can store notes for each individual password (and so, use this as a manual “custom fields” section).

Apart from passwords, we can store “secure notes” too.

screenshot

As the name hints, they are just a list of text notes. Simple, but not available on other password safe programs, and interesting to not only store passwords but sensitive info.

screenshot

Apart from this features, PasswordsPro supports inactivity protection, another usual feature that allows to auto-close the application after not being used for a given time (so you don’t have to worry leaving opened your safe lists).

And the last nice feature, the application supports being used as a portable app from a usb disk, just copying the .exe, a .dll and your license file.

 

My only real complaint about this application is that I haven’t found any details of what encryption algorithms are used, just “PasswordsPro uses encryption algorithms that are standard in the industry having a strong level of security”.

Posted by Kartones | with no comments
Filed under: , ,

If your gaming PC uses Windows XP (or Windows 2003) and you mainly use it for gaming (I have a “dedicated gaming PC” only for that and watching movies/listening to music), you can change one option in the boot.ini file to achieve two goals:

  • Force detection of 3 GB of RAM in XP (32bits/x86). I’ve seen a few systems who didn’t recognized correctly the 3 GB before this tweak.
  • Increase the user process address space to 3 GB. This way a single application can have as much as 3 GB for itself. This is great for videogames who eat almost all your PC resources.
    Note: The game’s exe must have been compiled with a flag to be “3GB-aware”, so not all games benefit from this.

The command to place on your boot.ini is “/3GB”.

 

Another common tweaks are disabling DEP/NoExecute (command “/noexecute=AlwaysOff”), moving your pagefile.sys file to another hard disk (instead of the one in which Windows is installed), or disabling pagefile at all if you have 3 or 4 GigaBytes of RAM (4 for Vista, 3 for XP).

If you have Vista you can also fully disable Aero and disable the “Desktop Window Manager Session Manager” and “Superfetch” Services. Also, if you don’t use them, disable the System Restore Points as they have a significant impact on HDD activity.

 

More info of Memory under Windows and of DEP.

 

Disclaimer: As I said, this is optimum for gaming only pcs. If you use your pc for working, development or other tasks it is not recommended for example to disable Superfetch.

Posted by Kartones | 3 comment(s)
Filed under: , ,

After getting tired of still getting spam (having Brendan Tompkins CAPTCHA installed), I decided to dig into the problem, and found the big FAIL: The captcha is client-side only, relying on a Javascript validation of a cookie for sending or not the comments.

As this proved to be unsuccessful (I was getting 2-3 spam emails/comment forms per day), I’ve scraped out almost all the original code excepting the image generation stuff and redone it both client-side (checking that the CAPTCHA field is not empty) and server-side (validating CAPTCHA field).

Also, it is no longer a simple “input the numbers you see”, because most spam bots have OCR and can read it, no matter how hard you do it (up to a point humans no longer can read it either, as sometimes happens with GMail new accounts validation one). So read the small instructions because today one person didn’t and was blaming the browser xD

It doesn’t appears to the community bloggers, but if they want to check how it looks they only need to logout and navigate for example to this post comments section.

On the technical part, instead of using the original Brendan’s ControlAdapter I’ve directly modified the ContactForm and WeblogPostCommentForm classes to support and validate the captcha with server-side code (and to make it slightly faster than dynamically adding controls at render time).

I might even play with creating two or three CAPTCHAs of different types (like doing math operations or choosing the right image or color) and display randomly one of them.

 

My knowledge of Community Server is slowly but surely increasing, and I’m starting to like a lot how the “templated” ASPX controls work. Once you find the desired control or form, you can do significant logic modifications with few lines of code (the captcha controls & checks don’t take more than 20 lines of code + 4 standard web controls…). Things like adding the CAPTCHA to all blogs is a matter of adding a few fields and formatting to each blog template, no more code is needed and everything “connects” automatically. I want to make deeper modifications but this is getting interesting :)

 

And finally, as a side-note, I found another interesting “hidden” feature, the HtmlScrubber component, which cleans and sanitizes post’s HTML elements and attributes. I wanted to create something similar (but as a Windows Live Writer plugin, to make blog posts more XHTML compilant), and it is not only already implemented on CS2007, but it is also very easy to extend.

As I’ve said more than once, Community Server is quite hard to learn, but then opens an endless world of possibilities!

 

Note: Right now it is only placed on two blogs (this and another one) for testing purposes (it logs failed CAPTCHA attempts), but if everything works as expected tomorrow night I’ll install it on all blogs of the community.

Since not much after I launched Kartones.Net I added an ASP.NET user control to make the typical visits counter for all bloggers that wanted it (including me :P). Apart from having no code-behind, it had the SQL access inside (using a stored procedure and getting the configuration of the DB from ConnectionStrings.config, but still not layered).

So one step that was much needed was converting this to a proper Community Server 2007 control, both to take advantage of CS configuration & data providers, and to make the process of adding or moving a visits counter as easy as one line of ASP.NET markup code.

Last night I did it, among with using CSS sprites to do only one http request to obtain the images, and here it is the how-to:

 

First of all, I wanted a new table, to make it as small as possible, although we could have used the existing cs_weblog_Weblogs table by only adding a new long/numeric field. This is my SQL to create it:

USE [yourdbname]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET
ANSI_PADDING
ON
GO

CREATE TABLE
[dbo].[cs_VisitCounters] (
    [CounterID] [
int] NOT NULL
,
    [HitsCounter] [
int] NOT NULL
,
CONSTRAINT [PK_CounterID]
PRIMARY KEY NONCLUSTERED 
(
    [CounterID]
ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF

ALLOW_ROW_LOCKS 
= ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY
]
)
ON [PRIMARY
]

GO
SET
ANSI_PADDING OFF

 

Then, we need a stored procedure to obtain the current hits. As I’m using a new table, what I do is if it doesn’t exists creates it (else updates with +1):

USE [yourdbname]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC
[dbo].[cs_RegisterVisitHit] (
    @CounterID   
INT
   
)
AS
BEGIN

    SET TRANSACTION
ISOLATION LEVEL READ
UNCOMMITTED

   
DECLARE @CounterValue
INT
    SELECT
@CounterValue =
0

   
SELECT 
       
@CounterValue =
[HitsCounter] 
   
FROM 
       
[yourdbname].[dbo].[cs_VisitCounters] 
   
WHERE 
       
[CounterID] =
@CounterID

   
IF @CounterValue =
0
   
BEGIN
        INSERT INTO 
           
[yourdbname].[dbo].[cs_VisitCounters]
           ([CounterID], [HitsCounter]) 
       
VALUES 
           
(@CounterID, 1
)
   
END
    ELSE
    BEGIN
        UPDATE 
           
[yourdbname].[dbo].[cs_VisitCounters] 
       
SET [HitsCounter] = (@CounterValue + 1
       
WHERE [CounterID] =
@CounterID
   
END
    
    SELECT
(@CounterValue+1)

END

GO

 

The SQL Part is done, now we need to add support for this stored procedure in the CS Data Providers.

First, we have to add an abstract method to CommunityServer.Components.CommonDataProvider:

public abstract long RegisterVisitHit(int CounterID);


And then, the actual code on the SqlDataProvider class:

public override long RegisterVisitHit(int CounterID)
{
   
long hits = 0
;

    using
(SqlConnection connection =
GetSqlConnection())
    {
        SqlCommand command
= new SqlCommand(this.databaseOwner + ".cs_RegisterVisitHit", connection)
;
       
SqlDataReader reader
;

       
command.CommandType = CommandType.StoredProcedure
;

       
command.Parameters.Add("@CounterID", SqlDbType.Int).Value = CounterID
;

       
connection.Open()
;
       
reader = command.ExecuteReader()
;

        if
(reader.Read())
            hits
= Convert.ToInt64(reader[0])
;

       
connection.Close()
;
   
}

   
return hits
;
}

As we can see on the previous code, the great thing of using CS Data Providers is that we forget about creating connections to the DB, just get the instance and execute whatever we want.


Now we need to implement the control. As we don’t want anything special, rendering as a simple LiteralControl would be enough, and we can directly inherit from CS WrappedContentBase class (one of the basic control classes). Here is the code of the full component:

using System;
using
System.Text
;
using
System.Web.UI
;
using
CommunityServer.Controls
;
using
CommunityServer.Components
;

namespace
KartonesNet.Components
{
   
public class
VisitsCounter : WrappedContentBase
    {
       
private int counterID = -1
;
        private int
padding = 0
;

        public string
CounterID
        {
           
get
           
{
               
return counterID.ToString()
;
           
}
           
set
           
{
               
int val
;
                if
(int.TryParse(value, out
val))
                {
                   
if (val > -1
)
                    {
                        counterID
= val
;
                   
}
                }
            }
        }

       
public string
Padding
        {
           
get
           
{
               
return padding.ToString()
;
           
}
           
set
           
{
               
int val
;
                if
(int.TryParse(value, out
val))
                {
                   
if (val > 0
)
                    {
                        padding
= val
;
                   
}
                }
            }
        }

       
protected override void
BindDefaultContent(Control ParentControl, IDataItemContainer DataItemContainer)
        {
            StringBuilder result
= new StringBuilder(350)
;
            long
hits = 0
;
            string
hitsString = string.Empty
;

            if
(counterID > -1
)
            {
                hits
= CommonDataProvider.Instance().RegisterVisitHit(counterID)
;
               
hitsString = hits.ToString()
;

                if
(padding > 0
)
                {
                   
while
(hitsString.Length < padding)
                    {
                        hitsString
= hitsString.Insert(0, "0")
;
                   
}
                }
               
for (int digit = 0; digit < hitsString.Length;
digit++)
                {
                    result.Append(
"<span class='counter' id='counter")
;
                   
result.Append(hitsString[digit])
;
                   
result.Append("'></span>")
;
               
}
                ParentControl.Controls.Add(
new LiteralControl(result.ToString()))
;
           
}
        }
    }
}

Some explanations and comments about the code:

  • As you might have noticed, the public properties are strings. This is because the CS control wrapping converts them to either objects or strings, but with a proper parsing it’s not any problem.
  • I added a padding property to add zeros at the left if we want, but the only requisite is the CounterID property.
  • We output directly inside a StringBuilder (remember to avoid string concatenations, even more if you can guess an approximate size of the StringBuilder capacity), and create <span> tags (I use it inside a <li> container, but otherwise you shold add too a <div> or similar container to group it).
  • We finally just add a LiteralControl to the parent control, but for more complex controls we can play here adding whatever we want.
  • CSS stuff is not handled there in purpose, delegating on each blog to implement the specific CSS (but removing unneeded complexity from the code).

The next step is to define the CSS stuff needed to display the counter digits (thanks here to my colleage Carlos Tallón for the cross-browser help):

span.counter
{
    background
:url('../images/counter_bulleted.gif') no-repeat;
    height
: 20px;
    overflow
: hidden;
    padding
: 4px 0 6px 15px;
    width
: 15px
}


#counter0
{
    background-position
:0px 0px
}


#counter1
{
    background-position
:-15px 0px
}

#counter2
{
    background-position
:-30px 0px
}

#counter3
{
    background-position
:-45px 0px
}

#counter4
{
    background-position
:-60px 0px
}

#counter5
{
    background-position
:-75px 0px
}

#counter6
{
    background-position
:-90px 0px
}

#counter7
{
    background-position
:-105px 0px
}

#counter8
{
    background-position
:-120px 0px
}

#counter9
{
    background-position
:-135px 0px
}

The counter class for spans defines the CSS sprite file, width and height (plus stuff for IE), and each counterX id defines the position of each digit inside the sprite file. I could (and probably should) have used classes for them too, but I wanted as few HTML code as possible (and I won’t be doing any JS DOM stuff with them).

The CSS sprite I used is this one:
CSS Sprite

We’re almost at the end, so now that we have everything, we need to plug our custom control, which means adding a new line under the <controls> section of Web.Config:

<add tagPrefix="KartonesNet" namespace="KartonesNet.Components" assembly="KartonesNet"/>


Finally, we can use the control on every page we want with a simple line of code:

<KartonesNet:VisitsCounter ID="VisitsCounter" CounterID="0" runat="server" />

 

And that’s all. You can see a live sample in the left column of this blog, or in other blogs of the community. By using CSS sprites we only make one http request call to get the counter digits so it’s a small cost, and being plugged in into Community Server we can do anything (like logging Event entries).

I have in mind creating at least two more controls which don’t need SQL code, so when I’m finished I’ll add them to my CS2007 Addon Pack.

Posted by Kartones | with no comments

After getting some feedback and visits from my review of a GUI prototyping tool, here we come with another review, this time of FlairBuilder.

FlairBuilder is a Adobe AIR made GUI prototypes editor, in some aspects similar to others in the market, but in others quite interesting. To test it, I’ve played a bit with my current mobile version of Kartones.Net:

Final Kartones.net mobile appearance

The editor is clean and focused on the design. almost no labels, all commands available, and stuff like content editing is done via double-clicking or by modifying the properties and events (more on this later).

FlairBuilder Screenshot

To create our windows/pages, we just drag & drop elements and modify their content, images, font and such. No big deal, just what we’re suppossed to do.

FlairBuilder Screenshot

Common stuff like alignment, grouping, and z-level management is done really easy. It reminded me a lot to how Powerpoint works, and in fact I haven’t even looked at the help (which I try to avoid in order to be like almost all of us are, non-manual readers xD).

FlairBuilder Screenshot

Apart from the basic features, I’ve liked from this application various small features:

First, it includes the concept of “masters”, much like ASP.NET master pages, or powerpoint templates: A basic layout to avoid redesigning the same once and again in every page. Much appreciated ;)

Second, the concept of project and its interaction: Not only as a group of different pages, FlairBuilder allows to create events to add interactivity to our prototypes. From button or hyperlink clicks (to go to another page, for example), to having a Video player control that actually renders youtube videos in the preview mode.

Third, the annotations: Very useful if you want to add notes of how something should behave (or if multiple people are editing a prototype).

And finally, the fact that for viewing the prototypes you don’t need anything but an internet connection. The viewer is only and you only need to supply a URL, like this one:
http://www.flairbuilder.com/viewer/?url=http%3A//www.flairbuilder.com/samples/Google.fbp

It works on Windows, Linux and Mac and the author is adding new features and controls.

As a conclussion, it implements some stuff commonly seen in this kind of software applications, but improves on another things like the no requisite for viewing our designs or the direct interaction with the elements.

More Posts Next page »