Wednesday, December 24, 2008

This  is  written to explain Dependency Properties in general as they are available in .NET Framework 3.0 but specifically targeting Silverlight 2.0, in most places effort has been placed to  explain the differences in Dependency Properties in WPF and Silverlight (With Silverlight perspective). But all examples are for Silverlight.

What is Dependency Property?

Dependency properties are new addition to .NET Framework 3.0, Dependency properties are completely new implementation of properties, all programmers are familiar with properties but Dependency properties have a significant amount of added value. They use a sophisticated storage and support higher level features such as property value change notification, property validation, property coercion, property metadata and default property value inheritance. Dependency properties can hold an optional default value, and can propagate the default value down the element tree.

Simply put, a Dependency Property is like any other property but can hold a default value, with built in mechanism for property value validation and automatic notification for changes in property value ( for anyone listening to property value - especially UI)  and any binding in Silverlight is to binded to a Dependency Property.

Dependency Properties Features:

  • Root class must be inherited from UIElement or DependencyObject (Eg., Controls, User Controls, Windows (WPF), or any class derived from UIElement (Button, List, Slider etc.))
  • Dependency Properties have to be registered using DependencyProperty.Register method.
  • Since DependencyProperty.Register is a static (C#) / Shared (VB.NET) method, all dependency properties should also be static/Shared fields, also the property changed callback methods should be static/Shared methods.
  • Any property must be a dependency property if the property is being binded to.
  • By convention, all Dependency Property fields should include the word “Property” at the end of the property name (e.g., for property Value, the Dependency Property name should be ValueProperty).

Defining and Registering a Dependency Property:

While working with .NET 3.0/.NET 3.5 Framework (WPF, Silverlight) you’ll spend more time using Dependency Properties instead of defining them, most of the time you won’t even realise you are using a Dependency Property, but you should be able to create and register Dependency Properties as they are key in designing custom controls, or if you wish to add additional properties for binding, animation or other UI features that would otherwise be not supported.

The syntax of defining a Dependency Property is different from traditional Properties, however features mentioned above should always be remembered while defining Dependency Properties.

Defining Syntax:

VB.NET

Public Class FrameworkElement
    Inherits UIElement

    . . . 
    Public Shared ReadOnly ValueProperty As DependencyProperty

    . . . 

End Class

C#

public class FrameworkElement : UIElement
{

    . . . 

    public static readonly DependencyProperty ValueProperty; 

    . . . 

}

Defining a dependency property is only the first step, in order of the Dependency Property to be used, it should be registered, the registration process also initialises a DependencyObject. 

The Framework ensures that the DependencyProperty objects is not instantised directly since DependencyObjects don’t have public constructors. Hence DependencyObject can only be initialised by using static/Shared DependencyProperty.Register method.

The keyword “Readonly” in the above definition means that the property can only be registered in the static (C#) / Shared (VB.NET) constructor of the class and once registered in can’t be changed(registration) or re-register hence it is readonly, and it doesn’t make the Dependency Property readonly, only that it can’t be reregistered.

Registering Dependency Property

To register the dependency property defined above we have to put them in static/Shared Constructor since they are declared as readonly. 

VB.NET

Public Class FrameworkElement
    Inherits UIElement


    Public Shared ReadOnly ValueProperty As DependencyProperty

    Shared Sub New()
        ValueProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(FrameworkElement), Nothing)
    End Sub


End Class

C#

public class FrameworkElement : UIElement
{

    public static readonly DependencyProperty ValueProperty;

    static FrameworkElement()
    {
        
        ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(FrameworkElement), null);
    }

}

In Silverlight 2.0, static/Shared method of DependencyProperty.Register  takes 4 parameters, there are no overloads available as they are in WPF. 

  1. Name of the property to be registered
  2. Data type of the Dependency Property being registered
  3. Owner (Data type of parent) of the Dependency Property being registered (typically the root class). 
  4. Metadata of the Dependency Property (Optional by specifying null (C#)/Nothing (VB.NET))

Note: You can also define and register DependencyProperty at the same time but you can optionally exclude “readonly” from the DependencyProperty definition.

C#: public static DependencyProperty ValueProperty = DependencyProperty.Register(…);

VB.NET: Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register(…)

 

Reading and Writing Dependency Property values

Once a Dependency Property is defined and registered, you can’t use these properties as traditional .NET properties, Dependency Properties values should be set/get in a different way.

Getting Dependency Property Value

The beauty of Dependency Properties is that it can be used or called by any class or object where the Dependency Property parent class is available (specifically the default value) , even if you haven’t locally defined the parent class, for example you can get the default value down the tree of the parent or child of the class without first initialising the class. The only condition is that the parent class assembly should be made available.

The method to be used to get the value of a Dependency Property is GetValue()

VB.NET  

GetValue(FrameworkElement.ValueProperty)

C#

GetValue(FrameworkElement.ValueProperty); 

Setting Dependency Property Value

Similar to getting the property, setting the property value of a Dependency Property should be done by a method SetValue(), the property name should be with reference to the owner class along with the DependencyProperty.

VB.NET

SetValue(FrameworkElement.ValueProperty, 4)

C#

SetValue(FrameworkElement.ValueProperty, 4);

 

 

 

Property Wrapper

There is a limitation on the methods GetValue and SetValue, in order for a DependencyProperty to be set/get a value the class setting/getting should also be inherited from DependencyObject/UIElement, since GetValue and SetValue methods are inherited from DependencyObject class. In order to overcome this and the simplify the use of Dependency Properties and to make easy and transparent transition (in using) between Dependency Property and Attached Property, Property wrappers are used and are defined within the same class as the definition of Dependency Property.

VB.NET

Public Property Value() As Integer
    Get
        Return GetValue(ValueProperty)
    End Get
    Set(ByVal value As Integer)
        SetValue(ValueProperty, value)
    End Set
End Property

C#

public int Value
{
    get
    {
        return (int) GetValue(ValueProperty);
    }
    set
    {
        SetValue(ValueProperty, value);
    }
}

 

By using property wrappers now the transition between Dependency Properties and normal Properties is transparent, we can carry on using Dependency Properties like we use Properties, in property wrappers any other code like raising an event or validating values should not be included, the wrapper automatically ignores any other code within the wrapper and only looks for SetValue/GetValue methods.

Dependency Property Metadata 

In WPF Dependency Property Metadata is very complex and there are many overloads available but in Silverlight 2.0, Dependency Property Metadata only support two objects, with a maximum of three overloads

  1. Default Value
  2. Property Changed Callback
  3. Default Value, Property Changed Callback

VB.NET

Public Class FrameworkElement
    Inherits UIElement

    Public Shared ReadOnly ValueProperty As DependencyProperty

    Shared Sub New()

        ValueProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(FrameworkElement), _
                  New PropertyMetadata(0, New PropertyChangedCallback(AddressOf FrameworkElementValueChanged)))

    End Sub

    Shared Sub FrameworkElementValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    End Sub
End Class

C#

public class FrameworkElement : UIElement
{

    public static readonly DependencyProperty ValueProperty;

    static FrameworkElement()
    {
        
        ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(FrameworkElement), 
            new PropertyMetadata(0, new PropertyChangedCallback(FrameworkElementValueChanged)));
    }

    static void FrameworkElementValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {

    }

} 

In above mentioned examples, the ValueProperty will hold a default value of 0  and every time the value is changed (except for setting the default value), a property changed callback method is invoked (in this example, FrameworkElementValueChanged), the property changed callback should also be a static/Shared method/function, which take two parameters – DependencyObject and DependencyPropertyChangedEventArgs. (This callback method is not generated automatically so the parameters should be remembered for future references).

The DependencyPropertyChangedEventArgs, holds three properties

  1. NewValue
  2. OldValue
  3. Property

The NewValue is the requested value to be changed, the old value is the current value that will be replaced and the property is the identifier of the property requested to be changed, if property changed callback is defined the property value will be changed as soon as the callback method is exited, or in case of a function when a true value is return (to accept the new value) or false (to reject the new value and revert the value to old value).

VB.NET 

Shared Function FrameworkElementValueChanged(ByVal sender As DependencyObject, _
                                            ByVal e As DependencyPropertyChangedEventArgs) As Boolean
        Dim FwE As FrameworkElement = CType(sender, FrameworkElement)
        Dim NVal As Integer = CType(e.NewValue, Integer)
        Dim OVal As Integer = CType(e.OldValue, Integer)

        If (NVal < FwE.Minimum) Then
            Return False
        Else
            Return True
        End If

        If (NVal > FwE.Maximum) Then
            Return False
        Else
            Return True
        End If

 End Function

C#

static bool FrameworkElementValueChanged(DependencyObject sender, 
            DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement FwE = sender as FrameworkElement;
            int NVal = (int)e.NewValue;
            int OVal = (int)e.OldValue;

            if (NVal < FwE.Minimum) return false; else return true;

            if (NVal > FwE.Maximum) return false; else return true;

        }

Since the callbacks methods are also static, you’ll have to cast the sender as the parent class in order for you to use the other methods/properties within the class.

In the above example, if the requested value change is within the Maximum and Minimum value then the new value is accepted or else it is rejected (Note: The callback method being called is a boolean function), this is where you can do your additional updates to any objects depending on the property value or raise events. 

Property Coercion

In WPF, Dependency Property metadata also includes a separate method for property coercion, when a value change is requested, it first execute property coercion callback which can modify the supplied value with property coercion callback, and then property changed callback is called. In Silverlight, Dependency Property metadata is very simple, as mentioned above there is no property coercion callback available, but you can get around that and coerce property value within the property changed callback.

VB.NET

Shared Sub FrameworkElementValueChanged(ByVal sender As DependencyObject, _
                                        ByVal e As DependencyPropertyChangedEventArgs)
    Dim FwE As FrameworkElement = CType(sender, FrameworkElement)
    Dim NVal As Integer = CType(e.NewValue, Integer)
    Dim OVal As Integer = CType(e.OldValue, Integer)

    If NVal < FwE.Minimum Then
        FwE.Value = FwE.Minimum
    End If

    If NVal > FwE.Maximum Then
        FwE.Value = FwE.Maximum
    End If

End Sub

C#

static void FrameworkElementValueChanged(DependencyObject sender, 
    DependencyPropertyChangedEventArgs e)
{
    FrameworkElement FwE = sender as FrameworkElement;
    int NVal = (int)e.NewValue;
    int OVal = (int)e.OldValue;

    if(NVal < FwE.Minimum)
    {
        FwE.Value = FwE.Minimum;
    }

    if(NVal > FwE.Maximum)
    {
        FwE.Value = FwE.Maximum;
    }

}

The above property changed callback is a method instead of a function, where if the requested value change falls beyond the acceptable value it will coerce the value to an acceptable value, in this example if a valid value change is requested and if it exits the method without any change then the value will be accepted.

Limitations of Dependency Property in Silverlight 2.0

Silverlight 2.0 have a very limited control and usage with Dependency Properties, and their existence in Silverlight is only to enable any value to be able to bind to the property rather than provide anything significant. There are many limitation on user defined Dependency properties in Silverlight that its almost unusable in normal circumstances, but in case of user defined custom controls it becomes a compulsion to use dependency properties since binding can only be done to a dependency property and a well built custom control should support binding.

The problem with user defined Dependency Property in Silverlight 2.0 is with the ability to be bind with itself. It can bind to a Dependency Property of another class (if that class is explicitly mentioned as data context). This inability of Dependency Properties renders the use of Dependency Properties in Silverlight to almost useless. Normally (in WPF) you can use a Dependency Property just like a Attached Property, even binding is done with relative easy and user will remain transparent to anything that goes beyond, and by default the context is its own class so there is no problem with binding with itself, but in Silverlight 2.0, Dependency Properties behave differently and should only be used in exceptional circumstances (designing custom controls) and understanding the limitations.

If you are considering using a Dependency Property only to notify changes to property value to UI then you can  use interface System.ComponentModel.INotifyPropertyChanged and use normal Properties instead of Dependency Properties, for example.

VB.NET

Imports System.ComponentModel
Public Class FrameworkElement Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler _ 
Implements INotifyPropertyChanged.PropertyChanged Private _Value As Integer Public Property Value() As Integer Get Return _Value End Get Set(ByVal value As Integer) If _Value <> value Then _Value = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Value")) End If End Set End Property End Class

C#

using System.ComponentModel;
namespace Elements
{
   public class FrameworkElement : INotifyPropertyChanged
   {

       public event PropertyChangedEventHandler PropertyChanged;

       private int _value;

       public int Value
       {
           get
           {
               return _value;
           }
           set
           {
               if (_value != value)
               {
                   _value = value;
                   PropertyChanged(this, new PropertyChangedEventArgs("Value"));
               }
           }
       }
   }
}

Note: Since in Silverlight an object can’t be binded to any Dependency Property within its own class, its fair to assume that all Dependency Properties in Silverlight are Public.

Microsoft hasn’t explained the limitation on usage of Dependency Properties in Silverlight 2.0, as far as I understand it has to do with the cross platform framework of Silverlight 2.0. But  hopefully Silverlight 3.0 will support advanced Dependency Property options, at least those that we already expect from Silverlight 2.0.

More on Dependency Properties and  Binding, how and why they both are related to each other in next topic to follow - Understanding: Binding in Silverlight.

Friday, October 31, 2008

Introduction

Creating custom controls is not as difficult as they actually appear, but before we go creating custom controls lets first look at the difference between Custom Controls (CC) and Custom User-Controls (CU).

Simply put Custom Controls (CC) are skinable, themable and reusable controls that once created can be used by simply loading the assembly in any project, where are Custom User-Controls are user controls that can be reused but they can't be skinned or themed. Technically they are both difference Custom Controls inherits from (System.Windows.Controls) Controls whereas User Controls inherits from (System.Windows.Controls.UserControl) UserControl. All controls that are used in Silverlight (eg., Button, TextBlock, TextBox) and UserControl is also a Control.

Lets just start by creating a custom control and we can discuss the technicalities where they arise. I'll do this in both C# and VB.NET.

Note: This was my first custom control I built on Silverlight 2 about couple of weeks ago, and I wrote this blog last week as well. I wanted to first introduce concepts like binding and dependency properties before I publish this, but since Microsoft have released Silverlight Toolkit and included NumericUpDown, this is purely academic now. But do enjoy and learn how to create Custom Controls. But the basic concepts are still going to follow.

Developing a Custom Control

In this example we'll create a custom control for NumericUpDown. Its a very simple control that can be easily built, I'll be using Microsoft Visual Studio 2008 and Microsoft Expression Blend 2 (SP1), we don't really need Expression Blend but sometimes it saves time using Expression Blend.

First, we'll set up and prepare to built a custom control and to test it.

1. Setup:

In Visual Studio Start a new Project with Silverlight Application Template and name it (I named it Silverlight Control Library), then select ASP.NET Web Application Project for testing.

2008-10-25_141920 2008-10-25_141938

Now the project for testing is ready and we need a new Silverlight Class Library Template project within the solution, so add new project and Name it (I named it Controls), this is where we'll be building our custom controls, by default you'll have a Class1.vb or Class1.cs file in the project delete the file, and add a new folder and rename it "themes" (The name of this folder is important, so keep it like it is). 

2008-10-25_142351 2008-10-25_142446 2008-10-25_142514

Now add an empty xaml file in that folder and name it "generic.xaml" (Again the name is important), add the following XAML tags in the the xaml file, this is the basic shell we'll always need in building custom controls.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
    <Style>
        
    </Style>
</ResourceDictionary>

It is very important that you remember to make the generic.xaml build action as Resource, this is to make sure that the template is packed in the same assembly as the control otherwise the template wont be available.

2008-10-25_210935

Add another folder and name it "NumericUpDown", this is where we'll be building our custom control.

2008-10-25_142528 2008-10-25_142541 2008-10-25_142558

2: Preparing

Now that the setup is complete we can start building the control but first we need to focus and gather facts about the custom control.

  1. Is a Numeric Only TextBox with 2 buttons for increasing and decreasing the value.
  2. Will have a Minimum and Maximum, along with an actual Value.
  3. TextBox should only take numeric input along with Up/Down buttons
  4. Optionally control can be made to use only Up/Down instead of directly entering the value in the TextBox.
  5. When the value is changed, the control will raise an event to notify anyone listening, with its own EventArgs.
  6. The Control should allow access and Binding on basic properties like IsEnabled, Foreground, Font Size etc.

3: Building

First, we'll add a Class file in "NumericUpDown" folder and name it "NumericBox", this will be the name of the control, but like I explained before all controls should inherit from Control, second you'll have to tell the compiler where default style for the control is, in order to do this we'll set the DefaultStyleKey value, so in constructor.

C#

public class NumericBox: Control
   {
       public NumericBox()
       {
           DefaultStyleKey = typeof(NumericBox);
       }
   }

VB.NET

Public Class NumericBox
    Inherits Control

    Public Sub New()
        DefaultStyleKey = GetType(NumericBox)
    End Sub

End Class

Now the constructor will look for default style in generic.xaml (under themes folder) for any style referencing to current assembly. 

Now we'll expose some properties externally for anyone to bind, you should be aware that any external property you want to bind to the control should be a DependencyProperty, any property (Attached Property / Dependency Property) can be binded but the property that it is binding to should be a DependencyProperty, so to keep this control bindable we'll have to expose Dependency Properties, If you have problem following this, leave a message and I'll try to explain DependencyProperty in detail.

Now, we already know that we need to expose three properties Minimum, Maximum and Value, in order for us to able to check if the values entered are valid we'll check them every time the any property is changed by using PropertyChangedCallback. (We'll implement Property Callbacks later)

C#

#region Dependency Properties
       public int Minimum
       {
           get { return (int)GetValue(MinimumProperty); }
           set { SetValue(MinimumProperty, value); }
       }

       public static readonly DependencyProperty MinimumProperty =
           DependencyProperty.Register("Minimum", typeof(int), typeof(NumericBox), 
           new PropertyMetadata(0, new PropertyChangedCallback(MinimumChanged)));

       private static void MinimumChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
       {

       }

       public int Maximum
       {
           get { return (int)GetValue(MaximumProperty); }
           set { SetValue(MaximumProperty, value); }
       }

       public static readonly DependencyProperty MaximumProperty =
           DependencyProperty.Register("Maximum", typeof(int), typeof(NumericBox), 
           new PropertyMetadata(10, new PropertyChangedCallback(MaximumChanged)));

       private static void MaximumChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
       {

       }

       public int Value
       {
           get { return (int)GetValue(ValueProperty); }
           set { SetValue(ValueProperty, value); }
       }

       public static readonly DependencyProperty ValueProperty =
           DependencyProperty.Register("Value", typeof(int), typeof(NumericBox),
           new PropertyMetadata(0, new PropertyChangedCallback(ValueChanged)));

       private static void ValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
       {

       }

#endregion

VB.NET

#Region "Dependency Properties"

    Public Property Minimum()
        Get
            Return GetValue(MinimumProperty)
        End Get
        Set(ByVal value)
            SetValue(MinimumProperty, value)
        End Set
    End Property

    Public Shared MinimumProperty As DependencyProperty = _
    DependencyProperty.Register("Minimum", GetType(Integer), GetType(NumericBox), _
                            New PropertyMetadata(0, New PropertyChangedCallback(AddressOf MinimumValueChanged)))

    Private Shared Sub MinimumValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    End Sub

    Public Property Maximum()
        Get
            Return GetValue(MaximumProperty)
        End Get
        Set(ByVal value)
            SetValue(MaximumProperty, value)
        End Set
    End Property

    Public Shared MaximumProperty As DependencyProperty = _
    DependencyProperty.Register("Maximum", GetType(Integer), GetType(NumericBox), _
                                New PropertyMetadata(10, New PropertyChangedCallback(AddressOf MaximumValueChanged)))

    Private Shared Sub MaximumValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    End Sub

    Public Property Value()
        Get
            Return GetValue(ValueProperty)
        End Get
        Set(ByVal value)
            SetValue(ValueProperty, value)
        End Set
    End Property

    Public Shared ValueProperty As DependencyProperty = _
    DependencyProperty.Register("Value", GetType(Integer), GetType(NumericBox), _
                                New PropertyMetadata(0, New PropertyChangedCallback(AddressOf ValueChanged)))

    Private Shared Sub ValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)


    End Sub

#End Region

Now we have three Dependency Properties exposed, we are setting the default values here but its a good practice to also set them in Style, so we'll go to generic.xaml (after compiling the project) and add a reference to current assembly and then add them to the style.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    xmlns:local="clr-namespace:Controls.NumericUpDown;assembly=Controls.NumericUpDown">
    
        <Style TargetType="local:NumericBox">
            <Setter Property="Minimum"  Value="0"/>
            <Setter Property="Maximum" Value="10"/>
            <Setter Property="Value" Value="0"/>
        </Style>
</ResourceDictionary>
    

Now that you understand how to expose properties, we'll first finish the job with Dependency Properties by validating the values.

C#

private static void MinimumChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    NumericBox NB = sender as NumericBox;

    int val = (int)e.NewValue;

    if (NB == null)
        return;

    if (val > NB.Maximum)
        NB.Minimum = (int)e.OldValue;

    if (NB.Value < val)
        NB.Value = val;
}

private static void MaximumChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    NumericBox NB = sender as NumericBox;

    int val = (int)e.NewValue;

    if (NB == null)
        return;

    if (val < NB.Minimum)
        NB.Maximum = (int)e.OldValue;
}

private static void ValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    NumericBox NB = sender as NumericBox;
    int val = (int)e.NewValue;
    if (NB == null)
        return;

    if (val < NB.Minimum)
    {
        NB.Value = NB.Minimum;
        NB._ValueChanged = false;
        return;
    }

    if (val > NB.Maximum)
    {
        NB.Value = NB.Maximum;
        NB._ValueChanged = false;
        return;
    }
    NB._ValueChanged = true;    
}

VB.NET

