09 June 2010

Regular expression to split a string

Parsing flat files can be quite annoying, as evidenced in the NEXUS format in Bioinformatics. Regular expressions help and here is a fun one that can be used to split a string on the ';' character, but not when it is enclosed in [] (which is a comment in NEXUS).

(.*?)(?<!\[[^\]]*?);

This can be the basis of any pattern that needs to skip over comments, although I have not tested it with nested comments. Thankfully, that was beyond the scope of what I needed.

24 May 2010

WPF: One-time, One-way and Two-way Data Binding a DataGrid to LINQ to XML

In my previous post I described my findings on how to bind a DataGrid to a collection object. Now I will examine another common scenario - binding a DataGrid to LINQ to XML a.k.a XLinq.

Basic data binding to XLinq

The goal is to bind the DataGrid to a collection of XElement items returned from XLinq. For a one-time binding, the obvious method works:

Source XML file:
<charsets>
    <charset name="COI" value="1-907" />
    <charset name="f1" value="dsldh" />
</charsets>

MainWindows.xaml.cs:
public XDocument Doc...
...
this.charsetGrid.DataContext = Doc.Root.Elements("charset");

MainWindow.xaml:
<Window x:class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        title="MyApp" height="478" width="644" x:name="mainWindow">
  <DockPanel name="dockPanel1">
    <DataGrid Name="charsetGrid" IsReadOnly="True">
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=Attribute[name].Value}" Header="Name" />
        <DataGridTextColumn Binding="{Binding Path=Attribute[value].Value}" Header="Value" Width="*" />
      </DataGrid.Columns>
    </DataGrid>
  </DockPanel>
</Window>

One-way and Two-way data binding to XLinq

There are some major gotchas when you are trying to get one-way and two-way data binding to work with XLinq. Here is the list of my findings:

1. The binding to the actual XElement collection has to be done from within XAML. Failing to do so results in a cryptic run-time error when entering edit mode.
2. Property changes of the bound XElements function properly out of the box.
3. The XElement collection does not support two-way changes (presumably because it does not have to map to a single node in the XML tree).
4. You will need to use the Elements and Attribute dynamic properties as the binding paths.
5. You will need to manually implement modifications to the collection structure (i.e. adding and removing items). Again, presumably, this is because the collection does not represent a node in the XML tree.

Here is the code that implements a two-way binding between a DataGrid and an XLinq XML document.

MainWindow.xaml.cs:
public partial class MainWindow : Window
{
    public string FileName  { get; set; }
    public XDocument Doc { get; set; }
    ...
    private void LoadData()
    {
        ...
        // Load the XML file and set the context for the binding
        Doc = XDocument.Load(FileName);
        this.charsetGrid.DataContext = Doc.Root.Element("charsets");
    }

    private void AddCharset_Click(object sender, RoutedEventArgs e)
    {
        // Manually add an XElement at the right position in the XML tree.
        // Make sure teh XElement is fully created (i.e. it has all attributes
        // and elements that are bound to columns in the DataGrid, or you will
        // have run-time problems.
        XElement elem = new XElement("charset",
            new XAttribute("name", string.Empty),
            new XAttribute("value", string.Empty),
        Doc.Root.Element("charsets").Add(elem);
    }

    private void RemoveCharsets_Click(object sender, RoutedEventArgs e)
    {
        // Delete the XElement selected in the DataGrid
        XElement elem = (XElement)charsetGrid.SelectedItem;
        elem.Remove();
    }
}

MainWindow.xaml:
<Window x:class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        title="MyApp" height="478" width="644" x:name="mainWindow">
  <DockPanel name="dockPanel1">
    <DataGrid Name="charsetGrid" IsReadOnly="False" ItemsSource="{Binding Path=Elements}">
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=Attribute[name].Value}" Header="Name" />
        <DataGridTextColumn Binding="{Binding Path=Attribute[value].Value}" Header="Value" Width="*" />
      </DataGrid.Columns>
    </DataGrid>
  </DockPanel>
