Here's the answer to yet another question asked on the Windows Mobile Developer Center Forums: How to implement a text ticker. The request implied for a flicker-free implementation, so a memory bitmap is required. This bitmap is generated whenever the screen changes size (CTextTickerView::OnSize) or when the text itself changes (CTextTickerView::SetTickerText). To calculate the text size when printed with the given font, a call is made to CDC::GetTextExtent which returns both the height and the width of the text in pixels. The off-screen is then created using the screen width and the text height plus a few padding pixels (CTextTickerView::ResizeBitmap).
Scrolling of the ticker is achieved through a timer that shifts the ticker printing position every 100 ms. The ticker is painted by invalidating its own rectangle and the whole painting process is performed on CTextTickerView::OnPaint. Note how the ticker text gets painted a second time tailing the first instance in order to give a continuous feel. Also note how the x offset is incremented by the timer handler. Go and have a look at the code!
After writing my last post, I started wondering if I could implement other useful gestures to control the touch list. So I started to wonder what kind of gestures would be needed for a real list-based application, like the CrypSafe prototype I have been working on. One of the issues that you will be confronted with is the requirement of, somehow, single out a single list item and then apply some sort of command to it. Think about deleting a list item: wouldn't it be nice that by clicking the item in a special way you would be able to do just so? There are a lot of other actions you might want to perform in a list item this way (edit, move up or down, you name it). Microsoft actually solved this issue some time ago with the "tap-and-hold" gesture, but gave it a very specific implementation with those circles rolling around the screen to denote it. Alas, I don't like it so I decided to reinvent the wheel and tweak the CTouchGesture in order to implement this gesture. It's actually quite simple: if you press an area of the screen and you don't move or release the finger for some time, a hold gesture is returned by the timer event.
To illustrate the use of this gesture, I implemented a very simple demo based on AppStart (yes, I was lazy) a so that it shows a "context toolbar" when you hold a given list item (see picture). In this case I added an "edit" and a "delete" button that you could use to change the list item. To make the whole thing a bit more fun, I added a small animation to the hold gesture where you can see the item expanding to reveal the "toolbar". Getting information about the pressed toolbar button would be a simple matter of overriding the HitTest function. Note that when the toolbar is displayed you can still flick and click because the item merely changed its size.
In this blog post I'm returning to the touch window code to show you a couple of improvements that were needed.
First, there was the issue of stopping the list halfway through a scroll. If you start dragging the list and then stop, wait a little and then release the finger or stylus, the list would still scroll. This is not an intended (nor intuitive) behavior: the user scrolls the list up to a position and he / she wants it to stay put by stopping the finger / stylus for a fraction of a second and then releasing it.
The second issue happened when the list was "bouncing back" if you forced it to scroll down from the top (or scrolling up from the bottom). Under some occasions you could actually stop the list from scrolling back to its "rest" position leaving it in an awkward state.
To solve the first issue I changed the bulk of the code in the CTouchGesture class and turned it into a state machine with four inputs:
Press - The user pressed the finger / stylus
Move - The user is dragging the finger / stylus
Release - The user released the finger / stylus
Timer - Called every 200 ms in order to check transient conditions
Each input is implemented by a method with the same name and the novelty here is that each of the first three returns a value of GestureType (NoGesture, ClickGesture, FlickGesture). The return type is used by the CTouchWindow mouse event handlers to determine what to do (nothing, a click or a flick). When flicking through the list, the CTouchGesture returns the delta movement through the new GetDelta function.
By implementing the gesture recognition as a state machine we get a much better chance of determining and handling the harder to detect situations, such as:
Input noise such as when the user flicks and accidentally releases the screen for a very short period. This is now handled by the code and the state machine assumes that the previous flick is still happening.
Intermediate motion stop, such as when the user is flicking and stops halfway. This situation is detected by the timer event that essentially resets all displacement and time accumulators and assumes that the user wants to do nothing (this is not interpreted as a click, of course).
Finally, I changed the code a little bit so that it does not allow the list to be in an "unnatural" position. Whenever the list stops it is checked for such a situation (showing the canvas to the top or bottom when the list is larger than the window). When such a situation is detected, the list is automatically scrolled back into position.
To illustrate all of this, I turned back to the AppStart sample simply because it has a bigger list. Here it is for your viewing pleasure (or not...):
... it's a feature! Really! I'm talking about my last post where I jumped to the conclusion that I had a bug in a particular line of code. In fact, that line of code is correct - the bug lies elsewhere. Let me explain.
As I stated before, there is no way (at least I never found a way to) use storage object with SQL Compact command parameters. To overcome this limitation the code must allocate the full size of the BLOB data on the binding buffer (which is not very efficient, but it's a viable solution). The problem with this is that the code has to know the size of the BLOB and adjust the binding buffer accordingly. If you use ADO .NET then you know how this issue is solved: the developer must create SqlCeParameters with the given maximum size of the parameter data. The whole purpose of this code is to avoid this situation and let the OLE DB library handle that chore for you.
Command parameter information is requested when you prepare a command and this is the reason why you see the dreaded 1024 constant in the code (the value is pretty arbitrary, by the way). This was the value I chose to represent the default value of the BLOB just to assume a value for it's length. The code then expects that you call SetParam for each parameter and explicitly set its size then. When the command is executed, the whole set of command parameters is looped through in order to get the size of the new binding buffer. If the new binding buffer is larger than the previous one it gets reallocated. And this is exactly where the bug was: the existing buffers were not being deleted and an obvious memory leaked was being created (the complaint reason from the reader who sent me the email message thet prompted me to look at this issue again).
To prove that the memory leak issue is gone in the new code, I set up a very simple demo project where three JPG images are inserted into a sample database table. The images are inserted using a SQL INSERT command and the code is arranged so that the images are processed in an increasing size order. This way you can actually see how the binding buffer increases.
Incidentally, the real bug was on the CCommand::BindParameters method where the m_pBuffer and m_pBindStatus arrays are allocated. These must be freed if non null before allocation.
I recently got an email from an interested reader pointing out an error in the SQL command parameter handling OLE DB Client code. If you remember my previous ramblings on this issue, the OLE DB provider for SQL Compact is unable to handle BLOB SQL command parameter through storage objects. This means that you must provide some information about the BLOB before using the command, just like you have to do if you use the ADO .NET stack. Now, if you look at the CCommand::CreateParameter method you will see the error:
Let me assure you that I was not taking any medication (or smoking funny stuff) when I wrote this line. This is a piece of code that I just did not return to so the error crept in and started to create lots of memory leaks. Solving this issue is not a simple matter of correcting this line to:
ULONG nParamSize = pParamInfo->ulParamSize;
Like I said, we must specify all the BLOB parameter sizes before we prepare the command and this means that CCommand::Prepare must also change. Instead of getting parameter information in the call to Prepare, the user will have the option of calling another method to get this information. In any case, there must be an option to provide custom command parameters. This is the topic of my next post.