Private Shared Sub MinimumValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    Dim NB As NumericBox = CType(sender, NumericBox)
    Dim Val As Integer = CType(e.NewValue, Integer)

    If NB Is Nothing Then Return

    If Val >= NB.Maximum Then NB.Minimum = CType(e.OldValue, Integer)

    If Val > NB.Value Then NB.Value = Val

End Sub

Private Shared Sub MaximumValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    Dim NB As NumericBox = CType(sender, NumericBox)
    Dim Val As Integer = CType(e.NewValue, Integer)

    If NB Is Nothing Then Return

    If Val <= NB.Minimum Then NB.Maximum = CType(e.OldValue, Integer)

    If Val < NB.Value Then NB.Value = Val

End Sub

Private Shared Sub ValueChanged(ByVal sender As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)

    Dim NB As NumericBox = CType(sender, NumericBox)
    Dim Val As Integer = CType(e.NewValue, Integer)

    If NB Is Nothing Then Return

    If Val < NB.Minimum Then
        NB.Value = NB.Minimum
        NB._ValueUpdated = False
        Return
    End If

    If Val > NB.Maximum Then
        NB.Value = NB.Maximum
        NB._ValueUpdated = False
        Return
    End If

    NB._ValueUpdated = True
End Sub

Now that basic logic is in place we'll finish with the basic structure, for this we need to define template in generic.xaml to provide the building blocks of the control (i.e., the look and feel of the control).

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    xmlns:local="clr-namespace:Controls.NumericUpDown;assembly=Controls.NumericUpDown">
    
        <Style TargetType="local:NumericBox">
            <Setter Property="Minimum"  Value="0"/>
            <Setter Property="Maximum" Value="10"/>
            <Setter Property="Value" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                  <ControlTemplate TargetType="local:NumericBox">
                       <Grid>
                          <Grid.ColumnDefinitions>
                              <ColumnDefinition Width="3*"/>
                               <ColumnDefinition Width="*"/>
                           </Grid.ColumnDefinitions>
                         <TextBox x:Name="NumericTextBox"  Grid.Column="0" Grid.ColumnSpan="2" IsTabStop="True"
                                  IsEnabled="{TemplateBinding IsEnabled}" 
                                  Foreground="{TemplateBinding Foreground}"
                                  Background="{TemplateBinding Background}"
                                  Visibility="{TemplateBinding Visibility}"
                                  Text="{TemplateBinding Value}"
                                  />
                         <Grid Grid.Column="1">
                             <Grid.RowDefinitions>
                                 <RowDefinition/>
                                 <RowDefinition/>
                             </Grid.RowDefinitions>
                             <Button x:Name="ValUp" Grid.Row="0" Margin="0,1,1,0" IsTabStop="False"
                                  IsEnabled="{TemplateBinding IsEnabled}"
                                  Background="{TemplateBinding Background}"
                                  Visibility="{TemplateBinding Visibility}"
                                  >
                                 <Path Fill="{TemplateBinding Foreground}" 
                                       Data="F1 M 4.81721,-3.05176e-005L 9.63441,8.3436L 2.49481e-006,8.3436L 4.81721,-3.05176e-005 Z "/>
                             </Button>
                             <Button x:Name="ValDown" Grid.Row="1" Margin="0,0,1,1" IsTabStop="False"
                                     IsEnabled="{TemplateBinding IsEnabled}"
                                     Background="{TemplateBinding Background}"
                                     Visibility="{TemplateBinding Visibility}"
                                     >
                                 <Path Fill="{TemplateBinding Foreground}" 
                                       Data="F1 M 4.81721,8.34363L 9.63441,0L 2.49481e-006,0L 4.81721,8.34363 Z "/>
                             </Button>
                         </Grid>
                     </Grid>
                    </ControlTemplate>
             </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
   

Note that we have binded all external properties to both the TextBox or Buttons, and pay attention that the Buttons and the TextBox have names, we use these names in our control class to access these objects to access events associated with them, if for some reason the names in the control templates change (user defined template/Skinned template), the whole control will fall apart, and there is no simple way to deal with it (we can deal with this by accessing the root element and checking its children, but that is a different story)

Now we have to access four events, TextBox.TextChanged, TextBox.KeyDown, ButtonUp.Click and ButtonDown.Click to do this we'll overload/override "OnApplyTemplate" Method, it has to be noted that you can't access the template from constructor, even though we set the DefaultKeyStyle in constructor so the only way to safely access the objects and capture events is to do it when the template is applied, you can get a child from the template using "GetTemplateChild" method.

C#

private TextBox TBxNum;
private Button ButUp;
private Button ButDw;

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    TBxNum = base.GetTemplateChild("NumericTextBox") as TextBox;
    ButUp = base.GetTemplateChild("ValUp") as Button;
    ButDw = base.GetTemplateChild("ValDown") as Button;

    if (TBxNum == null)
        return;

    if (ButUp == null)
        return;

    if (ButDw == null)
        return;

    TBxNum.TextChanged += new TextChangedEventHandler(TBxNum_TextChanged);
    TBxNum.KeyDown += new KeyEventHandler(TBxNum_KeyDown);
    ButUp.Click += new RoutedEventHandler(ButUp_Click);
    ButDw.Click += new RoutedEventHandler(ButDw_Click);
}

VB.NET

Private TBxNum As TextBox
Private ButUp As Button
Private ButDw As Button


Public Overloads Overrides Sub OnApplyTemplate()
    MyBase.OnApplyTemplate()

    TBxNum = CType(MyBase.GetTemplateChild("NumericTextBox"), TextBox)
    ButUp = CType(MyBase.GetTemplateChild("ValUp"), Button)
    ButDw = CType(MyBase.GetTemplateChild("ValDown"), Button)

    If TBxNum Is Nothing Then Return

    If ButUp Is Nothing Then Return

    If ButDw Is Nothing Then Return

    AddHandler TBxNum.TextChanged, AddressOf TBxNum_TextChanged
    AddHandler TBxNum.KeyDown, AddressOf TBxNum_KeyDown
    AddHandler ButUp.Click, AddressOf ButUp_Click
    AddHandler ButDw.Click, AddressOf ButDw_Click
End Sub

We need to capture Button.Click events to Up/Down the Value, but we are defining TextBox.KeyDown event to restrict the entry of non-numeric keys in the text box, and we'll update Value when the Value is typed in the TextBox, thats why we are capturing the TextBox.TextChanged Event, although we are already restricting the keyboard entry I still prefer to check if the Value entered in the TextBox is a number, and we can update the Value in one procedure.

C#

void TBxNum_TextChanged(object sender, TextChangedEventArgs e)
{
    if (Single.IsNaN(System.Convert.ToSingle(TBxNum.Text)))
        throw new NotFiniteNumberException(TBxNum.Text);
    else
       UpdateValue((int)System.Convert.ToInt64(TBxNum.Text));
}

void TBxNum_KeyDown(object sender, KeyEventArgs e)
{
    if (((e.Key >= Key.D0 && e.Key <= Key.D9) || (e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9) || e.Key == Key.Back))
        e.Handled = false;
    else
    {
        e.Handled = true;
    }
}

void ButUp_Click(object sender, RoutedEventArgs e)
{
    UpdateValue(Value + 1);
}

void ButDw_Click(object sender, RoutedEventArgs e)
{
    UpdateValue(Value - 1);
}

void UpdateValue(int val)
{
    _ValueChanged  = false;
    Value=int;
    if(_ValueChanged) 
    {
        TBxNum.Text = Value.ToString();
    }
}

