Viewing TFS Shelveset Contents

August 27, 2008 11:47 by ccrews

Earlier today, a developer came by my desk wishing to know who had a number of files checked out which were part of a rather large project he had just completed.

A real quick glance from the Team Foundation Services Source Control explorer told me that, in fact, no one had those files checked out... yet Visual Studio was showing that they were.

Apparently when a file is a part of a shelveset, Visual Studio displays the icon identifying the file is checked out to another user.  The developer was concerned that all his changes might be accidentally overwritten when the shelveset was unshelved and wanted to know, for curiosity sake, who had shelved changes for those files.

Long story short... I was actually the person who had the files in a shelveset (it's amazing how sleep can make you forget what you did just the day before), but finding that information out was not a straight-forward task... and I wasn't about to attempt an "Unshelve" for every shelveset, for every developer in our company.

I ended up writing the stored procedure below (for the TfsVersionControl database) which produces a list of files for a given shelveset.  It allows me to query any shelveset by shelvesetName, ownerID, or filePath; these parameters are mutually exclusive and the procedure is written to handle any combination.  The shelvesetName is a direct comparison (not case sensitive) while the filePath is a wildcard search on both ends of the path.


   1:  CREATE PROCEDURE sp_QueryShelvesetContents (
   2:      @ownerID as int = NULL,
   3:      @shelvesetName as nvarchar(64) = NULL,
   4:      @filePath as nvarchar(520) = NULL
   5:  )
   6:  AS
   7:      SET NOCOUNT ON
   8:   
   9:      BEGIN
  10:          --Prep the filePath for a LIKE statement
  11:          IF @filePath IS NULL            SET @filePath = '%$%'
  12:          SET @filePath = LTrim(RTrim(@filePath))
  13:          IF LEFT(@filePath, 1) <> '%'    SET @filePath = '%' + @filePath
  14:          IF RIGHT(@filePath, 1) <> '%'    SET @filePath = @filePath + '%'
  15:   
  16:          --Create a temp table to hold workspace IDs
  17:          CREATE TABLE #tmpWorkspaces (WorkspaceID int, OwnerID int)
  18:   
  19:          IF @shelvesetName IS NOT NULL
  20:              BEGIN
  21:                  --Add all shelvesets which match the shelveset name
  22:                  INSERT INTO #tmpWorkspaces
  23:                  SELECT WorkspaceID, OwnerID FROM dbo.tbl_Workspace WHERE WorkspaceName = @shelvesetName AND Type = 1
  24:              END
  25:          
  26:          IF @ownerID IS NOT NULL
  27:              BEGIN
  28:                  --Remove any shelvesets which don't match the owner passed in (even if it matched the workspace name)
  29:                  DELETE FROM #tmpWorkspaces WHERE OwnerID <> @ownerID
  30:                  
  31:                  IF @shelvesetName IS NULL
  32:                      BEGIN
  33:                          --Add all shelvesets which match the ownerid (and aren't already in the table)
  34:                          INSERT INTO #tmpWorkspaces
  35:                          SELECT W.WorkspaceID, W.OwnerID
  36:                          FROM dbo.tbl_Workspace AS W
  37:                          LEFT JOIN #tmpWorkspaces AS TW ON TW.WorkspaceID = W.WorkspaceID
  38:                          WHERE W.OwnerID = @ownerID AND TW.WorkspaceID IS NULL
  39:                      END
  40:          END
  41:   
  42:          IF (SELECT COUNT(WorkspaceID) FROM #tmpWorkspaces) > 0
  43:              BEGIN
  44:                  SELECT WS.WorkspaceID, WS.WorkspaceName AS ShelvesetName, I.IdentityID, I.DisplayName, PC.TargetServerItem
  45:                  FROM dbo.tbl_PendingChange AS PC
  46:                  INNER JOIN #tmpWorkspaces AS TW ON TW.WorkspaceID = PC.WorkspaceID
  47:                  INNER JOIN dbo.tbl_Workspace AS WS ON WS.WorkspaceID = TW.WorkspaceID
  48:                  INNER JOIN dbo.tbl_Identity AS I ON I.IdentityID = WS.OwnerID
  49:                  WHERE TargetServerItem LIKE @filePath
  50:              END
  51:          ELSE
  52:              BEGIN
  53:                  SELECT WS.WorkspaceID, WS.WorkspaceName AS ShelvesetName, I.IdentityID, I.DisplayName, PC.TargetServerItem
  54:                  FROM dbo.tbl_PendingChange AS PC
  55:                  INNER JOIN dbo.tbl_Workspace AS WS ON WS.WorkspaceID = PC.WorkspaceID AND WS.Type = 1
  56:                  INNER JOIN dbo.tbl_Identity AS I ON I.IdentityID = WS.OwnerID
  57:                  WHERE TargetServerItem LIKE @filePath
  58:              END        
  59:      END
  60:   
  61:      DROP TABLE #tmpWorkspaces

Examples

Example 1:  List of all files in your DEV branch which are part of any shelveset

	exec sp_QueryShelvesetContents @filePath = 'dev'				
	

Example 2:  List of all files in your DEV branch which are part of a shelveset for ownerID 5

	exec sp_QueryShelvesetContents @ownerID  = 5				
	

Example 3:  List all GridView.vb files which are part of any shelveset

	exec sp_QueryShelvesetContents @filePath = 'GridView.vb'				
	

sp_QueryShelvesetContents.zip (903.00 bytes)


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Show UpdateProgress when using an UpdatePanel with Triggers

August 23, 2008 14:05 by wjchristenson2

with ASP.NET AJAX Extensions 1.0 for ASP .NET 2.0...

You may have come across a situation where an UpdatePanel performs a partial-page update and the associated UpdateProgress control does not display.  The culprit is UpdatePanel AsyncPostbackTriggers.  If an UpdatePanel's partial-page update was triggered by a control outside the UpdatePanel, the UpdateProgress control is oblivious to this fact.  The workaround that I've found is to get a handle to the instance of the PageRequestManager class and add our own events to show and hide the UpdateProgress ourselves via JavaScript before and after the triggered request.

We are going to create a page that will add 2 numbers and show the calculated result.  We will have a TextBox for number 1, a TextBox for number 2, and a label to show the result all within an UpdatePanel.  The calculate Button will be outside the UpdatePanel and will thus be our AsyncPostBackTrigger.  We then have our UpdateProgress control that we want shown while the webpage calculates our results.  Here is the HTML markup to accomplish our base page.

 

   1:          <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
   2:            <Triggers>
   3:              <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
   4:            </Triggers>
   5:            <ContentTemplate>
   6:              <asp:TextBox ID="tbxValue1" runat="server" Text="1" Width="50" /> + <asp:TextBox ID="tbxValue2" runat="server" Text="1" Width="50" /> = <asp:Label ID="lblResult" runat="server" Text="2" />
   7:            </ContentTemplate>
   8:          </asp:UpdatePanel>
   9:          
  10:          <asp:Button ID="Button1" runat="server" Text="Calculate" />
  11:          
  12:          <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="UpdatePanel1" DynamicLayout="true" DisplayAfter="0">
  13:            <ProgressTemplate>
  14:              <div style="background-color: #ffffc9;">Calculating...<div>
  15:            </ProgressTemplate>
  16:          </asp:UpdateProgress>

Now that we have our markup finished, let's handle the click event of our calculate button.  To see the UpdateProgress control for a longer duration, I went ahead and added a sleep timer of 3 seconds to delay the calculation.

 

   1:      Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
   2:          System.Threading.Thread.Sleep(3000)
   3:          lblResult.Text = (CInt(tbxValue1.Text) + CInt(tbxValue2.Text)).ToString()
   4:      End Sub

Now for the actual meat of this post.  The following JavaScript calls the getInstance() method of the PageRequestManager object to get the instance of the PageRequestManager class.  We then add our functions to be called when the the request is initialized and ended.  This will allow us to show our UpdateProgress control when the request is initialized and then hide it again when the request is ended.  Here is the JavaScript to accomplish this.

 

   1:  <script type="text/javascript" language="javascript">
   2:  <!-- 
   3:   
   4:  var prm = Sys.WebForms.PageRequestManager.getInstance();
   5:  var postBackElement;
   6:   
   7:  function CancelAsyncPostBack() {
   8:    if (prm.get_isInAsyncPostBack()) {
   9:      prm.abortPostBack();
  10:    }
  11:  }
  12:   
  13:  prm.add_initializeRequest(InitializeRequest);
  14:  prm.add_endRequest(EndRequest);
  15:   
  16:  function InitializeRequest(sender, args) {
  17:    if (prm.get_isInAsyncPostBack()) {
  18:        args.set_cancel(true);
  19:    }
  20:    postBackElement = args.get_postBackElement();
  21:    if (postBackElement.id == 'Button1') {
  22:      $get('UpdateProgress1').style.display = 'block'; 
  23:    }
  24:  }
  25:  function EndRequest(sender, args) {
  26:    if (postBackElement.id == 'Button1') {
  27:      $get('UpdateProgress1').style.display = 'none';
  28:    }
  29:  }
  30:   
  31:  // -->
  32:  </script>

 

Note that we acquire what element fired the request.  We only want to show/hide our UpdateProgress control if the postBackElement was our calculate button.

 

TriggersUpdateProgress_Soln.zip (45.30 kb)


Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Silverlight Limitations

August 21, 2008 06:59 by wjchristenson2

Over the past couple of weeks, I've been working daily with Silverlight 2.0 b2.  I've attempted to do certain things in XAML and in my code that simply were not supported.  I know these features were available to me when I did some WPF development awhile ago and I know that some of the .NET objects I wanted to use programmatically are available in the .NET framework, however they are not available in Silverlight. 

What I found first, was a limitation of triggers.  I went to create a simple style change when the user hovered over a button (a style trigger).  I quickly found out that style triggers are not supported.  Digging a little deeper, I found out that Silverlight 2 b2 only supports the EventTrigger.

I then went to use an ArrayList and it was not supported.  I found a nice article written by Inbar Gazit of the Microsoft's Base Class Library team.  A few non-generic collections of the .NET framework are not supported in Silverlight.

Collections Not Supported by Silverlight:

  • ArrayList
  • BitArray
  • CaseInsensitiveComparer
  • CaseInsensitiveHashCodeProvider
  • CollectionBase
  • Comparer
  • CompatibleComparer
  • DictionaryBase
  • EmptyReadOnlyDictionaryInternal
  • Hashtable
  • IHashCodeProvider
  • KeyValuePairs
  • ListDictionaryInternal
  • Queue
  • ReadOnlyCollectionBase
  • SortedList
  • Stack
  • Inbar explains, "Three other generic types were also removed. Queue, Stack and LinkedList were removed from Silverlight. In this case it wasn't because they were non-generic but because they are not considered to be part of the core set of types that we deemed essential to be provided with Silverlight. Remember that Silverlight is a very small download and should only include the smallest set of APIs that will allow for useful development. It's very simple to implement Queue and Stack using List and LinkedList is just a different implementation of List with different performance characteristics and so it's not an essential part of our core collections group."


    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Silverlight Sortables

    August 19, 2008 09:02 by wjchristenson2

    If any of you have ever developed with the jQuery UI framework, you have probably seen or used their "Sortables".  A sortable is a list of items that you can drag around & adjust their sort order.  In this post, I am going to show you how we can sort Border objects within a StackPanel.  Here is a screen shot of what we are after:

    When the user drags an item over another, they will swap places.  A transparent placeholder will represent the current item being dragged.  First, let's setup our XAML page before we get into actual code.


       1:  <UserControl x:Class="Sortables.Page"
    
       2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    
       3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    
       4:      Width="400" Height="300">
    
       5:      <UserControl.Resources>
    
       6:          <Style x:Key="borderStyle" TargetType="Border">
    
       7:              <Setter Property="Width" Value="220" />
    
       8:              <Setter Property="Height" Value="50" />
    
       9:              <Setter Property="Margin" Value="5,5,5,0" />
    
      10:              <Setter Property="BorderBrush" Value="Black" />
    
      11:              <Setter Property="Background" Value="SteelBlue" />
    
      12:              <Setter Property="BorderThickness" Value="1" />
    
      13:              <Setter Property="CornerRadius" Value="10" />
    
      14:              <Setter Property="HorizontalAlignment" Value="Left" />
    
      15:          </Style>
    
      16:      </UserControl.Resources>
    
      17:      
    
      18:      <Canvas x:Name="LayoutRoot" Background="White">
    
      19:          <StackPanel x:Name="StackPanel1" Orientation="Vertical" Width="250">
    
      20:              <Border x:Name="border1" Style="{StaticResource borderStyle}">
    
      21:                  <TextBlock Text="Item 1" Foreground="White" Margin="0,10,0,0" HorizontalAlignment="Center" />
    
      22:              </Border>
    
      23:              <Border x:Name="border2" Style="{StaticResource borderStyle}">
    
      24:                  <TextBlock Text="Item 2" Foreground="White" Margin="0,10,0,0" HorizontalAlignment="Center" />
    
      25:              </Border>
    
      26:              <Border x:Name="border3" Style="{StaticResource borderStyle}">
    
      27:                  <TextBlock Text="Item 3" Foreground="White" Margin="0,10,0,0" HorizontalAlignment="Center" />
    
      28:              </Border>
    
      29:              <Border x:Name="border4" Style="{StaticResource borderStyle}">
    
      30:                  <TextBlock Text="Item 4" Foreground="White" Margin="0,10,0,0" HorizontalAlignment="Center" />
    
      31:              </Border>
    
      32:          </StackPanel>
    
      33:      </Canvas>
    
      34:  </UserControl>
    

    You can see we have a Canvas that has a StackPanel with 4 border objects.  We will be dragging (sorting) the items within the StackPanel.  This example builds off my previous post Drag and Drop in Silverlight.  I'm not going to review the code for dragging and dropping, so if you are having problems, revisit the previous tutorial.  The Border items in the StackPanel will utilize all of my basic drag & drop code from before, but it will also perform extra logic for the "sorting" abilities.  Let's first define our variables.


       1:      Private isMouseDown As Boolean = False
    
       2:      Private mousePosition As Point = Nothing
    
       3:      Private itemIndex As Integer = -1   'used to keep track of the index of the item we are dragging
    

    The itemIndex variable is used to keep track of what index the dragged item should be tied to within the StackPanel.  The other 2 variables are for dragging the item.  When the page is loaded, we'll wire in the necessary events for each Border object within our StackPanel.


       1:      Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
       2:          Me.InitializeComponent()
    
       3:   
    
       4:          For Each element As UIElement In StackPanel1.Children
    
       5:              If TypeOf (element) Is Border Then
    
       6:                  Dim item As Border = DirectCast(element, Border)
    
       7:                  AddHandler item.MouseLeftButtonDown, AddressOf item_MouseLeftButtonDown
    
       8:                  AddHandler item.MouseLeftButtonUp, AddressOf item_MouseLeftButtonUp
    
       9:                  AddHandler item.MouseMove, AddressOf item_MouseMove
    
      10:              End If
    
      11:          Next
    
      12:      End Sub
    

    When the user clicks a Border object within our StackPanel, well do our basic drag & drop routines and then we'll also add to that.  First we'll remove the Border object from the StackPanel and add it to our LayoutRoot (parent).  What this does is allow us to drag the Border object around the screen.  We also want to add a placeholder where the Border object used to be in our StackPanel.  I'll be using a transparent Rectangle object to accomplish this.  Here's the logic for the MouseLeftButtonDown event.


       1:      Sub item_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Border = DirectCast(sender, Border)
    
       3:          mousePosition = e.GetPosition(Nothing)
    
       4:          Me.isMouseDown = True
    
       5:          item.CaptureMouse()
    
       6:   
    
       7:          'get the item's position relative to the LayoutRoot canvas object
    
       8:          Dim newTopLeft As Point = GetRelativePosition(item, LayoutRoot)
    
       9:          item.SetValue(Canvas.TopProperty, newTopLeft.Y - item.Margin.Top)
    
      10:          item.SetValue(Canvas.LeftProperty, newTopLeft.X - item.Margin.Left)
    
      11:          item.SetValue(Canvas.ZIndexProperty, 2000)
    
      12:   
    
      13:          'define a rectangle placeholder for the item we are dragging
    
      14:          Dim recPlaceHolder As Rectangle = New Rectangle()
    
      15:          With recPlaceHolder
    
      16:              .Fill = New SolidColorBrush(Colors.Transparent)
    
      17:              .Margin = New Thickness(5, 5, 5, 0)
    
      18:              .Width = 220
    
      19:              .Height = 50
    
      20:              .HorizontalAlignment = Windows.HorizontalAlignment.Left
    
      21:          End With
    
      22:   
    
      23:          'remove the item we are dragging and insert our placeholder
    
      24:          itemIndex = StackPanel1.Children.IndexOf(item)
    
      25:          StackPanel1.Children.Remove(item)
    
      26:          StackPanel1.Children.Insert(itemIndex, recPlaceHolder)
    
      27:          LayoutRoot.Children.Add(item)
    
      28:      End Sub
    

    At this point, we have a placeholder inserted into our StackPanel where our dragged Border object used to be.  As we drag our Border object around the screen, we want to perform a "HitTest" to see if we are hovering over another Border object within the StackPanel.  If we are, we'll want to switch the placeholder with the hovered over Border object.  We also want to keep track of the new index for the dragged item.


       1:      Sub item_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Border = DirectCast(sender, Border)
    
       3:   
    
       4:          If (Me.isMouseDown) Then
    
       5:              Dim deltaV As Double = e.GetPosition(Nothing).Y - mousePosition.Y
    
       6:              Dim deltaH As Double = e.GetPosition(Nothing).X - mousePosition.X
    
       7:              Dim newTop As Double = deltaV + DirectCast(item.GetValue(Canvas.TopProperty), Double)
    
       8:              Dim newLeft As Double = deltaH + DirectCast(item.GetValue(Canvas.LeftProperty), Double)
    
       9:              item.SetValue(Canvas.TopProperty, newTop)
    
      10:              item.SetValue(Canvas.LeftProperty, newLeft)
    
      11:              mousePosition = e.GetPosition(Nothing)
    
      12:   
    
      13:              'run a hit test to see if the dragged item is hovering over another item in the StackPanel
    
      14:              For Each element As UIElement In HitTest(New Point(e.GetPosition(Nothing).X, e.GetPosition(Nothing).Y))
    
      15:                  If TypeOf (element) Is Border AndAlso TypeOf (DirectCast(element, Border).Parent) Is StackPanel AndAlso DirectCast(DirectCast(element, Border).Parent, StackPanel).Name = "StackPanel1" Then
    
      16:                      'item order changed - switch placeholder with hovered over item
    
      17:                      Dim newItemIndex = StackPanel1.Children.IndexOf(DirectCast(element, Border))
    
      18:                      Dim recPlaceHolder As Rectangle = DirectCast(StackPanel1.Children(itemIndex), Rectangle)
    
      19:                      StackPanel1.Children.RemoveAt(itemIndex)
    
      20:                      StackPanel1.Children.Insert(newItemIndex, recPlaceHolder)
    
      21:                      itemIndex = newItemIndex
    
      22:                      Exit For
    
      23:                  End If
    
      24:              Next
    
      25:          End If
    
      26:      End Sub
    

    When the user stops dragging their item, we want to snap the dragged item back into place within the StackPanel.  To accomplish this, we will remove the placeholder Rectangle object and insert the dragged Border object where the placeholder was within the StackPanel.  Here is the final event we handle and we are finished.


       1:      Sub item_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Border = DirectCast(sender, Border)
    
       3:          isMouseDown = False
    
       4:          mousePosition = New Point(0, 0)
    
       5:          item.ReleaseMouseCapture()
    
       6:   
    
       7:          'replace the placeholder with the dragged item
    
       8:          LayoutRoot.Children.Remove(item)
    
       9:          StackPanel1.Children.RemoveAt(itemIndex)
    
      10:          StackPanel1.Children.Insert(itemIndex, item)
    
      11:      End Sub
    

    Sortables.zip (551.97 kb)


    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Drag and Drop in Silverlight

    August 16, 2008 14:19 by wjchristenson2

    In this example, I am going to show you how to drag and drop objects in a Silverlight Application.  To make it a little more fun, we are going to be dragging ducks around a Duck Hunt for NES canvas.  Bring back the memories?  The following code snippet is our XAML document that gives us 2 ducks on our page.

     

       1:  <UserControl x:Class="Drag_N_Drop.Page"
    
       2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    
       3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    
       4:      xmlns:local="clr-namespace:Drag_N_Drop"
    
       5:      Width="500" Height="438">
    
       6:      <Canvas x:Name="LayoutRoot">
    
       7:          <Canvas.Background>
    
       8:              <ImageBrush ImageSource="Images/canvas.png" />
    
       9:          </Canvas.Background>
    
      10:          
    
      11:          <local:Duck Canvas.Left="140" Canvas.Top="20" />
    
      12:          <local:Duck Canvas.Left="240" Canvas.Top="20" />
    
      13:      </Canvas>
    
      14:  </UserControl>
    

    So we have 2 ducks on a canvas.  The next step is to wire each duck up so that when the user drags a duck across the screen, the duck moves with the cursor until they let go.  First we define our variables.  We need a variable to hold the X and Y coordinates of the mouse when we begin dragging and another variable to track if the user has their left mouse button down or not.

     

       1:  Private isMouseDown As Boolean = False
    
       2:  Private mousePosition As Point = Nothing
    

    We want to handle 3 mouse events for each duck: MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp.  When the page is loaded, I loop through through each duck on the canvas and add these 3 event handlers for them.

     

       1:      Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
       2:          Me.InitializeComponent()
    
       3:   
    
       4:          For Each element As UIElement In LayoutRoot.Children
    
       5:              If TypeOf (element) Is Duck Then
    
       6:                  Dim item As Duck = DirectCast(element, Duck)
    
       7:                  AddHandler item.MouseLeftButtonDown, AddressOf duck_MouseLeftButtonDown
    
       8:                  AddHandler item.MouseLeftButtonUp, AddressOf duck_MouseLeftButtonUp
    
       9:                  AddHandler item.MouseMove, AddressOf duck_MouseMove
    
      10:              End If
    
      11:          Next
    
      12:      End Sub
    

    When the user presses the left mouse button on a duck, we need to capture what the current X and Y coordinates are, set that the mouse left button is down, and capture the mouse events for the duck.

     

       1:      Sub duck_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Duck = DirectCast(sender, Duck)
    
       3:          mousePosition = e.GetPosition(Nothing)
    
       4:          Me.isMouseDown = True
    
       5:          item.CaptureMouse()
    
       6:      End Sub
    

    Take note of line 5.  The item.CaptureMouse() method basically tells Silverlight to only handle mouse events for the duck we are dragging.  As the user moves the mouse, the MouseMove event is fired.  This event will fire regardless if the user is dragging the duck or not.  Therefore we check to see if the left mouse button is down first.  If it is, it's being dragged so we want to refresh the duck's new position.  We use the current position of the mouse and calculate what the new position the duck should be set to relative to the canvas.


       1:      Sub duck_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Duck = DirectCast(sender, Duck)
    
       3:   
    
       4:          If (Me.isMouseDown) Then
    
       5:              Dim deltaV As Double = e.GetPosition(Nothing).Y - mousePosition.Y
    
       6:              Dim deltaH As Double = e.GetPosition(Nothing).X - mousePosition.X
    
       7:              Dim newTop As Double = deltaV + DirectCast(item.GetValue(Canvas.TopProperty), Double)
    
       8:              Dim newLeft As Double = deltaH + DirectCast(item.GetValue(Canvas.LeftProperty), Double)
    
       9:              item.SetValue(Canvas.TopProperty, newTop)
    
      10:              item.SetValue(Canvas.LeftProperty, newLeft)
    
      11:              mousePosition = e.GetPosition(Nothing)
    
      12:          End If
    
      13:      End Sub
    

    When the user releases the left mouse button, we release the mouse capture and set our isMouseDown variable back to false.

     

       1:      Sub duck_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseEventArgs)
    
       2:          Dim item As Duck = DirectCast(sender, Duck)
    
       3:          isMouseDown = False
    
       4:          mousePosition = New Point(0, 0)
    
       5:          item.ReleaseMouseCapture()
    
       6:      End Sub
    


    The final application looks like below.  You can drag the ducks around the canvas.

    Drag-N-Drop_Soln.zip (2.11 mb)


    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Call Javascript Method from Silverlight and Vice Versa

    August 11, 2008 14:49 by wjchristenson2

    Silverlight integrates seamlessly with Javascript and ASP.NET AJAX.  In this post, I am going to show you how to communicate back and forth between Silverlight and Javascript.  Silverlight managed code can interact with JavaScript by utilizing the classes within the System.Windows.Browser namespace.  Below is the markup for both the Silverlight application as well as the ASP.NET markup for our example.

    Silverlight XAML:

       1:      <Grid x:Name="LayoutRoot" Background="Silver">
    
       2:          <Grid.RowDefinitions>
    
       3:              <RowDefinition Height="25" />
    
       4:              <RowDefinition Height="25" />
    
       5:          </Grid.RowDefinitions>
    
       6:          <Grid.ColumnDefinitions>
    
       7:              <ColumnDefinition Width="150" />
    
       8:              <ColumnDefinition Width="150" />
    
       9:          </Grid.ColumnDefinitions>
    
      10:   
    
      11:          <TextBlock x:Name="tbkTest" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Foreground="Black">HELLO WORLD!!</TextBlock>
    
      12:          <TextBox x:Name="tbxText" Grid.Column="0" Grid.Row="1"></TextBox>
    
      13:          <Button x:Name="btnFire" Grid.Column="1" Grid.Row="1" Content="Fire JavaScript Alert"></Button>
    
      14:      </Grid>
    

    ASP.NET HTML:

       1:         <div  style="height:100%;">
    
       2:            <div>
    
       3:              <asp:TextBox ID="tbxText" runat="server" />
    
       4:              <input type="button" value="Set Silverlight Text" onclick="setText();" />
    
       5:            </div>
    
       6:          
    
       7:            <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/SilverlightJScriptInterop_App.xap" MinimumVersion="2.0.30523" Width="100%" Height="100%" />
    
       8:          </div>
    

    My plan is to:
    1)  Change the Silverlight TextBlock's text to the text I type into the ASP.NET TextBox when the user clicks the HTML button.
    2)  Fire a JavaScript alert consisting of the text I type into my Silverlight TextBox when the user clicks the Silverlight "btnFire" button.

     

    Call Silverlight Method from Javascript

    Using Javascript to interact with Silverlight is easy to do.  The first thing you need to do is expose objects of your Silverlight application so that JavaScript can interact with your managed code without requiring a round trip (PostBack) to the server.

       1:  Imports System.Windows.Browser
    
       2:   
    
       3:  <ScriptableType()> _
    
       4:  Partial Public Class Page
    
       5:      Inherits UserControl
    
       6:   
    
       7:      Public Sub New()
    
       8:          InitializeComponent()
    
       9:      End Sub
    
      10:   
    
      11:      Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    
      12:          'register silverlight object on the page for javascript to use
    
      13:          HtmlPage.RegisterScriptableObject("myObject", Me)
    
      14:      End Sub
    
      15:   
    
      16:      <ScriptableMember()> _
    
      17:      Public Sub SetText(ByVal text As String)
    
      18:          Me.tbkTest.Text = text
    
      19:      End Sub
    
      20:   
    
      21:      Private Sub btnFire_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnFire.Click
    
      22:          HtmlPage.Window.Invoke("alertText", Me.tbxText.Text)
    
      23:      End Sub
    
      24:  End Class
    

    Remember that the System.Windows.Browser namespace needs to be utilized so that Silverlight can interact with JavaScript.  You'll notice on line 3 that we give our class a ScriptableType() attribute so that we can interact with it via JavaScript.  Anything we wish to expose to JavaScript is also given the ScriptableMember attribute.  On line 13 you'll notice that we register an object on the page for JavaScript to use to access our exposed Silverlight objects.  So let's have some JavaScript pass in a string to our "SetText()" Silverlight method.

       1:  <script type="text/javascript" language="javascript">
    
       2:  function setText() {
    
       3:    var pluginObject = $find("<%=Xaml1.ClientID%>");
    
       4:    var plugin = pluginObject.get_element();
    
       5:    plugin.Content.myObject.SetText(getText());
    
       6:  }
    
       7:  function getText() {
    
       8:    var obj = document.getElementById("<%=tbxText.ClientID%>");
    
       9:    return obj.value;
    
      10:  }
    
      11:  function alertText(text) {
    
      12:    alert(text);
    
      13:  }
    
      14:  </script>
    

    When the user clicks on my HTML button, I call the setText() JavaScript function.  In line 3 we first get a handle to the Silverlight plugin.  We then get the reference to the actual Silverlight plugin element within the page.  Now we can access the exposed objects of our Silverlight application by using the object name we setup in the  Page_Loaded event.  Remember that we had the "SetText()" method that set the TextBlock's text.  We call that method on line 5 and pass in the text entered via the TextBox on the page.

     

    Call JavaScript Method from Silverlight

    Calling a JavaScript method from Silverlight is easy.  The HtmlPage.Window.Invoke method invokes a method on the current scriptable object and you can pass in one or more parameters if you wish.  In our example we wanted to pass in a string from our Silverlight application to our JavaScript function and alert the string.  First is the Silverlight managed code to invoke the JavaScript function and the second code snippet is the JavaScript function we invoked.

       1:      Private Sub btnFire_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnFire.Click
    
       2:          HtmlPage.Window.Invoke("alertText", Me.tbxText.Text)
    
       3:      End Sub
    

     

       1:  function alertText(text) {
    
       2:    alert(text);
    
       3:  }
    

    I've posted the solution so you can see the code in its entirety and see it in action.

    SilverlightJScriptInterop_Soln.zip (541.66 kb)


    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Silverlight Introduction

    August 9, 2008 09:22 by wjchristenson2

    Silverlight is often referred to as Microsoft's Flash.  Microsoft needed a cross-platform cross-browser implementation of the .NET Framework for building and delivering next generation media experiences and Rich Interactive Applications (RIA) for the web.  Playing WMV video, MP3 and WMA audio, progressively downloading & streaming media , vector drawing and animations, etc are all supported by Silverlight.

    Developers can write Silverlight applications in any .NET language and/or they can write them in entirely in XAML.  Silverlight integrates seamlessly with Javascript and ASP.NET AJAX.  For instance, a Silverlight function can call a Javascript function and vice versa.  Silverlight is also delivered to the browser in XAML.  This is interesting to me because search engines can then crawl the Silverlight object's XAML.  Therefore Silverlight application content can be easily indexed by search engines and thus are more findable than Adobe Flash movie content.

    I plan to start developing with Silverlight in the near future.  I'll be blogging on my experiences as time permits.


    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Bind a Collection to a GridView

    August 8, 2008 10:41 by wjchristenson2

    Over the past few months I've had some requests to show how you can bind a custom object collection to a GridView.  When looking deeper into the collections, I noticed that the developers were working way to hard to create their collection class.   The collections were inheriting from System.Collections.Specialized.NameObjectCollectionBase and implementing IEnumerator, IEnumerable, & IUpdatable, etc to accomplish some simple tasks (ie: add, remove, for each loops, etc).  When implementing these interfaces, the developers were manually having to wire everything in.  On top of this, they were having problems binding to .NET controls.  Another quick point to make here is that the specialized collection does type casting at run time.

    What I proposed was to inherit from System.Collections.Generic.List instead.  What this does is allow them to do their basic add, remove, sort, and manipulate their lists.  It also is strongly typed and can be accessed by index.  Generics provide better type safety and performance than non-generic collections (ie NameObjectCollectionBase mentioned above).

    I'm going to create a simple vehicle collection and bind a GridView to it using the Generic.List.


       1:  Public Class Vehicle
    
       2:      Private _Make As String = String.Empty
    
       3:      Private _Model As String = String.Empty
    
       4:   
    
       5:      Public Property Make() As String
    
       6:          Get
    
       7:              Return _Make
    
       8:          End Get
    
       9:          Set(ByVal value As String)
    
      10:              _Make = value
    
      11:          End Set
    
      12:      End Property
    
      13:   
    
      14:      Public Property Model() As String
    
      15:          Get
    
      16:              Return _Model
    
      17:          End Get
    
      18:          Set(ByVal value As String)
    
      19:              _Model = value
    
      20:          End Set
    
      21:      End Property
    
      22:   
    
      23:      Public Sub New()
    
      24:   
    
      25:      End Sub
    
      26:   
    
      27:      Public Sub New(ByVal make As String, ByVal model As String)
    
      28:          _Make = make
    
      29:          _Model = model
    
      30:      End Sub
    
      31:  End Class
    

    Here is the vehicle class (object) that we'll reference in our collection.  Now for the collection.


       1:  Imports System.Collections.Generic
    
       2:   
    
       3:  Public Class Vehicles
    
       4:      Inherits List(Of Vehicle)
    
       5:   
    
       6:  #Region "Constructors"
    
       7:      Public Sub New()
    
       8:          MyBase.New()
    
       9:      End Sub
    
      10:   
    
      11:      Public Sub New(ByVal capacity As Integer)
    
      12:          MyBase.New(capacity)
    
      13:      End Sub
    
      14:   
    
      15:      Public Sub New(ByVal collection As IEnumerable(Of Vehicle))
    
      16:          MyBase.New(collection)
    
      17:      End Sub
    
      18:  #End Region
    
      19:   
    
      20:  End Class
    

    You'll notice how we inherit from System.Collections.Generic.List and cast the collection as a type of Vehicle.  It's as simple as that.  I added 3 constructors for your own reference.  Now all we have to do is add some data to the collection and bind a GridView to it.


       1:  Imports CollectionDataBinding.BLL
    
       2:   
    
       3:  Partial Public Class _Default
    
       4:      Inherits System.Web.UI.Page
    
       5:   
    
       6:      Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
       7:          If Not Page.IsPostBack() Then
    
       8:              GridView1_DataBind()
    
       9:          End If
    
      10:      End Sub
    
      11:   
    
      12:      Private Sub GridView1_DataBind()
    
      13:          Dim myVehicles As Vehicles = New Vehicles()
    
      14:          myVehicles.Add(New Vehicle("Ford", "Mustang"))
    
      15:          myVehicles.Add(New Vehicle("Pontiac", "Grand AM"))
    
      16:   
    
      17:          Me.GridView1.DataSource = myVehicles
    
      18:          Me.GridView1.DataBind()
    
      19:      End Sub
    
      20:  End Class
    

    On page load, I bind the GridView if it is not a postback.  I create a new instance of the Vehicles collection and add 2 vehicles to it.  I then simply set the data source of the GridView to the Vehicle collection and call DataBind().  Now, each public property will be mapped and shown in your GridView if you have AutoGenerateColumns="true" set.  If you don't do this, you'd approach the custom column mappings the same way as with a DataTable, DataView, etc (TemplateField, Eval expressions).

    CollectionDataBinding_Soln.zip (89.23 kb)

    >>>>> Edit 8/21/2008 <<<<<

    I found another article that relates to this post.  It's a great reference as to the benefits of using generic collections.  The article lists the following reasons why you should use generic collections.

    1. Readability and simplicity of your code. Let’s take the case of getting the first string in a list of strings. With generics you would declare List<T> myList = new List<T>(); and then say string firstString = myList[0] just like working with arrays. This syntax is much more simple and readable than what you used to have to write which was ArrayList myList = new ArrayList(); and then string firstString = myList[0] as string;
    2. Performance. Every time you add a value type to a non-generic collection you have to box it to make it an object and every time you retrieve a value type from a non-generic collection you have to unbox it back from object. Even with reference type you still need to cast back to the correct type when retrieving items.
    3. Working against new smaller Framework SKUs. In Silverlight we decided to remove all the concrete non-generic collections completely from the codebase. This is mostly because the Silverlight core managed libraries is set to be the smallest useful set of classes. It’s also possible that other future small frameworks will not have the non-generic collections in store. If you ever plan to write code for those frameworks — you should convert it to use the generic collections.
    4. Better type-safe libraries. If you are developing libraries to be consumed by 3rd parties you should most definitely use generic collections when possible. This would allow consumer of your libraries to quickly figure out what’s expected to be stored in the collections instead of having to guess or figure out from documentation.

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Custom GridView Paging

    August 7, 2008 07:00 by wjchristenson2

    One of the first topics I searched for when working with the ASP.NET GridView control was how to page data with it.  I quickly discovered that the GridView control has built-in paging capabilities.  However, I wanted to customize my GridView pager.  The built-in pager just didn't cut it for me.  Not only did I want to customize the pager, I didn't want to have to define my pager template and wire everything in each time I used my GridView control because it would be used 100's of times.

    In order to encapsulate the paging features I desire, I am going to make my own custom GridView control.  In my pager I want the following:
    1) A "Page X of Y" caption
    2) Basic first, previous, next, and last navigation
    3) A page selector drop down list so the user can jump to a page quickly

    If you've used the GridView before, you may have utilized the built-in paging modes.  I don't necessarily want to remove that functionality.  I want to add to it.  So what I am going to do is create a public property called "PagerType" where we can specify if we want to use the GridView's built-in paging or if we want to use our custom paging.


       1:  Public Class PagingGridView
    
       2:      Inherits GridView
    
       3:   
    
       4:      Public Enum PagerTypes
    
       5:          Regular = 0
    
       6:          Custom = 1
    
       7:      End Enum
    
       8:   
    
       9:      <DefaultValue(PagerTypes.Custom), Category("Paging"), Description("Indicates whether to use the built-in custom pager or not.")> _
    
      10:      Public Property PagerType() As PagerTypes
    
      11:          Get
    
      12:              If Not ViewState("PagerType") Is Nothing Then
    
      13:                  Return DirectCast(ViewState("PagerType"), PagerTypes)
    
      14:              Else
    
      15:                  Return PagerTypes.Custom
    
      16:              End If
    
      17:          End Get
    
      18:          Set(ByVal value As PagerTypes)
    
      19:              ViewState("PagerType") = value
    
      20:          End Set
    
      21:      End Property
    

    From this code snippet we are inheriting from the GridView control, defining our 2 paging types (regular & custom), and we define our pager type property.  In order for our pager to be initialized, we need to override the InitializePager method.  What this method does is initialize the pager row when paging is enabled.  We need to determine if we want to allow the GridView to use its built-in pager or if we need to use our custom pager.  A simple select case statement will do the trick here.


       1:      Protected Overrides Sub InitializePager(ByVal row As System.Web.UI.WebControls.GridViewRow, ByVal columnSpan As Integer, ByVal pagedDataSource As System.Web.UI.WebControls.PagedDataSource)
    
       2:          Select Case Me.PagerType
    
       3:              Case PagerTypes.Custom
    
       4:                  InitCustomPager(row, columnSpan, pagedDataSource)
    
       5:              Case Else
    
       6:                  MyBase.InitializePager(row, columnSpan, pagedDataSource)
    
       7:          End Select
    
       8:      End Sub
    

    You'll notice that we have an InitCustomPager method we call to initialize our pager controls.  I'm not going to post the code snippet for this.  You can download the code and sift through it if you want.  The things to note is the wiring of events.  Our first, previous, next, and last image buttons throw a Command event and the drop down list throws a SelectedIndexChanged event.  Our PagingGridView control will handle these events and fire a the OnPageIndexChanging event which can be handled by the page.


       1:      Protected Sub ddlPages_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    
       2:          Dim newPageIndex As Integer = CType(sender, DropDownList).SelectedIndex
    
       3:          OnPageIndexChanging(New GridViewPageEventArgs(newPageIndex))
    
       4:      End Sub
    
       5:   
    
       6:      Protected Sub PagerCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.CommandEventArgs)
    
       7:          Dim curPageIndex As Integer = Me.PageIndex
    
       8:          Dim newPageIndex As Integer = 0
    
       9:   
    
      10:          Select Case e.CommandName
    
      11:              Case "First"
    
      12:                  newPageIndex = 0
    
      13:              Case "Previous"
    
      14:                  If curPageIndex > 0 Then
    
      15:                      newPageIndex = curPageIndex - 1
    
      16:                  End If
    
      17:              Case "Next"
    
      18:                  If Not curPageIndex = Me.PageCount Then
    
      19:                      newPageIndex = curPageIndex + 1
    
      20:                  End If
    
      21:              Case "Last"
    
      22:                  newPageIndex = Me.PageCount
    
      23:          End Select
    
      24: