XTab's Blog

Ged Mead's Blog at vbCity

vbCity Blogs moved to:
http://cs.vbcity.com/blogs
  Home :: Syndication  :: Login

JunJuly 2009Aug
SMTWTFS
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

Archives

Topics

Ramblings

VB.NET

Friday, July 03, 2009 #

Starting Point
In my earlier post, the HiliteTextBox, I created a simple WPF UserControl which is hosted in an ElementHost in a Windows Form. If the user tries to continue without entering text in the TextBox, a bright colored Border would appear around it and stay there until the user does enter something.

  

If we are going to go to the trouble of harnessing WPF's graphics power, we may as well go a step further and add some animation to the control. One simple thing we can do is to animate the Border Thickness.

This is the XAML for the UserControl I created previously. The only thing I have changed is the class name. It was originally HiliteTextBox, but for this version it will be named AnimatedTextBox :

<UserControl x:Class="AnimatedTextBox"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">

  <UserControl.Resources>

    <LinearGradientBrush x:Key="BrightGradient" EndPoint="0.056,0.993" StartPoint="0.634,0.342" SpreadMethod="Repeat">

      <GradientStop Color="#FFB50D0D"/>

      <GradientStop Color="#FFE80C2C" Offset="1"/>

      <GradientStop Color="#FFF4DF5A" Offset="0.17399999499320984"/>

      <GradientStop Color="#FFCA0C1F" Offset="0.357"/>

      <GradientStop Color="#FFDCD762" Offset="0.522"/>

      <GradientStop Color="#FFB70D36" Offset="0.716"/>

      <GradientStop Color="#FFDDEE49" Offset="0.823"/>

    </LinearGradientBrush>

  </UserControl.Resources>

 

  <Border x:Name="GradBorder"

           BorderBrush="LightGray"

           BorderThickness="5"

           CornerRadius="4,4,4,4">

 

    <TextBox x:Name="InputTextBox"

            TextWrapping="Wrap"

         TextChanged="InputTextBox_TextChanged" >

    </TextBox>

  </Border>

</UserControl>

And this is the code-behind in the UserControl:

    Private Sub InputTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs)

        HighlightEmpty()

    End Sub

 

    Public Sub HighlightEmpty()

        If InputTextBox.Text.Length > 0 Then

            GradBorder.BorderBrush = New SolidColorBrush(Colors.Black)

            GradBorder.BorderThickness = New Thickness(1)

        Else

            GradBorder.BorderBrush = FindResource("BrightGradient")

            GradBorder.BorderThickness = New Thickness(4)

        End If

    End Sub

 

    Public Function IsTBEmpty() As Boolean

        If InputTextBox.Text.Length = 0 Then

            Return True

        Else

            Return False

        End If

    End Function

Creating the Animation in Code

Generally, you will find XAML to be the best tool for creating animations - and if they are quite complex then Expression Blend is your best bet. However, you can create a simple animation in the UserControl using code. The following code-behind in the UserControl will cause the Border to change in size over a short period of time:

    Public Sub ChangeSize()

        Dim ThickAnim As New ThicknessAnimation

 

        With ThickAnim

            .To = New Thickness(14)

            .AutoReverse = True

            .Duration = New Duration(TimeSpan.FromSeconds(0.6))

        End With

 

        Me.GradBorder.BeginAnimation(Border.BorderThicknessProperty, ThickAnim)

    End Sub

Even if you have no experience of WPF, most of the above code is relatively easy to follow.

ThicknessAnimation is a class that 'does what it says on the tin', as we say here in the UK - it does exactly what you would expect. It animates the Thickness Property of a target element.

To sets the value of the Thickness that has to be reached by the conclusion of the animation.

AutoReverse ensures that the Thickness returns to its starting value by the end of the animation sequence.

Duration assigns the length of time for which the animation will run. Note that in the case of this AutoReversed animation both the increase (The To value) and the decrease (AutoReverse to the start value) in size are completed within the allowed Duration.

The BeginAnimation Method takes parameters of a kind that might be unfamiliar. It takes the WPF DependencyProperty for the Border class as the first argument and it takes the ThicknessAnimation instance as the second argument. The correct element is animated, of course, because the BeginAnimation method is applied to GradBorder, the Border instance we are dealing with.

Me.GradBorder.BeginAnimation(Border.BorderThicknessProperty, ThickAnim)

To get the animation to run, we can incorporate it into the code we used previously that tests if the TextBox is empty. This code is in the Windows Form on which we placed an ElementHost to house the WPF UserControl :

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If AnimatedTextBox1.IsTBEmpty = True Then

            '  Change to bright gradient border

            AnimatedTextBox1.HighlightEmpty()

 

            '  Animate

            AnimatedTextBox1.ChangeSize()

 

            ' Return focus

            Me.ElementHost1.Focus()

        Else

            MessageBox.Show("You are clear to continue")

        End If

    End Sub

If you try this code and run the project, click the Continue Button with no text in the TextBox, you will see that the Gradient Border appears and then the Border size is animated.

One thing you may have noticed is that the target in the Button Click event is an object named AnimatedTextBox1. If you scour through the small amount of code I have written so far, you won't find where I created this object. The reason being that I didn't. When you use the Smart Tag in the ElementHost and select the content, it will automatically create an instance for you and name it.

In the next part, I will create a different animation using what on the face of it seems to be a very complex block of XAML. When we take a closer look, you will see that it is not as complicated as it first appears.

posted @ 3:34 PM | Feedback (0)

Wednesday, July 01, 2009 #

Introduction     
A recent forum post on VBCity asked for a way to highlight an important TextBox in a Windows Form. Assuming that the plan is to highlight the TextBox if the user is about to leave the Form without entering any text into it, there are of course several ways this can be done using Windows Forms techniques. But I thought it might be interesting to try something different and see how easy it would be to create a WPF UserControl that incorporated a TextBox, a smack-you-in-the-eye gradient and a border with rounded corners*. I picked those last two features because they are not particularly easy to create in Windows Forms. The WPF UserControl would then have to be integrated seamlessly into the Windows Forms environment.