VB.NET

Private Sub TBxNum_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
    If (Single.IsNaN(System.Convert.ToSingle(TBxNum.Text))) Then
        Throw New NotFiniteNumberException(TBxNum.Text)
    Else
        UpdateValue(CType(TBxNum.Text, Integer))
    End If
End Sub

Private Sub TBxNum_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    If ((e.Key >= Key.D0 And e.Key <= Key.D9) OrElse (e.Key >= Key.NumPad0 And e.Key <= Key.NumPad9) _
        OrElse e.Key = Key.Back) Then e.Handled = False Else e.Handled = True
End Sub

Private Sub ButUp_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    UpdateValue(Value + 1)
End Sub

Private Sub ButDw_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    UpdateValue(Value - 1)
End Sub

Private Sub UpdateValue(ByVal val As Integer)
    _ValueUpdated = False
    Value = val
    If _ValueUpdated Then
        TBxNum.Text = val
    End If
End Sub

Now the control is up and ready for testing(don't forget to add reference to this assembly), you can make any changes to the source code as you see it fit, the only thing left is to raise an event when the value changes in case anyone is listening. For this we'll build a custom EventArgs to pass the changed value.

So add another file in the folder and name it "NumericBoxChangedArgs.cs" or "NumericBoxChangedArgs.vb"  as shown below.

2008-10-26_092756

Now, the NumericBoxChangedArgs class inheirts EventArgs and have a readonly property and a constructor to set the Value changed, it also have a delegate event handler with NumbericBoxChangedArgs signature.

C#

 

public delegate NumericBoxChangedHandler(object sender, NumericBoxChangedArgs e);

public class NumericBoxChangedArgs : EventArgs
{
    private readonly int _Value;

    public NumericBoxChangedArgs(int val)
    {
        _Value = val;
    }

    public int Value
    {
        get
        {
            return _Value;
        }
    }
}

VB.NET

Namespace NumericUpDown
    Public Delegate Sub NumericBoxChangedHandler(ByVal sender As Object, ByVal e As NumericBoxChangedArgs)

    Public Class NumericBoxChangedArgs
        Inherits EventArgs

        Private ReadOnly _val As Integer

        Public Sub New(ByVal val As Integer)
            _val = val
        End Sub

        Public ReadOnly Property Value() As Integer
            Get
                Return _val
            End Get
        End Property

    End Class
End Namespace

Now we just declare the delegate locally and raise event in it in UpdateValue procedure.

C#

public event NumericBoxChangedHandler NumericBoxChanged;

void UpdateValue(int val)
{
    _ValueChanged  = false;
    Value=int;
    if(_ValueChanged) 
    {
        TBxNum.Text = Value.ToString();
        NumericBoxChangedArgs NArgs = new NumericBoxChangedArgs(Value);
        NumericBoxChanged(this, NArgs);
    }
}

VB.NET

Public Event NumericBoxChanged As NumericBoxChangedHandler

Private Sub UpdateValue(ByVal val As Integer)
    _ValueUpdated = False
    Value = val
    If _ValueUpdated Then
        TBxNum.Text = Value
        Dim NArgs As New NumericBoxChangedArgs(Value)
        RaiseEvent NumericBoxChanged(Me, NArgs)
    End If
End Sub

All the points mentioned while preparing the control are now achieved (I have added couple of more Dependency Properties to the source code (IncrementStep, IncrementOnly). Our Custom Numeric Up/Down Box is now complete, you can download the source code for this custom control below, if you make any modifications to the source code, please let me know.

Licence

Creative Commons License
Silverlight NumericUpDown Control by Imran Shaik is licensed under a Creative Commons Attribution-Non-Commercial-Share Alike 2.0 UK: England & Wales License.
Based on a work at www.geekswithblogs.net.

Download Source Code/Binary

Version 1.0 (Both VB.NET & C#)

Sunday, October 26, 2008

There are many tutorials in Silverlight over the Internet that mostly cover the basics of Silverlight but when you start getting a bit ahead you get into trouble and the real lack of tutorials or examples when you get to Intermediate level and beyond there is much to be desired especially when it comes in examples on MSDN (specifically VB.NET), so I am starting a new series which I hope will cover from basic to advanced level, and I might try to go in as much detail as possible, basically I am finding it hard to make the transition from WPF to Silverlight and this blog series will be my way of keeping notes.

I might not get it right the first time or I might miss a few things so you are always welcome to correct and rectify me if I make any mistakes. The series isn't arranged in any order I'll just blog them as they leap in my mind, I might not blog some concepts but you are always welcome to post requests.

In near future I hope to post atleast the following (the order may change)

  1. Understanding: Silverlight Binding
  2. Understanding: Silverlight Dependency Properties
  3. Understanding: Silverlight User Controls
  4. Understanding: Silverlight Custom Controls
  5. Understanding: Dynamic Silverlight
  6. Understanding: Dynamic Silverlight XAML
  7. Understanding: Silverlight Templates

In addition, I'll also bring you examples in Developing series which use the concepts in Understanding series and develop real-world applications both in C# and VB.NET, and in this series you might see

  1. Developing: Custom User Controls
  2. Developing: Custom Controls
  3. Developing: Dynamic Custom Controls
  4. Developing: Commercial Applications

Have some patience and stay tuned.

Friday, October 24, 2008

As promised I'm going to show you how to animate custom controls using Opacity masks, if you are unfamiliar with Opacity masks, please check my previous blog entry: Working with Opacity masks

In this tutorial we'll create a custom button, animate it for mouse events and then still keep it flexible to them it

The Problem

Custom controls are notorious, especially when it comes to applying themes, it is very simple to create a custom button and animate it but it all falls apart when you try to keep it flexible to apply your themes, for example you can create a button apply animations on mouse enter and pressed states but then you can't keep your colour for external binding.

Solution:

Instead of applying animation to the colour itself you can apply animation to the opacity masks and keep the theme alive for external colour binding, here is how we'll go about it using Expression Blend .

I am sorry for the audio but its pretty simple to follow.

Silverlight Streaming (http://silverlight.live.com) is not working so please download the video

Note: At 2:20 I copied and pasted another Rectangle on back of the the main rectangle and coloured it black to avoid the transparency of opacity mask, it is a good practice in order to avoid making the button (or control in general) transparent, don't forget to experiment the background by using gradient brushes published before (Tip: With this Opacity Mask try the Large set of gradients for good effect).

Download

Download Source code (ThemedButton.Zip 34KB)

Wednesday, October 22, 2008

Opacity Masks are a great addition to Silverlight even thought it was there from the beginning, they are not as much helpful as otherwise they might be due to the lack of brushes in Silverlight (compared to WPF). And the addition of Alpha along with RGB in colour definition, opacity masks don't really gain much attention as they deserve. Although the use of Opacity Masks in Silverlight is very limited it is nonetheless a less important features, especially in animations and applying both vertical and horizontal gradients or both linier and radial gradient to the same object let us from start with looking at what opacity masks are and then how opacity masks are helpful in applying both vertical and horizontal gradients to the same object.

As you might already know that you can only attach one single colour schema (Either Solid colour or gradients) to an object, so we'll first begin with looking at what opacity masks are.

Opacity Masks.

First we'll look at what opacity masks do on a solid colour, and then we'll go in detail with complex gradients.

 2008-10-22_192437

Image 1: Rectangle with Solid colour

<Rectangle Fill="#FF0B1BC6" Stroke="#FF000000" RadiusX="8" RadiusY="8"/>

Now I have an object with solid colour (Rectangle with Colour FF0B1BC6 in this case) and lets say that we want to apply some transparency to the the object either at the beginning or at the end, so since Silverlight have Alpha channel in its colour definition along with RGB definition its simple to use it, Since we are dealing with a solid colour here its easy to just create a liner gradient with alpha reduced e.g.,

2008-10-22_192457

Image 2: Rectangle with Linear gradient with opacity

<Rectangle RadiusX="8" RadiusY="8" Stroke="#FF000000">
    <Rectangle.Fill>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="#7F0B1BC6"/>
            <GradientStop Color="#FF0B1BC6" Offset="1"/>
        </LinearGradientBrush>
    </Rectangle.Fill>
</Rectangle>

Notice the Colour between two gradient stops they are the same colour (0B0BC6) the only difference is the Alpha (7F and FF), at offset 0 (beginning of the gradient) we have 50% opacity and at the offset 1 (end of the gradient) we have 100% opacity.

It works but we can also achieve he same by applying opacity mask to the rectangle in Image 1,

2008-10-22_193332

Image 3: Rectangle with opacity mask

<Rectangle Fill="#FF0B1BC6" Stroke="#FF000000" RadiusX="8" RadiusY="8">
    <Rectangle.OpacityMask>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="#7F000000" Offset="0"/>
            <GradientStop Color="#FFFFFFFF" Offset="1"/>
        </LinearGradientBrush>
    </Rectangle.OpacityMask>
</Rectangle>

Notice Image 2 and Image 3, the difference isn't much, infact technically there isn't any difference because both of them are one and the same, the only difference is applying the opacity mask to a solid colour have achieved the same results as actually applying a gradient.

Uses of Opacity Mask

You might ask well if we can achieve the same results with both gradient and applying opacity mask then why use opacity mask, well the advantages of opacity masks are very clear, consider this you can only apply one gradient to an object but with the addition of opacity mask you can achieve both at the same time.

Here is an example,

Consider the following gradient

2008-10-22_194405 

Image 4: Rectangle with liner gradient from left to right

Now consider that we want darken the rectangle on the top and leave the bottom the same, given that we can only apply one gradient for a single object, so to achieve this we'll have to put the another rectangle on top of that with a black gradient running from top to bottom, and  giving the top about 50% opacity and the bottom 0% opacity.

Instead we can use opacity masks to achieve this,

2008-10-22_195548

Image 5: Rectangle gradient (left to right) with opacity mask (top to bottom)

 

<Rectangle RadiusX="8" RadiusY="8" Stroke="#FF000000">
            <Rectangle.OpacityMask>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="#7E000000" Offset="0.4"/>
                    <GradientStop Color="#FF000000" Offset="1"/>
                    <GradientStop Color="#B2000000" Offset="0.5"/>
                </LinearGradientBrush>
            </Rectangle.OpacityMask>
            <Rectangle.Fill>
                <LinearGradientBrush EndPoint="1.,0.5" StartPoint="0,0.5" >
                    <GradientStop Color="#FFC60B0B" Offset="0"/>
                    <GradientStop Color="#FFC60B0B" Offset="1"/>
                    <GradientStop Color="#FFAAB30C" Offset="0.175"/>
                    <GradientStop Color="#FE3B8C41" Offset="0.31"/>
                    <GradientStop Color="#FE2E6C6F" Offset="0.48"/>
                    <GradientStop Color="#FE21258D" Offset="0.70"/>
                    <GradientStop Color="#FE9E19A1" Offset="0.85"/>
                </LinearGradientBrush>
            </Rectangle.Fill>
        </Rectangle>

This trick is usually used in making the glass effects with gradients, and it is limited to dark backgrounds since in reality the opacity mask is actually making the object transparent than actually making it darker or lighter, which brings us to another usage of opacity masks.

Partially transparent objects, captures the imagination, and you can achieve them using opacity masks, for example consider the following image, its the same rectangle in Image 5 over other rectangles.

2008-10-22_200437

Objects using Opacity Masks

Opacity masks are not limited to objects rectangles or paths or gradients, opacity masks can also be applied to various objects to give partial transparency, including and not limited to images and vides infact you can apply opacity masks to all UI elements and you can apply any brush (Graident brush/image brush/Video Brush etc.) as a base for opacity mask.

Opacity Masks in Silverlight 2 are limited since other brushes (those available in WPF) are not available in Silverlight, but probably probably they might be avialble in future versions of Silverlight.

Opacity Masks and animation and themes

One of the areas where I usually use opacity masks are in themes and animation, I'll soon post a tutorial on using opacity masks in animation/ themes and in creating custom controls and templates, watch this space.

Tuesday, October 21, 2008

This is my second lot of Gradients they are few more but that is for some other day, here are about 14 new gradients for you, I need some patience and inspiration to make few so I might come back with more.

2008-10-21_165944 

XAML:

    <LinearGradientBrush x:Key="CyanThemeBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFC6ECFE"/>
        <GradientStop Color="#FF003FFF" Offset="1"/>
        <GradientStop Color="#FF37AFF9" Offset="0.5"/>
        <GradientStop Color="#FF0076DB" Offset="0.5"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="MagentaBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFFFCFFB" Offset="0"/>
        <GradientStop Color="#FF930E2F" Offset="1"/>
        <GradientStop Color="#FFFF659D" Offset="0.5"/>
        <GradientStop Color="#FFC7174B" Offset="0.5"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="BlackBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFE5E7FA"/>
        <GradientStop Color="#FF747C8D" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF0F0F0F" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="LightBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFE5E7FA"/>
        <GradientStop Color="#FFE3E6F5" Offset="0.5"/>
        <GradientStop Color="#FFD5DBF1" Offset="0.5"/>
        <GradientStop Color="#FFE1E4FF" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="TealGreen" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFE3FCA9" Offset="0"/>
        <GradientStop Color="#FF82B03C" Offset="1"/>
        <GradientStop Color="#FFD8FA6C" Offset="0.5"/>
        <GradientStop Color="#FFE0FF98" Offset="0.5"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="CyanLargeBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF003FFF" Offset="1"/>
        <GradientStop Color="#FF37AFF9" Offset="0"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="MagentaLargeBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF930E2F" Offset="1"/>
        <GradientStop Color="#FFFF659D" Offset="0"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="LightLargeBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFE3E6F5" Offset="0"/>
        <GradientStop Color="#FF9D9D9D" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="GreenLargeBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FF82B03C" Offset="1"/>
        <GradientStop Color="#FFD8FA6C" Offset="0"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="PurpleBG" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFB0B8FF"/>
        <GradientStop Color="#FF747C8D" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF151733" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="DarkGreen" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFB0FFC4"/>
        <GradientStop Color="#FF5D645F" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF15331F" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="DarkYellow" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFF3FFB0"/>
        <GradientStop Color="#FF63645D" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF2F3315" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="DarkGrape" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFFFB0FD"/>
        <GradientStop Color="#FF8D7485" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF331532" Offset="1"/>
    </LinearGradientBrush>
    <LinearGradientBrush x:Key="DarkBrown" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="#FFFFEAB0"/>
        <GradientStop Color="#FF8D8774" Offset="0.5"/>
        <GradientStop Color="#FF000000" Offset="0.5"/>
        <GradientStop Color="#FF332A15" Offset="1"/>
    </LinearGradientBrush>

 

Usage Syntax:

Its very simple just use above lines in App.xaml under <Application.Resources> and then you can use them any where in your project

<Rectangle ... Fill="{StaticResource DarkBrown}"/>

Download:

XAML Resource (0.5 KB) Download ZIP

Friday, October 17, 2008

I can just say that I'm happy to say good bye to Blend 2.5, it was really a pain to use to different Blend versions (2.0 and 2.5 as I mostly work on WPF) and since already bought the Expression Studio I was wondering if I had to buy Blend 2.5 just for Silverlight but now I am happy.

I might bring new tutorials and now that we have a stable version it will be better I was just waiting for the final and stable release so as to kick start developing commercial Silverlight applications.

I must add that since Silverlight 2 Beta 1 was released and my dissatisfaction was confirmed I really lost heart on Silverlight but full credit to Microsoft on updating Silverlight versions to its final release its more or less what I wanted, but I still have some unfortunate dissatisfaction with the final release, mostly the same issues as I had in Silverlight 2 Beta 1 (Keyboard entry in fullscreen mode, Sync calls to web services, Routed Events, Brushes) but things have changed a bit and there is always scope for improvement, I think I just have to hold them back till Silverlight 3 more likely to be 2010.

At any rate I was testing Binding with Dependency Property today and was completely surprised at the lack of support in Silverlight, I'll post my findings soon, plus there are few sample that I wrote on Silverlight 2 Beta 1 that I haven't posted, which I'll do soon.

Anyway, I haven't tested the full extent of Silverlight 2 and Blend, but now I'm onto it. Like I mentioned earlier since some of my previous projects I have undertaken I have also learned C#, so that is another arsenal I have now but I don't know what you guys want when I started with Silverlight on my previous blog I was the only one to provide Silverlight samples on VB.NET, so if you prefer VB.NET instead of C# then please contact me. I must add that I'll still be using VB.NET (VB Literals) in examples for handling XML data in Silverlight, and some other techniques/ideas I have under my sleeve (mostly writing dynamic XAML using VB Literals).

Tuesday, October 14, 2008

Well, finally Silverlight 2.0 is released, all these months (years?) of waiting and the day has finally arrived.

Emm... Where was I?

Busy as usual, but more to the point Silverlight 2 Beta didn't live upto my expectations and there were changing things up and down, right and left that I had to give myself a break... in the mean time I also learned C# so most of my work now will be on C# and VB.NET.

Expect regular blogs from now.

Sunday, March 30, 2008

In our previous tutorial we have seen how we can use XML axis properties to consume XML returned from a web service in Silverlight applications, in this example however we will look at how XML Literals and Embedded Expressions can be used to easily write fast and efficient Web Services.

*Note XML Literals and Embedded Expression are Visual Basic 9 Exclusive features you might need Visual Studio 2008 and .NET Framework 3.5 installed.

XML Literals and Embedded Expression are not limited to Silverlight/WPF you can use it where ever you want as long as you are using Visual Basic 9 with Visual Studio 2008.

XML Literals

So, what is XML Literals? XML literals is a nice and clean way to write XML in Visual Basic, to use XML Literals "option infer" should be set "on", before we look at XML Literals, a little about literals, what are literals, for any VB.NET programmer the following code should be very familiar.

Dim Product = "Visual Basic"
Dim Version = 9
Dim SubVersion = 9.0
Dim ThisTime = #3/30/2008 3:02:00 PM#
Dim Tomarrow = #3/31/2008#

Here, I am not declaring the type of the variable, but literals allow VB to understand that Product is a string,  version is integer, subversion is double, Thistime is DataTime and Tomarrow is Date, the values that I have provided are called literals, now it should be easy to understand when I say VB 9 supports XML Literals. So instead of using quotes and escape quotes to construct XML we can simple use XML literals and just start writing XML, the following is absolutely legitimate.

Dim Products = _
<VisualStudio Version="2008">
    <Products>
        <Language Name="Visual Basic" Version="9"/>
        <Language Name="C#" Version="3"/>
    </Products>
</VisualStudio>

Yes, no more escape quote to construct XML, just start writing it there and then.

Embedded Expressions

Now, you might argue that we don't use XML inside the programs, we just need a clean way to manipulate XML not define them in our programs, but I never said we are finished with it, there is more.

In our previous example we used XDocuments/XElement/XAttribute to write XML in our web service, which can also be used to write XML in C#

This is what we had,

<WebMethod()> _
   Public Function AllCountries() As String

       Dim sud As New CountryDataSetTableAdapters.CountryTableTableAdapter

       Dim XDataSet As New CountryDataSet.CountryTableDataTable

       sud.Fill(XDataSet)

       Dim MyNameSpace As XNamespace = "http://tempuri.org/Schema/Countries/"

       Dim XDoc As XDocument = New XDocument(New XDeclaration("1.0", "utf-8", "yes"), _
                                             New XElement(MyNameSpace + "Countries", _
                                                       From country In XDataSet _
                                                       Select New XElement("Country", _
                                                                            New XAttribute("Code", country.CountryISO), _
                                                                           New XAttribute("Name", country.CountryName))))

       Return XDoc.ToString
   End Function

But with Embedded Expressions and XML Literals we can make it even easier to write XML, but first what is embedded expressions, we have seen what XML literals is before and it is not very practical because we don't use XML in our program, but embedded expressions extend the user of XML Literals, embedded expressions are used to embedded values where required using <%= %> syntax.

So the previous WebMethod can be re-written using XML Literals and Embedded Expressions, but first let us find out what we are trying to write, this is the format of the XML we want to construct.

<Countries xmlns="http://tempuri.org/Schema/Countries">
    <Country Code="" Name=""/>
</Countries>

So we can replace our previous Web Service using XML Literals and embedded expression using something like,

    <WebMethod()> _
    Public Function AllCountriesUsingXMLLiterals() As String

        Dim sud As New CountryDataSetTableAdapters.CountryTableTableAdapter

        Dim XDataSet As New CountryDataSet.CountryTableDataTable

        sud.Fill(XDataSet)

        Dim XDoc = _
            <Countries xmlns="http://tempuri.org/Schema/Countries">
                <%= From country In XDataSet Select <Country Code=<%= country.CountryISO %> Name=<%= country.CountryName %>/> %>
            </Countries>

        Return XDoc.ToString
    End Function

Thats it! Working with XML have never been so easy, and it can't get easier than this. Enjoy the good features of VB 9. More examples coming soon.

Tuesday, March 25, 2008

There might be a few samples of working with web services in Silverlight but I promise I'll keep you interested with VB.NET exclusive feature VB Literals. This is going to open a whole new way of looking and working with XML and Web Services. 

*Note: Don't attempt to do everything I do in this tutorial in C# (espcially consuming WebService in Silverlight 2) because you simply can't, C# or any other language doesn't have this feature, so stick to VB.NET for consuming you can use C# for the rest, I'll warn when I am using VB.NET exclusive features, so if you want to convert this to C# you'll get enough warning.

In this tutorial we'll look at

  • Creating SQL Server 2005 Database
  • Retrieving Data from Database in DataSet
  • Consuming DataSet in ASMX WebService (using LINQ)
  • Consuming WebService in Silverlight 2 (using VB Literals)

Before you get started with this make sure you have installed

  • Visual Studio 2008
  • SQL Express 2005
  • Silverlight 2 Beta 1 Tools for Visual Studio

Before anything start with a Silverlight project, and make sure you select Web Solution for hosting, instead of HTML Test Page.

2008-03-25_014305 2008-03-25_014320

Step 1 : Creating SQL Server 2005 Database

Now we'll create a SQL Server and add data in the database, for this example I have used countries example, so first go to the web project, select the "App_Data" folder and right-click and select "Add New Item..."

2008-03-25_014611

Now Visual Studio will ask you the item you want, select "SQL Server Database", give a name to the database and then select select "Add",

2008-03-25_014637

This will add a new database to the Web Application, go to server explorer and select, and in Tables, select "Add New Table"

 2008-03-25_014702

Now define the table, I have inserted only two columns for this example

2008-03-25_014759 2008-03-25_014838

Now I have manually inserted rows in the table,

2008-03-25_015552 

Step 2: Retrieving data from Database to DataSet.

Now go to the web site project and add new item,

2008-03-25_015649

When the window pops up select "DataSet", select the file name I have used "CountryDataSet.xsd", Visual Studio will warn you that DataSet should go in "App_Code" Folder, select "Yes".

2008-03-25_015709 2008-03-25_015725

Now you'll have an empty DataSet in the solution.

2008-03-25_015742

Normally developers do this the "Old School" way, by setting the connections to the database, this way it will automatically make the connection string and generate datasets.

Now the trick to get data in Dataset is the most simplest way to do it.

Drag the table you want to add to DataSet space, if you have more than one table don't worry just drag all the tables you want in that dataset, if there are connections it will automatically set them all.

2008-03-25_015835

By default, the DataSet makes Fill,GetData table adapters, if you want to insert or update the tables you have to write new queries on the Table Adaptors.

Now your data is ready to be consumed by the webservice.

Step 3: Consuming DataSet in ASMX web service (Using LINQ)

Again go to the Web Site Solution and select "Add New Item..." this time select "Web Service".

2008-03-25_015913 

By default, the application makes Hello World example, delete that and add a new function.

But first, set the reference of the web service to "System.XML.Linq", Normally we use web services to communicate in XML format, but since Silverlight doesn't support "System.Data" it is not possible to return DataSet in XML format directly (if you attempt to return a DataSet directly Silverlight will throw errors when a reference is added) so we have to create our own XML String, so and we are going to use LINQ (Language Integrated Query) which is available in .NET Framework 3.0/3.5.

<WebMethod()> _
    Public Function AllCountries() As String

        Dim sud As New CountryDataSetTableAdapters.CountryTableTableAdapter

        Dim XDataSet As New CountryDataSet.CountryTableDataTable

        sud.Fill(XDataSet)

        Dim MyNameSpace As XNamespace = "http://tempuri.org/Schema/Countries/"

        Dim XDoc As XDocument = New XDocument(New XDeclaration("1.0", "utf-8", "yes"), _
                                              New XElement(MyNameSpace + "Countries", _
                                                        From country In XDataSet _
                                                        Select New XElement("Country", _
                                                                            New XAttribute("Code", country.CountryISO), _
                                                                            New XAttribute("Name", country.CountryName))))

        Return XDoc.ToString
    End Function

The Namespace is not required but it would be required to use VB literals when we are consuming this in Silverlight application.

Step 4: Consuming Web Service in Silverlight 2 (Using VB Literals)

Now we are finished with working on the web site project, and we will turn our focus on the Silverlight Project.

First we'll add the "Web Reference"

2008-03-25_022423

Since the Web Services is in the same solution, you can just click "Discover" and it will fetch the web service, give a namespace to the web service to easily recognise within the application.

2008-03-25_022517 2008-03-25_022536

In Silverlight projects System.Xml.Linq is not automatically selected so you have to explicitly include it, right click on the Silverlight Project and then Select "Add Reference", and add reference to System.Xml.Linq, even after you have added reference you still have to import it in the managed code (VB imports System.Xml.Linq/ include System.Xml.Linq)

2008-03-25_022642

Now we have to call the web service from Managed code.

Imports System.Xml.Linq

Partial Public Class Page
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub

    Private src As New CountryWebService.WebServiceSoapClient
    Private XMLData As New XDocument

    Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        src.AllCountriesAsync()
        AddHandler src.AllCountriesCompleted, AddressOf DownloadCompleted
    End Sub

    Private Sub DownloadCompleted(ByVal sender As Object, ByVal e As CountryWebService.AllCountriesCompletedEventArgs)

        XMLData = XDocument.Parse(e.Result)

    End Sub
End Class

Now you can set a break point on XMLData = XDocument.Parse(e.Result)  and test the XML we are getting back.

2008-03-25_023106

As we have XML Data available, normally we create a class object, initialise properties are pick each attribute of the XMLData and populate its property, this is the only way to do in C# but for VB.NET there is very simple way. 

*WARNING: VB Exclusive feature below*

In VB.NET we have an exclusive feature, called VB Literals, VB literals is a new feature in .NET framework 3.0, which makes life easy infact very easy, this is an extension of type infer available since early VB days, to use this option "Option Infer" should be set "On".

You can google for "VB Literals" for more info, I won't go in details here so I'll just go directly to the point.

Type infer means that we don't need to set the type while variable declaration, and this is an essential for VB Literals to work hence not included in C# or any other .NET languages.

But for this example, since we are working on XML and to enable Intellisense we would require to get the schema of the XML we would be working on, this is not essential but without it there won't be any Intellisense.

So first we'll get the schema for the XML we got back, to do this we'll set the breakpoint again and get the XML data from XMLData XDocument we are getting back from the webservice.

2008-03-25_023205

Next we'll add a schema file by "Add New Item..." in the Silverlight project, there is a Template "XML to Schema" for this available on MSDN. The Name of the schema doesn't matter here, because we won't be looking at it ever, Visual Studio will do its own magic.

2008-03-25_023255

Now since we already have the data copied in the clipboard, we'll select Add As XML and paste the copied XML to generate Schema.

2008-03-25_023307 2008-03-25_023326

Now we have to import the schema by Adding the reference, Visual Studio will automatically recognises the namespace available in the project, all we have to do is provide the alias. 

2008-03-25_023400   2008-03-25_023415

Now we are ready to do use VB Literals, simply declare a variable without any type declaration (Type Infer)

2008-03-25_023606

Visual Studio recognises the the data we are dealing with and started to help, without creating schema and importing we can still use VB Literals but there won't be any intellisense.

2008-03-25_023621

Visual Studio recognises the Elements but don't know where you want to start digging the XML so its showing "?" But once you select the starting Element, it will know where you are and what you are dealing with so now it is showing the "Right Mark", it is its way of saying you are definitely dealing with this. the options are "@" for attribute, "<>" for any Child Element (NOTE: If you are using Element, then you should get its value e.g., i.<ElementName>.Value)

2008-03-25_023726

After you finish writing your query, something wonderful happens, if you add only one single element the Type Inference selects the data as IEnumerable(Of String) but if you add more than one to the collection it will automatically change to IEnumerable(Of <anonymous type>)

2008-03-25_024200

2008-03-25_024214

To consume this data in Silverlight application you can use them directly.

In Page.xaml

<Grid x:Name="LayoutRoot" Background="White">
    <ListBox x:Name="CountryNames"/>
</Grid>

In Page.xaml.vb

CountryNames.ItemsSource = CountriesData

And when you run the application.

2008-03-25_024023

So the complete code looks something like,

2008-03-25_044631

More on VB Literals and the wonders we can do in Silverlight coming soon.