Blend not showing WPF UserControls

When you create a project (a WPF class library in my case) and you would like to edit the usercontrols in Blend, you can be in for a suprise.

Blend sometimes only shows you the XAML and won’t let you design the UserControl/window. The solutions is quite simple. You need to edit your .vbproj or csproj file.
For vb.net you have to add the following line after the </ProjectGuid> tag:   <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</ProjectTypeGuids>.

For C# you need to add the following line after the </ProjectGuid> tag:
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>.

More info about the ProjectTypeGuids can be found at stackoverflow.

Auto Window Mover (using WinApi)

Introduction:
I would like to share a class with you which I developed for a recent project. For this project i needed to display an error dialog from a printer driver in the middle of my window (topmost).
I also had to rename the window title.

Basic idea of the class:
The class uses it’s own thread, which supports cancellation, to scan for a given process name. When a process is found it will use the async windows API call to EnumWindows to get all Windows. I used a lambda expression for the EnumWindows callback to keep the code a bit cleaner (and i think it’s cool to use WinApi and lambda together :))
If a window is found for the process id found earlier, the display title is checked and altered if needed.
Then an event is raised with a ByRef parameter to get the new center point for the window. You can omit the event and set the center point property on the instance to if you prefer that.

AutoWindowMover:

    ''' The MoveWindow function changes the position and dimensions of the specified window. For a top-level window, the position and dimensions are relative to the upper-left corner of the screen. For a child window, they are relative to the upper-left corner of the parent window's client area.
    '''
    '''
<param name="hWnd" />Handle to the window.
    '''
<param name="X" />Specifies the new position of the left side of the window.
    '''
<param name="Y" />Specifies the new position of the top of the window.
    '''
<param name="nWidth" />Specifies the new width of the window.
    '''
<param name="nHeight" />Specifies the new height of the window.
    '''
