Keith’s CallBack
... keeping my wits

WPF ctrl + mouse scroll and zooming images

(Download demo (includes images, about 7mb))

For my Geo Tagger sample I wanted to have the right hand plane contain images in a list box (i.e. selectable) yet wanted to be able to hold ctrl and scroll the mouse wheel and have the images resize, similar to Windows Explorer, however I also wanted the list box to scroll.

The demo here does that nicely. It hocks into the ScrollViewer's HandlePreviewMouseWheel event and checks if the ctrl key is pressed. If so it marks the event as handled (thus stopping the ScrollViewer form scrolling) and programmatically increments or decrements a Slider which ultimately sets the width of the images which live in a WrapPanel.

The HandlePreviewMouseWheel handler is quite simple:

private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
  // if the user has the Ctrl key pressed we jump in and mark the event as handles, prevent the scrollviewer form
  // scrolling, at the same time we set the ZoomSlider thus resizing he photos smile
  if (sender is ScrollViewer && !e.Handled && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
  {
    e.Handled = true;

    int movement = 20;

    if (e.Delta > 0)
      ZoomSlider.Value += movement;
    else
      ZoomSlider.Value -= movement;
  }
}

Here are a few screen shots.

Normal:

Ctrl + Mouse scroll (or use slider) to resize thumbnails, normal width column.

Resized column (via dragging the grid splitter).

As above but with bigger thumbnails, note you can still scroll on the right side.

The demo code is based on the Photo Viewer Sample in the Windows SDK (for 2008 & .Net 3.5). Enjoy.

Thursday, September 18, 2008 11:10:23 AM (GMT Standard Time, UTC+00:00) #     Comments [0] |

GeoTagger TTD and Baby Dev’s

I've enlisted the services of my Girfriend to help me code (effectively expanding what we can now argue about LOL :(), she comes from a testing background but is jumping the fence (hence the term Baby dev as opposed to someone with a degree in ICT). I'm finding TTD a great way to teach her the ins and outs of development, however I've been showing here stubbing techniques as apposed to dropping RhinoMocks on her head. Elsewhere RhinoMocks has worked well for interaction based testing of the controllers.

I refer you to exhibit A, by Fowler: Mocks Aren't Stubs

Also a pict of the latest UI, note the compass icon beside the images that have been matched with a latitude and longitude smile

Monday, September 15, 2008 11:46:20 AM (GMT Standard Time, UTC+00:00) #     Comments [0] |

WPF: Transitionals, apply an effect on Content change

Check out the Transitionals project on CodePlex, it's a nice way to apply effects when content on a control changes.

Say you have something that displayed and frequently changes. For example the PopUp in the image below had a ContentPresenter, this was bound to a Photo object, this has a corresponding DataTemplate. Reassigning the ContentPresenters content would update the UI. (Mental note: I've posted the first pict of me on the blog… its all downhill from here smile).

I replaced the ContentPresenter with a TransitionElement, now when the content is re-assigned the transition to the new content is done with an effect (a fade effect in the code below).

<Popup
x:Name="mainContentPopup"
IsOpen="{Binding Path=Popup, Converter={StaticResource nullToBooleanConverter}}"
MouseDown="MainContentPopup_MouseDown"
Placement="RelativePoint"
HorizontalOffset="20"
VerticalOffset="20"
Margin="{DynamicResource BoxPadding}">

<transc:TransitionElement
x:Name="TransitionBox"
Content="{Binding Path=Photo}">
<transc:TransitionElement.Transition>
<transt:FadeTransition Duration="0:0:0.2" />
<!--<transt:TranslateTransition Duration="0:0:0.2" />-->
</transc:TransitionElement.Transition>
</transc:TransitionElement>
</Popup>

Download the project, they have a neat tester that you can easily view the transitions, below you can see the RollTransition captured half way thought the content transition.

Monday, September 15, 2008 11:23:16 AM (GMT Standard Time, UTC+00:00) #     Comments [0] |

GeoTagger

I'm pretty excited about this project at the moment, looking forward to getting some code up (its been a coding weekend smile):

Sunday, September 07, 2008 10:21:16 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

WPF, Virtual Earth and GPS

When WPF first came out it caught my attention, I'm a shallow sort of guy so fell for its looks LOL. Anyway around the same time I was playing with a GPS receiver for my windows mobile phone (RIP). I wrote an app that parsed the NMEA log from my GPS receiver. A segment from the log file looks like this:

$GPGSA,A,3,18,22,16,19,21,03,06,,,,,,1.9,1.2,1.5*3D
$GPGSV,3,1,12,18,65,101,36,22,59,001,37,16,59,294,39,21,38,132,34*7F
$GPGSV,3,2,12,03,29,226,25,07,28,066,13,06,19,065,17,24,07,118,18*76
$GPGSV,3,3,12,19,05,235,22,26,05,133,,14,00,013,,29,00,142,*78
$GPRMC,071042.000,A,3203.5736,S,11549.9239,E,28.21,275.21,240607,,*2F
$GPGGA,071043.000,3203.5726,S,11549.9146,E,2,07,1.2,29.4,M,-29.4,M,6.8,0000*7C
$GPGSA,A,3,18,22,16,19,21,03,06,,,,,,1.9,1.2,1.5*3D
$GPGSV,3,1,12,18,65,101,35,22,59,001,37,16,59,294,39,21,38,132,34*7C
$GPGSV,3,2,12,03,29,226,26,07,28,066,,06,19,065,18,24,07,118,*71

I then drew the track on screen using a WPF app, heres that app from a year ago:

Its was a big learning curve on the WPF front, not to mention the NMEA log file parsing (thanks to Jon Person for his code project article on GPS, part 1 and part 2).

Over the last weekend I've finally had some time to revisit this (since my last project at work was a WPF trading app I'm all excited about WPF smile). Using the beginnings of a virtual earth control from Marc Schweigert (blog post, screen cast on control), I put together an improved (structurally improved, perhaps not visually, yet) version of my app:

The code is more cleanly separated using MVC and Unity as an IoC, which for the size of what I was doing is perhaps overkill, however I always tend to intentionally over engineer these 'scratch' projects so I can try get certain concepts down (it's a coding for fun thing).

I will clean the code a bit and post it up for anyone that interested, so check back in a few weeks (not that I've posted this I have to do it, haha), I'm planning on adding some functionality to drag photos into the right hand panel and then geo tag them based on the time stamp of the log.

Keith

 

Saturday, September 06, 2008 7:02:23 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

WPF: Borrowing styles from other projects

My GeoTagger Application is looking a little better this morning after <cough>acquiring</cough> some styles from FamilyShow (don't worry I read the licence smile).

Before:

After (and after adding some image support):

With Expression Blend it's really easy to apply the styles to existing controls. All I've done was <coughCoughCough>ripped</coughCoughCough > BlackResources.xaml from the FamilyShow and included it in my project. Then simply applied its existing styles to my controls. Below you see how I've applied the background style to a style from the new resources which now show up in blend.

All this is very basic WPF'ish practices but perhaps not immediately intuitive that you can easily jump to blend and quickly change the look and feel if your resources are nicely names and located centrally.

You can also use the Resources to scan what's available:

Saturday, September 06, 2008 12:11:49 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

IPod/IPhone Remote and the Vista firewall.

Just downloaded the Remote application for my IPod touch, but it didn't work… boo!

First thought was the firewall, however ITunes was already listed there so I thought it may be something else. Regardless I turned the firewall off and it worked fine. Running netstat –a from PowerShell (or a command line if you prefer) showed me what ports were in use. The only ones that looked suspicious, given what I knew about registered devices on my wireless network, was communications to my machine from 192.168.0.5 via port 3689.

FileSystem C:\ [1#]> netstat –a

Active Connections
Proto Local Address Foreign Address State

….

TCP 192.168.0.3:3689 192.168.0.5:49434 ESTABLISHED
TCP 192.168.0.3:3689 192.168.0.5:49435 ESTABLISHED
TCP 192.168.0.3:3689 192.168.0.5:49436 ESTABLISHED
TCP 192.168.0.3:3689 192.168.0.5:49437 ESTABLISHED
TCP 192.168.0.3:3689 192.168.0.5:49438 ESTABLISHED
TCP 192.168.0.3:3689 192.168.0.5:49439 ESTABLISHED

Adding an exception for that port fixed the issue:

Tuesday, July 15, 2008 8:02:21 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

Couple of pictures from my travels

TODO: Blog more smile

A few picture I've taken on my travels, the rest are on flickr, I love flicker these days:
http://www.flickr.com/photos/keithwoods/

 

Sunday, July 13, 2008 9:37:53 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

RadioButtonList with OnClick event for each ListItem

Say you want to have a few radio buttons in the same group and want to do something when once is clicked. That is, you want to handle the JavaScript OnClick event,

You can try this:

<asp:RadioButtonList ID="costCenterIsCorrectRadioButtonList" RepeatDirection="Horizontal" runat="server" OnClick="alert(':( ... please work!')">

<asp:ListItem Text="Yes" Value="Yes" Selected="True"/>

       <asp:ListItem Text="No" Value="No" />

</asp:RadioButtonList>    

... and find that it's helpfully rendered on the containing element of the RadioButtonList, which is a table:

<table id="ctl00_DefaultContent_costCenterIsCorrectRadioButtonList" OnClick="alert('why!')" border="0"> ...

Ok, I'm smart, how about I try putting it on the List item:

<asp:ListItem Text="Yes" Value="Yes" Selected="True" onClick="alert('please work.. please!')"/>

That actually works, however this doesn't work too well if you need to dynamically set the value:

<asp:ListItem Text="Yes" Value="Yes" Selected="True" onClick="ToggleDisplay('<%=editCostCenterContainer.ClientID%>', this.selected)"/>

The above gets rendered like this

<input id="ctl00_DefaultContent_RadioButtonList1_0" type="radio" name="ctl00$DefaultContent$RadioButtonList1" value="Yes" checked="checked" onclick="ToggleDisplay('&lt;%=editCostCenterContainer.ClientID%>', this.selected);" />

So another approach is creating those list items in the code behind:

ListItemCollection yesNoListItems = new ListItemCollection();

ListItem yesListItem = new ListItem("Yes", "Yes");

yesListItem.Attributes.Add("onClick", "ToggleDisplay('" + editCostCenterContainer.ClientID + "', this.selected)");

ListItem noListItem = new ListItem("No", "No");

noListItem.Attributes.Add("onClick", "ToggleDisplay('" + editCostCenterContainer.ClientID + "', this.selected)");

yesNoListItems.Add(yesListItem);

yesNoListItems.Add(noListItem);

costCenterIsCorrectRadioButtonList.DataSource = yesNoListItems;

costCenterIsCorrectRadioButtonList.DataBind();

But for some reason when you do this it doesn't render the onClick onto the Html input element, strange....

So the final approach, that actually works, is to create a control that derives from RadioButtonList and override the RenderItem method to add the OnClick to each html input, the actual specifics below are honed to my specific needs (particularly having the same onClick for each radio button, I imagine most cases may require it to be different, but that's fairly easy once you get it going), but you get the gist:

(UPDATE: there is more generic code example below.)

using System;

using System.Web.UI;

using System.Web.UI.WebControls;

 

namespace SomeApp.Modules.Website.Controls

{

    public class RadioButtonListWithOnChangeHook : RadioButtonList

    {

        private string _scriptToAmendToEachOptionsOnClick = String.Empty;

 

        public string ScriptToAmendToEachOptionsOnClick

        {

            get { return _scriptToAmendToEachOptionsOnClick; }

            set { _scriptToAmendToEachOptionsOnClick = value; }

        }

 

        protected override void RenderItem(ListItemType itemType, int repeatIndex, RepeatInfo repeatInfo, HtmlTextWriter writer)

        {

            ListItem item = this.Items[repeatIndex];

            string postbackCode = String.Empty;

 

            writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value);

 

            if(AutoPostBack)

                postbackCode = Page.ClientScript.GetPostBackEventReference(this, String.Empty);

 

            if (!String.IsNullOrEmpty(ScriptToAmendToEachOptionsOnClick))

                postbackCode += ";" + ScriptToAmendToEachOptionsOnClick;

 

            if (!String.IsNullOrEmpty(postbackCode))

                writer.AddAttribute(HtmlTextWriterAttribute.Onclick, postbackCode);

 

            writer.AddAttribute(HtmlTextWriterAttribute.Id, UniqueID + IdSeparator + item.Value);

            writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio");

            writer.AddAttribute(HtmlTextWriterAttribute.Name, ClientID);

 

            if (item.Selected)

                writer.AddAttribute(HtmlTextWriterAttribute.Selected, "true");

 

            writer.RenderBeginTag(HtmlTextWriterTag.Input);

            writer.Write(item.Text);

            writer.RenderEndTag();

        }

    }

}

Now include it on the code in front:

<%@ Register TagPrefix = "custom" Namespace = "SomeApp.Modules.Website.Controls" Assembly = "SomeApp.Modules.Website" %>

<custom:RadioButtonListWithOnChangeHook id="costCenterIsCorrectRadioButtonListWithOnChangeHook" runat="server">

    <asp:ListItem Text="Yes" Value="Yes" Selected="True"/>

    <asp:ListItem Text="No" Value="No" />   

</custom:RadioButtonListWithOnChangeHook>   

You could specify ScriptToAmendToEachOptionsOnClick (the prop on my custom control) via and attribute on the control in the .aspx or in my case in the code behind as it's a dynamic value:

costCenterIsCorrectRadioButtonListWithOnChangeHook.ScriptToAmendToEachOptionsOnClick = editCostCenterContainer.ClientID;

... and that's it, the value of ScriptToAmendToEachOptionsOnClick gets added to the OnClick of each radio button.

UPDATE:

The above ended up refactored to the below for a more generic implementation which worked well for us:

using System;
using
System.Web.UI;
using
System.Web.UI.WebControls;

namespace ANameSpace
{
    public abstract class RadioButtonListWithOnClickHook : RadioButtonList
    {
        protected abstract string GetOnClickJavaScript(ListItem item);


        protected override void RenderItem(ListItemType itemType, int repeatIndex, RepeatInfo repeatInfo, HtmlTextWriter writer)
        {
            ListItem item = Items[repeatIndex];
            string postbackCode = String.Empty; 
            writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value);


            if (AutoPostBack)
                postbackCode = Page.ClientScript.GetPostBackEventReference(this, String.Empty);


            string customOnClick = GetOnClickJavaScript(item);


            if (!String.IsNullOrEmpty(postbackCode))
                postbackCode += ";"; 

            if (!String.IsNullOrEmpty(customOnClick))
                postbackCode += customOnClick;

            if (!String.IsNullOrEmpty(postbackCode))
              
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, postbackCode);
 

            writer.AddAttribute(HtmlTextWriterAttribute.Id, ClientID + "_" + item.Value);
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio");
            writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
 

            if (item.Selected)
                writer.AddAttribute(HtmlTextWriterAttribute.Selected, "true");
 

            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.Write(item.Text);
            writer.RenderEndTag();
        }
    }
}

Tuesday, October 16, 2007 5:21:17 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

Microsoft OneNote and its ability to search for text within images

I really love Microsoft OneNote (currently using v2007)

I’ve been using it for a couple of years now and have absolutely heaps of notes. One of the best features is the ability to take screen shots, simple press ‘windows key + s’ and the screen is blanketed out, you simple drag the curser from one point to another (which expands a rectangle of what you want to capture). The image below shows this, sure I could copy and paste the text and most times that appropriate, especially if you may want to select portions of the text later. But sometimes its quicker and often nicer to maintain the exact formatting, in which case I'll to take a screen shot.

I want to point out one of the cool features about screen clippings/images in OneNote, which is, it’s ability to scan the images for text and returns these in the search results. Below is the screen clipping taken above (remember this is an embedded image), I’ve done a search for GetHashCode and OneNote’s highlighted where it appears within the image. How cool is that (you don't need to do anything it just works).

Saturday, October 13, 2007 1:26:21 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

Byte[] and GetHashCode()

Today I was comparing 2 byte arrays and wasn't getting the same hash for both. I had just compared a bunch of other struts so for some silly reason I was thinking why isn't this returning the same hash.

For example I ran this in the intermediate window and got the following results:

(new Byte[] {0,0,0,0,0,1}).GetHashCode()

49129953

(new Byte[] {0,0,0,0,0,1}).GetHashCode()

10675717

When I'm dealing with collection, I don't think twice about the fact that 2 collections with the same objects, be it reference or semantic, should (in most cases) have different hash codes. I guess because bytes are bytes I forgot to apply the same principle here.

So I'll just remember that Byte[] doesn't override get hash code and really it shouldn't, just because bytes are bytes it doesn't mean that 2 arrays of identically bytes should necessarily return the same hash. That wouldn't work too well if you want to stick them into other data structure now would it.

Saturday, October 13, 2007 12:17:08 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |

Creating a simple workflow diagram in Visio (use the templates)

Jump into the help menu:

Open up the out of the box templates:

In my instance I used the cross functional flowchart, select it and click ‘create’. Set up the layout:

And sooner rather than later you’re on your way:

Mental note: Use the default templates a lot more from now on.

Saturday, October 13, 2007 12:13:31 PM (GMT Standard Time, UTC+00:00) #     Comments [0] |