Andreas Zenker

a boy named goo
HTTP 301: the zenker has Moved permanently

I have started a new blog recently on my own site. I have enjoyed blogging here and am thankful for the hosting provided by asp.net.

All my future blogs can be found here:

thezenker.com/blog

Thanks for reading

FlowDocument PageWidth, PagePadding and ColumnWidth Oh My!

The ability to create a living, breathing, document right inside a WPF UI is pretty incredible to me. Even after working with it for the last year or so, I am still impressed by the power of this largely forgotten feature of WPF.

One thing to remember about the FlowDocument model is that this is not your Mama’s WPF. The FlowDocument objects inherit from FrameworkContentElement, not FrameworkElement as the rest of the WPF control suite does. So, while the document related controls do inherit the goodness of DependencyObject and can support DependencyProperties etc., they miss the features added by UIElement and FrameworkElement which include little things like Visibility, Height and Width.

frameworkElement

frameworkContentElement

FlowDocument elements are meant to, well … flow. They fill the available page width, or the width made available by whatever parent they are constrained by such as a TableCell or Paragraph. So, while FrameworkContentElements may support layout specific properties such as Margin and Padding, if you want to get explicit about Width and Height you are either out of luck, going to embed some UIElements in your document (you cad!) or about to write some custom code to make it happen.

When you insert a FlowDocument into a WPF window you do have some ability to affect the layout of the document as a whole at the top level. So, while a Paragraph does not have a Width or Height property, the FlowDocument does its best to act like a real document and gives you PageWidth, PagePadding and ColumnWidth properties that will affect the overall layout and container size of your document.

PageWidth: this, as it indicates, is the width of the document page. The amount is set in device independent pixels (a pixel is 1/96 of an inch so 1” = 96 pixels). Keep in mind, when setting this value, that the page margin must also be considered.

PagePadding: this name, that is much more fitting in WPF then in document land, is actually the page margin. The amount of pixels (1/96 of an inch) between the edge of the paper and the content. So basically, PagePadding + PageWidth should equal, or at least not be greater than, the paper width. If you have 8.5” wide paper (816 pixels) and you have 1/2 margins (48 pixels * 2 = 96) then you only have 720 pixels to play with for PageWidth. PagePadding is of Type Thickness, so you can set a uniform value that applies to all Margins, or set each separately if desired.

ColumnWidth: This one is not related to the size of the container as much as it is how the content within the container is laid out. As the name indicates, it sets the desired width of the columns of the document. It is only desired since, by default, the layout will adjust the ColumnWidth to make best use of the available width of the page. To enforce your column width setting you need to set IsColumnWidthFlexible = False.

Another gotcha to keep in mind with column width is that the calculation of columns is based on “perceived pressure” of the container. So, just like ScrollBars in normal WPF windows, if you wrap your content in a control that assumes infinite space such as  a ScrollViewer, StackPanel, or FlowDocumentScrollViewer the content inside will feel no need to form up columns since it thinks it has infinity to render itself in.

The best way to get a feel for how these properties interact and affect each other is to play around with the options and view the results in the UI. To help with that I have included a sample WPF Window that allows you to dynamically change these values and get real time feedback.

The window xaml:

   1: <Window x:Class="FlowPageLayout"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     Title="FlowPageLayout"
   5:      Width="900">
   6:     <Window.Resources>
   7:         <Style TargetType="TextBox">
   8:             <Setter Property="Width" Value="50" />
   9:             <Setter Property="Margin" Value="10,0,15,0" />
  10:         </Style>
  11:     </Window.Resources>
  12:     
  13:     <DockPanel>
  14:         <StackPanel Orientation="Horizontal"
  15:                     DockPanel.Dock="Top">
  16:             <TextBlock Text="Reader (Paper) Width" />
  17:             <TextBox Text="816" Name="ReaderWidthText" LostFocus="ReaderWidthText_LostFocus"  />
  18:             <TextBlock Text="PageWidth (Pixels)" />
  19:             <TextBox Text="720" Name="PageWidthText" />
  20:             <TextBlock Text="PagePadding (Pixels)" />
  21:             <TextBox Text="48" Name="PagePaddingText" />
  22:             <TextBlock Text="ColumnWidth (Pixels)" />
  23:             <TextBox Text="100" Name="ColumnWidthText" />
  24:             <TextBlock Text="IsColumnWidthFlexible" />
  25:             <CheckBox IsChecked="True" Name="FlexibleChecked" />
  26:         </StackPanel>
  27:             <FlowDocumentReader Name="Reader" 
  28:                                 BorderBrush="Black" 
  29:                                 BorderThickness="1" 
  30:                                 Width="{Binding ElementName=ReaderWidthText, Path=Text, UpdateSourceTrigger=LostFocus}"
  31:                                 Margin="20">
  32:                 <FlowDocument Name="MainDocument"  
  33:                                       PageWidth="{Binding ElementName=PageWidthText, Path=Text}" 
  34:                                       PagePadding="{Binding ElementName=PagePaddingText, Path=Text}"
  35:                                       ColumnWidth="{Binding ElementName=ColumnWidthText, Path=Text}"
  36:                                       IsColumnWidthFlexible="{Binding ElementName=FlexibleChecked, Path=IsChecked}" 
  37:                                       FontSize="10" 
  38:                                       FontFamily="Arial" 
  39:                                       Background="White" >
  40:  
  41:                     <Paragraph >
  42:                         Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dignissim accumsan purus, ut faucibus turpis ultricies vel. Phasellus quis orci eu massa vulputate placerat. Nunc congue varius lacus in tincidunt. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec ut lorem ante, sed faucibus ipsum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pharetra, mauris vitae mollis congue, tortor libero sollicitudin justo, id posuere arcu arcu sed lorem.
  43:                     </Paragraph>
  44:  
  45:                     <Paragraph>
  46:                         Phasellus molestie dui id lorem cursus luctus. Nulla molestie urna vulputate est sodales sed tempus leo posuere. Suspendisse pharetra, odio et eleifend pulvinar, enim felis faucibus lectus, sit amet consequat turpis magna eget nunc. Aenean eget urna quis justo vestibulum rutrum vel non ligula. Donec blandit dolor quis orci vulputate pulvinar blandit diam dignissim. Nam nec sapien magna. Phasellus faucibus urna eu quam fermentum mollis. Quisque sit amet dolor nulla, eget vehicula nulla.
  47:                     </Paragraph>
  48:                     <Paragraph>
  49:                         Nam magna metus, vulputate nec condimentum non, pellentesque in ante. Etiam et enim vel massa semper luctus ac ut quam. Nulla libero nisi, molestie eu lacinia ac, tincidunt non mi. Etiam ornare mi lobortis nunc adipiscing non interdum metus pretium. Morbi felis nulla, dignissim ut blandit vestibulum, euismod ut ipsum. Mauris interdum commodo nisi, vitae pharetra lacus scelerisque eget. Aliquam vel neque nisi. Vivamus consequat scelerisque dignissim. Maecenas lobortis suscipit massa, quis feugiat tortor pulvinar vel. Proin sagittis felis sit amet nunc dignissim semper. Quisque lacinia, tellus sed placerat sagittis, velit justo consectetur est, id aliquam nunc est ac nisl. Curabitur convallis elementum ligula eu ullamcorper.
  50:                     </Paragraph>
  51:  
  52:                     <Paragraph>
  53:                         Suspendisse ac hendrerit enim. Curabitur non lacus sapien, a consequat magna. Mauris tempor, eros ac mollis dapibus, sem lacus facilisis mi, et tristique arcu felis quis ipsum. Nam lobortis, nisl at commodo feugiat, risus enim bibendum odio, quis feugiat libero neque nec quam. Nullam auctor, nibh in sodales adipiscing, tortor odio tristique diam, at molestie lorem eros eget tortor. Cras at velit et lectus malesuada faucibus id sit amet augue. Aliquam rutrum turpis velit. Nunc sit amet faucibus erat.
  54:                     </Paragraph>
  55:  
  56:                     <Paragraph>
  57:                         Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eu elit a ipsum malesuada imperdiet vitae nec libero. Donec sagittis quam vitae leo consequat id rutrum lacus rutrum. Nullam euismod, velit sit amet porta malesuada, sem libero tempor leo, egestas dignissim diam dolor faucibus massa. Suspendisse vel urna leo, sed vulputate dui. Pellentesque rutrum auctor dolor, blandit lobortis ligula hendrerit eget. In laoreet porttitor mollis. Nam et ligula est. Proin vel nisl quam. Donec posuere, sem a venenatis commodo, lorem massa pulvinar nisi, eget porta quam tellus nec turpis. Donec fringilla tincidunt nibh ut porttitor. Ut lacinia justo nulla. Maecenas ac nisi iaculis metus hendrerit tempus ac vel ante. Aliquam nec erat enim. Aenean turpis turpis, fringilla et auctor id, ullamcorper eget velit. Duis et dui odio, eget tincidunt metus.
  58:                     </Paragraph>
  59:  
  60:                     <Paragraph>
  61:                         Fusce aliquam nisi nec felis aliquam convallis. Pellentesque laoreet molestie nibh, nec cursus sem tempor eu. Donec sit amet purus orci. Pellentesque vehicula neque nec orci pulvinar elementum. Curabitur massa tellus, imperdiet sit amet rutrum ut, posuere porttitor urna. Aliquam condimentum lobortis felis. In sed feugiat ante. Praesent at diam nibh, sit amet blandit lorem. Nam consequat tellus non arcu porta aliquet. Vivamus orci ante, elementum vitae cursus eget, pulvinar nec mi. Curabitur eu lorem justo. Ut facilisis fringilla metus id tristique.
  62:                     </Paragraph>
  63:  
  64:                     <Paragraph>
  65:                         Aliquam sed nibh odio, et cursus augue. Aliquam tempor scelerisque eros, tempor scelerisque orci eleifend quis. Nullam vehicula nunc tortor. Donec id suscipit tellus. Etiam venenatis, ipsum vel porttitor porttitor, mi dui consectetur purus, at consequat nunc elit eget elit. Cras nec nibh sit amet ligula ullamcorper imperdiet. Sed hendrerit tempus eleifend. Sed blandit aliquet pretium. Ut porttitor, lacus quis pharetra imperdiet, neque nisl mollis velit, a dapibus erat lectus posuere turpis. In cursus rutrum erat. Nam mauris nisi, posuere quis mollis non, scelerisque ac sem. Phasellus vitae orci velit, id auctor quam.
  66:                     </Paragraph>
  67:                 </FlowDocument>
  68:  
  69:             </FlowDocumentReader>
  70:     </DockPanel>
  71: </Window>

The code behind that resizes the window to stay somewhat in line with the “paper” width

   1: Public Class FlowPageLayout
   2:  
   3:     Private Sub ReaderWidthText_LostFocus(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
   4:         If Me.Reader Is Nothing Then Exit Sub
   5:         Me.Width = Me.Reader.Width + 80
   6:     End Sub
   7:  
   8: End Class

The window allows changing the key properties in real time to test various combinations of settings. The FlowDocumentReader has a border defined, and a margin as well to help isolate the edge of the “paper” and make the document margin more visible.

flowLayoutWindow

Posted: Jan 11 2012, 11:18 PM by azenker | with no comments
Filed under: , , ,
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.

First choose a color scheme. I recently moved back to a dark theme son-of-obsidian. I usually feel the need to change up the scheme once or twice a year to give my eyes something different to look at. Extra motivation was added in dealing with OpenXml a lot lately and, as a result,parsing and comparing tons of xml files. I found a dark theme in gVim made the xml a bit easier to digest and I wanted to same experience in Visual Studio.

Here is a screen shot of Visual Studio with both an xaml and a vb file open.

darktheme1

I like the overall theme, but right away I see some issues with usability as highlighted by the numbered arrows.

  1. The line numbers are too dark. Whoever created this theme probably doesn’t have line numbers turned on so it would be easy to miss.
  2. The flashing, black on black, cursor is almost invisible.
  3. The default ViEmu search highlight color, yellow, stands out nicely but depending on what content is highlighted, the foreground color may not have enough contrast to be readable. The highlights in the right file fall in string literals and the Orange font comes thru well enough, but in the left file, the White foreground Method name combines with the Yellow to create a retina-searing glare.

Good news is that fixing all of these issues is fairly straightforward and also is a good exercise in learning how to configure the hybrid vim/Visual Studio environment.

Line numbers:

Fixing the line number foreground color turns out to be the easiest of the bunch, as it is just a setting in the Visual Studio Options. Go to Tools >> Options >> Fonts and Colors and look for the Line Numbers item under the Text Editor settings. Pick a custom color that makes you feel better about your line numbers.

linenumbers

Setting the cursor color.

This one is a bit more work. Visual Studio does not expose a setting to change this color. However, vim has options that can be set to change these colors. Vim is configured, in the real vim, using the .vimrc (or _vimrc) file in the program path. ViEmu has its own version of the file called viemurc. The biggest challenge you will face is figuring out just where this file should be created in order for ViEmu to find it when it loads up.

The ViEmu support says to simply save the file to the folder indicated by your HOMEDRIVE Or HOMEPATH environment variables. So, just where, exactly, are they? Well on my Windows 7 machine that equates to “C:\” and “Users\Andreas” respectively. However in my work environment one, or both, of these variables are set to network share paths. To find out for sure what folders are HOME for you, open up a CMD prompt and run the “set” command. This will list the current state of all environment variables.

setcommand

Now that you know where, create the what. Create a file called viemurc, no extension. Once it is created you can add the following lines that will configure the cursor colors.

   1: highlight Cursor guifg=white guibg=black
   2: highlight iCursor guifg=white guibg=steelblue
   3: set guicursor=n-v-c:block-Cursor
   4: set guicursor+=i:ver100-iCursor
   5: set guicursor+=n-v-c:blinkon0
   6: set guicursor+=i:blinkwait10

Ok, so that takes care of line numbers and cursors, now for the last item:

Vim search highlights:

The interesting thing about this one is that it looks easy at first but doesn’t seem to work. The same options screen we used for the line numbers above also has some ViEmu specific options, all prefixed with ViEmu (go figure). The HL Search Adornment and HL Search Font are what we are looking for.

viemuHLSetting

However, when you update these you will see the background change, but not the foreground. What gives? Seems that ViEmu respectfully defaults to not altering the Visual Studio configured foreground for search highlights and instead just paints an adorner behind the pattern text with the selected background color. That may work for you. If not, you need to turn this “polite” feature off in the ViEmu settings. Under the ViEmu menu in Studio, make sure the “use adornments” checkbox is unchecked and click Ok.

useAdornments

That’s it, now the background and foreground you set for vim searches will be honored.

So, just a few minutes to make peace between ViEmu and a dark Visual Studio Theme.

newcolors

References:

ViEmu documentation that includes how to set up viemurc file options

Stack Overflow question that shows how to change cursor colors

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

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.

I tried changing authentication settings to no avail. Based on a forum post from the iOs4 upgrade, I deleted the account and set it up from scratch and all was good.

Besides that the upgrade went pretty smooth, still I have to say all the “sell your soul to the iCloud” settings make me feel like I just gave my privacy away but that is another story.

 

 

 

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

Posted: Oct 14 2011, 07:07 PM by azenker | with no comments
Filed under:
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.

Other merge tools, such as WinMerge will make your manual merge experience much more pleasurable and cut down on bad decisions brought about by information obfuscation. Fortunately you can change the defaut merge tool in Visual Studio. The blog entry that I have been using as my guide in this lately is this good write-up  

Replace diff/merge tool in Visual Studio Team System with WinMerge | paulbouwer.com

Quote from his blog:

One thing I cannot get used to is the basic diff/merge tool. Where is the detail ? The screenshot below demonstrates the basic nature of the diff tool built into Visual Studio Team System. There is no indication of the number of differences between the files being diff’ed or even the actual differences on a particular line …

 

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

Posted: Sep 16 2011, 04:05 PM by azenker | with 3 comment(s)
Filed under: ,
Make your head hurt in a good way

I have been just as attentive to blog reading as I have to blog writing and was enjoying some extra-long fix/compile/test cycles today and was able to catch up on Eric Lippert’s blog. All I have to say is: “my head hurts … and it feels great!”.

So much of the content today is “fluffy” for lack of better word. Maybe you have been the victim of the YACPC? (Yet Another Cloud Podcast)

Have you heard? the cloud is wonderful, in the cloud small children never cry, there are no taxes and your cell phone bill makes sense. Everyone, go to the cloud, go to the cloud …. But I digress.

So, amidst all the fluffiness it is nice to have some clever, and even meaningful, content well delivered. If you haven’t been exposed yet I suggest you start with two recent posts:

The Truth About Value Types

Is Is As Or Is As Is

 

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

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:                                              />
Posted: Jun 02 2010, 09:36 PM by azenker | with no comments
Filed under: , , ,
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

Posted: Dec 02 2009, 08:13 PM by azenker | with 3 comment(s)
Filed under: , ,
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.

So, SharePoint Wiki is really convenient to set up. Add a new site, choose the Wiki template and you are off an running. That is until you decide you are going to try to add some serious amount of content. The built in editor is good for only simple pages and the image upload feature is … well … less than perfect shall we say. After attempting to upload my first image 3 times with no success, I started looking for other options. In my search I found a question on StackOverflow that led to a great work-around for me. Not sure if this has been posted elsewhere but I felt it was worth a shout-out.

In answer to a question about potential reasons to not use SP Wikis, jfaughnan posted an answer that included his approach to getting around the lousy editor experience in the Wiki:

“There is a convoluted workaround that I use. It takes advantage of the superb SharePoint support and image editing integrated with Windows Live Writer.

  1. Create a SP blog that will hold the images that will be referenced in the wiki.
  2. Use Windows Live Writer to post to the wiki-image-blog. Drop your image into WLW, resize it as needed, etc. If you like, use WLW to write your image associated wiki text first draft as well.
  3. After you post to the Wiki, copy and paste image and text into the Wiki editor rich text field.

This takes surprisingly little time, far less than any other option I've read of. I admit, it is convoluted.”

The author calls it “convoluted”, actually I found it to be a sheer stroke of genius. 15 minutes later I had my “staging blog” set up as a new site, had LiveWriter configured to post to the blog and was up and running with a nice editor, formatting for code samples and cut-and-paste and resize for pictures. LiveWriter handles uploading the images to the blog site. Once your new Wiki content is posted, open the post to edit in the blog site, copy the raw contents and paste them, images and all, into a new Wiki page. Tweak a few links and you are good to go.

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

Is My Blog Worth Stealing?

Apparently to some, it is.

I was helping a co-worker with the same issue that I blogged about in my last post on how to improve SharePoint list item delete calls. When I Googled (Binged if that makes you feel better) on the subject trying to find a clear answer to her question I found another blogger who had written on the same subject. Cool! Maybe he has the missing piece. I opened the link to find … my own post. There it was, in its entirety. With no clear attribution as to its original source. There was a link on the page back to my site, but why would anyone click it since the entire post was there for their reading pleasure.

So, tonight I did some more searches and found at least two other copies of the same post, one with absolutely no link back at all to my blog. More searches turned up other copies of my posts. So, am I upset? Heck yeah! What can I do about it? Probably nothing. I did a WhoIs search on one of the domains and practically had to download a new font to display the name of the registered domain owner. I won’t mention the sites here as I am loathe to do anything to raise their SEO rankings.

My approach then will be (for now): to not care. I will make a small gesture of adding a copyright notice to the bottom of each posting, even though legally this is not necessary, and, perhaps, adding a “Posted By: Andreas Zenker” tag as well. I’m not really thinking that this will actually stop these sites from scraping my content into their little pay-per-click campaign but maybe there stupid little servers will have to waste a few cycles parsing out some text before they copy my blog over. I’m sure the increased power consumption will cripple them. Take that! ;-)

Update: 9/19/2009

Actually the one site is in English, just hosted elsewhere. Funny thing is I see a new track-back from that site this morning. Once again my post is copied in full, the only reason there is a link to me at all is that I embedded a link to my blog in this post. Maybe that is the secret! I will make the copyright tag a link back to me. Also, they didn't waste any cycles on removing the copyright tag, it got copied as well.

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

More Posts Next page »