<param name="bRepaint" />Specifies whether the window is to be repainted. If this parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of moving a child window.
    ''' If the function succeeds, the return value is nonzero.
    ''' If the function fails, the return value is zero. To get extended error information, call GetLastError.
     _
    Public Shared Function MoveWindow(ByVal hWnd As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal bRepaint As Boolean) As Boolean
    End Function
 
     _
    Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, _
      ByVal hWndInsertAfter As IntPtr, _
      ByVal X As Integer, _
      ByVal Y As Integer, _
      ByVal cx As Integer, _
      ByVal cy As Integer, _
      ByVal uFlags As UInteger) As Boolean
    End Function
 
    Public Const SWP_NOSIZE As Integer = &amp;H1
    Public Const SWP_NOMOVE As Integer = &amp;H2
    Public Const SWP_NOZORDER As Integer = &amp;H4
    Public Const SWP_NOREDRAW As Integer = &amp;H8
    Public Const SWP_NOACTIVATE As Integer = &amp;H10
    Public Const SWP_DRAWFRAME As Integer = &amp;H20
    Public Const SWP_FRAMECHANGED As Integer = &amp;H20
    Public Const SWP_SHOWWINDOW As Integer = &amp;H40
    Public Const SWP_HIDEWINDOW As Integer = &amp;H80
    Public Const SWP_NOCOPYBITS As Integer = &amp;H100
    Public Const SWP_NOOWNERZORDER As Integer = &amp;H200
    Public Const SWP_NOREPOSITION As Integer = &amp;H200
    Public Const SWP_NOSENDCHANGING As Integer = &amp;H400
    Public Const SWP_DEFERERASE As Integer = &amp;H2000
    Public Const SWP_ASYNCWINDOWPOS As Integer = &amp;H4000
    Public Shared HWND_TOPMOST As IntPtr = (-1)
 
    Public Delegate Function CallBack( _
    ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
 
     _
    Private Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
    End Function
 
     _
    Private Shared Function SetWindowText(ByVal hwnd As IntPtr, ByVal lpString As String) As Boolean
    End Function
 
     _
    Private Shared Function GetWindowThreadProcessId(ByVal hwnd As IntPtr, _
                           ByRef lpdwProcessId As Integer) As Integer
    End Function
 
     _
    Private Shared Function GetWindowTextLength(ByVal hwnd As IntPtr) As Integer
    End Function
 
     _
    Private Shared Function GetWindowRect(ByVal hWnd As HandleRef, ByRef lpRect As RECT) As  Boolean
    End Function
 
     _
    Public Structure RECT
        Public Left As Integer
        ' x position of upper-left corner
        Public Top As Integer
        ' y position of upper-left corner
        Public Right As Integer
        ' x position of lower-right corner
        Public Bottom As Integer
        ' y position of lower-right corner
    End Structure
 
Declare Function EnumWindows Lib "user32" ( _
       ByVal x As CallBack, ByVal y As Integer) As Integer
 
    Private Const SLEEP_TIMEOUT = 1000
 
    '''
    ''' The process name to search
    '''
    '''
    '''
    '''
    Public Property SearchForProcessName As String
 
    '''
    ''' The rename text. If empty no renming will be done
    '''
    '''
    '''
    '''
    Public Property RenameWindowText As String
 
    Private _WindowsPositionCenterPoint As Point
    '''
    ''' The center point for the replace to
    '''
    '''
    '''
    '''
    Public Property WindowPositionCenterPoint As Point
        Get
            Return _WindowsPositionCenterPoint
        End Get
        Set(ByVal value As Point)
            _WindowsPositionCenterPoint = value
            MustChange = True
        End Set
    End Property
 
    'the cancel var. (The thread can be stopped by setting the var to true
    Private CancelationPending As Boolean = False
 
    'the scan thread we use for scanning and checking the drives
    Private ScanThread As Thread
 
    ''do we need to change the next step
    Private MustChange As Boolean = True
 
    'our process id to search the windows for
    Private Shared ProcessId As IntPtr
 
    Public Sub New(ByVal processName As String, ByVal positionCenter As Point, ByVal renameText As String)
        Me.SearchForProcessName = processName
        Me.WindowPositionCenterPoint = positionCenter
        Me.RenameWindowText = renameText
    End Sub
 
    '''
    ''' Starts the detector
    '''
    '''
    Public Sub StartDetecting()
        'only start when not starting yet
        If ScanThread Is Nothing Then
            CancelationPending = False
            ScanThread = New Thread(AddressOf DetectWorker)
            'start the thread
            ScanThread.Start()
        End If
    End Sub
 
    Public Sub StopDetecting()
        If ScanThread IsNot Nothing Then
            'set the cancel var
            Me.CancelationPending = True
            'and wait for the thread to finish
            Try
                Me.ScanThread.Join()
            Catch
            End Try
            ScanThread = Nothing
        End If
 
    End Sub
 
    '''
    ''' Our thread worker
    '''
    '''
    Private Sub DetectWorker()
        While Not CancelationPending
            If Not WindowPositionCenterPoint.X.Equals(Double.NaN) And Not WindowPositionCenterPoint.Y.Equals(Double.NaN) Then
                'we need to get the process id to scan for
                For Each proc In Process.GetProcessesByName(SearchForProcessName)
                    'we got it
                    ProcessId = proc.Id
                    'now we need to test all windws (
                    EnumWindows(Function(hwnd, lParam)
                                    Dim iProcessID As IntPtr
                                    'get the process id and compare it to the one found before
                                    GetWindowThreadProcessId(hwnd, iProcessID)
                                    If iProcessID = ProcessId Then
                                        'get the window text
                                        Dim normalText As String = RenameWindowText
                                        If Not String.IsNullOrEmpty(RenameWindowText) Then
 
                                            Dim length As Integer
                                            If hwnd &lt;= 0 Then
                                                Return True
                                            End If
                                            length = GetWindowTextLength(hwnd)
                                            If length = 0 Then
                                                Return True
                                            End If
                                            Dim sb As New System.Text.StringBuilder("", length + 1)
 
                                            GetWindowText(hwnd, sb, sb.Capacity)
                                            normalText = sb.ToString
                                        End If
 
                                        'chekc the text (also a variable (we have moved the window already or ot)
                                        If normalText &lt;&gt; RenameWindowText Or MustChange Then
                                            If Not String.IsNullOrEmpty(RenameWindowText) Then
                                                'rename it
                                                SetWindowText(hwnd, RenameWindowText)
                                            End If
 
                                            'raise our event
                                            Dim alternativeCenterPoint As New Point(Double.NaN, Double.NaN)
                                            Application.Current.Dispatcher.Invoke(Sub()
                                                                                      RaiseEvent BeforeShow(alternativeCenterPoint)
                                                                                  End Sub)
                                            If Not alternativeCenterPoint.X.Equals(Double.NaN) And Not alternativeCenterPoint.Y.Equals(Double.NaN) Then
                                                WindowPositionCenterPoint = alternativeCenterPoint
                                            End If
 
                                            'we need to postions of the window
                                            Dim rct As RECT
 
                                            Dim wdth = rct.Right - rct.Left + 1
                                            Dim height = rct.Bottom - rct.Top + 1
 
                                            'now position the window in the center
                                            SetWindowPos(hwnd, HWND_TOPMOST, CUInt(Math.Max(Me.WindowPositionCenterPoint.X - wdth / 2, 0)), CUInt(Math.Max(Me.WindowPositionCenterPoint.Y - height / 2, 0)), 0, 0, SWP_NOSIZE)
                                            MustChange = False
                                        End If
                                    End If
                                    Return True
 
                                End Function, 0)
                Next
            End If
 
            'wait for the next
            Thread.Sleep(SLEEP_TIMEOUT)
        End While
    End Sub
 
    Public Event BeforeShow(ByRef centerPoint As Point)
 
End Class

WPF quick tip

Every once in a while i stumble into this little problem, so i decided to write it down.
When you have a border with some other elements in it and you want handle a mouse event like MouseLeftButtonDown, you can run into the problem that only the border area of the border reacts to the event and not the inner space.

If you want all the contents of the border to react to the events, you’ll have to specify a background color. If you set the background to transparent, the control will look the same, but you can handle the events raised.

WPF Expander 2.0

For a project I’m working on in needed an expander that needs to be selectable in a listview without expanding when clicked on the header.

I also needed a property on the expander so that i am able to hide/show the arrow to expand the expander so i can hide the properties etc. in the expander when showing a list of items to select.

I’ve combined and edited two solotions found on stackoverflow.com.

To prevent an expander from collapsing when clicking on the header i have used this controltemplate: http://stackoverflow.com/questions/1396153/preventing-a-wpf-expander-from-expanding-when-header-is-clicked

To hide the arrow in the expander header i have used this post: http://stackoverflow.com/questions/1070685/hidding-the-arrows-for-the-wpf-expander-control.
The above code has to be edited to work with the controltemplate. I also converted it to VB.NET. This is the result:

Imports System.Windows.Controls.Primitives
 
Public Class ExpanderAttachedProperties
#Region "HideExpanderArrow AttachedProperty"
 
     _
    Public Shared Function GetHideExpanderArrow(ByVal obj As DependencyObject) As Boolean
        Return CBool(obj.GetValue(HideExpanderArrowProperty))
    End Function
 
     _
    Public Shared Sub SetHideExpanderArrow(ByVal obj As DependencyObject, ByVal value As Boolean)
        obj.SetValue(HideExpanderArrowProperty, value)
    End Sub
 
    ' Using a DependencyProperty as the backing store for HideExpanderArrow.  This enables animation, styling, binding, etc...
    Public Shared ReadOnly HideExpanderArrowProperty As DependencyProperty = DependencyProperty.RegisterAttached("HideExpanderArrow", GetType(Boolean), GetType(ExpanderAttachedProperties), New UIPropertyMetadata(False, AddressOf OnHideExpanderArrowChanged))
 
    Private Shared Sub OnHideExpanderArrowChanged(ByVal o As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        Dim expander As Expander = DirectCast(o, Expander)
 
        If expander.IsLoaded Then
            UpdateExpanderArrow(expander, CBool(e.NewValue))
        Else
            AddHandler expander.Loaded, AddressOf ExpanderLoaded
        End If
    End Sub
 
    Private Shared Sub ExpanderLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        UpdateExpanderArrow(sender, GetHideExpanderArrow(sender))
    End Sub
 
    Private Shared Sub UpdateExpanderArrow(ByVal expander As Expander, ByVal visible As Boolean)
 
        '        Dim headerGrid As Grid = TryCast(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(expander, 0), 0), 0), 0), 0), Grid)
 
        'get the togglebutton
        Dim tgBut As ToggleButton = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(expander, 0), 0), 0), 0)
        tgBut.Visibility = If(visible, Visibility.Collapsed, Visibility.Visible)
 
    End Sub
 
