| Comments

If you use the Slider object in Silverlight (or WPF for that matter) you may have experienced some similar frustration that I have recently.  Let’s take a look at what the Slider is first.

The Slider is a simple control on the surface providing a track and a “thumb” (if you aren’t familiar with that term) that enables value changing.  Some of the key properties are:

    • Minimum – the number that represents the lowest (left or bottom) value
    • Maximum – the number that represents the highest (right or top) value
    • LargeChange – the slip when a large change in value is desired
    • SmallChange – the slip when a small change in value is desired

It is the last two properties that caused me some headache.  To understand, let me try to explain the functionality first.  Looking at the image above, you’ll see that the track is a certain length.  In my setting I have Minimum set to 1 and Maximum set to 100 above.  Therefore the current Thumb position (Value) is about 20.  Notice where the mouse cursor is?  If I were to actually click there here is what the result would be:

What?!  You may be asking yourself the same thing.  Why didn’t the Thumb move to the curser clicked position?  How did it determine where to move?  Enter the Small/Large change values.  In my setting I have LargeChange set to 10.  What is happening is when I visually click on position at about 80, the Slider sees a large change occurring and moves one LargeChange value.  If I would have clicked close to the Thumb, SmallChange would have happened as well.  This is quite annoying when your users will probably expect that clicking on the track will take them to that point.

Incidentally, the WPF Slider has a property called IsMoveToPointEnabled which turns this functionality on.  This property is not available in the Silverlight Slider (as of Beta 2).

So what if you need that functionality?  Well, here’s one way (with a hat not to my colleague John Lemire for turning me on to this method).  This does involve some code and modifying the template, but it isn’t that painful at all.

Modify the Template

Using Expression Blend, we can modify what the template definition of a Slider is.  To do this, open up the project in Blend, select the Slider object, right-click and choose Edit Control Parts (Template)…Edit a Copy.  Give it a name (I named mine CustomSlider and kept it as a document resource as opposed to an Appliction one).  You’ll then see that you can now edit the template.  There is actually a template for Horizontal and Vertical (Slider has an orientation property), but let’s just concentrate on Horizontal.  The template is made up of: Rectangle, 2 RepeatButtons and a Thumb.  Make sure the Thumb is named “HorizontalThumb” as we’ll need to refer to it later.  Now let’s add some Rectangles.

We’re going to add Rectangles “on top” of the RepeatButtons in use.  You see, when you click on the track in Slider, you’re actually clicking on the RepeatButton areas.  So right now our default template should look like this (for the HorizontalTemplate area only):

   1: <Grid x:Name="HorizontalTemplate">
   2:     <Grid.ColumnDefinitions>
   3:         <ColumnDefinition Width="Auto"/>
   4:         <ColumnDefinition Width="Auto"/>
   5:         <ColumnDefinition Width="*"/>
   6:     </Grid.ColumnDefinitions>
   7:     <Rectangle Height="3" Margin="5,0,5,0" Grid.Column="0" Grid.ColumnSpan="3" Fill="#FFE6EFF7" Stroke="Black" StrokeThickness="0.5"/>
   8:     <RepeatButton IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"/>
   9:     <Thumb Height="18" x:Name="HorizontalThumb" Width="11" Grid.Column="1"/>
  10:     <RepeatButton IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"/>
  11: </Grid>

We’re now going to add 2 Rectangles, one after the left RepeatButton and one after the right RepeatButton, named LeftTrack and RightTrack respectively.  You can do this in the XAML code or just drag the Rectangles on the design surface in Blend and arrange accordingly.  The result should be this:

   1: <Grid x:Name="HorizontalTemplate">
   2:     <Grid.ColumnDefinitions>
   3:         <ColumnDefinition Width="Auto"/>
   4:         <ColumnDefinition Width="Auto"/>
   5:         <ColumnDefinition Width="*"/>
   6:     </Grid.ColumnDefinitions>
   7:     <Rectangle Height="3" Margin="5,0,5,0" Grid.Column="0" Grid.ColumnSpan="3" Fill="#FFE6EFF7" Stroke="Black" StrokeThickness="0.5"/>
   8:     <RepeatButton IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"/>
   9:     <Rectangle x:Name="LeftTrack" Grid.Row="1" Fill="#00FFFFFF" Cursor="Hand"/>
  10:     <Thumb Height="18" x:Name="HorizontalThumb" Width="11" Grid.Column="1"/>
  11:     <RepeatButton IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}" x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"/>
  12:     <Rectangle x:Name="RightTrack" Grid.Column="2" Grid.Row="1" Fill="#00FFFFFF" Cursor="Hand"/>
  13: </Grid>

