Andreas Zenker

a boy named goo

  • Visual Studio ViEmu and “the Dark Side” all happy together

    So, if you like vi as an editor and you use Visual Studio, pony up the money and get ViEmu. Best money invested in my productivity and ergonomic efforts of this past year, hands down. If you love to hate your mouse you owe it to yourself to investigate ViEmu. There is also another free emulator that is available as an extension from the extensions online gallery called VsVim. However the rest of this blog has to do with configuring ViEmu to work with your particular choice of Visual Studio color scheme.

    Read more...

  • iOS5 email may need to be redefined

    Got my iOs5 update yesterday, mostly cool. However I was unable to send from my Verizon account, kept getting errors like “server does not allow relaying” when I tried to send or reply to mail. I was able to download new mail no problem.

    Read more...

  • Friends Don't Let Friends Use VS Merge Tool

    There are many things that Visual Studio does so well out-of-the-box, and then there is merging conflicts. The built-in merge tool is about as helpful as high beams on your car on a foggy night. The screen is full of information you just can't tell what it means.

    Read more...

  • Custom Filter for a WPF TextBox

    So, I had a need to implement a TextBox in WPF with auto filtering functionality. For example the initial requests were to limit a TextBox to numeric characters only. This was simple enough. Handle the PreviewTextInput Event and only allow numeric characters to be entered. So my first, simplistic, hack version was simply this:

       1: 'handle numeric only textboxes
       2: Private Sub TextBox_PreviewTextInput(ByVal sender As Object, ByVal e As System.Windows.Input.TextCompositionEventArgs) Handles Me.PreviewTextInput
       3:    If NumericValuesOnly AndAlso Not IsNumeric(e.Text) Then
       4:        e.Handled = True
       5:    End If
       6: End Sub

     

    Of course, the next request was for a non-numeric TextBox that allowed any character except numerical. So, I could just invert the logic I have shown above. Or, based on a good recommendation from a fellow developer, I could take a few minutes and make the filtering functionality “real” by having a Regular Expression being used for all filtering under the hood and then allow custom Regular Expressions to support those “one-off” situations that will inevitably come up.

    So, I added some properties and fields to support the new functionality. A CustomTextFilterRegexPattern String to allow custom filtering. A mode flag to allow the RegEx to function as an “Allow” filter or a “Deny” filter.

       1: Private mTextFilteringRegEx As Regex
       2: Private Shared sNumericRegex As New Regex("[0-9]")
       3:  
       4: Private mCustomTextFilterRegexExpression As String
       5: Public Property CustomTextFilterRegexExpression() As String
       6:   Get
       7:       Return mCustomTextFilterRegexExpression
       8:   End Get
       9:   Set(ByVal value As String)
      10:       mCustomTextFilterRegexExpression = value
      11:   End Set
      12: End Property
      13:  
      14: Private mIsFilterInAllowMode As Boolean
      15: Public Property IsFilterInAllowMode() As Boolean
      16:   Get
      17:       Return mIsFilterInAllowMode
      18:   End Get
      19:   Set(ByVal value As Boolean)
      20:       mIsFilterInAllowMode = value
      21:   End Set
      22: End Property

    Then I added two properties to make defining built-in filters easier.

       1: Private mNumericValuesOnly As Boolean
       2: Public Property NumericValuesOnly() As Boolean
       3:     Get
       4:         Return mNumericValuesOnly
       5:     End Get
       6:     Set(ByVal Value As Boolean)
       7:         mNumericValuesOnly = Value
       8:     End Set
       9: End Property
      10:  
      11: Private mNonNumericValuesOnly As Boolean
      12: Public Property NonNumericValuesOnly() As Boolean
      13:     Get
      14:         Return mNonNumericValuesOnly
      15:     End Get
      16:     Set(ByVal value As Boolean)
      17:         mNonNumericValuesOnly = value
      18:     End Set
      19: End Property

    Now in the Initialized Handler I wire up to the Paste Command and also set up my internal Regex to match what options have been defined in the XAML.

       1: Private Sub Textbox_Initialized(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Initialized
       2:     Dim pasteBinding As New CommandBinding(ApplicationCommands.Paste, _
       3:             New ExecutedRoutedEventHandler(AddressOf OnPasteCommandExecute))
       4:     Me.CommandBindings.Add(pasteBinding)
       5:  
       6:     'todo - add check to enforce mutually exclusive settings
       7:  
       8:     If NumericValuesOnly Then
       9:         mTextFilteringRegEx = sNumericRegex
      10:         IsFilterInAllowMode = True
      11:     End If
      12:  
      13:     If NonNumericValuesOnly Then
      14:         mTextFilteringRegEx = sNumericRegex
      15:         IsFilterInAllowMode = False
      16:     End If
      17:  
      18:     If Not String.IsNullOrEmpty(CustomTextFilterRegexExpression) Then
      19:         mTextFilteringRegEx = New Regex(CustomTextFilterRegexExpression)
      20:     End If
      21: End Sub

    All that is left now is to handle PreviewTextInput and the Paste Command Execute Handler.

    It was easy enough to deny characters in the PreviewTextInput, if the new value did not match the current filter setting then cancel the event by marking it Handled.

    For the paste command, however, I needed to process an entire string. The negative filter was easy, any match of a “deny” filter would cancel the paste command. For the positive filter, how could I easily screen a string of unknown length for any characters that did not match the filter Regex. Since the filter was a character filter, would I need to loop thru the entire string and check each value? Then I realized that using the Regex to do a Replace() and set the replace string to String.Empty() would only leave a non-empty string as the result if there were invalid characters in the string.

       1: Private Sub OnPasteCommandExecute(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
       2:     Dim toExecutePaste As Boolean = True
       3:     If Not mCustomTextFilterRegexExpression Is Nothing Then
       4:         Dim pasteText As String = Clipboard.GetText()
       5:         If IsFilterInAllowMode Then
       6:             'try a replace, if any characters survive the replace then the string is invalid
       7:             If Not String.IsNullOrEmpty(mTextFilteringRegEx.Replace(pasteText, String.Empty)) Then
       8:                 toExecutePaste = False
       9:             End If
      10:         Else
      11:             'any match on the deny mode filter equals a cancel of the paste
      12:             If mTextFilteringRegEx.IsMatch(pasteText) Then
      13:                 toExecutePaste = False
      14:             End If
      15:         End If
      16:  
      17:     End If
      18:  
      19:     If toExecutePaste Then Me.Paste()    
      20:     e.Handled = True
      21: End Sub
      22:  
      23: Private Sub Textbox_PreviewTextInput(ByVal sender As Object, ByVal e As System.Windows.Input.TextCompositionEventArgs) Handles Me.PreviewTextInput
      24:    'If filtering is applied and we fail the regex then cancel event
      25:    If Not mTextFilteringRegEx Is Nothing Then
      26:        If mTextFilteringRegEx.IsMatch(e.Text) = Not IsFilterInAllowMode Then
      27:            e.Handled = True
      28:        End If
      29:    End If
      30: End Sub

     

    So now I can implement a TextBox with custom filtering like this:

       1: <myFramework:CustomTextBox  UIProperty="{Binding Path=IntegerValueUIProperty}"
       2:                                     CustomTextFilterRegexExpression="[4,5,6,x,y]"
       3:                                     IsFilterInAllowMode="True"
       4:                                              />

    Read more...

  • Better than Contains()

    Sometimes you wish you could do better than the method you need to use. Case in point: Contains(). It is a fine method and is meant for more than strings. If you need custom logic to decide if two objects are equal or “the same” for the sake of sorting or searching you write your own comparer and pass that in. So, I don’t have anything against the Contains() method for any reason, I only find it a bit too generic when all I have is a list of strings and want to see if I have a match that doesn’t care about case.

    I figured LINQ might be a good fit. So I whipped up a simple* LINQ expression. I was able to do a select using a String.Compare() call, then I could pass in the ignoreCase parameter to get my desired results.

         Private Sub CheckIfAllowed(ByVal myTargetPath As String)
            Dim allowedPageList As New List(Of String)
            allowedPageList.Add("Logoff.aspx")
            allowedPageList.Add("AccessDenied.aspx")
            allowedPageList.Add("Login.aspx")
    
            Dim found = From p In allowedPageList Where String.Compare(myTargetPath, p, ignoreCase:=True) = 0
            If found.Count > 0 Then
                MessageBox.Show("Found")
            Else
                MessageBox.Show("Not Found")
            End If
        End Sub

    So, that was pretty cool. The next logical step seemed to be to create an extension for the List(Of String) class.

         ''' <summary>
            ''' Searches the List(Of String) using case-insensitive LINQ query
            ''' </summary>
            ''' <param name="list"></param>
            ''' <param name="value">the string to search for</param>
            ''' <returns>true if item is found. default: false</returns>
            ''' <remarks></remarks>
            <Extension()> _
            Public Function ContainsIgnoreCase(ByVal list As List(Of String), ByVal value As String) As Boolean
                Dim found = From p In list Where String.Compare(value, p, ignoreCase:=True) = 0
                Return found.count > 0
            End Function

    So I can have this “new function” available on all List(Of String) instances.

      If allowedPageList.ContainsIgnoreCase(myTargetPath) Then
             MessageBox.Show("Found")
      Else
             MessageBox.Show("Not Found")
      End If

    After I created my extension I thought: “I probably just duplicated something that was already part of a special collection type” so I looked thru the collection types and, under the Specialized namespace I found the class StringCollection. This exposes a Contains() method as well. However, even though it is a string specific collection the documentation indicates:

    “This method determines equality by calling Object.Equals. String comparisons are case-sensitive.”

    *I still can’t write one without Googling for samples. Learning more about the syntax is always on my to do list.

    Posted on ASP.NET Weblogs        © Copyright 2009 - Andreas Zenker

    Read more...

  • Taming the SharePoint Wiki

    Our current project team decided to post development documents, guidelines, specs etc in a SharePoint site dedicated to project. We also decided we would try a Wiki to post coding standards and best practices, samples, tips on using certain WPF controls etc.

    Read more...