#End Region
End Class

BitmapImage shows wrong PixelWidth and PixelHeight

Today, i encountered a bug. I was loading images from a stream using the code below:

    '''
    ''' Returns a frozen bitmap from stream.
    '''
    '''
 
    '''
    ''' The stream will be closed!
    Function GetBitmapSourceFroStream(ByVal source As IO.Stream) As BitmapSource
        Dim retImg As New BitmapImage()
        retImg.CacheOption = BitmapCacheOption.OnLoad
        retImg.BeginInit()
        retImg.StreamSource = source
        retImg.EndInit()
        retImg.Freeze()
        source.Close()
        Return retImg
    End Function

This code looks fine (at least it does to me) and compiles without errors.
Some (larger) BitmapImages returned by this function will have a PixelWidth and PixelHeight of 1, without an exception or anything.

The solution: Place the CacheOption = … line after BeginInit. :S

Update Entity Framework object on new context

Just a quick post people looking on this using google (a sort of retweet).
If you need to update a changed Entity on a context which was disposed, you can use the ApplyCurrentValues: http://msdn.microsoft.com/en-us/library/dd487246.aspx

Quick sample:

    Public Sub Main()
        Dim ent = GetEntity()
        Dim modifiedEntity = ChangeEntity(ent)
        UpdateOnOtherContext(modifiedEntity)
    End Sub
 
    Public Function GetEntity() As SomeEntity
        Dim context As New SomeContext
        Return (From ents In context.SomeEntities Where ents.SomeParam = True).singleOrDefault
    End Function
 
    Public Function ChangeEntity(ByVal ent As SomeEntity) As SomeEntity
        ent.SomeOtherParam = "changed"
        Return ent
    End Function
 
    Public Sub UpdateOnOtherContext(ByVal ent As SomeEntity)
        Dim context As New SomeContext
        context.SomeEntities.ApplyCurrentValues(ent)
    End Sub

