Why do up-down controls have the arrows backwards?

Date:December 22, 2005 / year-entry #395
Tags:history
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20051222-12/?p=32873
Comments:    29
Summary:When you create an up-down control (some people call a "spinner" control) in its default configuration, the up-arrow decrements the value and the down-arrow increments it. Most people expect the up-arrow to increment and the down-arrow to decrement. Why is it backwards? The up-down control is a victim of Windows' reversed y-axis. Mathematically, the (0, 0)...

When you create an up-down control (some people call a "spinner" control) in its default configuration, the up-arrow decrements the value and the down-arrow increments it. Most people expect the up-arrow to increment and the down-arrow to decrement. Why is it backwards?

The up-down control is a victim of Windows' reversed y-axis.

Mathematically, the (0, 0) coordinate should be at the bottom left corner of the screen (and in fact that's where OS/2 puts it), with y increasing as you move up the screen. Windows, on the other hand, puts the (0, 0) coordinate at the upper left corner, with y increasing as you move down the screen.

What does that have to do with anything?

The up-down control can be positioned horizontally or vertically. Let's first look at the less problematic horizontal configuration. Windows and mathematics agree that the x coordinate increases to the right, and the right-arrow (the arrow with higher x-coordinate) causes the value to increase. (Let's leave right-to-left languages out of the picture for the purpose of this discussion.)

Once you have the horizontal version of the control working, converting it to a vertical control is a simple matter of interchanging the x- and y-axes.

That's why the up-arrow decreases the value. The up-arrow points towards smaller y-coordinates and consequently decrements the value.

It's perfectly logical and simultaneously counter-intuitive. (It's slightly more intuitive if you imagine the value attached to the up-down control as controlling the y-coordinate of an imaginary object on the screen. In that case, clicking the up-arrow causes the y-coordinate to decrease and the object moves up the screen.)

Fortunately, this wackiness doesn't last long, because the moment you change the range of the up-down control from the (not very useful) default of 0–100 to whatever you actually need, the arrows behave "normally" again.

Perhaps intuitiveness should have won out over logic. But what's done is done, and, as noted above, the problem goes away soon enough.

[Raymond is currently away; this message was pre-recorded.]


