Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for Silverlight

February 25, 2011

Silverlight: Wish List II

Filed under: Silverlight @ 8:04 pm

Just in time for the Academy Awards, this post sounds more like a movie title (similar to Death Wish III) than a post asking for new features. But here it is, my "wish list" for features in Silverlight Version "Next".

1) Setting the default file name and location in the print dialog.

a) This item requires that we have access to a system constant (similar to Environment.SpecialFolder). If necessarily, this could be limited to only the MyDocument folder.

2) Ability to select landscape or portrait printing.

3) Improved printing support for styling XAML elements.

a) My thought is to have some type of IsForPrinting flag on most UI elements. If this flag is set to true, the element does not appear except when printed. If this flag is set to false, the element does not appear when printing. If not set, it appears for both display and printing. This would allow developers to define one XAML page layout that would work for both display and printing.

b) A nicety here would also allow for setting the Visual Studio designer so you can see the "display view" or the "print view" so you can WYSIWYG both the display and print view of each page.

4) Expanded built in commanding support so that the InvokeCommandAction is not required.

a) See this reference for more information.

5) Fix to the themes to prevent the chart legend from going missing.

a) See this reference for more information.

6) Keep the expand/collapse of regions in the .xaml files.

a) This would be especially useful in the theme and style files. I spent a while and closed every style element in my theme and style files so I could easily scroll through to find the one style that I needed to edit. To my chagrin, the next time I opened the file the nodes were all opened.

7) Provide some type of refresh button or gesture to refresh the designer.

a) Frequently, changes to the theme or style file were not immediately reflected in the designer. So you either have to make an error in the xaml so it will allow you to fix the error and regenerate the designer UI, or you have to wait for a compile to cause the designer UI to update.

Any other suggestions?

Enjoy!

February 19, 2011

Silverlight Charting: Displaying Data Above the Column

Filed under: Silverlight @ 8:24 pm

When displaying a column chart, you may want to display the column value above the column in the chart. Something like this:

image

This prior post sets the ground work for displaying a chart. This post covers how to modify the chart to display text above each column.

Like most other changes to the chart columns, the key is the DataPointStyle. This time, however, you need to change the Template property.

<toolkit:ColumnSeries DependentValuePath="Project1Score"
                        IndependentValuePath="StudentName"
                        ItemsSource="{Binding StudentList}"
                        Title="{Binding TitleList[0]}">
    <toolkit:ColumnSeries.DataPointStyle>
        <Style TargetType="toolkit:ColumnDataPoint">
            <Setter Property="Background">
                <Setter.Value>
                    <RadialGradientBrush GradientOrigin="-0.1,-0.1"
                                            Center="0.075,0.015"
                                            RadiusX="1.05"
                                            RadiusY="0.9">
                        <GradientStop Color="#FFFDE79C" />
                        <GradientStop Color="#FFF6BC0C"
                                        Offset="1" />
                    </RadialGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="DependentValueStringFormat"
                    Value="{}{0:N0}%" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate
                        TargetType="toolkit:ColumnDataPoint">
                        <Grid Background=
                              "{TemplateBinding Background}">
                            <TextBlock Text=
                     "{TemplateBinding FormattedDependentValue}"
                                        FontWeight="Bold"
                                        Margin="0,-15,0,0"
                                        HorizontalAlignment="Stretch"
                                        TextAlignment="Center" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            </Style>
    </toolkit:ColumnSeries.DataPointStyle>
</toolkit:ColumnSeries>

The most important change is the Margin on the TextBlock. Setting the top margin to a value of –15 will display the text above the column in the chart.

Enjoy!

Silverlight Charting: Setting the Tooltip

Filed under: Silverlight @ 6:04 pm

By default, Silverlight charts display a tooltip when the user hovers over the chart but it is not very fancy. Here is an example:

image

So it says 97. Is that a count? Number of points? Percent?

This prior post sets the ground work for displaying a chart. This post covers how to modify the tooltip to display more meaningful information.

The key to modifying the tooltip is to set the DataPointStyle. Setting the DependentValueStringFormat property allows you to format the tooltip as desired.

<toolkit:ColumnSeries.DataPointStyle>
    <Style TargetType="toolkit:ColumnDataPoint">
        <Setter Property="DependentValueStringFormat"
                Value="{}{0:N0}%" />
        </Style>
</toolkit:ColumnSeries.DataPointStyle>

Some of the more common formatting strings:

  • {}{0:C}
    • Currency
  • {}{0:N1}%
    • Numeric value, formatted to 1 decimal place and with a % sign
  • {}{0:p2}
    • Percent value, formatted to 2 decimal places
  • {}{0:MM/yy}
    • Month and year (2/11)
  • {}{0:MM/dd}
    • Month and day (2/19)
  • {}{0:MMM}
    • Month short name (Feb)
  • {}Some other text
    • Any desired text

The problem with setting the DataProintStyle, however, is that you lose all of the automatic color selection for your chart. Your chart will turn orange as shown below.

image

So you then have to manually set your colors. Setting the colors for the chart is demonstrated in this prior post.

Combining the color and tooltip formatting is as follows:

    <toolkit:ColumnSeries.DataPointStyle>
        <Style TargetType="toolkit:ColumnDataPoint">
            <Setter Property="Background">
                <Setter.Value>
                    <RadialGradientBrush GradientOrigin="-0.1,-0.1"
                                            Center="0.075,0.015"
                                            RadiusX="1.05"
                                            RadiusY="0.9">
                        <GradientStop Color="#FFFDE79C" />
                        <GradientStop Color="#FFF6BC0C"
                                        Offset="1" />
                    </RadialGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="DependentValueStringFormat"
                    Value="{}{0:N0}%" />
            </Style>
    </toolkit:ColumnSeries.DataPointStyle>

The final result is shown below.

image

Use this technique any time you want to format your tooltips in a chart.

Enjoy!

Silverlight Charting: Setting Colors

Filed under: Silverlight @ 4:47 pm

When working with charts, by default the built-in styling sets the chart colors. But there may be times that you want to control the colors. This post covers a technique for manually setting the chart colors.

This prior post sets the ground work for displaying a chart. This current post adds color definition to the chart from that prior post.

The key to setting the color for a bar in the chart is to set the DataPointStyle. Setting the background color of the data point sets the bar color. You can use any brush, including a RadialGradientBrush as shown in the example below.

    <toolkit:Chart Name="chart1"
                    Title="Scores"
                    VerticalAlignment="Stretch"
                    HorizontalAlignment="Stretch">
    <toolkit:ColumnSeries DependentValuePath="Project1Score"
                            IndependentValuePath="StudentName"
                            ItemsSource="{Binding StudentList}"
                            Title="{Binding TitleList[0]}">
        <toolkit:ColumnSeries.DataPointStyle>
            <Style TargetType="toolkit:ColumnDataPoint">
                <Setter Property="Background">
                    <Setter.Value>
                        <RadialGradientBrush GradientOrigin="-0.1,-0.1"
                                                Center="0.075,0.015"
                                                RadiusX="1.05"
                                                RadiusY="0.9">
                            <GradientStop Color="#FFFDE79C" />
                            <GradientStop Color="#FFF6BC0C"
                                            Offset="1" />
                        </RadialGradientBrush>
                    </Setter.Value>
                </Setter>
            </Style>
        </toolkit:ColumnSeries.DataPointStyle>
    </toolkit:ColumnSeries>

    <toolkit:ColumnSeries DependentValuePath="Project2Score"
                            IndependentValuePath="StudentName"
                            ItemsSource="{Binding StudentList}"
                            Title="{Binding TitleList[1]}">
        <toolkit:ColumnSeries.DataPointStyle>
            <Style TargetType="toolkit:ColumnDataPoint">
                <Setter Property="Background">
                    <Setter.Value>
                        <RadialGradientBrush GradientOrigin="-0.1,-0.1"
                                                Center="0.075,0.015"
                                                RadiusX="1.05"
                                                RadiusY="0.9">
                            <GradientStop Color="#FF9DC2B3" />
                            <GradientStop Color="#FF1D7554"
                                            Offset="1" />
                        </RadialGradientBrush>

                    </Setter.Value>
                </Setter>
            </Style>
        </toolkit:ColumnSeries.DataPointStyle>

    </toolkit:ColumnSeries>
    <toolkit:ColumnSeries DependentValuePath="Project3Score"
                            IndependentValuePath="StudentName"
                            ItemsSource="{Binding StudentList}"
                            Title="{Binding TitleList[2]}">
        <toolkit:ColumnSeries.DataPointStyle>
            <Style TargetType="toolkit:ColumnDataPoint">
                <Setter Property="Background">
                    <Setter.Value>
                        <RadialGradientBrush GradientOrigin="-0.1,-0.1"
                                                Center="0.075,0.015"
                                                RadiusX="1.05"
                                                RadiusY="0.9">
                            <GradientStop Color="#FFFBB7B5" />
                            <GradientStop Color="#FF702828"
                                            Offset="1" />
                        </RadialGradientBrush>
                    </Setter.Value>
                </Setter>
            </Style>
        </toolkit:ColumnSeries.DataPointStyle>
    </toolkit:ColumnSeries>
</toolkit:Chart>

The result is shown below.

image

Use this technique any time you need to set the colors of the columns in the chart.

Enjoy!

Silverlight Simple MVVM Printing

Filed under: C#,Silverlight,VB.NET @ 2:02 pm

Many users want to print information from your line of business (LOB) application. This post covers the simple case where the user wants to print a copy of what they see on the screen. But instead of putting the print code in the code behind file, it follows a Model-View-View/Model (MVVM) approach.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: If you are new to MVVM, this prior post provides an introduction.

Silverlight 4 introduced two features that are the basis of this post: printing support and commanding.

Using the built-in commanding feature to support printing requires the following steps:

1) Create a command class that implements the ICommand interface.

2) Create a property of type ICommand in the View/Model class for a PrintCommand.

3) Build a button to launch the printing and bind the Command property of the button to the property created in 2 above.

Creating a Command Class

The code for this class was provided in this prior post and is not repeated here.

Creating a Property in the View/Model

Add three things to the View/Model for each command:

1) Command property.

2) Code to set the command property.

3) Method defined for the command property delegate.

The code is shown below with the statements for 1, 2, and 3 above shown in red.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Printing;
using InStepSM.Library;
using InStepSM.SL.Web;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private ICommand _PrintCommand;
        public ICommand PrintCommand
        {
            get
            {
                return _PrintCommand;
            }
        }

        private ICommand _SaveCommand;
        public ICommand SaveCommand
        {
            get
            {
                return _SaveCommand;
            }
        }
        
        private StudentContext StudentContextInstance;

        private ObservableCollection<Student> _StudentsList;
        public ObservableCollection<Student> StudentsList
        {
            get
            {
                return _StudentsList;
            }
            set
            {
                if (_StudentsList != value)
                {
                    _StudentsList = value;
                    OnPropertyChanged("StudentsList");
                }
            }
        }

        public StudentsViewModel()
        {
            LoadData();
            DefineCommands();
        }

        private void DefineCommands()
        {
            _SaveCommand = new DelegateCommand<Student>
                            (OnSaveCommand);
            _PrintCommand = new DelegateCommand<Panel>
                            (OnPrintCommand);
        }

        /// <summary>
        /// Loads the data for the application.
        /// </summary>
        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                                   GetStudentsQuery(),
                                   OnStudentsLoaded, null);
            }
        }

        /// <summary>
        /// Loads temporary data for use in the designer.
        /// </summary>
        /// <remarks></remarks>
        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                        new ObservableCollection<Student>();
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email = "testemail@live.com",
                RegistrationDate = DateTime.Now
            });
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Frodo",
                Age = 32,
                Email = "testemail@yahoo.com",
                RegistrationDate = DateTime.Now
            });
            StudentsList = temp;
        }

        private void OnPrintCommand(Panel p)
        {
            PrintDocument pd = new PrintDocument();
            pd.PrintPage += (s, e) =>
                {
                    p.Width = e.PrintableArea.Width;
                    p.Height = e.PrintableArea.Height;
                    e.PageVisual = p;
                    e.HasMorePages = false;
                };

            pd.Print("Student List");
        }

        private void OnStudentsLoaded(LoadOperation lo)
        {
            StudentsList = 
   new ObservableCollection<Student>(StudentContextInstance.Students);
        }

        private void OnSaveCommand(Student s)
        {
            StudentContextInstance.SubmitChanges();
        }

    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web
Imports System.Windows.Printing
Imports System.Windows.Controls

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

        Private _PrintCommand As ICommand
        ”’ <summary>
        ”’ Processes the Save command
        ”’ </summary>
        Public ReadOnly Property PrintCommand() As ICommand
            Get
                Return _PrintCommand
            End Get
        End Property

        Private _StudentsList As ObservableCollection(Of Student)
        Public Property StudentsList() As _
                            
ObservableCollection(Of Student)
            Get
                Return _StudentsList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _StudentsList IsNot value Then
                    _StudentsList = value
                    OnPropertyChanged("StudentsList")
                End If
            End Set
        End Property

        Private StudentContextInstance As StudentContext

        Private _SaveCommand As ICommand
        ”’ <summary>
        ”’ Processes the Save command
        ”’ </summary>
        Public ReadOnly Property SaveCommand() As ICommand
            Get
                Return _SaveCommand
            End Get
        End Property

        Public Sub New()
            LoadData()
            DefineCommands()
        End Sub

        Private Sub DefineCommands()
            _SaveCommand = New DelegateCommand(Of Student)(
                            AddressOf OnSaveCommand)
            _PrintCommand = New DelegateCommand(Of Panel)(
                            AddressOf OnPrintCommand)
        End Sub

        ”’ <summary>
        ”’ Loads the data for the application.
        ”’ </summary>
        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                 StudentContextInstance.Load(StudentContextInstance.
                               GetStudentsQuery(),
                               AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        ”’ <summary>
        ”’ Loads temporary data for use in the designer.
        ”’ </summary>
        ”’ <remarks></remarks>
        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp

        End Sub

        Private Sub OnPrintCommand(ByVal p As Panel)
            Dim pd As PrintDocument = New PrintDocument()
            AddHandler pd.PrintPage,
                Sub(s, e)
                    p.Width = e.PrintableArea.Width
                    p.Height = e.PrintableArea.Height
                    e.PageVisual = p
                    e.HasMorePages = False

                End Sub
            pd.Print("Student List")
        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = 
New ObservableCollection(Of Student)(StudentContextInstance.Students)
        End Sub

        Private Sub OnSaveCommand(ByVal s As Student)
            StudentContextInstance.SubmitChanges()
        End Sub

    End Class
End Namespace

The PrintCommand is the property that implements ICommand. The UI can be bound to this property.

The DefineCommands method assigns the PrintCommand to a new instance of the DelegateCommand class. Notice that it uses a Panel as the parameter. That allows passing any control that inherits from Panel to the PrintCommand. Controls that inherit from Panel include Grid and StackPanel.

The OnPrintCommand is the method to be executed when the command occurs. This method creates an instance of PrintDocument, which is the primary class for handling printing in Silverlight. It then defines a delegate for the PrintPage event. Finally, it calls the Print method of the PrintDocument to start the printing process.

The PrintPage event is generated for each page to be printed. When the PrintPage event occurs, the delegate code sets the size of the passed in control to fit the printable area of the page. It then assigns the PageVisual to the control. All controls in the tree of the control assigned to the PageVisual are printed. Setting the HasMorePages to false indicates that there is only one page to print.

Binding to the Command

This last step is to define a control in the xaml that launchs the printing. In this example, a Print button is used. The xaml for the button is as follows:

<Button Command="{Binding PrintCommand}"
        CommandParameter="{Binding ElementName=LayoutRoot}"
        Content="Print"
        HorizontalAlignment="Right"
        Name="PrintButton"
        Margin="10"
        Width="60" />

The Command property of the Button is bound to the PrintCommand property from the View/Model. The CommandParameter property of the Button is bound to the LayoutRoot element of the page, which is the main grid on the page. The resulting printed output is shown below:

image

But maybe you don’t want the entire page with the title and buttons. If so, just assign a different control to the CommandParameter.

For example, you can put a StackPanel around just the DataGrid and pass the name of the StackPanel in as the CommandParameter. The resulting printed output then only includes the DataGrid.

image

Use this technique any time you need to print the content or some portion of the content of your Silverlight page to the printer.

Enjoy!

February 14, 2011

Silverlight MVVM Commanding II

Filed under: C#,Silverlight,VB.NET @ 12:47 am

This prior post introduced commanding in Silverlight for use with MVVM. However, the built-in Silverlight commanding only works for controls that inherit from ButtonBase, such as Button and HyperlinkButton. This post provides information on implementing commanding for other Silverlight controls.

For example, the application displays a DataGrid. A click on a grid row should display information about the row that was clicked. But DataGrid does not inherit from ButtonBase and therefore does not support the built-in commanding.

The key to extending Silverlight commanding is Expression Blend’s InvokeCommandAction, which requires the Expression Blend SDK for Silverlight 4. If you don’t have this SDK, you can download it here.

In your Silverlight project, reference the System.Windows.Interactivity.dll. On my system, this file was in C:\Program Files (x86)\Microsoft SDKs\Expression\Blend\Silverlight\v4.0\Libraries. Set it to Copy Local so that it is deployed with your application.

Using the InvokeCommandAction commanding feature in a Silverlight MVVM application requires three basic steps:

1) Create a command class that implements the ICommand interface.

2) Create a property of type ICommand in the View/Model class.

3) Bind the Command property of any control to the property created in 2 above.

If you read the prior post on Simple MVVM Commanding, you will notice that these are the same three steps. The only difference is the syntax for step #3.

Creating a Command Class

The Command class used for InvokeCommandAction is the same class you use for the built-in command feature. The code for this class was provided in this prior post and is not repeated here.

Creating a Property in the View/Model

Add three things to the View/Model for each command:

1) Command property.

2) Code to set the command property.

3) Method defined for the command property delegate.

The code is shown below with the statements for 1, 2, and 3 above shown in red.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Input;
using InStepSM.Library;
using InStepSM.SL.Web;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private ICommand _SelectedCommand;
        public ICommand SelectedCommand
        {
            get { return _SelectedCommand; }
        }
       
        private StudentContext StudentContextInstance;

        private ObservableCollection<Student> _StudentsList;
        public ObservableCollection<Student> StudentsList
        {
            get
            {
                return _StudentsList;
            }
            set
            {
                if (_StudentsList != value)
                {
                    _StudentsList = value;
                    OnPropertyChanged("StudentsList");
                }
            }
        }

        public StudentsViewModel()
        {
            LoadData();
            DefineCommands();
        }

        private void DefineCommands()
        {
            _SelectedCommand = new DelegateCommand<Student>
                            (OnSelectedCommand);
        }

        /// <summary>
        /// Loads the data for the application.
        /// </summary>
        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                                      GetStudentsQuery(),
                                      OnStudentsLoaded, null);
            }
        }

        /// <summary>
        /// Loads temporary data for use in the designer.
        /// </summary>
        /// <remarks></remarks>
        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                   new ObservableCollection<Student>();
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email = "testemail@live.com",
                RegistrationDate = DateTime.Now
            });
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Frodo",
                Age = 32,
                Email = "testemail@yahoo.com",
                RegistrationDate = DateTime.Now
            });
            StudentsList = temp;
        }

        private void OnStudentsLoaded(LoadOperation lo)
        {
            StudentsList = new ObservableCollection<Student>(StudentContextInstance.Students);
        }

        private void OnSelectedCommand(Student s)
        {
            string prompt = string.Format(
                "Selected student is: {0}, {1}",
                s.LastName, s.FirstName);
            MessageBox.Show(prompt, "Students", MessageBoxButton.OK);
        }
    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

        Private _SelectedCommand As ICommand
        Public ReadOnly Property SelectedCommand() As ICommand
            Get
                Return _SelectedCommand
            End Get
        End Property

        Private _StudentsList As ObservableCollection(Of Student)
        Public Property StudentsList() As
                                 ObservableCollection(Of Student)
            Get
                Return _StudentsList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _StudentsList IsNot value Then
                    _StudentsList = value
                    OnPropertyChanged("StudentsList")
                End If
            End Set
        End Property

        Private StudentContextInstance As StudentContext

        Public Sub New()
            LoadData()
            DefineCommands()
        End Sub

        Private Sub DefineCommands()
            _SelectedCommand = New DelegateCommand(Of Student)(
                            AddressOf OnSelectedCommand)
        End Sub

        ”’ <summary>
        ”’ Loads the data for the application.
        ”’ </summary>
        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                               GetStudentsQuery(),
                               AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        ”’ <summary>
        ”’ Loads temporary data for use in the designer.
        ”’ </summary>
        ”’ <remarks></remarks>
        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp

        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = New ObservableCollection(Of Student)
                              (StudentContextInstance.Students)
        End Sub

        Private Sub OnSelectedCommand(ByVal s As Student)
            Dim prompt = String.Format("Selected student is: {0}, {1}",
                s.LastName, s.FirstName)
            MessageBox.Show(prompt, "Students", MessageBoxButton.OK)
        End Sub

    End Class
End Namespace

The SelectedCommand is the property that implements ICommand. The UI can be bound to this property.

The OnSelectedCommand is the method to be executed when the command occurs.

The SelectedCommand is set to an instance of the DelegateCommand, passing in OnSelectedCommand as the delegate.

Note that in this example, the parameter is used in the message box to display the name of the selected student.

Binding to the Command

This last step is where the InvokeCommandAction technique is different from the built-in commanding used for ButtonBase controls.

Insert the following lines of code within the control that generates the event associated with the command.

<i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseLeftButtonUp">
        <i:InvokeCommandAction
            Command="{Binding SelectedCommand}"
            CommandParameter="{Binding SelectedItem,
                        ElementName=studentDataGrid}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

NOTE: The Interactivity namespace is defined as follows:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=
                       System.Windows.Interactivity"

In this example, the Interaction code is added to the DataGrid. The EventName property defines the event that triggers the command. In this case, the MouseLeftButtonUp event. When the user clicks on a row in the DataGrid, the SelectedCommand is executed.

NOTE: To find the set of valid EventName values for any control, consult the msdn documentation. For example, for the DataGrid, the valid set of EventName values are listed under "Events" in this msdn link.

The Command binds to the defined command property, in this case the SelectedCommand. The optional CommandParameter binds to any information passed to the command. In this example, the DataGrid’s selected item is passed to the SelectCommand, passing in the selected student.

The full xaml is shown below.

<navigation:Page x:Class="InStepSM.SL.Views.Students"
       
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=
            System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Students Page" 
xmlns:sdk=
           
http://schemas.microsoft.com/winfx/2006/xaml/
            presentation/sdk

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=
            System.Windows.Interactivity"
xmlns:vms="clr-namespace:InStepSM.SL.ViewModels">

    <!– Reference the View/Model –>
    <navigation:Page.DataContext>
        <vms:StudentsViewModel />
    </navigation:Page.DataContext>

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBlock x:Name="HeaderText"
                   Grid.Row="0"
                   Grid.Column="0" Grid.ColumnSpan="2"
                    Style="{StaticResource HeaderTextStyle}"
                    Text="Students" />
             
        <sdk:DataGrid AutoGenerateColumns="False"
                Grid.Row="1" Grid.Column="1"
                Height="200"
                HorizontalAlignment="Stretch"
                ItemsSource="{Binding StudentsList}"
                Name="studentDataGrid"
                RowDetailsVisibilityMode="VisibleWhenSelected"
                VerticalAlignment="Top"
                Width="457">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseLeftButtonUp">
                    <i:InvokeCommandAction
                        Command="{Binding SelectedCommand}"
                        CommandParameter="{Binding SelectedItem,
                                   ElementName=studentDataGrid}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

            <sdk:DataGrid.Columns>
            <sdk:DataGridTextColumn x:Name="firstNameColumn"
                                    Binding="{Binding Path=FirstName}"
                                    Header="First Name"
                                    Width="SizeToHeader" />
            <sdk:DataGridTextColumn x:Name="lastNameColumn"
                                    Binding="{Binding Path=LastName}"
                                    Header="Last Name"
                                    Width="SizeToHeader" />
            <sdk:DataGridTextColumn x:Name="ageColumn"
                                    Binding="{Binding Path=Age}"
                                    Header="Age"
                                    Width="SizeToHeader" />
            <sdk:DataGridTextColumn x:Name="emailColumn"
                                    Binding="{Binding Path=Email}"
                                    Header="Email"
                                    Width="120" />
            <sdk:DataGridTemplateColumn x:Name="registrationDateColumn"
                                    Header="Registration Date" 
                                    Width="SizeToHeader">
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <sdk:DatePicker SelectedDate="{Binding                    Path=RegistrationDate, Mode=TwoWay,
                   NotifyOnValidationError=true, 
                   ValidatesOnExceptions=true,
                   TargetNullValue=”}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding
                      Path=RegistrationDate, StringFormat=\{0:d\}}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        </sdk:DataGrid.Columns>
        </sdk:DataGrid>

        <Grid Grid.Row="2"
              Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

        </Grid>
    </Grid>
</navigation:Page>

Running the application and clicking on a row results in the following:

image

Use this technique to add commanding to any Silverlight control.

Enjoy!

February 12, 2011

Silverlight Simple MVVM Commanding

Filed under: C#,Silverlight,VB.NET @ 6:25 pm

As you build your line of business (LOB) applications, you will need to process commands. In many cases, these commands are executed when the user clicks a button. For example, when the user clicks the Save button, the code needs to process a Save "command" and perform the save.

One way to handle the commands in your application is in the code behind. Just double-click on the button in the Visual Studio designer and type code into the resulting event handler. But this technique does not work if you want to follow Model-View-View/Model (MVVM).

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: If you are new to MVVM, this prior post provides an introduction.

Luckily Silverlight 4 provides for commanding, but only with buttons and hyperlinks. This post describes how to use the built-in commanding for these simplest scenarios. (A later post will cover more complex scenarios.)

Using the built-in commanding feature in a Silverlight MVVM application requires three basic steps:

1) Create a command class that implements the ICommand interface.

2) Create a property of type ICommand in the View/Model class.

3) Bind the Command property of a Button or Hyperlink to the property created in 2 above.

Creating a Command Class

The first step to using the commanding feature in a Silverlight MVVM application is to create a command class for each command. Considering how many commands you may have in a LOB application, this step will get boring very quickly. StudentSaveCommand, StudentAddCommand,  StudentDeleteCommand, ScoreSaveCommand, ScoreAddCommand, etc, etc you get the picture.

So a better option is to create one generalized command class that you can use for all of your commands. This is what the Prism framework did, what many other blog posts on this topic did, and what this post will do.

The code is shown in C# and VB, and then explained.

In C#:

using System;
using System.Windows.Input;

namespace InStepSM.Library
{
    ///<summary>
    /// Gneric ICommand implementation to assist with processing
    /// commands in MVVM.
    ///</summary>

    public class DelegateCommand<T> : ICommand
    {
        /// <summary>
        /// Defines the method for the action to be executed.
        /// </summary>
        private readonly Action<T> executeAction;

        /// <summary>
        /// Defines the function that determines whether the
        /// action can be executed.
        /// </summary>
        private readonly Func<T,bool> canExecuteAction;

        ///<summary>
        /// Defines an event to raise when the values that
        /// affect "CanExecute" are changed.
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Constucts an object that can always execute.
        /// </summary>
        /// <param name="currentExecuteAction"></param>
        /// <remarks></remarks>
        public DelegateCommand(Action<T> executeAction)
            : this(executeAction, null)
        {
        }

        /// <summary>
        /// Constructs an object to execute.
        /// </summary>
        public DelegateCommand(Action<T> executeAction,
            Func<T,bool> canExecuteAction)
        {
            this.executeAction = executeAction;
            this.canExecuteAction = canExecuteAction;
        }

        /// <summary>  
        /// Defines the method that determines whether the command can
        /// execute in its current state.  
        /// </summary>   
        public bool CanExecute(object parameter)
        {
            if (canExecuteAction != null)
            {
                return canExecuteAction((T)parameter);
            }
            return true;
        }

        /// <summary>  
        /// Defines the method to call when the command is invoked.  
        /// </summary>   
        public void Execute(object parameter)
        {
            if (CanExecute(parameter))
            {
                executeAction((T)parameter);
            }
        }
    }
}

In VB:

Imports System.Windows.Input

”’ <summary>
”’ Generic ICommand implementation to assist with processing
”’ commands in MVVM
”’ </summary>
Public Class DelegateCommand(Of T)
    Implements ICommand

    ”’ <summary>
    ”’ Defines the subroutine for the action to be executed.
    ”’ </summary>
    Private ReadOnly executeAction As Action(Of T)

    ”’ <summary>
    ”’ Defines the function that determines whether the 
    ”’ action can be executed.
    ”’ </summary>
    Private ReadOnly canExecuteAction As Func(Of T, Boolean)

    ”’ <summary>
    ”’ Defines an event to raise when the values that
    ”’ affect "CanExecute" are changed
    ”’ </summary>
    Public Event CanExecuteChanged(ByVal sender As Object, 
                          ByVal e As System.EventArgs) _
         Implements System.Windows.Input.ICommand.CanExecuteChanged

    ”’ <summary>
    ”’ Constructs an object that can always execute.
    ”’ </summary>
    Public Sub New(ByVal currentExecuteAction As Action(Of T))
        Me.New(currentExecuteAction, Nothing)
    End Sub

    ”’ <summary>
    ”’ Constructs an object to execute.
    ”’ </summary>
    Public Sub New(ByVal currentExecuteAction As Action(Of T),
            ByVal currentCanExecuteAction As Func(Of T, Boolean))
        executeAction = currentExecuteAction
        canExecuteAction = currentCanExecuteAction
    End Sub

    ”’ <summary>
    ”’ Defines the method that determines whether the command can 
    ”’ execute in its current state.
    ”’ </summary>
    Public Function CanExecute(ByVal parameter As Object) As Boolean _
                 
Implements System.Windows.Input.ICommand.CanExecute
        If canExecuteAction IsNot Nothing Then
            Return canExecuteAction(DirectCast(parameter, T))
        Else
            Return True
        End If
    End Function

    ”’ <summary>
    ”’ Defines the method to be called when the command is invoked.
    ”’ </summary>
    Public Sub Execute(ByVal parameter As Object) _
              Implements System.Windows.Input.ICommand.Execute
        If CanExecute(parameter) Then
            executeAction(DirectCast(parameter, T))
        End If
    End Sub
#End Region

End Class

The class name, DelegateCommand, is the name used by Prism and the name selected for this post. It is a generic class with T representing the type of the parameter passed to the command, if any.

The executeAction property is a delegate that defines a method to execute when the command is invoked. For a Save command for example, the executeAction property may be SaveStudent(). So when the user clicks the Save button, the SaveStudent method in your View/Model is executed.

The canExecuteAction property is a delegate that defines a method that determines whether or not the executeAction method can be executed based on the current state of the application. For a Save command for example, the canExecuteAction property may be Validate(). So the SaveStudent method won’t be executed if the student is not valid.

The CanExecuteChanged defines an event to raise when the values that affect the canExecutAction changes. This is beyond the scope of this post but will be further detailed in a later post.

The constructors construct an instance of the command either with just an executeAction, or with both an executeAction and a canExecuteAction.

The CanExecute method executes the delegate defined in the canExecuteAction property, passing in any parameter. If the property is not set, it always returns true so the method can be executed.

NOTE: If you have a Button in the UI bound to a command and the canExecuteAction method returns false, Silverlight will automatically disable the button since the action cannot be executed.

The Execute method first calls the CanExecute method to ensure that the command can be executed. If that method returns true, it executes the delegate defined in the executeAction, passing in any parameter.

Creating a Property in the View/Model

Add three things to the View/Model for each command:

1) Command property.

2) Code to set the command property.

3) Method defined for the command property delegate.

The code is shown with the statements for 1, 2, and 3 above shown in red.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Input;
using InStepSM.Library;
using InStepSM.SL.Web;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private ICommand _SaveCommand;
        public ICommand SaveCommand
        {
            get
            {
                return _SaveCommand;
            }
        }

        private StudentContext StudentContextInstance;

        private ObservableCollection<Student> _StudentsList;
        public ObservableCollection<Student> StudentsList
        {
            get
            {
                return _StudentsList;
            }
            set
            {
                if (_StudentsList != value)
                {
                    _StudentsList = value;
                    OnPropertyChanged("StudentsList");
                }
            }
        }

        public StudentsViewModel()
        {
            LoadData();
            DefineCommands();
        }

        private void DefineCommands()
        {
            _SaveCommand = new DelegateCommand<Student>
                            (OnSaveCommand);
        }

        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop =
                    StudentContextInstance.Load(StudentContextInstance.
                                    GetStudentsQuery(),
                                    OnStudentsLoaded, null);
            }
        }

        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                         new ObservableCollection<Student>();
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email = "testemail@live.com",
                RegistrationDate = DateTime.Now
            });
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Frodo",
                Age = 32,
                Email = "testemail@yahoo.com",
                RegistrationDate = DateTime.Now
            });
            StudentsList = temp;
        }

        private void OnStudentsLoaded(LoadOperation lo)
        {
            StudentsList = 
    new ObservableCollection<Student>(StudentContextInstance.Students);
        }

        private void OnSaveCommand(Student s)
        {
            StudentContextInstance.SubmitChanges();
        }

    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

        Private _StudentsList As ObservableCollection(Of Student)
        Public Property StudentsList() As
                                 ObservableCollection(Of Student)
            Get
                Return _StudentsList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _StudentsList IsNot value Then
                    _StudentsList = value
                    OnPropertyChanged("StudentsList")
                End If
            End Set
        End Property

        Private StudentContextInstance As StudentContext

        Private _SaveCommand As ICommand
        Public ReadOnly Property SaveCommand() As ICommand
            Get
                Return _SaveCommand
            End Get
        End Property

        Public Sub New()
            LoadData()
            DefineCommands()
        End Sub

        Private Sub DefineCommands()
            _SaveCommand = New DelegateCommand(Of Student)(
                            AddressOf OnSaveCommand)
        End Sub

        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                                GetStudentsQuery(),
                                AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp

        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = 
  New ObservableCollection(Of Student)(StudentContextInstance.Students)
        End Sub

        Private Sub OnSaveCommand(ByVal s As Student)
            StudentContextInstance.SubmitChanges()
        End Sub

    End Class
End Namespace

The SaveCommand is the property that implements ICommand. The UI can be bound to this property.

The OnSaveCommand is the method to be executed when the command occurs.

The SaveCommand is set to an instance of the DelegateCommand, passing in OnSaveCommand as the delegate.

Note that in this example, the parameter is not used. Since the entire grid is saved in a save operation in this example, there is no processing needed for the selected student.

Binding to the Command

The last step is the easiest: just bind the Command property of the Button (or Hyperlink) to the command property created in the prior step.

<Button x:Name="SaveButton"
        Grid.Column="1"
        Command="{Binding SaveCommand}"
        CommandParameter="{Binding SelectedItem,
                           ElementName=studentDataGrid}"
        Content="Save"
        HorizontalAlignment="Right"
        Margin="10,10,0,10"
        Width="60"/>

The Command property of the Button is bound to the SaveCommand property in the View/Model. When the button is clicked, the Execute method of the DelegateCommand is executed which in turn calls the OnSaveCommand method in the View/Model. This method ultimately calls the SubmitChanges method, saving any changes.

The CommandParameter property of the Button is bound to the SelectedItem of the DataGrid. This passes the currently selected Student to the command.

In this specific example, the parameter is not used so you only really need to set the Command property, not the CommandParameter property.

Use this technique any time you need to process commands from the Button or Hyperlink controls. (Or any other control that inherits from ButtonBase.) If you need to process commands from other controls, such as a click on a grid row, you need more code. But that is for a later post.

Enjoy!

February 7, 2011

Simple Silverlight MVVM Base Class

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 12:54 am

As you start to add more Views and associated View/Models to your Silverlight line of business (LOB) application, you will notice that the classes include some common and repeated code. Building a simple base class for your View/Models provides a single place for that common code.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: If you are new to MVVM, this prior post provides an introduction.

To keep things simple, the ViewModelBase class defined here supports two features that you will need in most of your View/Model classes:

  • Implementing INotifyPropertyChanged

When building an MVVM application, the View/Model includes a set of properties that expose the data for your user interface. The View (xaml) connects the user interface element properties, such as a TextBox property, to the View/Model properties using binding.

When the View/Model property is updated, the user interface must be notified that the property is changed so that the user interface is updated. This is done using the PropertyChanged event that is part of INotifyPropertyChanged.

  • Determining whether the view is running from the designer.

If you reference the View/Model in the View’s xaml, the Visual Studio designer creates an instance of the View/Model when you open the View in the designer. Any code you define in the View/Model constructor is then executed. This makes it easy to define design-time data and populate the controls, improving your design-time experience.

But you only want the design-time data at design time. Define a flag accessible in the constructor to determine whether to load the design-time data or the actual data.

NOTE: Code in your constructor that accesses data using WCF RIA Services must not execute at design-time or Visual Studio won’t open the designer.

The ViewModelBase class code is shown below in both VB and C# and then described in detail.

In C#:

using System;
using System.ComponentModel;

namespace InStepSM.SL.ViewModels
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        private static bool? _IsInDesignMode;
        public static bool IsInDesignModeStatic
        {
            get
            {
                if (! _IsInDesignMode.HasValue)
                    _IsInDesignMode =
                       DesignerProperties.IsInDesignTool;
                return _IsInDesignMode.Value;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            // Send an event notification that the property changed
            // This allows the UI to know when one of the items changes
            if (!String.IsNullOrEmpty(propertyName) &&
                    PropertyChanged != null)
                PropertyChanged(this,
                     new PropertyChangedEventArgs(propertyName));
        }
    }
}

In VB:

Imports System.ComponentModel

Namespace ViewModels
    Public Class ViewModelBase
        Implements INotifyPropertyChanged

        Private Shared _IsInDesignMode As Boolean?
        Public Shared ReadOnly Property IsInDesignModeStatic As Boolean
            Get
                If Not _IsInDesignMode.HasValue Then
                    _IsInDesignMode = DesignerProperties.IsInDesignTool
                End If
                Return _IsInDesignMode.Value
            End Get
        End Property

        Public Event PropertyChanged(ByVal sender As Object, 
            ByVal e As System.ComponentModel.PropertyChangedEventArgs) 
         Implements
         System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Protected Sub OnPropertyChanged(ByVal propertyName As String)
            ‘ Send an event notification that the property changed
            ‘ This allows the UI to know when one of the items changes
            If Not String.IsNullOrEmpty(propertyName) Then
                RaiseEvent PropertyChanged(Me,
                    New PropertyChangedEventArgs(propertyName))
            End If
        End Sub
    End Class
End Namespace

The IsInDesignModeStatic property defines whether the application is currently in design mode. If this flag is true, then the View is currently open in the Visual Studio (or Expression Blend) designer. If it is false, then the View is opened at runtime.

[For more information on detecting design mode, see this post from Laurent Bugnion.]

The PropertyChanged event defines the event for the INotifyPropertyChanged interface. The OnPropertyChanged method raises the PropertyChanged event. Code in the View/Model calls OnPropertyChanged in the setter for any property that could be bound to the View.