Rotate images based on EXIF Orientation

For one of my latest projects I had to automate the process of taking pictures and uploading them to a server. I have used the Canon EOS SDK to control the cameras remotely.
Before uploading the images to the server, i had to rotate the images based on the camera orientation, so the users can view their picture in the correct orientation (landscape or portrait). The Canon EOS i used stores the camera orientation in the EXIF data, so all i had to do was read the EXIF orientation field, rotate the image, set the orientation field to the new orientation and save the image.

I have used this EXIF library for reading and writing the EXIF fields: http://code.google.com/p/exiflibrary/
(Link changed: thanks oozcitak)

The following code uses the system.drawing namespace.

'''
        ''' This function will read the exif information from the stream, rotates and/or flips the image based on the
        ''' EXIF information field, saves the image using a jpeg encoder with a given quality level,
        ''' This function will always save the file to disc, even if nothing is done.
        '''
        '''
The inputstream. WARNING: If you use a filestream as an iput stream, make sure that you don't save the image to the sam location
        ''' Throws an exception if no EXIF data is found, invalid jpeg or invalid stream
        Public Shared Sub SaveRotateFlipBasedOnExifOrientation(ByVal bitmapStream As IO.Stream, ByVal saveToLocation As String, Optional ByVal encodingQuality As Integer = 95)
            'let's get started...
            'get the flipped/rotated/nothing done bitmap
            Dim rotatedFlippedBitmap As Bitmap = GetRotateFlipBasedOnExifOrientationBitmap(bitmapStream)
            'save it and set the exif orientation to normal
            'Save it to our stream (as a jpeg to preserve the EXIF)
            Dim newStream As MemoryStream = Converter.EncodeBitmapAsJpegStream(rotatedFlippedBitmap, encodingQuality)
            'we need to read the exif again, and change the orientation, and finally
            Dim newExif As ExifLibrary.ExifFile = ExifInfo.GetExif(newStream)
            If newExif.Properties.ContainsKey(ExifLibrary.ExifTag.Orientation) Then
                newExif.Properties(ExifLibrary.ExifTag.Orientation).Value = ExifLibrary.Orientation.Normal
            Else
                newExif.Properties.Add(New ExifLibrary.ExifUShort(ExifLibrary.ExifTag.Orientation, ExifLibrary.Orientation.Normal))
            End If
            'save the file
            newExif.Save(saveToLocation)
 
            'cleanup
            newStream.Close()
            newStream.Dispose()
            rotatedFlippedBitmap.Dispose()
            rotatedFlippedBitmap = Nothing
 
        End Sub
 
        '''
        ''' Returns a bitmap that can be rotated based on the Exif rotation data
        '''
        '''
 
        '''
        '''
        Public Shared Function GetRotateFlipBasedOnExifOrientationBitmap(ByVal bitmapStream As IO.Stream) As Bitmap
 
            'get the bitmap
            Dim rotateFlipBitmap As New Bitmap(bitmapStream)
 
            'get the orientation field from the exif (if exists)
            Dim orient As ExifLibrary.Orientation = GetExifOrientation(bitmapStream)
            If orient &lt;&gt; ExifLibrary.Orientation.Normal Then
                'we need to rotate/flip
                'check the orientation and rotate/flip the image
                If orient = ExifLibrary.Orientation.RotatedLeft Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.Rotate270FlipNone)
                ElseIf orient = ExifLibrary.Orientation.RotatedRight Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.Rotate90FlipNone)
                ElseIf orient = ExifLibrary.Orientation.RotatedRightAndMirroredVertically Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.Rotate90FlipXY)
                ElseIf orient = ExifLibrary.Orientation.MirroredHorizontally Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY)
                ElseIf orient = ExifLibrary.Orientation.MirroredVertically Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.RotateNoneFlipX)
                ElseIf orient = ExifLibrary.Orientation.Rotated180 Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.Rotate180FlipNone)
                ElseIf orient = ExifLibrary.Orientation.RotatedLeftAndMirroredVertically Then
                    rotateFlipBitmap.RotateFlip(RotateFlipType.Rotate270FlipXY)
                End If
            End If
 
            Return rotateFlipBitmap
 
        End Function
 
  '''
        ''' Returns the orientation of an image based on exif data
        '''
        '''
 
        '''
        '''
        Private Shared Function GetExifOrientation(ByVal bitmapStream As Stream) As ExifLibrary.Orientation
            'let's get started...
            'check if the stream is a the beginning
            If bitmapStream.CanSeek Then
                If bitmapStream.Position &lt;&gt; 0 Then
                    bitmapStream.Seek(0, SeekOrigin.Begin)
                End If
            End If
 
            'Read the exif info
            Dim exif As ExifLibrary.ExifFile = ExifInfo.GetExif(bitmapStream)
            Dim orient As ExifLibrary.ExifProperty = (From prop In exif.Properties Where prop.Key = ExifLibrary.ExifTag.Orientation Select prop.Value).SingleOrDefault
            'clenaup
            exif = Nothing
            If orient IsNot Nothing Then
                Return orient.Value
            Else
                Return ExifLibrary.Orientation.Normal
            End If
 
        End Function
 
        '''
        ''' Encodes a bitmap as a JPEG to a memorystream
        '''
        '''
 
        '''
 
        '''
        '''
        Public Shared Function EncodeBitmapAsJpegStream(ByVal bitmap As System.Drawing.Bitmap, ByVal quality As Integer) As MemoryStream
            Dim retStream As New IO.MemoryStream()
            SaveBitmapAsJpeg(bitmap, retStream, quality)
            Return retStream
        End Function
 
     Public Shared Sub SaveBitmapAsJpeg(ByVal bitmap As System.Drawing.Bitmap, ByVal toStream As Stream, ByVal quality As Integer)
            Dim params As New EncoderParameters(1)
            Dim imgCodecInfo As ImageCodecInfo = CodecInfo.GetEncoderInfo(System.Drawing.Imaging.ImageFormat.Jpeg)
            Dim myEncoder As Encoder = Encoder.Quality
            Dim param As New EncoderParameter(myEncoder, CType(quality, Long))
            params.Param(0) = param
            bitmap.Save(toStream, imgCodecInfo, params)
            If toStream.CanSeek Then
                If toStream.Position <> 0 Then
                    toStream.Seek(0, SeekOrigin.Begin)
                End If
            End If
        End Sub