Visually you won’t be able to notice any difference because we added Rectangle elements that are have 0% opacity, so the Slider still looks the same.

Create Custom Slider Code

Ok, now let’s move on to code.  I use Visual Studio 2008, so I’ll be referring to that.  In Blend, you can right-click on the project and choose to Edit in Visual Studio which will open the project in Visual Studio for you.  Once open in Visual Studio, add a new Class to your project, I called mine CustomSliderControl.  We’re going to inherit from Slider so we can implement all the same features, we are just augmenting one of them.

We will need a reference to the Thumb and the two Left/Right Track elements we just added, so we’re going to make those member variables we can refer to later.  We’re going to override one function: OnApplyTemplate.  The overriding of OnApplyTemplate is only to set our member variables to the elements we’ll use in the other functions as well as set event handlers on our Left/Right Track Rectangles we added to the template.  Since our elements exist in a ControlTemplate, rather than use something like FindName, we’re going to use GetTemplateChild and refer to the name (the x:Name) of the element.

We’re also implementing our own function OnMoveThumbToMouse, which will be attached to the MouseLeftButtonDown events on Left/Right Track.  This function basically gets the Point position of the event (the mouse click) and sets the Value of our Sider to that position (based on looking at where it is in relation to the other elements).  When it is all said and done, our CustomSliderControl code in complete should look like this:

   1: using System;
   2: using System.Net;
   3: using System.Windows;
   4: using System.Windows.Controls;
   5: using System.Windows.Documents;
   6: using System.Windows.Ink;
   7: using System.Windows.Input;
   8: using System.Windows.Media;
   9: using System.Windows.Media.Animation;
  10: using System.Windows.Shapes;
  11: using System.Windows.Controls.Primitives;
  12:  
  13: namespace CustomSlider_CS
  14: {
  15:     public class CustomSliderControl : Slider
  16:     {
  17:         private Thumb _HorizontalThumb;
  18:         private FrameworkElement left;
  19:         private FrameworkElement right;
  20:  
  21:         public CustomSliderControl() { }
  22:  
  23:         public override void OnApplyTemplate()
  24:         {
  25:             base.OnApplyTemplate();
  26:  
  27:             _HorizontalThumb = GetTemplateChild("HorizontalThumb") as Thumb;
  28:  
  29:             left = GetTemplateChild("LeftTrack") as FrameworkElement;
  30:             right = GetTemplateChild("RightTrack") as FrameworkElement;
  31:  
  32:             if (left != null) left.MouseLeftButtonDown += new MouseButtonEventHandler(OnMoveThumbToMouse);
  33:             if (right != null) right.MouseLeftButtonDown += new MouseButtonEventHandler(OnMoveThumbToMouse);
  34:         }
  35:  
  36:         private void OnMoveThumbToMouse(object sender, MouseButtonEventArgs e)
  37:         {
  38:             Point p = e.GetPosition(this);
  39:  
  40:             Value = (p.X - (_HorizontalThumb.ActualWidth / 2)) / (ActualWidth - _HorizontalThumb.ActualWidth) * Maximum;
  41:         }
  42:     }
  43: }

Using the Custom Control

Now that we have this custom Slider, let’s use it.  If we go back to our Page.xaml (or whatever XAML where you had the Slider control to begin with) we need to add a few things.  First at the top, we need to add our XAML namespace so we can use it.  For my sample code I used this (my UserControl decorations):

   1: <UserControl
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     x:Class="CustomSlider_CS.Page"
   5:     xmlns:my="clr-namespace:CustomSlider_CS"
   6:     Width="Auto" Height="Auto" 
   7:     xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">

Notice the “xmlns:my” decoration that points to my CLR namespace for my project.  This will enable me to use this in my XAML later.  Now I can simply find where I used <Slider/> and change to <my:CustomSliderControl/> like this:

   1: <my:CustomSliderControl Cursor="Hand" HorizontalAlignment="Stretch" 
   2:                                 Margin="8,17,8,20.0499992370605" VerticalAlignment="Stretch" 
   3:                                 LargeChange="10" Maximum="100" SmallChange="1" Value="20" 
   4:                                 x:Name="MySlider" Style="{StaticResource CustomSlider}"/>

I don’t have to change anything else, nor do I have to change the Style attribute.  Since our custom control inherits from Slider, all the other types still apply.  You’ll notice that the style itself still has the TargetType=”Slider” attribution and this will work for our custom type because it is a derivative.  Here’s the result (using Silverlight Streaming): click here.

Summary

So as you can see we can extend some of the base controls rather easily.  I hope this didn’t sound too confusing because it really isn’t.  You can download the entire source here in C# or VB to see for yourself.  This particular extension may be helpful when using Slider as a media track representing the timeline for a particular media element.

I hope this helps!

| Comments

Today Visual Studio 2008 has released SP1 which not only brings some fixes, but also is an added value service pack, bringing some new functionality to WPF as well as enabling a “client” deployment pack of the .NET framework so that those deploying .NET framework with your client applications can have a much smaller footprint (by about 80+%).

With the release of SP1 for Visual Studio 2008 today, the Silverlight team has also updated their tools for Silverlight 2 Beta 2.  Read again: a tools update for Silverlight 2 Beta 2 is needed and available for you.  If you install Visual Studio 2008 SP1 and your Silverlight projects aren’t working anymore, that’s why!

Here’s the download links:

Please note that this update to the Silverlight tools can be used for Visual Studio 2008 RTM as well as the just-released SP1, but NOT for VS 2008 SP1 beta.  Yeah, if that’s mind blurring, let’s boil it down:

Do you want to install VS2008 SP1 (release) and still work with Silverlight 2 applications?  If yes, download the updated Silverlight tools and install after you install VS208 SP1.

I hope that makes your decision easier.  Oh and if you installed SP1 Beta at any time, check out this post from Heath.

| Comments

When developing Virtual Earth applications I find myself always having the SDK documents open in the background for reference.  While this isn't a bad practice, I've historically only used them for parameter reference, etc.  I longed for the time that I could get cheater help intellisense for the Virtual Earth API. 

When Visual Studio 2008 came out with Javascript intellisense, I figured the day has come.  But unfortunately, the Javascript intellisense isn't enabled for external (external==not-the-same-app-domain) files.  The thing about the implementation of the Javascript intellisense in VS2008 is that you can just make a reference to a file for the intellisense and it doesn't have to actually be the implemented file.  Make sense?  Probably not. 

My colleague Marc has created a Codeplex project for enabling Virtual Earth intellisense.  Basically he's created a helper Javascript file that you can reference in your project to enable the intellisense.  Note that this does not actually get referenced in your project (meaning, the Javascript file isn't downloaded to your users), but rather just leveraging the VS2008 Javascript intellisense reference scheme to 'trick' the IDE into giving you intellisense for your referenced Virtual Earth API.  This is accomplished because the Javascript reference of the helper file is a design-time only helper...not affecting any true reference to the Virtual Earth control.

Once you do that, you'll get something like this:

Marc did a great job getting a lot up and running with this helper file.  He's recorded a short screencast on how it works to help you understand it a little better.  And if you are interested in helping contribute to the project, please watch the screencast for more information.

| Comments

thanks to all who came out to the denver devdays big event this week.  this was the first time i've traveled in a while since various family ailments and situations.  i had a good few days in denver with some good peeps.  thanks to beth massi for joining our developer crowd in denver.  she was awesome and had a crowd wherever she was.

i attempted to do justice to office development in one of the sessions.  one of the challenges on that topic is that 'office' now encompasses a lot of things...here's my known short list:

    • word, excel, access, outlook, onenote, infopath, powerpoint
    • smart tags
    • sharepoint
    • communicator
    • excel server
    • workflow
    • groove

so when you have an hour for a developer session, which do you pick.  i chose to pick the office client applications and demonstrate how visual studio 2008 enables writing office client applications easier than ever.  i chose this because doing office client development in the past (even with 2005) wasn't really a no-brainer.  there were still a lot of configurations as well as still some things you couldn't do.  with office 2007 and vs2008, it is a no-brainer now.  vs2008 (professional+) now comes with the office tools built-in...no more needing to download a separate client (or pay for a separate tool).

in my session i attempted to cover four key areas (only three of which we got to).  i wanted to demonstrate the UI customization features, outlook form regions, word content controls and task/action pane development.  the slides for my presentation are at the end of this post (PPT 2007 and PDF) and as promised there is an appendix in there with some information we didn't get to.  the two most important links in the slides are the ones to the Office MsoId sheet and the OfficeImageId worksheet (which you need the developer options to be enabled in Excel to see the gallery options).  get these files.  download.  save.  you'll need them.  and when you can't find them you'll need a mt. dew (or scotch or whatever your calming choice is).  don't ask me why the MsoId's are not enabled in the designers of the office components...i've asked and don't know.

the first thing we covered was the office ui customization.  vs2008 provides a new visual designer for the ribbon.  you can still do the RibbonXML if you're insane you want to.  as we demonstrated, almost everything can be accomplished in the ribbon designer.  intercepting commands (such as FileSave) is something you'd need to much with the RibbonXML for and the designer provides an 'export to ribbon xml' feature so you can do most of it visually.  vs2008 provides a great design-time experience as it provides a ribbon as the design-time experience.  most everything after that is choosing which tab (custom or built-in using one of the idMso values from the worksheet), and adding controls.

this capability enables a rapid development timeline of creating customized ui features that are familiar to your users and integrate with your own application.  i demonstrated my flickr add-in which i install on the TabInsert area of office applications:

in outlook development, vs2008 has made this easier now.  we can now extend the default outlook message class UI implementations (i.e., IPM.Contact, IPM.Appointment, etc.) through designers in visual studio.  the tool enables us to choose how we want our customizations to be as well (replacements, adjoining, etc.).  the image below is the adjoining one we created with integrating virtual earth into the contact form to pinpoint in the contact form the address of the selected contact:

when writing outlook form regions (and as we saw in all other areas as well), the development isn't 'office-ish' at all.  once you've decided where/how the form region is going to interact, now you are just writing managed code.  you can integrate with wcf services, use linq, whatever...it is the same .net framework you know and love.  the office api's are now exposed to you to interact with as well.  as an example, anything in the contact item is easily and readily accessible to the developer to use and/or alter.  the same goes for word, excel, etc.

the last area was the task/action panes.  to clarify the terms:

    • task pane: an implementation of a pane that is application-wide -- every document will be able to use the pane
    • actions pane: document-specific panes that are a part of a document/template but not installed as add-in to the global

the distinction between these two is pretty much at that level above.  there are some subtle differences, but for the most part that is the major difference you need to know.  the panes are implemented as user controls for your app/doc add-in now.  so as a developer you now have a user control surface where you can add controls, interact with the document to get values, etc.  to add your custom pane you would write code like this (using excel as an example and a task pane):

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
      CustomTaskPane ctp = this.CustomTaskPanes.Add(new UserControl1(), "Pane Title");
      ctp.Visible = true;
      ctp.Width = 250;
}

the UserControl1 would be whatever your user control representing the task pane you want to display.  remember, that each add-in has that "ThisAddIn" stub generated for you.  there can be multiple task panes for an app/document.  so if you need more you can go nuts.  but i'd be sure you take into account the user experience and ensure that you aren't crowding the main focus of the functionality (the document) for the user.  panes are dockable through the DockPosition property.  if i wanted my pane to be docked on the bottom i could use:

ctp.DockPosition = Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionBottom;

but one thing to keep in mind is other properties.  for example, if i added the line above to my initial code and had the width property, i'd get an exception.  i'm trying to set a width when a bottom-docked item fills the user's width -- no can do.  of course i should probably implement better checking and simply handle that scenario.

the action panes are no different (other than how they are added is via a different class instead of CustomTaskPane) and simply are scoped to the document.  both are implemented as user controls and you can put windows controls on there and interact in code however you want.  in fact we demonstrated how we could implement XAML into a task pane.  here's a screen shot of the task pane with a XAML rectangle (which animates, but hey, it's a screenshot) and an embedded media element of a video.  you could think of documents that might be handbooks/trainings and include video with it so as the reader (or form-filler-outer) is looking at the document they might get live help via video:

i didn't get to the word content controls in my session, apologies.  we also didn't talk about sharepoint development, etc.  i think you could spend a whole time on that.  you tell me, what concerns you about sharepoint development?

i mentioned a few developer tips as well that i'll emit here:

    • remember 'Globals'
    • create a stub mail profile (control panel -- Mail) so when writing outlook applications you aren't constantly trying to connect with a real mail system.
    • click the 'office circle' and go to application options, add-ins, manage com add-ins to remove/clean up your developer litter

here are the slide decks: PPT 2007 and PDF

i hope that those who attended learned at least one thing new.  some of my demos weren't cooperating despite me staying up until 3am doing them three times.  such is life.  thanks again to those who came.  be sure to check out beth's post as she used VB XML literals to leverage Office Open XML to write a mail merge in XML code which generate word documents...it was pretty slick.

| Comments

finally want to try out that feature to debug your apps and step into System.Web.dll?  it has arrived.

shawn burke has all the blow-by-blow details on setting it up.