For example of how to use this ViewModelBase class, here is a StudentsViewModel for a Students View that inherits from the ViewModelBase.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using InStepSM.SL.Web;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private StudentContext StudentContextInstance;

        private ObservableCollection<Student> _StudentsList;
        public ObservableCollection<Student> StudentsList
        {
            get
            {
                return _StudentsList;
            }
            set
            {
                if (_StudentsList != value)
                {
                    _StudentsList = value;
                    OnPropertyChanged("StudentsList");
                }
            }
        }

        public StudentsViewModel()
        {
            LoadData();
        }

        /// <summary>
        /// Loads the data for the application.
        /// </summary>
        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop = StudentContextInstance.Load
                       (StudentContextInstance.GetStudentsQuery(),
                                         OnStudentsLoaded, null);
            }
        }

        /// <summary>
        /// Loads temporary data for use in the designer.
        /// </summary>
        /// <remarks></remarks>
        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                               new ObservableCollection<Student>();
            temp.Add(new Student {LastName = "Baggins",
                                  FirstName = "Bilbo",
                                  Age = 111,
                                  Email = "testemail@live.com",
                                  RegistrationDate = DateTime.Now});
            temp.Add(new Student {LastName = "Baggins",
                                  FirstName = "Frodo",
                                  Age = 32,
                                  Email = "testemail@yahoo.com",
                                  RegistrationDate = DateTime.Now});
            StudentsList = temp;
        }

        private void OnStudentsLoaded(LoadOperation lo )
        {
            StudentsList = 
   new ObservableCollection<Student>(StudentContextInstance.Students);
        }
    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

        Private StudentContextInstance As StudentContext

        Private _StudentsList As ObservableCollection(Of Student)
        Public Property StudentsList() As
                                 ObservableCollection(Of Student)
            Get
                Return _StudentsList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _StudentsList IsNot value Then
                    _StudentsList = value
                    OnPropertyChanged("StudentsList")
                End If
            End Set
        End Property

        Public Sub New()
            LoadData()
        End Sub

        ”’ <summary>
        ”’ Loads the data for the application.
        ”’ </summary>
        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                             GetStudentsQuery(),
                             AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        ”’ <summary>
        ”’ Loads temporary data for use in the designer.
        ”’ </summary>
        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp
        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = New ObservableCollection(Of Student)
                                  (StudentContextInstance.Students)
        End Sub
    End Class
End Namespace

This code begins by declaring an instance of a StudentContext. The StudentContext, Student, and Students class referenced by the above code were all generated by Visual Studio as part of WCF RIA Services. See this prior post for details on setting up the database, Entity Framework (EF) model, and associated WCF RIA Services code.

NOTE: This same code also works if you use WCF RIA Services with something other than EF, such as LinqToSql or your own business objects.

The StudentsList property of the ViewModel class defines the list of students. The View binds to this property to display the list of students in a DataGrid. Notice the OnPropertyChanged call. This ensures that the user interface is notified when the StudentsList property changes.

The constructor calls the LoadData method to load the data. The LoadData method uses the IsInDesignModeStatic property from the base class to determine whether the View is in design mode or run mode.

If in design mode, the application loads some hard-coded sample data. This data appears in the designer as shown below:

image

Having design-time data makes it easier to see what the user interface will look like. This is especially useful when you style the DataGrid.

If the View is not in design mode, the code calls the Load method of the StudentContext. This method is generated as part of the WCF RIA Services feature. The first parameter to the Load method is the name of the query to call, which is also in the generated code. Since Silverlight data access is asynchronous, this method call will not wait for the data to be retrieved.

The second parameter to the Load method is the callback function. This callback function is executed when the load is complete, allowing you to work with the data as needed.

In this case, the OnStudentsLoaded method simply assigns the resulting data to the StudentsList property. The Setter of the StudentsList property then raises the OnPropertyChanged event and the grid is updated to display the list of students.

Here is the associated xaml:

<navigation:Page x:Class="InStepSM.SL.Views.Students"       
xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation=
       "clr-namespace:System.Windows.Controls;
       assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="Students Page" 
xmlns:riaControls=
       "clr-namespace:System.Windows.Controls;
        assembly=System.Windows.Controls.DomainServices"
xmlns:sdk=
    "
http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
 xmlns:vms="clr-namespace:InStepSM.SL.ViewModels">

    <!– Reference the View/Model –>
    <navigation:Page.DataContext>
        <vms:StudentsViewModel />
    </navigation:Page.DataContext>

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <TextBlock x:Name="HeaderText"
                   Grid.Row="0"
                   Grid.Column="0" Grid.ColumnSpan="2"
                    Style="{StaticResource HeaderTextStyle}"
                    Text="Students" />
             
        <sdk:DataGrid AutoGenerateColumns="False"
                Grid.Row="1" Grid.Column="1"
                Height="200"
                HorizontalAlignment="Stretch"
                ItemsSource="{Binding StudentsList}"
                Name="studentDataGrid"
                RowDetailsVisibilityMode="VisibleWhenSelected"
                VerticalAlignment="Top"
                Width="457">
        <sdk:DataGrid.Columns>
        <sdk:DataGridTextColumn x:Name="firstNameColumn"
                                Binding="{Binding Path=FirstName}"
                                Header="First Name"
                                Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="lastNameColumn"
                                Binding="{Binding Path=LastName}"
                                Header="Last Name"
                                Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="ageColumn"
                                Binding="{Binding Path=Age}"
                                Header="Age" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="emailColumn"
                                Binding="{Binding Path=Email}"
                                Header="Email" Width="120" />
        <sdk:DataGridTemplateColumn x:Name="registrationDateColumn"
                                    Header="Registration Date"
                                    Width="SizeToHeader">
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <sdk:DatePicker SelectedDate="{Binding
                        Path=RegistrationDate,
                        Mode=TwoWay,
                        NotifyOnValidationError=true,
                        ValidatesOnExceptions=true,
                        TargetNullValue=”}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding
               Path=RegistrationDate, StringFormat=\{0:d\}}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        </sdk:DataGrid.Columns>
        </sdk:DataGrid>

        <Grid Grid.Row="2"
              Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

        <Button x:Name="SaveButton"
                Grid.Column="1"
                Margin="10,10,0,10"
                Content="Save"
                HorizontalAlignment="Right"
                Width="60" />
        </Grid>
    </Grid>
</navigation:Page>

The result looks something like this:

image

NOTE: The resulting page is currently read-only because there is no Save implementation defined in the above code. Handling the Save command is information for a later post.

The ViewModelBase class defined in this post provides a starting point for building your View/Model base class.

Enjoy!

February 5, 2011

Updating Data in a Silverlight Application: EF

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 4:16 pm

This prior post covered the details of setting up a database, defining an entity model using Entity Framework, building WCF RIA Services to access that entity model, and hooking it all up to a Silverlight DataGrid. That is all well and good. But most line of business (LOB) applications also need to update data. That’s the purpose of this post.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

NOTE: This post assumes you already have the database, entity model, and WCF RIA Service classes in place. If you need information on how to perform these prerequisite steps, see this prior post.

The first step in adding update functionality is to modify the UI. Add a Save button to a page containing a DataGrid.

The xaml for the Save button looks like this:

<Button x:Name="SaveButton"
        Grid.Column="1"
        Margin="10,10,0,10"
        Content="Save"
        HorizontalAlignment="Right"
        Width="60"
        Click="SaveButton_Click" />

Then add the following to the code behind:

In C#:

private void SaveButton_Click(object sender, RoutedEventArgs e)
{
    studentDataGrid.CommitEdit();
    studentDomainDataSource.SubmitChanges();
}

In VB:

