Using Visual Studio's Regex Find and Replace

Aug 31, 01:47 PM

The Visual Studio Find and Replace dialog is often overlooked, and when parts of it are looked at (Regex searching) it often gets a bad rep. Sure it doesn’t implement all of the Regex syntax (non greedy search springs to mind), but that’s not to say it isn't useful.

For instance, I was working on some code that involved a Model View Presenter type style, but used Subroutines (void methods) rather than WriteOnly properties for brevity (in C# you can do a Set only property in 1 line, VB it takes 5). As the View is doing nothing other than assigning labels from these "Setters" who cares how many lines it takes?

A quick breakdown of the parts of the expressions used:

Finding:
{}      //Tag an expression, used in replacements.  Numbered sequentially from 1, not 0.
(.*)    //Any character, any number of times, as many as possible.
\       //escape character, allows us to search for a literal '.' or other Regex used symbol.

Replacing:
\1      //The content of a tagged expression.
\n      //New line
\t      //Tab (although after running all these find and replaces, a quick {CTRL+E, CTRL+D} (format document) does most of the tidying for you).

So we start with the Interface:

Public Interface IProcessDetailsView
    Sub FileID(ByVal value As Integer)
    Sub SubmittedBy(ByVal value As String)
    Sub ReceivedDate(ByVal value As DateTime)
    //...
End Interface

So in the find and replace dialog I enter the following:

Find what: 
Sub {(.*)}\(ByVal value As {(.*)}\)

Replace with:
WriteOnly Property \1() As \2

The interface definition now changes to this:

Public Interface IProcessDetailsView
    WriteOnly Property FileID() As Integer
    WriteOnly Property SubmittedBy() As String
    WriteOnly Property ReceivedDate() As DateTime
    //...
End Interface

Not too difficult right? Good. Now onto the View's methods:

Public Class ProcessDetails
    Implements IProcessDetailsView

    Public Sub FileID(ByVal value As Integer) Implements IProcessDetailsView.FileID
        lblFileID.Text = value.ToString
    End Sub

    Public Sub SubmittedBy(ByVal value As String) Implements IProcessDetailsView.SubmittedBy
        lblAccountName.Text = value
    End Sub

    //...
End Class

Into the find and replace dialog:

Find what: 
Public Sub {(.*)}\(ByVal value As {(.*)}\) Implements IProcessDetailsView\.(.*)

Replace with:
Public WriteOnly Property \1() As \2 Implements IProcessDetailsView.\1\n\t\tSet(ByVal value As \2)

Find what: 
End Sub

Replace with:
End Set\n\tEnd Property

You could do this with one expression, although I have found its far less hassle to use two find and replace runs rather than trying to find new lines etc

Public Class ProcessDetails
    Implements IProcessDetailsView

    Public WriteOnly Property FileID() As Integer Implements IProcessDetailsView.FileID
        Set(ByVal value As Integer)
            lblFileID.Text = value.ToString
        End Set
    End Property

    Public WriteOnly Property SubmittedBy() As String Implements IProcessDetailsView.SubmittedBy
        Set(ByVal value As String)
            lblAccountName.Text = value
        End Set
    End Property

End Class

Now the main reason for this change was the presenter code, which doesn’t sit right with me. At a glance, am I expecting something to be calculated or what?

Public Sub Display(ByVal processHistory As ICVProcessHistory)
    _view.FileID(processHistory.FileID) 
    _view.SubmittedBy(processHistory.AccountName)
    //...
End Sub

Find and replace dialog again:

Find what: 
\_view\.{(.*)}\({(.*)}\.{(.*)}\)

Replace with:
_view.\1 = \2.\3

Which gives us this:

Public Sub Display(ByVal processHistory As ICVProcessHistory)
    _view.FileID = processHistory.FileID
    _view.SubmittedBy = processHistory.AccountName
    //...
End Sub

Much better in my opinion.

Andy

Code, .net

Comments...

---

Multilining If statements conditions should be banned. now.

Mar 24, 11:03 AM

Multilining if statement conditions is bad. I was modifying some code and came across this:

If String.IsNullOrEmpty(_selectedGUID) OrElse _
_selectedGUID = FeeAgreement.GetDefaultContractAgreementGuid OrElse _
_selectedGUID = FeeAgreement.DefaultPermAgreementGuid Then

    fgFeeAgreements.SetCellCheck(rowAdded, 0, CheckEnum.Checked)
    _selectedTitle = ag.Title
    _lastIndexRowSelected = rowAdded

End If

Which at a glance looks like this:

Single Line If
Variable Assignment
Variable Assignment

One person suggested that if someone had to do multiline the condition they could at least indent it. That’s not much good either though:

If String.IsNullOrEmpty(_selectedGUID) OrElse _
    _selectedGUID = FeeAgreement.GetDefaultContractAgreementGuid OrElse _
    _selectedGUID = FeeAgreement.DefaultPermAgreementGuid Then

    fgFeeAgreements.SetCellCheck(rowAdded, 0, CheckEnum.Checked)
    _selectedTitle = ag.Title
    _lastIndexRowSelected = rowAdded

End If

Looks like this:

If Condition Then
    Variable Assignment
    Variable Assignment

You could one line the whole thing, which while I think is better than multi line conditionals, still isn't great as I cant see all of it on a normal sized screen (read "work supplied screen").

If String.IsNullOrEmpty(_selectedGUID) OrElse _selectedGUID = FeeAgreement.GetDefaultContractAgreementGuid OrElse _selectedGUID = FeeAgreement.DefaultPermAgreementGuid Then

    fgFeeAgreements.SetCellCheck(rowAdded, 0, CheckEnum.Checked)
    _selectedTitle = ag.Title
    _lastIndexRowSelected = rowAdded

End If

So, Why not just do it as suggested in Code Complete, which fits on my screen and explains the comparisons:

Dim isContract As Boolean = (_selectedGUID = FeeAgreement.GetDefaultContractAgreementGuid)
Dim isPerm As Boolean = (_selectedGUID = FeeAgreement.DefaultPermAgreementGuid)

If String.IsNullOrEmpty(_selectedGUID) OrElse isContract OrElse isPerm Then

    fgFeeAgreements.SetCellCheck(rowAdded, 0, CheckEnum.Checked)
    _selectedTitle = ag.Title
    _lastIndexRowSelected = rowAdded

End If

I don’t know who wrote the above original code, and I don’t much care either.
I do however think that the people who like the original style are clinically insane...and I work with at least one like this!

Some unit tests wouldn’t go amiss either. Well, tests of any kind would be a good start...

Andy

Bug, Code, .net

Comments...

---

Converting from NUnit to MSTest

Jan 12, 06:52 AM

While this is not something I personally would want to do, we (for whatever reason...) are to use MSTest at work (I think it is due to the whole "Its Microsoft, so it’s supported" argument).

Now as no one else on the team does any kind of unit testing (serious), the only test projects we have are written by me, on the quiet before being told if I wanted to unit test then use MSTest. So onto the point of this article.

When you create a project for tests with nunit, you just create a Class Library, add a reference to nunit (and Rhino.Mocks of course), build it and run with your preferred method (I like TDD.Net, but that involves paying for at work...so no go there).

When you want to do tests with MSTest, you just create a Test Project and start writing tests. On closer inspection, it’s just a Class Library with a reference to Microsoft.VisualStudio.QualityTools.UnitTestFramework. So converting one to the other should be easy, right?

Well not quite. While there is nothing in the GUI to suggest so, you need to modify the csproj/vbproj file to get it to work. This post on MSDN, had all the details, but in the interest of having things in more than one place (not very DRY I will admit, but there), here are the steps:

  1. Remove Reference to Nunit.Core & Nunit.Framework
  2. Add Reference to Microsoft.VisualStudio.QualityTools.UnitTestFramework
  3. Find and Replace:
    • using NUnit.Framework; with using Microsoft.VisualStudio.TestTools.UnitTesting; (I actually use a project level import, so I skip this)
    • [TestFixture] -> [TestClass]
    • [Test] -> [TestMethod]
    • [SetUp] -> [TestInitialize]
    • [TearDown] -> [TestCleanup]
    • [TestFixtureSetUp] -> [ClassInitialize]
    • [TestFixtureTearDown] -> [ClassCleanup]
  4. Change your Asserts:
    • Assert.Greater(x, y) -> Assert.IsTrue(x > y)
    • Assert.AreEqual(x, Is.EqualTo(y).IgnoreCase) -> Assert.AreEqual(x, y, True)
  5. The 'hidden' part. In your project file, locate <PropertyGroup> (not the one specifying debug|release settings), and add the following to it:
    • 512
    • *.csproj files add:
      <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    • *.vbproj files add:
      <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F}</ProjectTypeGuids>

