in

vbCity Blogs

New (temp) place for vbCity Blogs

Ged Mead's Blog

July 2009 - Posts

  • How To Format a Numeric Value to your Local Currency

      Here's a useful little tip for if you have a numeric value and you need to convert it into your local currency.

      Let's assume you take in a value as a Double and you want the output to be in the format of currency, with two decimal places (in the case of the UK to represent Pence).

      Here's the Double: 

    Code Copy
         Dim TotalCost As Double = 123.4567

     

      To show this formatted, you can use the String.Format method, a placeholder for the variable and the 'c' attribute to ensure the currency format is used:

     Code Copy

           Label1.Text = String.Format("Total Cost = {0:c}", TotalCost)

     

      The result would look like this:

      

      Notice that it automatically rounds up for you, changing '4567' to '46'.

  • Using ElementHost in VS 2010

     

      I'm a big fan of WPF and WinForms Interop, so have often advised people to try the ElementHost when they want to do something graphically complex on a Windows Form. This useful little container allows you to create a WPF UserControl right there inside the WinForms project and then display those WPF graphics in a Windows Form.

      One of the negative things about this in VS 2008 is that you always get a kind of error message inside the ElementHost at Design Time which reads "Unable to generate a preview". It's not really an error, but it is negative feedback.

    Although this message is disconcerting at first, you soon get used to it, and the WPF UserControl will display nicely once you actually run the project.

      I was hoping that VS 2010 would fix this issue and that you would be able to see the WPF content in the ElementHost right there on the Window at Design time. The good news is that you can. The bad news is that the pseudo-error has been replaced with a genuine error!

       The error message is: "Type 'WindowsFormsIntegration.Design.HostedComponent' is not available in the target framework". This message appears whether you choose Version 3.5 or 4 of the .NET Framework for the project. I was particularly surprised that I still got the error with 3.5 apps. You wouldn't have thought that a problem with v4 would be a breaking change in the earlier Framework, but this seems to be the case.

      Anyway, I did some searching and found the fix documented in the Microsoft .NET Framework 4 Beta 1 Readme. It requires an additional Reference which is tucked away in the Visual Studio 2010 sub-folder in the Program Files folder. The exact path in my installation is:

       C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\WindowsFormsIntegration.Design.dll

      You have to use the 'Browse' Tab instead of the more usual .NET tab of course to find and add this dll. You may also see a further warning message regarding the use of this dll with earlier versions of the Framework. This indicates that if you have your Framework set to 3.5 you don't need this dll (because it is a Framework 4 requirement). However, I couldn't get ElementHost to work properly in either 3.5 or 4 mode before I added this Reference, so that all left me a little confused - but happy, because it now works as it should.

      Having sorted that out, I can now see the WPF UserControl on the Windows Form at Design Time, as you can see from the screenshot below. This is much more useful because it is often necessary to tweak the layout of the UserControl or other settings to make it fit in with the layout of other Windows Forms controls on the Form. Now that I can see it at Design time, I don't have to keep running and re-running the project to see the effect of changes.

      I hope that this requirement to add the reference will be fixed by the time the final version of VS 2010 ships, but in the meantime I'll not complain about the small amount of work needed to gain the additional benefit.

      

    Don't forget that you should already have the standard:

    • PresentationCore
    • PresentationFramework
    • WindowsBase
    • WindowsFormsIntegration

    References in place for this kind of Interop task.

    Afternote:  Having learnt about this dll, I did also go to the Visual Studio 9 folder, found the VS2008 equivalent and added  it as a reference to my VS2008 projects.  It didn't fix the 'Unable to generate a preview' message though.

     

  • Sorting Arrays

    Sorting a single array is a simple task. You just run the Shared Sort method on the array: 

            Dim arCustomers() As String = {"Williams", "Brown", "Smith", "Jones", "Green"}

            Array.Sort(arCustomers)

            For Each Str As String In arCustomers

                Console.WriteLine(Str)

            Next 

    The resulting output is:

    Brown
    Green
    Jones
    Smith
    Williams

    Things get more complicated if you have two arrays that have a logical link - maybe one array contains a list of names and a second array contains a numeric value for each of those names: 

        Dim arNames() As String = {"Smith", "Jones", "White", "Mead", "Knobbe", "Baranovsky", "Bonner"}

        Dim arIDNumber() As Integer = {234, 123, 765, 999, 357, 543, 666} 

    If you want to sort the Names array so that the name with the lowest IDNumber is first and the name with the highest IDNumber is last, then you can use an overloaded version of the Sort method: 

            '  Sort the array of Names based on their IDNumbers

            Array.Sort(arIDNumber, arNames) 

    Note that you must assign the Key (the IDNumbers array) before the Item (the Names array) to get the correct result, which is:

    Jones
    Smith
    Knobbe
    Baranovsky
    Bonner
    White
    Mead

    Jones has an IDNumber of 123 and Mead has an IDNumber of 999.

    Of course, the sort doesn't have to have a numeric key. It is just as easy to sort the IDNumber values based on the alphabetical order of the Names, if this is needed. 

            Array.Sort(arNames, arIDNumber) 

    One potential Gotcha that isn't immediately obvious is that this Sort action will also sort the IDNumber array at the same time. It makes complete sense for this to happen when you think about it, but if for some reason you only wanted the re-sorted arrays to be temporary, you will have take a slightly different approach.

    One of the easiest ways is to create a clone of the original arrays and you can sort the cloned copies while leaving the original in their default order. 

            Dim arNames2() As String = arNames.Clone()

            Dim arScores2() As Integer = arIDNumber.Clone()

            Array.Sort(arScores2, arNames2) 

    You will then have your original arrays left unchanged and the index sorted versions available for use.

  • WPF: How To Animate a Gradient Brush

     

    In the previous blog item in this series, I created a simple Animation in Visual Basic Code. This used a DoubleAnimation to animate the thickness of the gradient border. That example doesn't even begin to scratch the surface of what you can achieve with WPF Animation and - as you have seen demonstrated in the previous items - how you can include these animations in Windows Forms.

    In this part, we will look at a slightly more complex animation. Or at least one that seems complex at first, but a quick analysis reveals that it is really rather simple and easily created by writing the XAML by hand.

    In order to see the animation effect, I recommend you download the project attached to this blog item and run it. When you click the Button if there is no text in the TextBox, two animations will run. The first is the resize border one that was covered in the previous blog. The second animation changes the position of each of the colors in the gradient to give the impression of movement.

    If you are new to WPF, you perhaps won't be too comfortable with the idea of hand-writing the XAML. You can of course use a tool, such as Expression Blend, to create the XAML for you. A time-limited trial version of Expression Blend is available, so if you decide that the hand-written route isn't for you, then you can certainly use Blend instead. (That said, there is something of a learning curve involved in getting to grips with Blend too - especially if, like me, you have more of a Developer mindset than Designer).

    One of the reasons I decided to add this part to the blog was that sometimes you need to start the animation running from some user action. In this case, the user action will be the clicking of a button on a totally unrelated UI - a Windows Form, as it happens (although of course the same approach works fine with an all-WPF application too.)

    So, firstly, here is the XAML that goes in the WPF UserControl to create the moving gradient effect: 

    (Note that there is a formatting problem with the blog editor.  The GradientStop isn't really a Devil's Head it is the numeral 6 in square square brackets - [ 6 ], but without the spaces.  )

        <Storyboard x:Key="MoveColors" >

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

             AutoReverse="True" 

             Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStopsDevil.Offset"

                    BeginTime="0:0:0.6" />

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

                  Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[5].Offset"

                    BeginTime="0:0:0.6" />

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

                 Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[4].Offset"

                    BeginTime="0:0:0.6" />

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

             Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[3].Offset"

                    BeginTime="0:0:0.6" />

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

                 Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[2].Offset"

                    BeginTime="0:0:0.6" />

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

                Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[1].Offset"

                    BeginTime="0:0:0.6" />  

     

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

                           AutoReverse="True"

                 Storyboard.TargetName="GradBorder"

                   Storyboard.TargetProperty="BorderBrush.GradientStops[0].Offset"

                    BeginTime="0:0:0.6" />

     

        </Storyboard> 

    That may look like a lot of typing, but there's a whole lot of repetition in there, which means you can copy and paste the majority of it, then just make minor tweaks. 

     <Storyboard x:Key="MoveColors" > 

    The first line shown above simply creates a new Storyboard instance and assigns it a name, or Key, that we can use to reference it.

    The next block creates a DoubleAnimation.  

          <DoubleAnimation By="0.2" Duration="0:0:1.3"

             AutoReverse="True" 

             Storyboard.TargetName="GradBorder"

             Storyboard.TargetProperty="BorderBrush.GradientStopsDevil.Offset"

             BeginTime="0:0:0.6" /> 

    Most of us tend not to think of Types such as Double numeric values when we are thinking about animation. In our mind's eye we are more likely to picture the physical result of such a change - the size of an element or its position, its shape or its color, perhaps. But it is perfectly possible to change the value of a Double Type, taking a set amount of time to make the change, and then having this change be visible to a user by assigning it to a property of an element that takes type Double.

    That's exactly what we are doing here - changing the value of the Double that is being used to set the position of a GradientStop in a LinearGradientBrush. Here's a more detailed breakdown of what's going on:

    By changes the Double value by the amount stated - in this case by 0.2. In our demonstration animation this moves one of the colors that make up the gradient 0.2 along the gradient's linear scale.

    Duration sets the length of time taken to change the value from its starting value to the end of the By value. In this case it is 1.3 seconds.

    AutoReverse set to True will ensure that the value returns to its starting value by the end of the Duration. In our gradient example this will cause the color to move 0.2 out and then return the same 0.2 distance back.

    Storyboard.TargetName uses the WPF Attached Property syntax to identify which element is the target of the animation.

    Storyboard.TargetProperty in a similar way identifies the property of the target element which is to be animated. See the further explanation below about the identification of the property.

    BeginTime is an optional pause feature. The animation will only begin after an initial pause of (in this example) 0.6 seconds.

    The TargetProperty in each of these DoubleAnimations is the Offset value of a GradientStop. If you look at the XAML which creates the BrightGradient LinearGradientBrush, you will see that it contains seven colors, each of which has its position in the gradient assigned by its Offset value. All we are doing in this animation is shunting each of the colors along by a value of 0.2 and this creates the effect of the color movement.

    The particular block of XAML shown above animates the last GradientStop, which has an index of 6 - the index being zero based. When you look at the six DoubleAnimations which follow that one, the only difference is that the Index has changed so that the next GradientStop is targeted. So by copying and pasting the first DoubleAnimation, pasting it six times and then changing the value of the Index, this complex looking animation is shown to be very basic in reality.

    All that remains now is to wire up this Storyboard in the code-behind.  

        Public Sub MoveColors()

            Dim SB As Storyboard = CType(FindResource("MoveColors"), Storyboard)

            If SB IsNot Nothing Then

                SB.Begin()

            Else

                MessageBox.Show("Can't find storyboard")

            End If

        End Sub 

    The FindResource method is very useful in these kind of situations in WPF. I used it in the original example to change the LinearGradientBrush on the Border element and now we can employ it to find the animation. It allows you to dig into the XAML, find the markup you need for a particular task and use it. In this cas, the Resource we are looking for is the Storyboard that has a Key of 'MoveColors' - the one we have just dissected above.

    Over in the Windows Forms Form1 code, the Button click has only one additional line from where we left it at the end of the previous blog. This calls the MoveColors procedure shown above, which in turn runs the animation. The additional line is: 

               MovingGradientTextBox1.MoveColors() 

    You can see the remaining unchanged Button Click event code in the sample project attached.

  • How To Animate a TextBox in Windows Forms

    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.

  • WPF and WinForms Interop: A Highlighted TextBox

    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.

Copyright 1998-2009 vbCity.com LLC
Powered by Community Server (Non-Commercial Edition), by Telligent Systems