Protein is Good for You, part 2 (Matt Gertz)
In yesterday’s blog post, I walked through an engine for translating DNA to its amino acid results via messenger RNA. In today’s blog, we’ll work on the visualization using WPF StackPanels. (This example requires VS2008, although it could certainly be written in WinForms as well with just a little bit of extra work.)
Caution: The point of this blog is to demonstrate StackPanels, and specifically how you can control their orientation and nesting. Consequently, for the sake of that argument, I’m creating a lot of text objects (one for each base and each amino) and nesting them to demonstrate this. For a really robust program, I would ditch the extra objects and just do owner draw directly. In other words, don’t use this example on enormous 20000-sequence strands of DNA, as neither you nor your PC would like the performance results.
In the last post, I added a ScrollViewer to the Window, changed it to scroll horizontally instead of vertically, but otherwise left it alone. Now we’ll dig into the details of how we’ll populate it with StackPanels.
StackPanels expose a very easy way to organize a set of drawable objects on the screen. StackPanels expose a Children collection, and when you add objects to the that collection, they’ll display in the order you add them and in the direction you specify. What’s really cool is that StackPanels will nest with each other, and I’ll take advantage of that fact by creating three StackPanels that organize information horizontally (one each for DNA, mRNA, and aminos), and then adding them to a parent StackPanel so that they line up with each other. I’ll then add the parent StackPanel to the ScrollViewer so that the alignment of all three sub-panels can be seen.
Each of the sub-panels will contain one of more text objects which either contain the abbreviation of a base, the abbreviation of an amino, or blank space (representing unused intron material). Aminos correspond to three bases, and so those text objects need to be three times wider. I’ll create some helper functions to return the appropriate text objects:
Const baseWidth As Integer = 16
Private Function DrawBase(ByVal base As Char) As TextBlock
Dim baseTextBlock As New TextBlock
Select Case base ‘ Switch for colors
baseTextBlock.Background = Brushes.LightSalmon
baseTextBlock.Background = Brushes.LightGoldenrodYellow
baseTextBlock.Background = Brushes.LightGreen
baseTextBlock.Background = Brushes.LightSkyBlue
baseTextBlock.Background = Brushes.LightSteelBlue
baseTextBlock.Inlines.Add(New Bold(New Run(base)))
baseTextBlock.Height = 32
baseTextBlock.Width = baseWidth
baseTextBlock.TextAlignment = TextAlignment.Center
That last function will be used by both DNA and RNA, creating and returning a fixed block of an appropriate color (a different one for each base) 16 x 32 pixels with the base abbreviation (passed into the function) centered within it. I’ve chosen light colors so that the dark text will show up against the background properly.
For the amino chains, it’s pretty similar, except that each block is three times wider, to keep the amino lined up with the codon:
Private Function DrawAmino(ByVal amino As String) As TextBlock
Dim aminoTextBlock As New TextBlock
Select Case amino ‘ Switch for colors
aminoTextBlock.Background = Brushes.AliceBlue
p; Case “Leu”
aminoTextBlock.Background = Brushes.Beige
aminoTextBlock.Background = Brushes.Cyan
aminoTextBlock.Background = Brushes.LightSeaGreen
‘ (Etc… the other aminos omitted for brevity’s sake; see final code for full list.)
aminoTextBlock.Inlines.Add(New Bold(New Run(amino)))
aminoTextBlock.Height = 32
aminoTextBlock.Width = baseWidth * 3
aminoTextBlock.TextAlignment = TextAlignment.Center
And for empty space, I just pass in the number of spaces required and multiple it by the baseWidth:
Private Function DrawNoMap(ByVal noMapSize As Integer) As TextBlock
Dim noMapTextBlock As New TextBlock
noMapTextBlock.Background = ScrollViewer1.Background
noMapTextBlock.Height = 32
noMapTextBlock.Width = baseWidth * noMapSize
Now I have the tools; we can put them to good use. First, let’s create & populate the DNA StackPanel:
Private Function DrawBases(ByVal s As String) As StackPanel
‘ DNA goes here (horizontal orientation):
Dim myStackPanel As New StackPanel
myStackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left
myStackPanel.VerticalAlignment = System.Windows.VerticalAlignment.Top
myStackPanel.Orientation = Orientation.Horizontal
‘Add child elements to the parent StackPanel.
For i As Integer = 0 To s.Length – 1
This is a simple function that does three things:
(1) Create a stack panel that has horizontal orientation, containing object that start from the top left.
(2) Step through each base in the DNA or RNA and add the TextBlock for it created in DrawBase.
(3) Return the completed StackPanel
I can use this for both DNA and mRNA just by passing in the appropriate string. Aminos are a bit trickier because I’ve decided to save them as lists of strings rather than one long string (to make it easier to access individual proteins should I wish to expand this program in the future):
Private Function DrawAminos() As StackPanel
‘ Aminos go here (horizontal orientation):
Dim myStackPanelAminos As New StackPanel
myStackPanelAminos.HorizontalAlignment = System.Windows.HorizontalAlignment.Left