Make part of an image transparant (c#)

Ever wanted to make a part of an image transparent through code?
It can be done quite easily using GDI.

private static void MakeTransparent(string inputFileName, string outputFileName)
        {
            Bitmap bmpIn = (Bitmap)Bitmap.FromFile(inputFileName);
            Bitmap converted = new Bitmap(bmpIn.Width, bmpIn.Height, PixelFormat.Format32bppArgb);
            using (Graphics g = Graphics.FromImage(converted))
            {
                // Prevent DPI conversion
                g.PageUnit = GraphicsUnit.Pixel;
                g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                // Draw the image
                g.DrawImageUnscaled(bmpIn, 0, 0);
            }
            converted.MakeTransparent(converted.GetPixel(0, 0));
            converted.Save(outputFileName, ImageFormat.Png);
            converted.Dispose();
            bmpIn.Dispose();
        }

The line ‘converted.MakeTransparent(converted.GetPixel(0, 0));’ takes the color from the left upper corner (0,0) and makes every pixel with that color transparent in the resulting image.

WPF DoEvents equivalence for VB.NET

Sometimes you need to wait for the WPF render process to complete. For example when you need to take a synchronous ‘screenshot’ of your WPF app using the RenderTargetBitmap.

Since there are no events you can subscribe to, nor any DoEvents like equivalents in WPF, you can use the one posted by MCMoser on http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/157a2852-fac9-4791-a1d3-395fe702d13c:

Public Shared Sub DoEvents(ByVal dispatch As Dispatcher)
dispatch.Invoke(New Action(AddressOf DoEventsHandler), DispatcherPriority.Background)
End Sub
 
Private Shared Sub DoEventsHandler()
End Sub

This call will synchronously invoke a method on the same thread as the WPF render process. It will be executed when all preceding task are executed.

Try to minimize the use of this function. When i tried this function in a program which records an animation it gave me a ComExcption with Hresult: 0x88980403.

WPF UIElement extension methods for ‘screenshot’ and location

For a project I’m working on i need to take screenshots of a bunch of WPF pages. In WPF this is an easy task. Especially when somenone already has created an extension method for it:. The extension method is in C#, so i have converted it to VB.NET and added another extension method as well.

Sometimes  you need the location of a specific UIElement in your WPF app. Since all UIElements’ locations are relative to their parent container, it’s obvious that there isn’t a ‘Location’ property.
When creating screenshots of you WPF pages, you also might want to store the location and size of the UIElements. You can use ‘TranslatePoint’ for the location (and ActualWidth and ActualHeight for the size), or this extension method, which will add a Location function to UIElement.

Screen shot and location extension method for VB.NET

Imports System.Windows
Imports System.Windows.Media.Imaging
Imports System.Windows.Media
Imports System.IO
Imports System.Runtime.CompilerServices
 
Module UIElementExtensions
    '''
    ''' Gets a JPG "screenshot" of the current UIElement
    '''
    ''' UIElement to screenshot
    ''' Scale to render the screenshot
    ''' JPG Quality
    ''' Byte array of JPG data
     _
    Public Function GetJpgImage(ByVal source As UIElement, ByVal scale As Double, ByVal quality As Integer) As Byte()
        Dim actualHeight As Double = source.RenderSize.Height
        Dim actualWidth As Double = source.RenderSize.Width
 
        Dim renderHeight As Double = actualHeight * scale
        Dim renderWidth As Double = actualWidth * scale
 
        Dim renderTarget As New RenderTargetBitmap(CInt(renderWidth), CInt(renderHeight), 96, 96, PixelFormats.Pbgra32)
        Dim sourceBrush As New VisualBrush(source)
 
        Dim drawingVisual As New Media.DrawingVisual()
        Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
 
        Using drawingContext
            drawingContext.PushTransform(New ScaleTransform(scale, scale))
            drawingContext.DrawRectangle(sourceBrush, Nothing, New Rect(New Point(0, 0), New Point(actualWidth, actualHeight)))
        End Using
        renderTarget.Render(drawingVisual)
 
        Dim jpgEncoder As New JpegBitmapEncoder()
        jpgEncoder.QualityLevel = quality
        jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget))
 
        Dim _imageArray As Byte()
 
        Using outputStream As New MemoryStream()
            jpgEncoder.Save(outputStream)
            _imageArray = outputStream.ToArray()
        End Using
 
        Return _imageArray
    End Function
 
    ''' 
    ''' Returns the location relative the 'relativeTo' UIElement
    ''' 
    ''' 
    ''' 
     _
  Public Function Location(ByVal source As UIElement, ByVal relativeTo As UIElement) As Point
        Return source.TranslatePoint(New Point(0, 0), relativeTo)
    End Function
End Module