(*If this isn't cutting-edge enough for you, I will be looking at adding animation to the control in a follow up blog post).

So here's our demo Windows Form that will contain two TextBoxes. The WinForms TextBox is already in place. We will create the WPF one shortly.

  

The WPF UserControl
The first step is to add a WPF UserControl to the Windows Forms project. This is as simple as hitting Ctrl+Shift+A and selecting "WPF" from the Add New Item menu which appears. The only choice you will then see is "User Control (WPF)", so select this and change the default name to 'HiliteTextBox.xaml'. Then click the Add button.

  

The WPF UserControl will be added to your Windows Forms project and behind the scenes all the required References will be added also. The UserControl will be on display as the selected item in Visual Studio and you will see that all the WPF tools are in the Toolbox, not the WinForms ones.

Delete the default Grid from the UserControl and then drag a Border element from the Toolbox and drop it on the XAML pane, on one of the empty lines where the Grid used to be. You might think it strange that you drag a visual element and drop it into the markup area. There's nothing to stop you from dropping it in the Design pane instead. If you do this, you will find that several properties are automatically added for you and, as it happens, we don't want those particular properties set and you would therefore have to delete them.

Assign the Name 'GradBorder' to the Border. Create an empty line between the opening and closing tags of the Border. Drag a TextBox from the Toolbox and drop it on the empty line. Name the TextBox 'InputTextBox'.

Here's the Markup as at this point:

<UserControl x:Class="HiliteTextBox"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="300" Height="300">

  <Border x:Name="GradBorder">

    <TextBox x:Name="InputTextBox"></TextBox>

  </Border>

</UserControl>

Don't worry that nothing seems to be happening in the Design pane. We'll soon change that. With the Border selected in the XAML pane, move over to the Properties window and change the BorderBrush to LightGray, the BorderThickness and CornerRadius properties to 4. (Still no change in the Design pane; no worries).

The plan is to have a plain gray border round the TextBox by default and have this change to a bright gradient if the user tries to continue without entering any text in the TextBox. To set this up, first create a new empty line between the opening tag of the Window and the opening tag of the Border. Copy and paste the following markup, which creates a LinearGradientBrush that will be stored as a Resource.

  <UserControl.Resources>

    <LinearGradientBrush x:Key="BrightGradient" EndPoint="0.056,0.993" StartPoint="0.634,0.342" SpreadMethod="Repeat">

      <GradientStop Color="#FFB50D0D"/>

      <GradientStop Color="#FFE80C2C" Offset="1"/>

      <GradientStop Color="#FFF4DF5A" Offset="0.17399999499320984"/>

      <GradientStop Color="#FFCA0C1F" Offset="0.357"/>

      <GradientStop Color="#FFDCD762" Offset="0.522"/>

      <GradientStop Color="#FFB70D36" Offset="0.716"/>

      <GradientStop Color="#FFDDEE49" Offset="0.823"/>

    </LinearGradientBrush>

  </UserControl.Resources>

There is a very good reason for storing the gradient brush separately and naming it with the 'BrightGradient' Key, as you will see soon.

We will set a few properties on the TextBox. Select the TextBox in the XAML pane and then move to the Properties Window and set the TextWrapping property to 'Wrap'.

In the XAML pane, create a new empty line inside the opening tag of the TextBox. Then type in: 'TextChanged='.    As soon as you do this, an Intellisense popup will appear, inviting you to create a new event handler for the TextChanged event.



Press the TAB key and the rest of the line will be completed for you. It will be assigned a default name and this event handler will have been added to the code-behind for you.

Your markup for the Border and TextBox should now look like this:

  <Border x:Name="GradBorder"

           BorderBrush="LightGray"

           BorderThickness="1"

           CornerRadius="4,4,4,4">

 

    <TextBox x:Name="InputTextBox"

            TextWrapping="Wrap"

            TextChanged="InputTextBox_TextChanged">

    </TextBox>

  </Border>

Open up the vb file for the UserControl - HiliteTextBox.xaml.vb - in the project files in Solution Explorer. The InputTextBox_TextChanged event handler will be there for you.
Before we code the event handler though, first create a Sub that will check if the TextBox contains any Text. If it does, the Border will be set to the non-highlighted LightGray; if it doesn't, the Border will be the bright gradient. Enter the following code:

Imports System.Windows

Imports System.Windows.Media

    Public Sub HighlightEmpty()

        If InputTextBox.Text.Length > 0 Then

            GradBorder.BorderBrush = New SolidColorBrush(Colors.Black)

            GradBorder.BorderThickness = New Thickness(1)

        Else

            GradBorder.BorderBrush = FindResource("BrightGradient")

            GradBorder.BorderThickness = New Thickness(4)

        End If

    End Sub

The only code that may be new to you might be the FindResource method. This is a really useful feature in WPF, which allows you to ferret through the project and pull out any specific resource that you want to use. The 'BrightGradient', you will recall, is the Key that I assigned to the LinearGradientBrush that I stored as a UserControl Resource earlier.

The Imports statements at the top of the file ensure that you are accessing elements in the System.Windows namespace.

Hosting the UserControl    
Now the UserControl is built, it has to be hosted in the Windows Form. Rebuild the project and then select the Form1.vb file. The control that WinForms uses to host WPF UserControls is the ElementHost. This probably won't be in your Toolbox by default, so if it isn't listed, right-click on the Toolbox and select Choose Items...   Select the ElementHost from the .NET Framework Components Tab.

Drag an instance of the ElementHost from the Toolbox on to the surface of the Form. It will be too large by default, but before you change its size, click on small down arrow next to 'Select Hosted Content' and select HiliteTextBox from the list:

   

The Smart Tag window will close and you can then adjust the size of the ElementHost. As you can see from the screenshot, an error message appears inside the ElementHost. You can safely ignore this as it is only tells you that the Windows Form can't display the visuals of the UserControl at Design time.

Next we need to add code to the Continue button. For our demo purposes, this will be sufficient:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        HiliteTextBox1.HighlightEmpty()

    End Sub

If you look back at the earlier code, you will know that the HighlightEmpty method checks if the TextBox has Text entered and sets the Border accordingly. Although the Button on the Windows Form can't access the graphical elements of the UserControl directly, the HighlightEmpty procedure allows for this interchange between the two.

Try running the application now. Don't enter any text into the UserControl, then press the Continue button. You should see the highlight gradient border appear around the TextBox.

  

If you then insert some text and press the Continue button again, the highlight border will be replaced with the gray one.

Adding Some Refinements
That pretty much fulfills the spec, but there are a couple of things I would definitely add. The first feature is that if the user is warned about not entering text, then I would like to give some positive feedback as soon as they do so. That is, before they press the Continue button for a second time. This is easily achieved by adding a line of code to the TextChanged event handler of the InputTextBox.

Go back to the WPF UserControl code-behind file and add a call to the HighlightEmpty procedure:

    Private Sub InputTextBox_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs)

        HighlightEmpty()

    End Sub

When you run the application again and press the Continue button with no text in the TextBox, the highlight border appears as before. Now though, when you enter a character, the highlight Border disappears. As a small bonus, if the user then deletes all the text in the TextBox, the highlight will reappear, just to make certain they know about it.

The second thing I would like to add is a way of giving the developer a choice of actions on the Continue Button Click event. What I mean by this is that at the moment, the call goes to the HighlightEmpty procedure of the UserControl. This approach makes it difficult to build choices into the Button Click event, along the lines of "Depending whether the TextBox is empty, do something here in the Form's logic".

There are several easy ways of doing this, including adding a Boolean Property to the UserControl or creating a simple Function. This tests if the InputTextBox is empty and if it is, it Returns True. Based on the returned value, the Button event handler can follow one course of action or another.

Here's a Function that can be added to the UserControl:

    Public Function IsTBEmpty() As Boolean

        If InputTextBox.Text.Length = 0 Then

            Return True

        Else

            Return False

        End If

    End Function

You can then put whatever code you want in the Button Click event handler. Maybe something like:

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        If HiliteTextBox1.IsTBEmpty = True Then

            HiliteTextBox1.HighlightEmpty()

            Me.ElementHost1.Focus()

        Else

            MessageBox.Show("You are clear to continue")

        End If

    End Sub

You can apply this approach of using a WPF UserControl in a Windows Form to get access to lots of those enhanced visual goodies that WPF offers, including Animation. As you have seen, creating a WPF UserControl hosted in an ElementHost is a very easy process. As an alternative to fiddling with individual controls on a Windows Form, you can of course include complete WPF Windows in Windows Forms projects.

posted @ 4:49 PM | Feedback (0)

Monday, June 29, 2009 #

  In this article, we will look at how to alter the background color that appears when a user selects an item in a WPF ListBox

  I've been using DataTemplates to organize the layout of WPF ListBoxes for quite a while now. Not only in my WPF Applications, but also by using WPF/WinForms Interop (See my article on devcity for info on how to do this). 

  If you have used DataTemplates yourself, you will know that they can make a great job of packaging up the data display and allowing you to easily create UIs that would be extremely difficult in Windows Forms.

  I was working on a ListBox creation task this week. Nothing very fancy - each item comprised of an Image and two items of text inside a Gradient Border:

  

  The DataTemplate makes a fair job of tweaking the presentation of the data, but when I ran it and selected an item I still got the standard solid blue selection color:

  

  I thought it would be better if I could replace the standard solid blue with something else, but this wasn't something I could achieve by tweaking the DataTemplate. What is needed is a change to the ItemContainerStyle .

   It's important to understand the role of these two styling tools. The DataTemplate allows you to package up the individual items of data for display. In the case of a ListBox, you can think of the DataTemplate as sitting inside the ListBoxItem. The ItemContainerStyle is one level up from this. It is responsible for the overall look of the ListBoxItem. So if it helps you to picture the respective roles, you could think of the DataTemplate being contained in the ItemContainerStyle, although that's not exactly how it works.

   The question is, how do you change the ItemContainerStyle to create a different selection color and, once this is done, how do you then get the ListBox to apply it?

   The answer to the second question is shorter, so I'll deal with that one first. ItemContainerStyle is a property of the ListBox.

 <ListBox ItemContainerStyle="{StaticResource SimpleListBoxItem}">