Private Sub SaveButton_Click(ByVal sender As System.Object,
                   ByVal e As System.Windows.RoutedEventArgs) _
                   Handles SaveButton.Click
    studentDataGrid.CommitEdit()
    StudentDomainDataSource.SubmitChanges()
End Sub

This code first completes any current edit operation on the grid. Then it uses the DomainDataSource that was defined in the xaml to submit the changes to the database.

When you run the application, the result will look something like this:

image

Edit the data in the grid and click Save to save the changes.

If you are happy with using a DomainDataSource and with saving the data in the code behind, you are done. If you want to handle the data retrieval without a DomainDataSource and follow a more MVVM approach, you still have some work to do. But that is another post.

Click here to download the associated source code.

Enjoy!

EDIT 2/8/2011: Added the link to the source code.

January 31, 2011

Accessing Data in a Silverlight Application: EF

Filed under: ASP.NET RIA Services,C#,Silverlight,VB.NET @ 12:29 am

One of the challenges of building a Silverlight line of business (LOB) application is getting the data from the business database. The business database resides on a server computer and the Silverlight application resides on each user’s computer. This post covers using Entity Framework (EF) to get the data from the database server to the Silverlight application.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

The primary choices for getting data into your Silverlight application are:

  • Build an entity class in the Silverlight application that is populated from a WCF service.
  • Generate an entity class in the Silverlight application using WCF RIA services and Entity Framework.
  • Generate an entity class in the Silverlight application using WCF RIA services and your business objects (sometimes called Plain Old CLR Objects or POCOs). See this prior post for more information on using WCF RIA with your business objects.

Plus there are third party products that provide similar functionality.

This post focuses on the second approach: using Entity Framework and WCF RIA Services.

Prerequisites

Before beginning, you need a database. I had two choices when preparing the example for this post:

1) Build a database with a single table for the student and scores. This option would keep things very simple and readily map to the class created in the prior posts of this series.

2) Build a database with several tables that more closely map to a "real" database design. Very rarely would a real LOB application access only a single table.

I picked option 2 in order to provide a more realistic example. So if you want to work through this example, build a database. I called mine InStepSM for InStep Student Management, which is the sample application built in this blog series. Then add three tables:

image

Enter some test data into each table. Be sure to match up the foreign keys.

Entity Framework

Once you have the database, you are ready to build the Entity Framework model.

1) Open the Web project of your Silverlight solution.

2) Add an ADO.NET Entity Data Model using the appropriate VB.NET or C# template.

image

3) Select to generate the data model from the database.

image

4) Select the connection to the desired database.

image

5) Select the desired tables, views, or stored procedures. In this example, all three tables are selected.

image

The result is an Entity Framework data model (edmx):

image

WCF RIA Services

With the entity model in place, use WCF RIA Services to generate the business classes and WCF communication code in your Silverlight project. You can then use the generated business classes in your Silverlight application for create, retrieve, update, delete (CRUD) operations.

1) Enable WCF RIA Services in your Silverlight application by setting the WCF RIA Services Link in the Silverlight project properties to reference the Silverlight Web project.

image

2) Build the solution.

NOTE: This is required or the Domain Service won’t know about your entity model and the following steps won’t work as expected.

3) In the Web project, build a Domain Service class for one of your entities.

image

4) Select the appropriate entities from the resulting dialog:

image

Since Step 3 identified a StudentService class, Step 4 selects the Student entity. If you want your Silverlight code to change the data, be sure to check "Enable editing". Leave it unchecked if you want read-only data.

When you click OK, Visual Studio generates the service code in VB.NET or C#.

5) Repeat steps 3 and 4 for each entity.

If you want to build along with this example, add a ScoreService  that references the Score entity and a ScoreTitleService that references the ScoreTitle entity. Be sure to check "Enable editing" for both of them.

Silverlight

Wow! Quite a bit of work. But we are finally ready to hook the data to the Silverlight user interface.

1) Add a page to your Silverlight project that will allow the user to review or edit student information.

If you are working through the set of blog posts, add a new Page called Students. (Be sure to add a link from the main page to this page using the technique from this prior post.)

2) When the Silverlight page is active in the designer, show the available Data Sources: Data | Show Data Sources.

There should be a data source for each domain service you added above.

image

3) Drag and drop the Student data source onto the page.

image

Silverlight generates the UI, including all of the required binding. By default, it adds controls for each field in the table in alphabetical order. You can delete, or re-arrange as desired.

NOTE: If you want to define the desired order of the columns BEFORE dragging and dropping the grid onto the form, you can edit the generated metadata file. In the Web project, locate the StudentService.metadata.cs or StudentService.metadata.vb file. If you don’t see the metadata files, recompile to generate them. Add attributes onto the properties as shown below and recompile.

In C#:

[Display(Order=3)]
public Nullable<int> Age { get; set; }

[Display(Order=2)]
public string Email { get; set; }

[Display(Order=0)]
public string FirstName { get; set; }

[Display(Order=1)]
public string LastName { get; set; }

In VB:

<Display(Order:=3)>
Public Property Age As Nullable(Of Integer)

<Display(Order:=2)>
Public Property Email As String

<Display(Order:=0)>
Public Property FirstName As String

<Display(Order:=1)>
Public Property LastName As String

4) Run the application.

image

Now the real question is … how the heck did that work?

The xaml that was generated in Step 3 above includes a DomainDataSource control:

<riaControls:DomainDataSource AutoLoad="True"
     d:DesignData="{d:DesignInstance my:Student, CreateList=true}"
     Height="0"
     LoadedData="studentDomainDataSource_LoadedData"
     Name="studentDomainDataSource"
    
QueryName="GetStudentsQuery"
     Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:StudentContext />
    </riaControls:DomainDataSource.DomainContext>

The code items shown in bold are the most important. The QueryName property defines the query that is executed to obtain the data. The DomainContext property defines the name of the class containing that query method. But try to locate either of those in your code and you won’t find them.

Select the Silverlight project in Solution Explorer and click on the Show All Files button in the Solution Explorer toolbar.The Generated_Code folder then appears. In that folder is an InStepSM.SL.Web.g.cs or InStepSM.SL.Web.g.vb file. That file contains all of the code in the Silverlight project that was generated by Visual Studio from the information in the domain service classes. That code contains the StudentContext class which has the GetStudentsQuery method referenced by the DomainDataSource control.

The GetStudentsQuery method uses WCF to call the Web project’s StudentService which in turn uses EF to retrieve the data and return it to the Silverlight project. The binding in the xaml does the rest.

Use the steps above any time you want to use EF and WCF RIA Services to access data in your application.

Click here to download the associated source code.

Enjoy!

EDIT 2/5/2011: Added a few more columns to the Student table to make the later update features more interesting. Also added a note about editing the metadata.

EDIT 2/8/2011: Added the link to the source code.

« Previous PageNext Page »

© 2021 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs