1 Previous Next 

Kens Silverlight Databinding Presentation


Link to slides and sample code for Silverlight Databinding presentation given at the

 

South West Florida Code Camp Sept 2008

Orlando Sql Saturday Oct 2008

 

http://www.vb-tips.com/downloads/Silverlightdatabinding.zip




Silverlight 2.0 Interacting with html


With SilverLight 2.0 you can interact and handle events with the html elements on your page.  Here is a simple example that places a select (drop down control) on a web page which will change the color of a ellipse on a SilverLight app.  So lets start with the html

 

<body style="height: 100%; margin: 0;">
    <form id="form1" runat="server" style="height: 100%;">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <br />
    <span>Select a Color </span>
    <select id="ddColor">
        <option>Red</option>
        <option>Blue</option>
        <option>Green</option>
    </select>
    <br />
    <br />
    <div style="height: 100%;">
        <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/HtmlAndSilverlight.xap"
            MinimumVersion="2.0.30923.0" Width="100%" Height="100%" />
    </div>
    </form>
</body>

 

 

Now the XAML

 

<UserControl x:Class="HtmlAndSilverlight.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Canvas x:Name="LayoutRoot" Background="Tan" >
        <Ellipse x:Name="el" Width="400" Height="300" Fill="Red"></Ellipse>
    </Canvas>
</UserControl>

 

 

Now build the app so we have intellisense for the controls.  Ok first off lets get access to the drop down (select) on the web page. Then we can use AttachEvent to handle the onchange event.   

 

Dim cbo As HtmlElement
Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    cbo = HtmlPage.Document.GetElementById("ddColor")
    cbo.AttachEvent("onchange", AddressOf ColorChanged)
End Sub

 

Once the user selects a color we will change the color of the ellipse.  Here is the complete code listing

 

Imports System.Windows.Browser

Partial Public Class Page
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub
    Dim cbo As HtmlElement
    Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        cbo = HtmlPage.Document.GetElementById("ddColor")
        cbo.AttachEvent("onchange", AddressOf ColorChanged)
    End Sub

    Private Sub ColorChanged(ByVal sender As Object, ByVal e As HtmlEventArgs)
        Dim x = CInt(cbo.GetAttribute("selectedIndex").ToString)
        Select Case x
            Case 0
                el.Fill = New SolidColorBrush(Colors.Red)
            Case 1
                el.Fill = New SolidColorBrush(Colors.Blue)
            Case 2
                el.Fill = New SolidColorBrush(Colors.Green)
        End Select
    End Sub
End Class

 

Hope this helps




Silverlight 2.0 Create a context menu


Silverlight 2 does not come with a context menu control.  You could always handle the html document's oncontextmenu event and open a popcontrol to use as a context menu.  This sample should help you get started.

 

<UserControl x:Class="SilverlightContextMenu.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid>
        <Rectangle x:Name="LayoutRoot" Fill ="Green" ></Rectangle >
        <Popup x:Name="menu">
            <StackPanel>
                <Button x:Name="btnRed" Content="Red"></Button>
                <Button x:Name="btnWhite" Content="White"></Button>
                <Button x:Name="btnBlue" Content="Blue"></Button>
            </StackPanel>
        </Popup>
    </Grid>
</UserControl>
Imports System.Windows.Browser

Partial Public Class Page
    Inherits UserControl

    Dim WithEvents cm As ContextMenuCatcher

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Sub btnBlue_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnBlue.Click
        LayoutRoot.fill = New SolidColorBrush(Colors.Blue)
        menu.IsOpen = False
    End Sub

    Private Sub btnRed_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnRed.Click
        LayoutRoot.fill = New SolidColorBrush(Colors.Red)
        menu.IsOpen = False
    End Sub

    Private Sub btnWhite_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnWhite.Click
        LayoutRoot.fill = New SolidColorBrush(Colors.White)
        menu.IsOpen = False
    End Sub

    Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        cm = New ContextMenuCatcher(LayoutRoot)
        menu.IsOpen = False
    End Sub

    Private Sub cm_RightClick(ByVal sender As Object, ByVal e As RightClickEventArgs) Handles cm.RightClick
        menu.HorizontalOffset = e.AbsolutePoint.X
        menu.VerticalOffset = e.AbsolutePoint.Y
        menu.IsOpen = True
    End Sub

    Private Sub menu_MouseLeave(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles menu.MouseLeave
        menu.IsOpen = False
    End Sub

End Class

Public Class ContextMenuCatcher
    Public Event RightClick(ByVal sender As Object, ByVal e As RightClickEventArgs)
    Dim ctrl As UIElement

    Public Sub New(ByVal c As UIElement)
        ctrl = c

        HtmlPage.Document.AttachEvent("oncontextmenu", AddressOf OnContextMenu)
    End Sub

    Private Sub OnContextMenu(ByVal sender As Object, ByVal e As HtmlEventArgs)
        System.Diagnostics.Debug.WriteLine(e.OffsetX.ToString)
        System.Diagnostics.Debug.WriteLine(e.OffsetY.ToString)
        Dim pt As New Point(e.OffsetX, e.OffsetY)
        e.PreventDefault()
        e.StopPropagation()
        RaiseEvent RightClick(Me, New RightClickEventArgs(ctrl, pt))
    End Sub

End Class

Public Class RightClickEventArgs

    Dim m_Source As UIElement
    Public Property Source() As UIElement
        Get
            Return m_Source
        End Get
        Set(ByVal value As UIElement)
            m_Source = value
        End Set
    End Property

    Dim m_RelativePoint As Point
    Public Property RelativePoint() As Point
        Get
            Return m_RelativePoint
        End Get
        Set(ByVal value As Point)
            m_RelativePoint = value
        End Set
    End Property

    Dim m_AbsolutePoint As Point
    Public Property AbsolutePoint() As Point
        Get
            Return m_AbsolutePoint
        End Get
        Set(ByVal value As Point)
            m_AbsolutePoint = value
        End Set
    End Property

    Friend Sub New(ByVal source As UIElement, ByVal absolutePoint As Point)
        Me.Source = source
        Me.AbsolutePoint = absolutePoint
        Me.RelativePoint = GetPosition(source)
    End Sub

    Public Function GetPosition(ByVal relativeTo As UIElement) As Point
        Dim transform As GeneralTransform = Application.Current.RootVisual.TransformToVisual(relativeTo)
        Return transform.Transform(AbsolutePoint)
    End Function
End Class
 
You also have to make the silverlight control windowless for this to work

<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightContextMenu.xap" MinimumVersion="2.0.30923.0" Width="100%" Height="100%" Windowless="true" />




Silverlight 2 RC0 DataGrid CommittingEdit work around


In Silverlight 2 Beta 2 DataGrid had a CommittingEdit event which was a great event to update the data in an ado.net dataservice.   Unfortunately this event was removed in the RC0 of the datagrid.  As a work around Jonathan Shen suggested using a template column and using the LostFocus event to update your dataservice data.

 

 

                 <data:DataGridTemplateColumn Header="Command">
                        <data:DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding FirstName}"></TextBlock>
                            </DataTemplate>
                        </data:DataGridTemplateColumn.CellTemplate>
                        <data:DataGridTemplateColumn.CellEditingTemplate>
                            <DataTemplate>
                                <TextBox Text="{Binding FirstName}" LostFocus="TextBox_LostFocus"></TextBox>   //you can detect other events.
                            </DataTemplate>
                        </data:DataGridTemplateColumn.CellEditingTemplate>
                    </data:DataGridTemplateColumn>

 

Well this works fine but I don't want to have to define all my columns this way.  Sometimes it is nice to just let the datagrid autogenerate the columns. So I decided to use the DataGrid's PreparingCellForEdit to add the handler for the LostFocus event.  Couple of other things you need to do.  First Remove the old event handler so the event does not fire multiple times.  Second we need a variable to keep track of the item that was edited when the lost focus event is fired we will be on another record.

 

Dim prod As Northwind.Products

Private Sub dgProducts_PreparingCellForEdit(ByVal sender As Object, ByVal e As System.Windows.Controls.DataGridPreparingCellForEditEventArgs) Handles dgProducts.PreparingCellForEdit
    RemoveHandler e.EditingElement.LostFocus, AddressOf Textbox_LostFocus
    AddHandler e.EditingElement.LostFocus, AddressOf Textbox_LostFocus
    prod = DirectCast(dgProducts.SelectedItem, Northwind.Products)
End Sub

Friend Sub Textbox_LostFocus(ByVal sender As Object, ByVal e As EventArgs)
    proxy.UpdateObject(prod)
End Sub

 

Here is the complete code

 

Imports System.Collections.ObjectModel
Imports System.Data.Services.Client
Imports System.Diagnostics

Partial Public Class Page
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub

    Dim q As DataServiceQuery(Of Northwind.Products)
    Dim proxy As Northwind.NorthwindEntities

    Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
        Dim address = New Uri(Application.Current.Host.Source, "../WebDataService1.svc")

        proxy = New Northwind.NorthwindEntities(address)

        Dim qry = From p In proxy.Products Select p
        q = CType(qry, Global.System.Data.Services.Client.DataServiceQuery(Of Northwind.Products))

        q.BeginExecute(AddressOf ProductsLoaded, Nothing)

    End Sub

    Friend Sub ProductsLoaded(ByVal ar As System.IAsyncResult)
        Dim c = q.EndExecute(ar)
        Dim d As New ObservableCollection(Of Northwind.Products)
        For Each p In c
            d.Add(p)
        Next
        Dim GetOnRightThread As New SetTheDataSource(AddressOf SetDataSource)
        Dispatcher.BeginInvoke(GetOnRightThread, New Object() {d})
    End Sub

    Delegate Sub SetTheDataSource(ByVal d As ObservableCollection(Of Northwind.Products))

    Friend Sub SetDataSource(ByVal d As ObservableCollection(Of Northwind.Products))
        dgProducts.ItemsSource = d
    End Sub

    Dim prod As Northwind.Products

    Private Sub dgProducts_PreparingCellForEdit(ByVal sender As Object, ByVal e As System.Windows.Controls.DataGridPreparingCellForEditEventArgs) Handles dgProducts.PreparingCellForEdit
        RemoveHandler e.EditingElement.LostFocus, AddressOf Textbox_LostFocus
        AddHandler e.EditingElement.LostFocus, AddressOf Textbox_LostFocus
        prod = DirectCast(dgProducts.SelectedItem, Northwind.Products)
    End Sub

    Friend Sub Textbox_LostFocus(ByVal sender As Object, ByVal e As EventArgs)
        proxy.UpdateObject(prod)
    End Sub

    Friend Sub SaveComplete(ByVal ar As System.IAsyncResult)
        proxy.EndSaveChanges(ar)
    End Sub

    Private Sub btnSave_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnSave.Click
        proxy.BeginSaveChanges(AddressOf SaveComplete, Nothing)
    End Sub
End Class


kick it on DotNetKicks.com



Asp.Net Routing


 

New to the .Net Framework 3.5 SP 1 is the System.Web.Routing namespace.  The classes in the routing namespace allow you to use urls that do not map to a web page.

 

 

For this example I created a new web application.  To start off with lets add a reference to the system.web.routing and system.web.abstractions.  Open up the web.config file and lets add the UrlRoutingModule to the httpmodules section

 

        <httpModules>
            <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </httpModules>

 

Next we need to add a WebFormRouteHandler class which will be our route handler.

 

Imports System.Web.Routing
Imports System.Web.Compilation

Public Class WebFormRouteHandler
    Implements IRouteHandler

    Private _Path As String
    Public Property Path() As String
        Get
            Return _Path
        End Get
        Set(ByVal value As String)
            _Path = value
        End Set
    End Property

    Public Sub New(ByVal p As String)
        Path = p
    End Sub

    Public Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler Implements System.Web.Routing.IRouteHandler.GetHttpHandler
        For Each value In requestContext.RouteData.Values
            requestContext.HttpContext.Items(value.Key) = value.Value
        Next
        If Not String.IsNullOrEmpty(Path) Then
            Return TryCast(BuildManager.CreateInstanceFromVirtualPath(Path, GetType(Page)), IHttpHandler)
        Else
            Return Nothing
        End If

    End Function
End Class

 

Finally we have to add global.asax so we can start the routing when the web site starts up

 

 

Imports System.Web.SessionState
Imports System.Web.Routing

Public Class Global_asax
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the application is started.
        Dim userHandler As New WebFormRouteHandler("~/user.aspx")
        With RouteTable.Routes

            ' pattern of the url to match
            .Add(New Route("user/{user}", userHandler))
        End With
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the session is started
    End Sub

    Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires at the beginning of each request
    End Sub

    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires upon attempting to authenticate the use
    End Sub

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when an error occurs
    End Sub

    Sub Session_End(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the session ends
    End Sub

    Sub Application_End(ByVal sender As Object, ByVal e As EventArgs)
        ' Fires when the application ends
    End Sub

End Class

 

Finally we need 2 web pages

 

Default.aspx

 

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="RoutingTest._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <h1>ASP.NET System.Web.Routing with WebForms sample</h1>
    <div><a href="user/ken">User test</a></div>
    </div>
    </form>
</body>
</html>

 

user.aspx

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="User.aspx.vb" Inherits="RoutingTest.User" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
            <h1>
            Users</h1>
        <div>
            User:
            <%=Context.Items("user")%></div>
        <hr />
        <div>
            Change the user in the URL.</div>
        <div>
            The WebFormRouteHandler adds the matched values to the HTTPContext.Items collection
            (exists for lifetime of request only).</div>
    </div>
    </form>
</body>
</html>

 

Hope this helps




1 Previous Next