As you would expect, this property takes a Style as its value. And that leads us to the answer to the first question - you create a Style, but (and here's the important bit) you ensure that the Style also contains the ControlTemplate for the ListBox.

  When you stop and think about it, this has to be right. As soon as you find you want to change the fundamental look of the ListBox element, you know you have to work with its Template. How much trouble you then go to in order to rewrite the ControlTemplate is a matter of choice.

   I began by looking at the full ListBox template, which is fairly lengthy, but then realized two things:

  1. I already had my DataTemplate in place to do the fancy stuff, so all I needed to do was find the part of the Template that deals with the change of color when an item is selected and tweak this. Everything else could be left unchanged.
  2. I didn't need to make any changes to the ListBox template. What I was dealing with here was the ListBoxItem template.

  When the requirement is as simple as this, a useful approach is to use the Simple Styles, which are available from many sources. If you haven't come across these yet, they are a cut-down version of the full Styles that are used to create the standard WPF elements. One easy way to access and download them is to enter "Styling with ControlTemplates Sample" in the "Look For" text box of the Index, (not the online Search box) on the Visual Studio Help page. This will reveal the sample page, which includes a link to download all the simple styles.

  So I took the simple style version of the ListBoxItem and changed the Setter for the IsSelected Trigger. Here is the finished Style:

    <Style x:Key="SimpleListBoxItem" TargetType="ListBoxItem">

     <Setter Property="FocusVisualStyle" Value="{x:Null}" />

      <Setter Property="Template">

        <Setter.Value>

          <ControlTemplate TargetType="ListBoxItem">

            <Border

           Name="Border"

           Padding="2"

           SnapsToDevicePixels="true">

              <ContentPresenter />

            </Border>

            <ControlTemplate.Triggers>

              <Trigger Property="IsSelected" Value="true">

                <Setter TargetName="Border" Property="Background"

                     Value="{StaticResource AuthorGradient}"/>

              </Trigger>

            </ControlTemplate.Triggers>

          </ControlTemplate>

        </Setter.Value>

      </Setter>

    </Style>

  Compared to the full version, that's fairly brief. You will see that I changed the Background color of the Border that contains the ListBoxItem in the Template* from the default to a gradient brush named AuthorGradient that I created and stored in the App.xaml file as an Application scope Resource.

  * Don't confuse the Border in the Style's Child ControlTemplate with the Border that is used in the DataTemplate.

  

  Now when an item is selected, the user sees this gradient instead of the plain blue:

  

  So the full markup for the ListBox is as follows:

      <ListBox x:Name="TemplatedListBox"

      HorizontalAlignment="Left"  Margin="10"   

      ItemTemplate="{StaticResource GreenDataTemplate}"

      ItemsSource="{Binding}"

      ItemContainerStyle="{StaticResource SimpleListBoxItem}" >

      </ListBox>

  You can see that the ListBox's ItemTemplate is set to the DataTemplate which controls the layout of the data in each ListBoxItem. The ItemContainerStyle contains the template for the ListBoxItem - the one where I changed the selected item color from plain blue to gradient green.

  In order for this particular example to work, you will of course need to have a DataSource available to the ListBox's ItemsSource, to which it can bind. However, you don't need to have a bound ListBox, as you will see at the end of this item.

  The DataTemplate I used to create the Border/Image/TextBlocks combination is:

    <DataTemplate x:Key="GreenDataTemplate" >

      <Border BorderBrush="{StaticResource GreenGradient}" BorderThickness="4"

             CornerRadius="4" Margin="4,2">

        <StackPanel Orientation="Horizontal">

          <Image Source="{Binding Path=ImagePath}" Margin="2,0,4,0"

            Width="60" Height="70" Stretch="UniformToFill"   />

 

          <StackPanel Margin="1" Width="120">

            <TextBlock Padding="2,17,5,2"

               FontSize="13"

                FontWeight="Bold" Foreground="DarkGreen"

                Text="{Binding Path=Name}" Height="56">

            </TextBlock>

 

            <TextBlock Foreground="Green"

               Margin="12,2,0,2"

               Text="{Binding Path=Published}" >

            </TextBlock>

 

          </StackPanel>

        </StackPanel>

      </Border>

    </DataTemplate>

  I placed this in the Window.Resources block. Again, this will only work if you have a DataSource which contains the three fields I have used in the Bindings for the Image and TextBlocks - ImagePath, Name and Published.

  If all this DataBinding and DataTemplates is confusing or more than you need, and you simply want to change the selected item background color, then the following ListBox will be sufficient:

    <ListBox ItemContainerStyle="{StaticResource SimpleListBoxItem}">

      <ListBoxItem> <TextBlock Margin="4" Padding="5" FontSize="14">Item 1</TextBlock></ListBoxItem>

      <ListBoxItem>

        <TextBlock Margin="4" Padding="5" FontSize="14">Item 2</TextBlock>

      </ListBoxItem>

    </ListBox>

  When an item is selected, the ItemContainerStyle will kick in and the user will see the gradient:

  

  And finally, if you don't even want the gradient, but just need a different solid color, then simply make the change in the Style:

    <Style x:Key="SimpleListBoxItem" TargetType="ListBoxItem">

     <Setter Property="FocusVisualStyle" Value="{x:Null}" />

      <Setter Property="Template">

        <Setter.Value>

          <ControlTemplate TargetType="ListBoxItem">

            <Border

           Name="Border"

           Padding="2"

           SnapsToDevicePixels="true">

              <ContentPresenter />

            </Border>

            <ControlTemplate.Triggers>

              <Trigger Property="IsSelected" Value="true">

                <Setter TargetName="Border" Property="Background"

                     Value="Yellow"/>

              </Trigger>

            </ControlTemplate.Triggers>

          </ControlTemplate>

        </Setter.Value>

      </Setter>

    </Style>

  As you can see, in this case, I have changed the highlight color to Yellow by editing the Setter for the Border in the IsSelected trigger. The result will be:

  

So, whether you want to create multicolor effects, tied in with bound DataSources, or even if you simply want to change the selected item solid color, you now know how to do it.

posted @ 3:43 PM | Feedback (0)

Wednesday, June 17, 2009 #

This will be something of a 'Work in Progress' blog item, as I plan to jot down differences between WPF and Silverlight as I find them, either in my own work or from other sources. It can be difficult to keep up to speed on what the precise differences are at any given time. The SDKs, Toolkits, roll out of new editions of both platforms, etc all cause the sands to keep on shifting. Anyhow, I hope those that I do identify will be of help to those of you who are familiar with WPF, but maybe not so much with Silverlight.

The topic headings are listed alphabetically.

Button Content
In WPF, you can create a Button and assign its Content in the following way:

 <Button Margin="5" >SilverlightButton>



In Silverlight, you don't have this facility and have to explicitly identify the Content property:

 <Button Margin="5" Content="Silverlight">Button>

Button ClickMode
This is more a matter of user expectation than different properties. The WPF Button has a ClickMode property. This is available in both WPF and Silverlight. The three available values are:

  • Hover
  • Press
  • Release
As you would expect, when the Click event of the button will fire depends on this particular setting.
The reason why I include it as a difference is that I think it is unlikely that you will often use the 'Hover' option in a desktop application, because that isn't a reaction your users will expect. In a Silverlight, browser based environment, of course, it may well be that users will be happy with a button click firing when they hover over a button. (Actually, I'm not sure that 'hover' is totally accurate. It really seems to fire on the immediate mouse enter action and I haven't found a setting that allows a built in delay.)

The default value - both for WPF and Silverlight - is 'Release'.

Chrome and Decorators
Most of the Chromes and Decorators, which can be used to fine tune control templates to a very detailed level, are not available in Silverlight2.

Commands
Silverlight 2 does not support the WPF Commands structure. It does however support the ICommand interface.

Controls (Visual Elements)
It's hard to keep up with the permutations and changes, but I think the following breakdown is accurate:

1. Available in WPF (.NET 3.5, Visual Studio SP1), but not in Silverlight 2:

  • FlowDocument Readers
  • Frame
  • Glyph
  • GroupBox
  • InkCanvas
  • ListView
  • Menu
  • NavigationWindow *
  • Page *
  • PageFunction *
  • RichTextBox
  • StatusBar
  • UniformGrid
  • VirtualizingStackPanel
  • WebBrowser
  • Window
  • WindowsFormsHost
* These items are not necessary in Silverlight, because it is browser based.

2. Available in Silverlight 2 , but not in WPF with Visual Studio 2008:
  • DataGrid *
  • DatePicker *
  • HyperlinkButton
  • MultiScaleImage

* These are not included in WPF in Visual Studio 2008, but are downloadable as part of the WPF Toolkit and will almost certainly ship with VS 2010. You can see info and download the WPF Toolkit from here.


3. Available in the Silverlight 2 Toolkit and in WPF in Visual Studio 2008.
  • Calendar
  • Expander
  • GridSplitter
  • TabControl
  • TreeView
  • ViewBox
  • WrapPanel

4. Available in the Silverlight 2 Toolkit, but not in WPF.
  • Accordion
  • AutoCompleteBox
  • ButtonSpinner
  • Data Visualization/Chart Tools
  • DomainUpDown
  • NumericUpDown
  • TimePicker
  • TransitioningContentControl


5. Available in the WPF SDK, but not in Silverlight 2.
  • Ribbon

DataBinding

  • Silverlight 2 does not support One-Way To Source binding.
  • With WPF, the default binding mode is dependent upon the dependency property, but is usually TwoWay. The default binding mode in Silverlight 2 is OneWay.
  • Silverlight does not support Explicit DataBinding. It does however support the key approaches of LostFocus and PropertyChanged.
  • Silverlight 2 does not support MultiBinding.
  • Silverlight 2 does not support Element Binding (e.g. Binding a Slider value directly to a property on another element).
  • Silverlight 2 does not support the ObjectDataProvider.

DependencyProperties
Silverlight 2 does not support Read Only DependencyProperties.

Deployment Options
A Silverlight application is deployed to a web server and hosted in a web browser. (The next version of Silverlight should also include the ability to view the application offline). WPF applications may be deployed as a standard installed application or via Click Once. WPF also offers a browser hosted version - the XAML Browser Application (XBAP). In view of the limitations of XBAPs it is likely that most developers would now choose Silverlight over WPF for browser hosted applications.

DrawingBrush
The WPF DrawingBrush is not supported in Silverlight 2. However, there is little that this brush does that can't be achieved with other approaches. The key loss would be the ability to tile, but there are workarounds for this.

Effects
Effects (as opposed to BitmapEffects, which are no longer recommended for use) are only available in WPF. The next version of Silverlight should have the ability to create Pixel Shaders, and therefore Effects.

File Access
WPF has full access to local files in the same way as other .NET desktop applications. Silverlight runs in a Low Trust environment, so Users have restricted access to local files. Access to files in Silverlight is only available by means of the OpenFileDialog and even then is effectively only Read Only access. In Silverlight 2, saving data to file can only be achieved by using Isolated Storage.

Hardware Acceleration
By default, WPF hands off much of the heavy duty graphics work to the GPU, assuming that the graphics card meets the minimum specification. Silverlight uses software rendering.

Hyperlinks
In WPF you can create a Hyperlink by using the Hyperlink element. Generally you use these in Page based applications and FlowDocuments. Silverlight uses the HyperlinkButton.
Both elements use the NavigateUri property to assign the address of the link. The Silverlight version seems to be more versatile. Although it is called 'button', it doesn't have the standard rectangular button appearance. In fact, if you drag one from the Toolbox, you will see its markup in the XAML pane, but nothing shows in the Design pane.
The intention is that you use the HyperlinkButton as a canvas (with a small 'c', not a Canvas element). You then add Content to the button and this is what creates the interface that the user sees. The Content can be anything from a simple single element to a more complex interface - such as a StackPanel which contains multiple elements:

<HyperlinkButton NavigateUri="http://www.devcity.net" Width="60"

                Margin="15" HorizontalAlignment="Left"

                VerticalAlignment="Top">

      <HyperlinkButton.Content>

        <StackPanel>

          <Image Source="ShelfUnit_WithCans.png">Image>

          <TextBlock>  Click Me! TextBlock>

        StackPanel>

      HyperlinkButton.Content>

    HyperlinkButton>

      

Isolated Storage
There are significant differences between the implementation of local isolated storage in Silverlight 2 and WPF. Silverlight 2 restricts the availability of disk space. The default setting is 1 MB. This may be increased with explicit user authorization.

Key Enumeration
WPF contains more precise Keyboard Key definitions in its Key enumeration. For example, it

KeyEventArgs
WPF implements several properties in KeyEventArgs; Silverlight implements two - Key and PlatformKeyCode.
WPF does not implement PlatformKeyCode. This is used in Silverlight to identify non-Windows keys.

Markup Extensions
Silverlight only supports a small sub-set of markup extensions. They are:

  • Binding
  • StaticResource
  • TemplateBinding
  • x:Null
It is not possible to create your own markup extensions in Silverlight.

Mouse Events
Silverlight supports a limited set of Mouse events:

  • LostMouseCapture
  • MouseEnter
  • MouseLeave
  • MouseLeftButtonDown
  • MouseLeftButtonUp
  • MouseMove
This means that Silverlight doesn't support mouse wheel, left/right mouse button differentiation or any of the PreviewXX mouse events that are available in WPF.

Network
Silverlight 2 is limited to asynchronous network calls.
Silverlight 2 can communicate to Network Resources via Sockets.
Silverlight 2 supports only SOAP 1.1 over HTTP, and does not support other web services not compliant with WS-I Basic Profile 1.0.

Path Based Animations
This feature is not available in Silverlight 2. Linear interpolated and KeyFrame animations are available.

Printing
No PrintDialog or Printing facility in Silverlight 2.

Resources
Silverlight 2 does not support MergedDictionaries.
Silverlight 2 does not include the FindResource method.

RoutedEvents
Silverlight 2 only supports Bubbling and Direct Routed Events. WPF also supports Tunneling Events.

Security
WPF desktop applications require FullTrust permission in order to run. XBAPs run with partial trust. Silverlight 2 applications run in low trust.

Styles
WPF offers the choice of Named Styles and Typed Styles. Named Styles allow you to set the Style on an element by using its Key. Typed Styles do not have a Key and all elements of the TargetType within scope of the Style will be assigned the Style.
Silverlight doesn't recognize Typed Styles and if they are used, the assignment of the Style will fail silently.
Silverlight doesn't allow the use of the 'BasedOn' feature to inherit from Styles.
When setting the TargetType, Silverlight does not use the markup extension. You simply assign the name of the TargetType in double quotes. If you forget, and force of habit makes you add the curly braces and markup extension in Silverlight, you won't get a design time error, but it will fail at run time.

3D Graphics
Silverlight doesn't have the full range of 3D creation tools that are available in WPF.

Tiled Brushes
Silverlight 2 doesn't support the TileMode in ImageBrush.

Transitions
Silverlight will not accept Triggers and uses the Visual State Manager (VSM) to organize animations and transitions for user interaction. VSM's capabilities currently are limited to the equivalent of standard Style Triggers and EventTriggers. There are no features equivalent to DataTriggers or MultiTriggers in Silverlight.
VSM for WPF will be available soon.

Validation
WPF supports the creation of ValidationRules. This feature is not available in Silverlight 2.

Visual Brush
Silverlight2 doesn't support WPF's Visual Brush. As this is a brush of very limited use, it's not likely to be much of a problem.

Visual State Manager (VSM)
Currently this is a Silverlight only feature. It will be introduced into WPF in the future.  

Visual Studio Designer
Visual Studio 2008 has the facility for you to drag and drop, reposition, resize, etc, elements in the Design pane with WPF. For Silverlight it seems to offer editing tools only in the XAML pane. The Design pane is a kind of 'Read Only' version of what is created in the markup.

VisualTreeHelper
This class exists in both WPF and Silverlight 2, but because of differences in the underlying base classes of the two platforms they have different implementations. The key difference is that WPF offers the HitTest method, whereas Silverlight2 uses FindElementsInHostCoordinates.
With the exception of GetChild, GetParent and GetChildrenCount, Silverlight2 doesn't support any of the GetXXX methods found in WPF.

Watermarked TextBox
This is a Silverlight only control. The name says it all - basically a TextBox that allows you to include light shaded text to help the user. If memory serves, this was in Kevin's famous Bag o'Tricks.

Note: Most of the differences described above are also included in an excellent White paper comparison document published by Wintellect. You can download a copy from here.

posted @ 10:24 PM | Feedback (0)

Sunday, June 14, 2009 #

 As a Developer, Visual Studio is my comfort zone but I thought the time had come to see what the experience of creating a Silverlight 2 project in Expression Blend would be like. Having spent the past six months working almost exclusively on Windows Presentation Foundation, I was particularly interested to find out how different the WPF and Silverlight experience would be.

The first thing I had to do was update my copy of Expression Blend 2. Out of the box, your only Silverlight option is a Silverlight 1 site. But if you go to the download site here you can download the latest Service Pack. This incorporates the changes that were available in the Blend 2.5 Beta, the main one being that you can build a Silverlight 2 Application.

 Probably the biggest difference between the two versions of Silverlight is that Silverlight 2 gives you the option of using C# or Visual Basic as the code-behind. If, like me, you're not a Javascript expert, but have served your time on the .NET learning curve, this is a really welcome option.

 If you have used WPF, you will know that by default when you create a new WPF Application you are presented with a Window instance containing a Grid as the root element on which to start creating your UI. In Silverlight 2, the default is a user control, again containing a Grid as the root element. As Silverlight is a browser plug in*, rather than the desktop application of WPF, this approach makes sense. However, I have to doubt the wisdom of this User Control being named "Page.xaml" by default, as this might cause confusion to those of us who have also created Pages in WPF Applications and think of Pages as being, well, er, pages!.

 To add some elements to the User Control, you simply drag them from the Asset Library on to the design surface. I added a TextBlock, an Image and a Button.

   

 If you are not familiar with Expression Blend, the Asset Library - where you drag your elements from - is the last icon on the left, which I have circled in Red. I added the png file which is used as the Source property of the Image to the project files.

 A Button that does nothing when clicked won't impress anyone, so my next task was to fix this. My inclination was to switch straight away to Visual Studio, but I thought I would see what Blend had to offer in the way of event handler creation.

 The Properties Pane in Blend has a button which causes the Properties to be listed (the default choice) and next to that there is a button for events. You can see this in the screenshot below.

    

 Expression Blend is essentially a tool for creating visual user interfaces. So, unsurprisingly, all you get when you choose that Events pane button is a list of common events for the particular element or control. What is quite impressive though is that if you type in the proposed name for the event handler (as I have done in the example below) and then hit the Enter key, not only does this cause Visual Studio to fire up, but when you look at the code-behind page you will see that a handler of the name you input will be created and it will have the correct signature. You can then simply insert whatever code for what you want to happen as a result of the button click.

    

 One thing I was keen to try out was Silverlight's Visual State Manager, about which I had heard much, but understood nothing. As it seems certain that WPF will have VSM soon, I wondered how much easier (or not) VSM would be compared to the current WPF Storyboard and Triggers approach.

I experimented by adding a simple animation to a button, transforming its size when the mouse entered its area. (I will be writing a full article on Visual State Manager soon, which will include VSM for controls which have CommonStates built in for you in Blend - such as the Button - as well as VSM for those that don't, such as Images.)

 The steps for the Button are as follows:

  1. Drag a Button from the Asset Library on to the drawing surface (Note to self: Must get used to calling this the 'Artboard').
  2. Right click on the button and choose 'Edit Control Parts (Template)'.
  3. Select 'Edit a Copy'.
  4. Rename the Key if you want to.
  5. Click OK.
If you now look at the States Pane :-



you will see that a standard set of States for a Button object are displayed for you. These are the four main CommonStates
  • Normal
  • MouseOver
  • Pressed
  • Disabled
and two FocusStates:
  • Focused
  • Unfocused

 If you want the visual state to change when the Button is pressed, you add a Transition to the Pressed State in that pane. This is done by clicking on the small 'Arrow and plus symbol' icon to the right of the word 'Pressed' in the States Pane. When you left click on this icon you will see a context menu which shows combinations of state changes. Select the first one - '*>Pressed'. The area round the Artboard should now have a red border and the "State recording is on" message appears at the top left corner. (If for any reason it is not on, simply click the mouse over the dot to the left of the "State recording is off" message.)

  As my initial experiment, I wanted to reduce the width and height of the Button by 15% when it is pressed. Here are the steps:

  1. Select the part of the Button that you want to animate.
  2. Go to the Properties pane.
  3. Scroll down until you reach the 'Transform' section.
  4. Select the Scale transform icon (the arrow coming out of a rectangle).
  5. Change the value of X to 0.85.
  6. Change the value of Y to 0.85.
  7. Press F5 to test the project.
  8. Click on the button to ensure that the animation works.
  9. Close the Browser.
  10. In the Objects and Timeline pane in Blend, click on the 'Return Scope to [UserControl]' icon at the right hand side of the Style name.

  I had quite a lot of trouble with this. I found that if I clicked on the button on the Artboard and made the transformations (or any other animations, for that matter), I was not getting the results I expected. I eventually worked out that the trick is to select the exact element you want to animate from the Objects and Timeline pane. In the case of the button transformation, the item to select is the parent grid. In some cases, you may need to expand the dropdown list to get to the exact item you need and you may have to experiment some before you get it right. I think this kind of problem is only likely to occur where you stick with the standard template; clearly, if you build up your own set of elements on the Artboard to make a styled button of your own, you will be more familiar with exactly which part(s) you want to animate.

 Another little thing to watch out for which might confuse at first, is that the States pane seems to 'remember' the last set of States you dealt with. If you then select a different object, (for example, the TextBlock in my initial user Control) and the States haven't been changed or set for this object, you will still see the States from the preceding object in the pane.

 Because I was familiar with the Triggers and Storyboard approach, I did want to see if this was still available as an option in Blend. It doesn't seem to be, but of course I can always do this in XAML in Visual Studio, using an EventTrigger. However, I can see that the Visual State Manager is going to be an easy beast to tame, so I don't think I'll need to revert to the old way very often.

 So at this stage, my first impression is that I will be content to use Blend for complex graphics and animations, gradients and visual states. For most other things, I think I will still be using Visual Studio whenever possible.




 * At least it is a browser plug in for the time being. Silverlight 3, which is coming up fast, is going to offer an out-of-browser experience. That is, you will be able to include code that will let the user access the Silverlight User Control when offline. My first thought was "Isn't that what WPF does?", but then realized that Silverlight is cross-browser, cross-platform whereas WPF is not. It will be interesting to see where this leads in the long term.

posted @ 9:50 AM | Feedback (0)

Tuesday, June 09, 2009 #

  I've always found ListViews quite fascinating. Slightly confusing sometimes, but fascinating nevertheless.

  As I have often been heard to say, it's the little Gotchas that'll get ya.     Take, for instance, the subject of this blog - copying an item between ListViews. The scenario is that you let the user click on an item in ListView1 and if they want this item copied to ListView2, they hit a button.

Now, you would probably think that all you need to do is identify the currently selected item and add it straight to the second ListView. Something like:

 Code Copy

ListView2.Items.Add(ListView1.SelectedItems(0))

  But if you do try this, you will get an error.

 

  

  The text of the error message pretty much says it all. What you have to do (assuming that you aren't prepared to remove the item from the original ListView) is to clone it. You will then be allowed to add the clone to the second ListView.

  Although the cloning isn't difficult, you do have to be aware of the need to cast the selected item to ListViewItem if you have Option Strict On. To be honest, I found this a bit strange at first. If I lift a ListViewItem from a ListView, I didn't expect to have to cast it to what it is - i.e a ListViewItem.

  I'm not entirely sure why this occurs and wonder if the underlying reason for this is that the ListViewItem is stored in the SelectedItems collection of the ListView as a generic object. Anyway, Casting it back to a ListViewItem at the point where the cloning takes place, fixes this without any problem.

  This code works well:

Code Copy
If ListView1.SelectedItems.Count > 0 Then
    Dim lvi As New ListViewItem
    lvi = ListView1.SelectedItems(0)
    Dim lvi2 As New ListViewItem
    lvi2 = CType(lvi.Clone, ListViewItem)
    ListView2.Items.Add(lvi2)
End If

  You'll have noticed that I built in a test to ensure that an item is currently selected. It's an easy thing to forget and is sure to bring your app to a grinding halt before long if you don't build this in.

  You can see where I have cast the selected item (aka lvi) to ListViewItem in Line 5. Intriguingly, casting to ListViewItem in the third line of code, e.g.

  Code Copy

lvi = CType(ListView1.SelectedItems(0), ListViewItem)

   doesn't cut it.

   

posted @ 9:44 AM | Feedback (2)

  Sometimes you don't have control over how the data is saved to a text file. For instance, some items might be saved with quotation marks around words or phrases. If you want to read the file but not show these marks then you'll need a way to remove them.

  Like a lot of things, it's actually very easy when you know how. You can use the built-in Replace function of the String class, replacing the marks as you find them. The trick though (and to my mind, the less than totally intuitive bit) is knowing how many quotation marks to use in the first argument of the Replace method's parameters. This is the 'OldChar' parameter, i.e the one you want to replace.

  You would think, wouldn't you, that you could put a Quotation Mark inside a pair of Quotation Marks like this:-

 

Code Copy
MyString.Replace(""", "")

But if you try that, you will find that it doesn't work. What you actually have to do is include a second Quotation Mark inside the outside ones. In other words, you need four Quotation Marks in a row.

 

Code Copy
MyString.Replace("""", "")

  It's only a tiny change, but it will move your mental state from annoyed confusion to enlightened contentment. Or something like that, anyway.

  So putting this together with code that reads from a file and displays the result (minus Quotation Marks) in a ListBox, you have:

Code Copy
Private Sub RemoveQuotes(ByVal filename As String, ByVal target As ListBox)
        '  A StreamReader to fetch the data
        Dim sr As New IO.StreamReader(filename)
        '  A string to hold each line as it is read
        Dim line As String = String.Empty

        ' Read from the file
        ' As long as there is something left to read
        Do While sr.Peek <> -1
            ' Replace the Quotation Marks with Nothing
            line = sr.ReadLine.Replace("""", "")

            ' Add edited text to a ListBox
            target.Items.Add(line)
        Loop

        '  Tidy up when finished
        sr.Close()
        sr = Nothing
End
Sub

  If you prefer your code to be broken down into clearer steps, you could do this instead:

 

Code Copy
    Private Sub RemoveQuotes(ByVal filename As String, ByVal target As ListBox)
        '  A StreamReader to fetch the data
        Dim sr As New IO.StreamReader(filename)
        '  A string to hold each line as it is read
        Dim line As String = String.Empty

        ' Read from the file
        ' As long as there is something left to read
        Do While sr.Peek <> -1
            '  Read the next line
            line = sr.ReadLine

            ' Replace the Quotation Marks with Nothing
        line = line.Replace("""", "")

            ' Add edited text to a ListBox
            target.Items.Add(line)
        Loop

        '  Tidy up when finished
        sr.Close()
        sr = Nothing
    End Sub

 

  Either way, your quotation marks will be history.

posted @ 9:35 AM | Feedback (0)

  One of the things that first caught me out in WPF was the simple topic of colors. For example, let's say you want to reset the BackColor of a Form in Windows Forms.

Easy enough. This will do the job:

Code Copy

Me.BackColor = Color.CadetBlue

  When it comes to WPF, you'll know that we are dealing with a Window, instead of a Form and have probably already picked up that BackColor is now Background.  You can however, still use "Me" to reference the Window.

 But if you were to try something like:

 Code Copy

Me.Background = Color.CadetBlue
    ' or even
Me.Background = Colors.CadetBlue

 you would be disappointed.

 You would however get some help from Intellisense (at least with the second version). The error message tells you that a Color cannot be converted to a Brush. And there's the answer to the problem.

The Background property doesn't take a Color - it takes a Brush, which of course can, and usually does, have a color assigned to it. Don't forget though that you are not limited to a single solid color; there are many gradient, tile and image based options that you can choose when it comes to brushes in WPF.

 So this code will work fine in WPF:

 Code Copy

  Me.Background = New SolidColorBrush((Colors.CadetBlue))

 Ah yes, I hear you say, but what about the theory that you should use XAML for the look and code-behind for the behaviour? Well, I can't disagree with you there and personally I would use:

<Window x:Class="Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" 
Background="CadetBlue">

  where the Background property for the Window is set there in the XAML. However, there may well be times when you want the user to have a say in color choices and in those cases it can be easier to take the user's input and deal with it in the code-behind.

  For example, if the user was empowered to enter values for the ARGB components then you might use an approach like the following:

 Code Copy

        Dim col As New System.Windows.Media.Color
        '  In reality the values below could be
        ' selected by the user and passed in
        col = Color.FromArgb(214, 122, 52, 24)
        Dim br As New SolidColorBrush(col)
        Me.Background = br

      It would also be quite easy to create a display in WPF where you bind, for example, sliders to the Brush that is used for the background. But I won't go any deeper into that just now, as this sub-set of blog items is meant only to help identify those missing WinForms favorites and repatriate them as WPF troops.

posted @ 9:33 AM | Feedback (0)

  This is a question that seems to come up a lot in the forums:- How can I restrict the TextBox input to numerals, or only a single occurrence of a decimal point, or some other restriction?

  As ever, there are several approaches. If the restriction is something basic, such as numerals only then the easy approach is to use the KeyPress event. What you can do is stop the character from appearing in the TextBox, test to see if it is allowable and, if it is, then allow it to continue.

Letters Only
   To take an example which only allows letters of the alphabet, it would look like this:

    Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress

        e.Handled = True

 

        If e.KeyChar Like "[A-z]" Then e.Handled = False

 

    End Sub

  In this snippet, it is the e.Handled = True which blocks the input temporarily. The next line assesses whether the key press is a letter of the alphabet*, either lower or upper case, and if it is then the handled setting is reversed. This allows the key press to be passed to the TextBox display. If it fails the test, the block on this key press remains.

  * Depending on your locale and keyboard, some other keys are allowed. These include symbols that are used in combination with characters in some languages, such as accents. In most cases this is the behaviour you will want.

Specific Keys
Sometimes you may want to allow certain keys. A common situation is where you will let the user use the Backspace to correct an error when inputting:

    Private Sub TextBox2_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox2.KeyPress

        e.Handled = True

 

        If e.KeyChar Like "[A-z]" _

        Or e.KeyChar = Chr(&H8) Then

            e.Handled = False

        End If

 

    End Sub

  In this case, it is the Chr(&H8) which identifies and allows the Backspace.

Numbers Only
  Another common requirement is to restrict input to numerals. Of the several possible approaches, using IsNumeric is one of the most straightforward:

    Private Sub TextBox3_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox3.KeyPress

        e.Handled = True

        If IsNumeric(e.KeyChar) Then e.Handled = False

    End Sub

  Sometimes that is too restrictive though. What happens if you want to allow the user to enter decimal points or (depending on their locale) commas to break up large numbers? Allowing these individual characters is simple, but there is another potential catch as we will see in a moment:

    Private Sub TextBox4_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox4.KeyPress

        e.Handled = True

        If IsNumeric(e.KeyChar) _

        Or e.KeyChar = "." _

        Or e.KeyChar = "," Then

            e.Handled = False

        End If

    End Sub

Only One Decimal Point
  In most cases where users are inputting numeric values you will want to restrict them to a single decimal point. The code above will allow multiple entries. Again, there are several solutions, but the following one will usually do the job:

    Private Sub TextBox5_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox5.KeyPress

        e.Handled = True

        If e.KeyChar <> "." Then

            If IsNumeric(e.KeyChar) Then e.Handled = False '

        ElseIf TextBox5.Text.Contains(".") Then

            MessageBox.Show("Only one decimal point allowed")

        Else

            e.Handled = False

        End If

    End Sub

Command Keys
   If you use any of the previous methods, you will be able to control the standard input keys. But there is another group of keys - Command keys - which won't be excluded by the use of the e.Handled approach. These include such keys as Home, End, Tab, and so on. You may risk alienating your users by excluding these, but there may be times when it is reasonable to do so, in which case you'll need to know how.

  A good way is to intercept the message at the window level and you can do this by overriding the ProcessCmdKey function. Here's how:

  Create a new class which inherits from the basic TextBox. Override the ProcessCmdKey function and test for the currently pressed key in a similar way to that used in the earlier examples. If the key is one you want to suppress then you return True and the Windows message pump will ignore it.

  The following code will be all you need:

Public Class CustomTextBox

 

    Inherits System.Windows.Forms.TextBox

 

    Sub New()

        Me.BackColor = Color.Azure

    End Sub

 

    Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean

 

        ' Declare a variable of type Keys enumeration

        ' named keyPressed.

        ' Cast the msg's WParam as a KeyEnum value

        ' and assign it to the keyPressed variable.

 

        Dim keyPressed As Keys = CType(msg.WParam.ToInt32(), Keys)

 

        ' Process the key that is pressed.

        '  If keyPressed = Keys.Home Or keyPressed = Keys.End Then Return True

        If keyPressed = Keys.Tab Then Return True

 

        ' Return the Command key message

        Return MyBase.ProcessCmdKey(msg, keyData)

 

    End Function

 

End Class

  The light blue back color is simply to make this sub-classed TextBox look slightly different from the default one for demo purposes, but of course is not a key part of the key checking functionality. As you can see, my example blocks the Tab key. You can add or replace other keys, such as Home and End.

Multiple Options
  Handling the KeyPress is fine if you only have a few TextBoxes for which you are controlling input. If there are going to be a lot of them throughout your application, or if you have different input rules for several TextBoxes, then again it may be worth your while to create your own inherited version.

  The following example deals with some of the previous scenarios, but allows the input rule to be selected from an enumeration of choices. The choices used here remain basic, but of course you can expand this idea much further.

  Here is the code:

Public Class RestrictedTextBox

    Inherits System.Windows.Forms.TextBox

 

    Enum RestrictionCategory

 

        NoRestriction

        NumeralsOnly

        LettersOnly

        AlphanumericOnly

 

    End Enum

 

    Private _allowedKeys As RestrictionCategory

 

 

    Property AllowedKeys() As RestrictionCategory

 

        Get

            Return _allowedKeys

        End Get

 

        Set(ByVal Value As RestrictionCategory)

            Select Case Value

                Case 1 To 3 ' One of the enum choices

                    _allowedKeys = Value

                Case Else ' No restriction

                    _allowedKeys = 0

            End Select

 

        End Set

 

    End Property

 

 

    Protected Overrides Sub OnKeyPress(ByVal e As KeyPressEventArgs)

 

        MyBase.OnKeyPress(e)

 

        '  Test whether key is allowed, based on the current choice

        '  from the enum

 

        Select Case _allowedKeys

 

            Case 1 'Numerals only

 

                If IsNumeric(e.KeyChar) Then

                    Exit Sub

                Else

                    e.Handled = True

                End If

 

            Case' Letters Only

 

                If e.KeyChar Like "[A-z]" Then

                    Exit Sub

                Else

                    e.Handled = True

                End If

 

            Case' Alphanumeric

                If e.KeyChar Like "[A-z]" _

                Or IsNumeric(e.KeyChar) Then

                    Exit Sub

                Else

                    e.Handled = True

                End If

 

        End Select

 

    End Sub

 

End Class

  The key areas are the enumeration which is called RestrictionCategory. These are automatically assigned values from 0 to 3. The Property AllowedKeys and its backing Field carry out the standard roles of a Property, the user being able to set the AllowedKeys property in code. (You could improve this by having the property appear in the Properties Window).

  The core of this class is the overridden OnKeyPress method. This checks for the chosen enumeration and then either allows or applies the blocking filter to the currently pressed key. This works in a very similar way to the individual KeyPress approach used in the earlier examples.

  By default, all keys will be allowed and to set the enumeration of your choice, you simply include code similar to the following somewhere appropriate in your form (I've used the Form Load event for my example):

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Me.RestrictedTextBox1.AllowedKeys = RestrictedTextBox.RestrictionCategory.NumeralsOnly

 

    End Sub

Summary
  I think that a combination or extension of any of the above approaches will enable you to control exactly what you will allow the user to input into a TextBox.

posted @ 9:28 AM | Feedback (0)

Friday, January 23, 2009 #

  I've been meaning to blog about the tools I use regularly and find useful and I've managed to get a couple written this month. I'm always a bit concerned that blogs like this might end up looking like some kind of infomercial. But I eventually came to the conclusion that I've always thought that personal recommendation was the best kind of recommendation, so if I like a tool I should tell the world. Whether the world sets any store by my opinion is a matter for the world to decide! J

   I have been using Camtasia Studio on and off for a couple of years now. When I first got hold of a copy, I needed it to create some demo videos. What I really liked about it was that, with no previous experience of screen recording, I was able to make the recording successfully after only a couple of tries. OK, so when I look back at that effort now, maybe "successfully" is an extravagant claim, but it was good enough for what I wanted to do at the time.

   Although there are still some (many!) features that I haven't yet got round to using, it has still been a really useful tool for me. When I first got it, I was more interested in grabbing the screen action and recording some audio narration. More recently, I needed to improve the narration side and so was looking for a way to record the voice independently and (often) edit parts of it without having to re-record the whole thing for the umpteenth time.

   Camtasia Studio comes with a subset of handy little apps, one of which is their Audio Editor. This has been really useful, as it enabled me to make those voice recordings and edit them easily. I also found myself doing a lot of "Inserting Silence" to replace the intakes of breath and the occasional unwanted clunk when I'm waving my hands around and hit the boom mike.

   The tutorials are excellent and there is a lively set of User forums.    FAQs are available to help you with those tricky things that should go right but somehow don't.

The work area is as easy to use as you could reasonably hope for with an application that seems to have so many options. As you can see from the screenshot, the timeline allows for more than one audio track.



Version 6 of Camtasia comes with even more presets to help you create the best screen size, resolution and (often important for web distribution or viewing) file size.

   I'm currently working on creating some courses, all of which contain video demos. As I get more used to using it, I am of course finding that I can get from starting idea to finished production much quicker. When I've finished my current project, as a long term plan, I hope to be able to create a set of short videos to be made available for free here on VBCity.

   All in all, I'm really pleased with it and many a time I've silently thanked the friend who first put me on to it. You can download a free 30 Day trial version from the TechSmith site here.

posted @ 9:28 AM | Feedback (0)

Sunday, January 18, 2009 #

  I now have several WPF books. I've written reviews on some of them and often mentioned them in answers to Forum questions from members who want to start out in WPF.

  When I wrote those articles and answers, my opinion was mostly based on my experience as a general reader who was interested in seeing what WPF had to offer and dabbling with it at a fairly elementary level. For the past three months though, I've been involved with WPF on a daily basis in a training capacity. During this time, it's become clear that I turn to some of the books much more regularly than others - and to some of them only when fairly desperate).

  With the very clear understanding up front that this is my personal experience, opinion and preference - which may be completely different from anyone else's - here is my view on the various books.

The book I first turn to is :
Pro WPF with VB 2008: Windows Presentation Foundation with .NET 3.5
by Matthew MacDonald.

  Comprehensive, detailed, clearly written, understandable code snippets, and downloadable code samples that work.

  It's almost impossible to fault this book.

  And of course, as the title says, the code behind samples are all in VB.NET - the only book I know of so far that is aimed specifically at VB developers.

  However, if you are a C# developer then that won't impress you much. For you, Apress and Matthew MacDonald have produced the equivalent C# title - Pro WPF with VB 2008: Windows Presentation Foundation with .NET 3.0. The key differences obviously being the absence of "VB.NET" in the title and the slightly earlier Framework.

  I do have this version too and find it very useful when I want to try and understand the C# approach to a concept that I understand in VB.

  The next book I reach out for is usually
Windows Presentation Foundation Unleashed
by Adam Nathan.

  The key value of this book is that it is packed with little hints, tips, Gotchas and information that will help you fight your way through the minefield that you encounter when first starting with WPF.

  One negative for me is that the code samples are in C#. There are however many useful XAML samples.

  The book is in full color. This makes it slightly more attractive, but isn't a key issue for me.

  Compared to most other WPF books, it appears to be relatively slim. In spite of this, I have still found it to be a gold mine of really useful WPF nuggets.

  Third in line is:
Programming WPF
by Chris Sells and Ian Griffiths.

  Another C# book, but again contains a lot of XAML. Ian Griffiths was one of the first writer/speaker/presenter/trainers on WPF in general and XAML in particular, so there are some useful insights to be had here.

  On the negative side, my impression is that the authors prefer to use code behind sometimes where I would have liked to see the XAML alternative.

  Make sure you get the Second Edition if you decide to go for this book. The First Edition is inevitably way out of date now.

  Surprisingly (to me anyway, considering I spent a lot of time converting the code in the next book from C# to VB) the Petzold book only comes fourth in my list of most used books. This is:
Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation
by Charles Petzold.

  If you want intricate details and almost philosophical insight into the structure of WPF, then this would be the book for you. As with all Petzold books, it painstakingly works its way through the minutiae of the subject.

  If I have a criticism, it would be that sometimes the sackcloth and ashes approach to developing (such as manually creating everything yourself, even if it is available for free) doesn't always work for me. I understand the purity of the approach, but this means that it can sometimes be difficult to just dip into this book for a quick answer to a particular question.

  However, if you really - really - want to get at the nuts and bolts of WPF, then you will probably not find one that has more detail.

  The fifth book in my WPF stable (for now, anyway!) is:
Professional WPF Programming: .NET Development with the Windows Presentation Foundation
by Chris Andrade and others.

  When there were very few other books available, this one had some benefits. The authors had each looked at specific areas and came up with some guidance. Some parts were clearly better than others, but overall it was an easier read than MSDN at the time, but basically rather shallow coverage.

  It rarely gets opened these days.

  I have the greatest respect for anyone who takes the time, trouble and effort to write or co-write a book. Knowing how long it takes me to write a short article or a training package, I wouldn't dream of criticising someone who will have spent many long, hard months writing (and no doubt re-writing) a book. So, as I said at the start, the opinions in this blog item only reflect my personal experience of using this collection of books; other readers with other backgrounds and author style preferences may well have different opinions.

  As WPF picks up speed and interest grows, I'm sure there will be many other WPF books published in the coming months. There are already several that were published at the end of 2008 that I haven't read.   No doubt the temptation to pick up another title or two will become irresistible as 2009 progresses and, if so, I will post up my thoughts on those too in the hope that it might help you decide the best way of spending your hard-earned cash.

posted @ 12:10 PM | Feedback (0)

  Some weeks ago I wrote a blog item about how to get the functionality of PerformClick in WPF.

  I remember thinking at the time that it was a fairly clunky workaround and wondered why PerformClick hadn't been included in WPF. Of course there may be lots of reasons, beginning with 'No-one thought of it' and ending with 'It was just too difficult'.

  But while looking at an apparently unrelated problem this week, it occurred to me that maybe there is a more elegant solution staring me in the face - WPF's Commands.

  In many cases where PerformClick is required, the situation is that whatever action is wired up to the Button's Click event is often also of interest to other controls - perhaps a menu item, a keyboard shortcut or a user instigated selection of some kind or other elsewhere.

  WPF Commands, if this is a new area for you, is a variation on the theme of Events. Whereas events are generally locked in to a specific action, a Command on the other hand is more along the lines of a loosely-coupled task that can be subscribed to from several places.

  Of course,you may be sat there thinking that it doesn't take a coding genius to create code for events that can be re-used and accessed from several different points. The point is though that Commands are specifically designed for this purpose and they have a feature that I think makes them almost unique. (Can you be 'almost' unique, the literary critic part of my brain asks? Never mind, you know what I mean!)

  Commands have the ability to turn themselves off in situations where it wouldn't be appropriate for them to be available - and back on again when it is. A common example is the Paste Command, where it doesn't make sense to have it available unless there is some data in the Clipboard.

  Similarly, you might build in a test where the Exit command isn't allowed to function unless the user has taken some action, such as saving data. There are lots of similar examples.

  WPF comes with a bundle of pre-built Commands for common tasks, including Copy, Cut, Paste, Find, Save, Close, Play, Zoom, Align and many more. Naturally, the designers of WPF don't know the exact actions you will want to link to each of the commands in every circumstance, so there is still some work for you to do. Commands are clever - but they're not magic!

  Apart from the built in Commands, and on the basis that there are never enough of these kind of things, you can create Commands of your own relatively easily. You can set the bindings and the rules that decide when it may be able to execute and when not (as per the Paste example earlier).

  All of which brings me back to my original thought. If you can create a Command that both a Button and (in the case of the Forum question that got me thinking about this) a Menu Item can bind to, then that really removes the need to worry about PerformClick. Command would be a neater and more sophisticated way of achieving the same effect.

  If you want more info on Commands, here is a useful link to an MSDN Introduction to Commands article.

posted @ 10:04 AM | Feedback (0)

Friday, December 12, 2008 #

  When it comes to text handling in WPF, you have a massive range of tools available, some of which can offer you a level of granularity that was previously unachievable. I plan to write about some of these text tools, such as Spans, Runs and Glyphs in future blogs, but for today I want to look at something a little less deep.

  Recently I needed to insert a carriage return in a block of text. The text was being created in the XAML markup, and I didn't particularly want to resort to code-behind for what is essentially a UI feature. So I had to do a bit of digging to find how I could create, for instance, this result:-

  

  where, if I don't override it, the two words would be placed one after the other on one line because there is more than enough width for them both.

  As you might expect, there are several ways of achieving this, but I think the easiest one is to use a LineBreak element. This snippet shows the key part of the code that creates and formats the Button's Content, which in this example is of course text only.

    <Button Margin="12,45" >

      <TextBlock> Click <LineBreak/> Me!</TextBlock>

    </Button>

   You might think it a bit strange when you see that XML type item stuck into the middle of a block of text, but this is perfectly valid syntax. Because the Content isn't a single block of text, it's necessary to use the TextBlock to house the text and the LineBreak instruction.

  Actually, what I really wanted to do wasn't quite as plain as the above screenshot. It was more along these lines:-

  

  The markup for this is still not particularly complex. It just requires more properties to be set specifically:

    <Button  Margin="12,45" Background="LightSkyBlue">
      <Button.Content>
        <TextBlock TextAlignment="Center" FontSize="16" FontFamily="Calibra"
                   FontWeight="Bold" Foreground="Navy" FontStyle="Italic" >
          Click <LineBreak/> Me!</TextBlock>
      </Button.Content>
    </Button>

  I did mention that there are several ways and these include using a StackPanel containing multiple TextBlocks or setting the Padding property on a single TextBlock. An alternative you might consider is to preserve the whitespace. This effectively means that however you enter the text layout in your XAML will be faithfully reproduced in the WPF display.

  The following markup:

    <Button  Margin="12,40">
      <TextBlock FontSize="14" VerticalAlignment="Center"
HorizontalAlignment="Center" xml:space="preserve">Click
Me!</TextBlock>
    </Button>

  will achieve a similar result:

  

  However, I really don't think the way the Markup sits in the XAML markup window is particularly neat. If you're interested in experimenting, try moving the word "Me!" so that it is more neatly aligned. Or Start the word "Click" on a new line of its own, maybe aligned with the markup above it.

  The results are unlikely to be what you wanted to achieve and this is because the preservation is absolute. A XAML twist on WYSIWYG!

  However, I included it here because the space preserve approach will be very useful to you in those cases where you want more than a single space between words or characters. Simply type out the text with the exact spacing that you require, set the XML Space attribute to Preserve and your requirements will be honored.

    <Button  Margin="12,40">
      <TextBlock FontSize="14" VerticalAlignment="Center"
          HorizontalAlignment="Center" 
          xml:space="preserve">Name:    Ged</TextBlock>
    </Button>

  

 

   So there are a couple of tips for dealing with text in WPF.  There's more to come!

posted @ 4:51 PM | Feedback (0)

Thursday, December 11, 2008 #

  For several years, whenever I've needed to include code snippets in my blogs and articles, I've used a neat little application that VBCity member HotDog wrote. This has stood me in good stead until the day that XAML arrived on the scene. Unfortunately, XAML kinda confuses HotDog's AutoFormatter tool and I end up with missing angle brackets.

  A bit of searching led me to a tool called CopySourceAsHTML. Although I had seen this used with Visual Studio 2005, there was a minor problem using it with 2008 for a while, but this was easily fixed with a minor tweak.

  Now though there is an official Visual Studio 2008 version. The extra good news about this is that it will also copy XAML to HTML.

  One thing to watch out for if you have used the tool before and usually right click on a code snippet to copy is as HTML:

  

     

 

   you will expect to see the Copy As HTML option in the menu.

  However, this doesn't appear in the context menu if you right click on the markup in the XAML Markup Pane. But it is available and all you have to do is go to the IDE main menu, select Edit and the Copy As HTML menu item will be available for you there.

  This great little utility is written (and updated) by Colin Coller of J T Leigh and Associates. You can download it from here.

posted @ 9:18 AM | Feedback (0)

  One of the things that often pulls me up in mid flow when I'm putting WPF UIs together in a Window is that some of the core properties seem to have disappeared. Now, some of these are so fundamental that I know they have to be there, but obviously hiding under a different name.

  Just recently, this happened to me twice in a row and I ended up scrolling through the Properties in the Object Browser, getting dizzy with the whizzing list of choices until I finally found them. All I was looking for was a very simple thing in both cases. I just wanted to apply some text to a control.

  The first one was the good old CheckBox. In Windows Forms this has a Text property, of course, and it's whatever textual information you want to place at the side of the little square check area. The WPF CheckBox will allow you to do a zillion clever things to change its look - add Borders, TextBlocks, Images, Panels, whatever you like - and at times I'm really happy about that. But as it happened this time I just wanted to plonk a couple of words of description at the side.

  You'll have gathered that there isn't a Text property for the WPF CheckBox and after a bit of a search, I did find its replacement - the Content property. If you assign plain text to this property, WPF will honor your choice. So this:

   17 <CheckBox x:Name="CheckBox1" Content="Click Here" />

 

  will do the job nicely.

  Subsequently I discovered that the Content property is in fact the default property of a CheckBox, so this too will work:

   17  <CheckBox x:Name="CheckBox1">"Click Here"</CheckBox>

 

  Of course if you do want something more exotic, then you still use the Content property - you just add whatever multiple layers of content you desire.

   The other control for which I had to search for the same kind of thing was the Expander. Again I only wanted a property that would just tack some plain text to the side of the Expander's little arrow.

  This time the answer is to use the Expander's Header property.

<Expander Header="See More Choices"></Expander>

 

You can assign plain text to this property, but again you have the option to, well, expand the Expander's Header to make it a little more graphically interesting.

 

     

  using this XAML:

      <Expander.Header>

          <Border BorderBrush="Navy" BorderThickness="2" CornerRadius="6">

            <TextBlock Margin="10,2,10,2"

            Foreground="Navy">What do you want to do?</TextBlock>

          </Border>

        </Expander.Header>

 

  Although Content is the default property, don't fall into the trap of trying to use it as:

   20  <Expander> Click to view choices </Expander>

  because that inserts the text as the first (and in this case, only) item in the area that you get to see when the Expander arrow is clicked. It will be the only item because Content isn't a container and you need to use something like a StackPanel if you have more than one child that you want to include in the expanded area.

  Something like this:

      <Expander.Content>

          <StackPanel >

            <CheckBox Margin="2,4" Content=" Do This"></CheckBox>

            <CheckBox Margin="2,4" Content=" Do That"></CheckBox>

            <CheckBox Margin="2,4" >

              <CheckBox.Content>

                <StackPanel Orientation="Horizontal">

                  <Image Width="16" Source="DCP_1997.jpg"></Image>

                  <TextBlock Text=" View Profile"></TextBlock>

                </StackPanel>

              </CheckBox.Content>

            </CheckBox>

            <TextBlock TextWrapping="Wrap" Margin="3,30,0,0"

          Text="Select a task from the list above." />

          </StackPanel>

        </Expander.Content>

 

  to produce this result:

 

  

posted @ 9:13 AM | Feedback (0)