This was all I had to do to get our (my) tests running again under MSTest. Except they didn’t run, with the lovely error of:

The location of the file or directory 'D:\Projects\Dev\SDK\Rhino.Mocks.dll' is not trusted.

That’s odd, the file is on my hard disk, its not a network share, so what’s the problem? Right click on Rhino.Mocks.dll and: Unblock File

Click the button, hit Apply, re-run the tests. All Working now :)

There are a few other points mentioned on the MSDN post too which you may run into:

If you have relied on NUnit TestFixtureSetup and TestFixtureTearDown methods to do non-static things, will have to move functions in the former to a constructor and the latter to a destructor. In MSTest, both of these methods must be declared as static.

If you are relying on AppDomain.CurrentDomain.BaseDirectory to get the root directory, your test will break. The fix is explained at http://www.ademiller.com/blogs/tech/2008/01/gotchas-mstest-appdomain-changes-in-vs-2008/. Basically, you need to set your BaseDirectory in your MSTest TestClass constructor like this:

string currDir = Environment.CurrentDirectory.Substring(0, Environment.CurrentDirectory.IndexOf("TestResults"));
AppDomain.CurrentDomain.SetData("APPBASE", currDir);

MSTest launches each test method in a separate STA thread instead of the MTA thread you may be expecting. This probably won’t give you any problems.

Hope that helps everyone who has to do this kind of conversion.

Andy

Code, .net

Comments...

---

Thanks Google for solving my problem!

Dec 16, 12:06 PM

Following on from yesterday's post about separation on concerns and where to put some undefined logic for a multi state checkbox, I did a fair amount of research.

I must say the Quince website is a good repository of UI Design Patterns, as is Welie. I couldn’t find anything like what I was after, which I guess means I shouldn’t be doing it this way?

After a while a brainwave struck me: "Gmail lets you select things, how does it do it?” One click on the Gmail icon and I'm presented with this:

Gmail Selection

Perfect. So I went back to my sponsor and showed them a mock-up with this style of selection. The reaction was: "Oh I like that". Excellent news, for me its easier code to write (I’m happy with a for loop setting a grid cell to true in the view) and if they want to add other selections its easy enough (though there is not much else they could select by...).

The moral of the story? If in doubt, copy Google.

Andy

Code, Design, .net

Comments...

---

Functionality and Seperation of Concerns

Dec 15, 12:37 PM

When I am writing a winform in a MVP style, I often wonder how far to go with the separation. Say I have the following situation:

A Small form which should display a list of messages, and allow the user to select which ones they want processed. It processes each message in turn. If a message has more than one attachment, a dialog is shown to ask the user to select which attachment should be used for that message.

Now while this is fairly simple, my interface for the message dialog looks like this:

Public Interface IMessageSelector

    Event Submit()
    Event Cancel()

    WriteOnly Property Messages() As IList(Of MessageData)

    ReadOnly Property Selected() As IList(Of String)
    ReadOnly Property AttachmentView() As IAttachmentScreen

    Sub ShowScreen()
    Sub CloseScreen()
    Sub DisplayWarning(ByVal text As String)

End Interface

In the form I have (roughly) the following:

Public Class frmMessages
    Implements IMessageSelector
    '...'

    Public WriteOnly Property Messages() As IList(Of MessageData) Implements IMessageSelector.Messages
        Set(ByVal value As IList(Of MessageData))

            For Each d As MessageData In value

                Dim r As Grid.Row = grid.Rows.Add
                r("id") = d.ID
                r("subject") = d.Subject
                r("from") = d.Sender
                r("received") = d.SendDate

            Next

            flx.AutoSizeCols()

        End Set
    End Property

    Public ReadOnly Property Selected() As IList(Of String) Implements IMessageSelector.Selected
        Get
            Dim result As New List(Of String)

            For i As Integer = 1 To grid.Rows.Count - 1

                If Convert.ToBoolean(grid(i, "selected")) Then
                    result.Add(grid(i, "id").ToString)
                End If

            Next

            Return result

        End Get
    End Property

End Class

Now I think that this is ok. There is not logic as such in the population property, and the Selected property just determines which rows have had their checkboxes ticked.

However it has been requested that I add a 'Select All/None' checkbox to the form. Where do I add the code for this? As they want a checkbox to tick or detick its not as trivial as it could be. If it were separate buttons, I could just use a for loop in each setting the values to True or False. A checkbox however has some uncertainties:

  • Checking the master checkbox should make all rows checked. Fine.
  • DeChecking the master checkbox should make all rows unchecked. Also fine.
  • Checking one row when none are checked should do what to the master checkbox?
  • DeChecking one row when all are checked should do what to the master checkbox?
  • 25%/50%/75% of rows are checked, what does the master checkbox look like?
  • Some rows are checked. What happens when the checkbox is clicked?

So many questions for such a simple looking feature. With so many possibilities for it maybe it should go into the presenter/interface? At least it’s testable then. Maybe a separate controller for it as it’s not really anything to do with the purpose of the form?

If anyone knows of answers to this I would be very interested to hear them.

Andy

Code, Design, .net

Comments...

---

« Older