</Window>

Note how in line 6 of the XAML we bind the ItemsSource to Elements in XAML instead of code.

WPF: One-time, One-way and Two-way Data Binding a DataGrid to a collection

If you are trying to use WPF Data Binding to a collection, MSDN is currently largely useless.

Basic data binding to a collection

The WPF DataGrid can bind to any object that exposes IEnumerable.

I have a DataGrid and I want to bind it to a collection of custom objects. The collection is a List. The first problem is how to expose an object that is really only available in code to XAML. This should work (but for some reason, does not):

MainWindow.xaml.cs
public partial class MainWindow : Window
{
public List<CharSet> CharSets { get; set; }
...
}

MainWindow.xaml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyApp" Height="478" Width="644"
x:Name="mainWindow">
  <DockPanel Name="dockPanel1">
  <DataGrid Name="charsetGrid" ItemsSource="{Binding ElementName=mainWindow, Path=CharSets}" />
  </DockPanel>
</Window>

Even though the binding engine does not report a problem with getting the CharSets property, the binding does not actually work. Various experiments with setting DataContext did not go any better.

I had to make the following modifications to get this to work:
MainWindow.xaml.cs
public partial class MainWindow : Window
{
    public List<CharSet> CharSets { get; set; }
    ...
    private void LoadData()
    {
        ...
        this.charsetGrid.ItemsSource = CharSets;
    }
}

MainWindow.xaml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyApp" Height="478" Width="644"
x:Name="mainWindow">
  <DockPanel Name="dockPanel1">
    <DataGrid Name="dataGrid1">
      <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
        <DataGridTextColumn Header="Value" Binding="{Binding Value}" Width="*" />
      </DataGrid.Columns>
    </DataGrid>
  </DockPanel>
</Window>

Name and Value are public properties on the objects in the collection.

One-way and Two-way data binding to the collection

99% of the time when you bind to a collection, you will want to reflect and changes to it automatically. A lot of the time you will also need to update the collection from the DataGrid. In other words, you will be doing one-way or two-way data binding most of the time. It is surprising then that it is so difficult to find good information on how to do this.

Most sources will direct you to use an ObservableCollection, in order to implement INotifyCollectionChanged, but this is unnecessary and diminishes the usefulness of data binding - after all, you want your control to bind seamlessly with your engine code, not have to tailor your engine code to your UI. A List or Collection will provide you with the needed functionality.

The other piece of a advise you will find is to provide property change notifications for the objects that you are storing by making them implement INotifyPropertyChanged. This seems to be completely unnecessary when binding to a List. The code above in fact already provides full two way binding (as long as the DataGrid properties allow it).

04 May 2010

WPF: Bolding a portion of text in a TextBlock

I am using WPF and .NET 4 in my latest project and I ran into a bit of a snag, trying to figure out how to display formatted text in a TextBlock programmatically. The process is far from obvious, so I thought I'd capture it here.

The following code makes a part of the text bold:

      
TextBlock fileNameStatus;
...
// Usually we need to clear the TextBlock text
fileNameStatus.Inlines.Clear();
fileNameStatus.Inlines.Add("Editing file: "); // Plain text
Run textRun = new Run(_fileName);
textRun.FontWeight = FontWeights.Bold; // Bold text
fileNameStatus.Inlines.Add(textRun);

The control of the font is done through the Run type and its FontXXX properties.

25 November 2008

New MicroSat Radar in C#

I am writing a new version of MicroSat Radar in C# 2008 and .NET 3.5.

MicroSat Radar is a DNA microsatellite detection program that scans DNA sequences and reports potential microsatellite sites (base pair repeats).

The original MicroSat Radar was written in Delphi and has been used by graduate students in the biology department of UT Austin. It has a simple user interface that allows the entry of the DNA sequence and the parameters of the scan. The results appear in a list box in the UI.


My goal for version 2 is to leverage the new features of C# 2008 and extend the basic design to allow savign the results of scans, creating a database of sequences and results, and improving detection by checking the reverse sequence.