Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,523
|
Comments: 51,145
Privacy Policy · Terms
filter by tags archive
time to read 1 min | 193 words

It is rare that I get impressed by something, but I have to admit that balsamiq has managed to really impress me.

Balsamiq mockups is a flash application that you can use to very quickly build a UI mockup. That in itself is not really new. What is new is that they have somehow managed to take this to a completely new level from the point of view of usability. It took me 7 minutes, from first use of their program, to get this thing working:

image

Aaron Jensen talked about it as well, which is how I heard about it, and damn am I thankful for that.

To say that I am impressed is not sufficient, I am going to buy a copy just because I didn't have to (I was able to create the above picture using the free version). If I could do it, and I am so dense in matters of UI that I cannot draw a straight line with a ruler, that is saying a lot. That I could do it so far, that is saying even more.

time to read 1 min | 147 words

Let us take a look at this dialog, shall we?

image

What I see is that we have two text fields and two checkboxes, in a fairly big dialog, to express the following bit of information:

image

I honestly have no idea what the purpose of this is. To make it harder to input the information, presumably. Given a URL, I now need to split it apart manually, I need to know that https is usually on 443, etc.

It also mean that when I am talking to a user, I need to give her three pieces of information, and explain where to put each of them, rather than sending a URL that she can just copy / paste in place and be done with it.

time to read 3 min | 482 words

Update: This article is fron 1999, which I somehow missed, not news by far. Still wrong, even for its time, though.

This really annoys me, the author of the article tries to preach an OO methodology for writing UI. In general, I don't have an issue with that, but his arguments contains this:

An object-oriented solution tries to encapsulate those things [adding a field to a screen] that are likely to change in such a way that a change to one part of the program won't impact the rest of the program at all. For example, an object-oriented solution to the problems I just discussed requires a Name class, objects of which know how to both display and initialize themselves.

I am not sure where to begin. To start with, an entity should not have ties to the UI. Then we have this dream vision about editing a single class, and suddenly all the relveant screen sprout the new field, and we can start working on it. UI doesn't work like this. It doesn't work like this because adding a new field to screen can horribly break the screen. It might push the Ok/Cancel button outside of the visible realm of the screen, it might break the tab order, it will definately break the flow of the screen.

I am all for OO and encapsulation, but this is not a problem that you can generalize.

This just gets better when you keep on reading:

The other bugaboo that I want to put to death is the notion of different views into the same object, usually characterized by the question: "Suppose you need to display this data as a pie chart over here and a grid over there? How can you do this if the object displays itself?"

Now let's get real. How often in your work has this problem actually come up?

Um, all the time? The simplest version is showing items in a list and then in a details view, but I just finished talking about the different views that I present to the various aspects in my systems. Beyond that, I have different views for different screens. In one screen, I am showing an Employee's personal data, in another the Employee's salary data, etc.

time to read 6 min | 1033 words

As you probably noticed, I am doing a fair amount of integration testing with ASP.Net currently. I am using Selenium, and after a brief struggle to get myself aligned with it, I think that I like it :-)

It is simple, it is extensible, and it works. Here is a simple example, I needed to check that a drop down is disabled, Selenium has no support for this out of the box, so I could just extend the DefaultSelenium class (in Selenium RC for .NET) and add this:

public bool IsDisabled(string partialElementId)

{

    string result = GetElementAttribute(partialElementId, "disabled");

    return bool.Parse(result);

}

 

public string GetElementAttribute(string partialElementId, string attribute)