Comments (29)
  1. Stu says:

    I think I would class that as a bug, especially as it is corrected when the range is changed.

  2. Ytram says:

    I would tend to agree with Stu. If it was a configurable property of the control, then it would be something you simply change to perform the way you want.

    I think it’s worth noting that the updown control in .NET winforms does not behave the way Raymond described.

  3. skrållan says:

    It’s both counter-intuitive AND illogic when the behaviour is inverted to the scrollbar.

  4. The designers of some Windows API calls should be shot and left on an anthill to die.

    Which brings me to a question: Is there a reason, in the 32-bit Windows world, to have a one API call perform multiple separate functions?

    A typical function that needs to be broken up is LoadImage. It loads bitmaps, cursors and icons, from files and resources. To use it you must know in advance what type of resource you are loading, and you must cast its result to the desired type.

    Is there a reason why we don’t have six functions (LoadBitmapFromFile, LoadBitmapResource, etc.) as entry points instead of this monstrousity?

    Dejan

  5. Binsky says:

    I actually inverted the response of the updown control when I used it in a custom component, to make sure that it does work intuitively…

    Thanks for telling me where this came from, that almost made it a logical choice! :D

  6. I’ve always wondered why spinner controls behave this way. Thanks for the explanation Raymond.

    Brian

  7. This behaviour is a blessing : That way, devs (and testers) have a fairly immediate feedback when they forget to set the range (because there’s no such thing as a valid default range).

    BTW, I always believed it was the reason for this behaviour: Force programmers to set the range.

  8. CornedBee says:

    Serge: If the designers wanted to achieve this, I do hope they’d have been smart enough to set the default range to 0-0.

  9. Fox Cutter says:

    skrållan: Actually this is exactly the same way a scroll bar behaves. Using a text file for example, if line 100 is at the top of the screen and you click up on the scroll bar like 99 is now at the top.

    It just seems correct because the contests of the screen moved up even though the offset from the top of the page went down. The up-down control just shows more of it and it breaks the users mental picture of how the world works.

    Luckily it is a very easy fix.

  10. Jerry Pisk says:

    I’m too lazy to try it – does the behavior change even if I change the range to 0 – 100? In either case it’s an obvious bug, different behavior based on set range is just wrong. Oh and is this documented, that the controls behave differently based on what range they’re set to?

  11. Steve Hazel says:

    if i recall, MSDN specifically states that this behavior is to force the app developer to set the range. And if you ask me, the Win32 API is almost a perfect OS API. Listen to all these java wimps whine… Shame on you…:)

  12. It *is* a configurable property. See UDM_SETRANGE.

  13. Kyralessa says:

    And then there’s the .NET PrintPreviewDialog. The up button scrolls to higher-numbered pages, and the down button scrolls to lower-numbered pages.

    Trouble is, this being a document, you’d expect that you’d scroll down to page forward through it, just like you do in Word…

  14. Norman Diamond says:

    The up-down control is a victim of Windows’

    > reversed y-axis.

    Why isn’t the progress control a victim too? If a progress bar is horizontal then it grows in order of increasing x. If a progress bar is vertical then it grows in order of DECREASING y. When you properly set the range of a vertical progress bar, it still grows in order of decreasing y. If you want it to grow in order of increasing y then you get to roll your own because there’s no setting for it.

    > (Let’s leave right-to-left languages out

    > of the picture for the purpose of this

    > discussion.)

    Ditto. When I was taught that mathematical definitions of which axis is x and which is y depend on which country’s mathematical definitions you use, the example involved a country whose language is left-to-right.

  15. JamesW says:

    @Dejan Jelovic

    ‘Which brings me to a question: Is there a reason, in the 32-bit Windows world, to have a one API call perform multiple separate functions? ‘

    The HtmlHelp API is my ‘favourite’ example of this. You get one function:

    HWND HtmlHelp(

    HWND hwndCaller,

    LPCSTR pszFile,

    UINT uCommand,

    DWORD dwData

    );

    which does completely different things depending on which one of twenty values is placed in uCommand. Data is exchanged to and fro by casting strings, structs, whatever so that it fits into dwData. Who dreamt this one up? To the anthill with them!

  16. AC says:

    Well I may feed the troll, but I must note that anthill proposing dolt plainly doesn’t know what he’s talking about.

    He obviously never did real systems programming in C and with real memory constrains and with the goal that API survives through the releases. Otherwise, he’d spare us from himself on this occasion.

    An API call is a "doorway" between the user and the system. The smaller the door, the better it can be controlled: you don’t want to expose what you don’t have to. But the same stands for the number of the doors. When the C-style DLL based API is designed, the good thing is to spare the number of the function calls, even if the single API call branches to other functions inside of the DLL (for example, less GetProcAddress calls are needed to use your DLL in some scenarios). So the memory is really spared by reducing the number of API functions. Not to mention that the interface retained higher flexibility — adding one more enumerated value for existing API function makes much less problems during the new releases of the API than always adding new function calls.

  17. fraggle says:

    So basically, the Windows developers were lazy and simplicity of implementation won over simplicity of interface?

  18. JamesW says:

    ‘dding one more enumerated value for existing API function makes much less problems during the new releases of the API than always adding new function calls. ‘

    Taken to it’s logical conclusion we end up with each dll having a single exposed function:

    DWORD DoStuff(COMMAND_ENUM, void* inOut);

    Yes, that’s much better.

  19. mirobin says:

    James, that is pretty much how OS calls were made on my old Atari ST. Set a few registers, trigger an interrupt, and check the registers again to fetch the result. The interrupt handler had a giant jump table that took the various inputs and made the call you specified.

    The C libraries that provided a friendly face on the OS calls were nothing more than a bunch of inline assembly functions …

    Ah, the days when you could poke around at the hardware and have fun … :)

    There IS a reasonable amount of compartmentalization you can make with an API design, especially when software will be written targeting multiple generations of your platform. Delay load hell anyone?

  20. JamesW says:

    mirobin

    That’s pretty much what the syscall interface does on OSes today. Load up some registers and trigger a switch to kernel mode. On Windows one register takes a value to determine the system service required and another takes a pointer to the parameters. A syscall/sysenter instruction does the rest. ntdll provides the friendly face hiding the assembly.

  21. asdf says:

    My all time favorite is this:

    HRESULT IDirect3DDevice9::SetRenderState(

    D3DRENDERSTATETYPE State,

    DWORD Value

    );

    To see why, head on over to the documentation:

    Values for the this render state are floating-point values. Because the IDirect3DDevice9::SetRenderState method accepts DWORD values, your application must cast a variable that contains the value, as shown in the following code example.

    pDevice9->SetRenderState(D3DRS_FOGSTART, *((DWORD*) (&fFogStart)));

  22. Tony says:

    Interesting… I always assumed it was backwards because it was implemented as a one-line listbox. If a listbox contained the values 1, 2, 3, 4, then pressing the up arrow would move you to a lower number. Another example is if you press the up arrow on a scrollbar, the position is decreased. It’s just being (wrongly) consistent with the other controls.

    Your (0,0) explaination makes no sense to me — everything in Windows would be backwards if that was the case. I think you are just making this up.

  23. Vince P says:

    Tony said:

    >Your (0,0) explaination makes no sense to me — everything in Windows would be backwards if that was the case. I think you are just making this up.

    I say:

    <sarcasm>Yeah, sure .. he’s just making it up. Had nothing better to do.</sarcasm>

  24. PatriotB says:

    JamesW re: HtmlHelp():

    From what I understand, the HtmlHelp() function was designed to be as similar to the existing WinHelp() function as possible, hence the messiness.

  25. Ash says:

    Did anyone try using the spinner on the "partition size" entry in W2k’s Disk manager?

    Ok, Raymond knowns how to explain WHY things are the way they are, but when you are partitioning your HDD on w2k and using the spinner the largest partition size you get is 7 MBs, and it goes down to NEGATIVE values from there on, I think you won’t be amuzed.

    The fact is the world doesn’t care about the amuzing logical/illogical games that some MS programmer is endulging.

    I know it’s probably not Raymond’s problem and he probably never intended to defend anyone, but this just goes a long way to show you even a critical part of Windows is badly broken because of this problem.

    AND FOUR SERVICE PACKS DIDN’T FIX IT!

    Perhaps Raymond needs to write an internal email explaining WHY it is important to FIX this in production code even though it IS LOGICAL (according to him, at least.)

    -Ash

  26. Michael puff says:

    Ui. That was my suggested topic. ;) Two days later I found out myself, that if you set the range, it behaves like it is supposed to be. Thanks for your explanation. But if you don’t know that you could go crazy about it. :(

  27. Vince P says:

    Ash’s comments are interesting. He’s B&M about how this needs to get fixed… but how can it be "fixed" the broken behavior is what is now expected (in the cases where the ranges weren’t changed).. if MS suddenly "fixed" this "critical" problem then all those application would suddenly start doing the opposite thing.

    Ash, how could you justify such hysterics to get this fix when the cure would be worse than the disease?

  28. com2kid says:

    0,0 at the upper left corner is perfectly logical, if you look back to CRTs. CRTs shoot a beam across the screen, left to right, top to bottom, in the same order that you read a page. In the early days of programming, when the programming actually had to deal with the CRT output directly, having coordinates start at the upper left made sense.

    This is true if you look at command lines, the cursor starts in the upper left corner, when going to design GUIs, programmers just kept the same coordinate system, as the early GUIs also had to deal directly with the CRT as well.

    Of course now days we are almost completely divorce from dealing with CRTs directly, but the upper left as 0,0 design idea is still useful, as when reading documents (the current interface metaphor), that is where our eyes head. Of course there are exceptions to this rule (languages that read right to left), but with only four directions available, any configuration imaginable is only a sign flip away (so long as the given UI component allows us to flip directions!)

  29. Norman Diamond says:

    Thursday, December 29, 2005 12:53 PM by com2kid

    > 0,0 at the upper left corner is perfectly

    > logical

    Yes, and furthermore it’s usually no problem.

    > left to right, top to bottom, in the same

    > order that you read a page

    That depends on which "you" you’re talking to. If reading a printed page on dead trees, then the usual order of reading is from top to bottom and from right to left. In countries that aren’t either the world’s largest (by population) or historically influenced by that one, the order tends to prioritize horizontal, but either left-to-right or right-to-left depending again on which "you" you’re talking to.

    Computer displays usually tend to prioritize horizontal except when applications such as Microsoft Word are preparing documents intended to be published on dead trees. But again the left-to-right or right-to-left depends on which "you".

    Technical textbooks, even when printed on dead trees, tend to use the same orderings as computer displays instead of mass market books, magazines, and newspapers.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index