{

    string evalScript = string.Format(@"

this.page().findElement('//select[contains(@id, \'{0}\')]').{1};

", partialElementId.Replace("'", @"\'"), attribute);

    string result = GetEval(evalScript);

    return result;

}

Note the funky syntax that I have to use in order to get around the ASP.Net element naming. Another thing that I run into was the compilation model for ASP.Net. That one was a biggie.

The problem was that I wanted to specify a default value for page load times (all pages must load in under 0.5 seconds, for instnace). The problem was the just from the command line build, it started giving errors about timeouts when loading a page. Accessing the page manually or running the tests from Visual Stuido always succeeded.

After a bit of head scratching I realized that the problem was that the build recompiled the site, so every time that I hit a new page, ASP.Net had to compile it for the first time. I ended up keeping a list of pages that I already visited and increasing the wait time for pages that I access for the first time.

The last problem that I have is getting the correct information from the pages, let us take a page that threw an error, in the Selenium test, I usually just get an error about an expected element not found. I added an assert that check that "error" and "exception" do not appear on the page, but I didn't figure out yet how to take just the exception text from the page and put it in the error message for the test.

time to read 2 min | 252 words

David Hayden has a post (ASP.NET AJAX Web Controls - AJAX Experience Without Learning AJAX) that really bothers me. He talks about the different components that he tested for Ajax support, and conclude with:

I was thinking that the AJAX experience was going to be a lot of work, but my guess is that the control vendors are taking care of this for me. And, quite frankly, I didn't want to have to learn this technology in depth if I could get away with it.

Frankly, I would like to know as much about Ajax as I could get away with. The problem is that you can't escape from the complexity. You can wrap it, hide it, stash it in the attic and throw away the key, but it will come back and hunt you.

(Image from clipboard).png Complexity, meet Ajax, you are going to be best friends.

The law of leaky abstractions holds quite well for Ajax. Eventually, you will need to do something slightly beyond what is supported by the controls, or you will get an error that can't be fixed by setting a property somewhere, or you'll need to debug an issue. Those are the cases where the willful ignorance will come to haunt you.

time to read 37 min | 7228 words

I have talked recently about the problems with 3rd party controls and I made the statement that most of the time, I would want to do this on my own, since figuring out how to get to where I want with a 3rd party control often takes as much time as developing the functionality myself. Scott Bellware commented on this post, asking how to get this functionality in MonoRail.

Here is a simple implementation of a grid ViewComponent in MonoRail. It supports header / footer (which the GridView doesn't), empty template, alternating rows schemes, and pagination. I feel that this encompass quite a large precentage of the feature that are in the ASP.Net GridView, so let us see how we build it. We start by deriving from ViewComponent:

public class BasicGridComponent : ViewComponent

We then define the sections that we support. If you are coming from Web Forms world, you can think of View Components as Server / User Controls and sections as the equipollent of ITemplate.

static readonly string[] sections = new string[]

    {

        "header", "footer",

        "pagination", "empty",

        "item", "alternateItem",

        "tablestart", "tableend"

    };

 

public override bool SupportsSection(string name)

{

    return Array.IndexOf(sections, name) != -1;

}

Now, let us build the all important Render() method:

public override void Render()

{

    IPaginatedPage source = (IPaginatedPage) ComponentParams["source"];

 

    ShowStartTable();

    ShowHeader(source);

   

    if(source != null && source.TotalItems>0)

    {

        ShowRows(source);

    }

    else

    {

        ShowEmpty();

    }

 

    ShowFooter();

    ShowEndTable();

    ShowPagination(source);

}

One interesting thing to note here is that we are using IPaginatedPage as our data source, which provides us with most of the paging support out of the box. We are mostly delegating to methods that does a single thing, mostly, they provide default overridable functionality. Here is a sample of such a method:

private void ShowEmpty()

{

    if(Context.HasSection("empty"))

    {

        Context.RenderSection("empty");

    }

    else

    {

        RenderText("Grid has not data");

    }

}

Most of the other methods are implemented in a similar fashion, we provide a default implementation and we can override it in place by specifying the appropriate section. ShowStartTable, ShowHeader, ShowEmpty and ShowFooter all share the same concepts. ShowPagination share the same idea, but is a bit more complex, because it outputs the entire pagination toolbar, which has a lot of conditionals.

ShowRows, however, is very interesting:

protected virtual void ShowRows(IPaginatedPage source)

{

    bool hasAlternate = Context.HasSection("alternateItem");

    bool isAlternate = false;

    foreach(object item in source)

    {

        PropertyBag["item"] = item;

 

        if (hasAlternate && isAlternate)

            Context.RenderSection("alternateItem");

        else

            Context.RenderSection("item");

 

        isAlternate = !isAlternate;

    }

}

We check if we have an alternateItem section, and render the sections accordingly. The PropertyBag["item"] allows the section to access the current item in a convient fashion.

Here is the code for this View Component:

<?brail

component BasicGridComponent, {"source": contacts}:

  section header:

?>

    <th>EMail</th>

    <th>Phone</th>

<?brail

  end

  section item:

?>

<tr style="background-color: #fea;">

  <td>${item.Email}</td>

  <td>${item.Phone}</td>

</tr>

<?brail

  end

  section alternateItem:

?>

<tr style="background-color: white;">

  <td>${item.Email}</td>

  <td>${item.Phone}</td>

</tr>

<?brail

  end

end

?>

But, you know what, this is too much for me to write. Let us say that I love the AutoGenerateColumns option in the GridView. What would it take to implement it as a ViewComponent? Well, as it turns out, not much, really.

public class GridComponentWithAutoGenerateColumns : BasicGridComponent

{

    private PropertyInfo[] properties;

 

    protected override void ShowRows(IPaginatedPage source)

    {

        if (properties == null)//there are no rows, if this is the case

            return;

        bool isAlternate = false;

        foreach(object item in source)

        {

            RenderText("<tr>");

            foreach(PropertyInfo info in properties)

            {

                if(isAlternate)

                    RenderText("<td class='alternateItem'>");

                else

                    RenderText("<td class='item'>");

                RenderText(info.GetValue(item,null).ToString());

                RenderText("</td>");

                isAlternate = !isAlternate;

            }

            RenderText("</tr>");

        }

    }

 

    protected override void ShowHeader(IPaginatedPage source)

    {

        if(source!=null && source.TotalItems>0)

        {

            IEnumerator enumerator = source.GetEnumerator();

            enumerator.MoveNext();

            object first = enumerator.Current;

            properties = first.GetType().GetProperties();

            foreach(PropertyInfo property in this.properties)

            {

                RenderText("<th>");

                RenderText(property.Name);

                RenderText("</th>");

            }

        }

        else

        {

            RenderText("<th>empty grid</th>");

        }

    }

}

And now, the code for this view component turns into:

<?brail component GridComponentWithAutoGenerateColumns, {"source": contacts} ?>

So, in about 170 lines of code we created a grid components that gives you quite a bit of functionality. It is an basic grid component that we can configure quite liberaly all over the place. It supports paging seamlessly, and with a tiny bit of work, we managed to make it support AutoGenerateColumns in another 30 lines or so. The full code is here, feel free to poke around.

All in all, it took me about half an hour to write this View Component. Just to put this in perspective, I spent more time writing this entry...

time to read 1 min | 110 words

Mike Gunderloy is talking about the "royality free" office UI license. He raises some very important issues with regard to the mere feasability of the license. The basic issue here is that the ability to license a UI improvement means that you are allow to not license the improvement. And if Microsoft manage to make this a legal thing, all software deveopers are going to be in a very sad state all of a sudden.

The problem with this is that Apple has previously successfully sued people for copying their Look & Feel, so I can see where Microsoft is coming from...

time to read 1 min | 182 words

So, after praising Trac so much, I decided that I need another this from it, and that was a windows client. Specifically, what I wanted is for QA to be able to enter bug reports that included screen shots without a lot of hassle. There is nothing that explains most bugs better than a screen shot with some marking.

The result is this:

(Image from clipboard).png

(Image from clipboard).png

(Image from clipboard).png

(Image from clipboard).png

The easiest part of this application was capturing the screen, actually. The hardest was interfacing with Trac. Xml RPC is not as easy as it can be when you are used to wsdl generated code, I am afraid.

Note: This is not public yet. The code is in the rhino tools repository, but I want to test it in more scenarios, specifically, ones hat involve windows authentication.

time to read 11 min | 2013 words

Let us assume that you have the following class hierarchy:

(Image from clipboard).png

Now, what do you think the result of this code will be?

BindingList<Animal> animals = new BindingList<Animal>();

animals.Add(new Dog());

animals.Add(new Cat());

GridView gridView = new GridView();

gridView.DataSource = animals;

gridView.DataBind();

Ten points goes to the lady on the right that said that it will produce the following error:

Unhandled Exception: System.Reflection.TargetInvocationException: Property accessor 'Name' on object 'AnimalDesc.Cat' threw the following exception:'Object does not match target type.' ---> System.Reflection.TargetException: Object does not match target type.

The reason for this bug is that the TypeDescriptors in .Net are not aware of polymorphism. Even though both Cat and Dog inherit from Animal and has a Name property, and that the list that I passed to the DataSource is BindingList<T>, the TypeDescriptor only looks at the first item in the list, and uses it to describe all types in the list. This can cause problems when the collection that you pass to the GridView contains an inheritance hierarchy.

After butting my head against this issue for too long, I finally came up with this solution:

public class AnimalTypeDescriptionProvider : TypeDescriptionProvider

{

    public AnimalTypeDescriptionProvider() 

        :base(TypeDescriptor.GetProvider(typeof(Animal)))

    {

    }

 

    public override Type GetReflectionType(Type objectType, object instance)

    {

        return base.GetReflectionType(typeof(Animal), instance);

    }

 

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)

    {

        return base.GetTypeDescriptor(typeof(Animal), instance);

    }

   

    public static void Register()

    {

        TypeDescriptor.AddProvider(new AnimalTypeDescriptionProvider(), typeof(Animal));

    }

}

Then, I call the AnimalTypeDescriptionProvider.Register(); method in the start of the application.

What this does is it lies to the data binding infrastructure, and tells them that any type that inherits from Animal is actually an Animal, and should be treated appropriately.

This solution is good enough for now, but it will prevent me from databinding a list of Dogs, for instance.

time to read 2 min | 254 words

Here is the story:

When the a node in the graph is hovered via the mouse, a connection point (a node can have several) is made visible, and the user can drag the connection point to another node (when it enter the second node, the second's connection points will be visible) and create a connection between the two connection points. A visible line should exist when dragging the connection, and after it was attached it should be permenantly visible.

I got this to work without tests, since I just can't think of how to test this stuff. Right now I'm heavily using the functionality that Netron provides (which does 96% of the above) and the implementation uses windows events and state tracking quite heavily to get this functionality.

I suppose I can try to refactor the logic out so it would be testable, but that would mean duplicating the events structure for WinForms. In addition, I can see no way to test that the correct stuff is drawn to the screen and I am not fond of taking a screen shot and then comparing the known good to the result on a bit by bit basis. If for no other reason than that I can think of several dozens of tests for the functionality that I need.

Any suggestions on how to test this?

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. Challenge (75):
    01 Jul 2024 - Efficient snapshotable state
  2. Recording (14):
    19 Jun 2024 - Building a Database Engine in C# & .NET
  3. re (33):
    28 May 2024 - Secure Drop protocol
  4. Meta Blog (2):
    23 Jan 2024 - I'm a JS Developer now
  5. Production Postmortem (51):
    12 Dec 2023 - The Spawn of Denial of Service
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}