Thanks to all who attended last nights “Parallel Pitfalls” talk – as promised, here are the slides and demos from that talk.
Author Archives: mark
Adding a watermark to a PasswordBox in a Windows Store app
In the previous post, I wrote about a Blend behavior for Windows Store apps to add a watermark to a TextBox. The next question I got was “Well, what about a PasswordBox?” PasswordBox is a bit tricker since it doesn’t allow text to be displayed in the clear – so our little trick of changing the Text property doesn’t work here. So, instead, let’s get a little hacky (or clever depending on how you look at it I suppose). We can use the same series of events (GotFocus/LostFocus/Loaded) but instead of changing text, let’s add a new TextBlock into the visual tree of the PasswordBox to display our watermark text.
The PasswordBox visual control template looks like this (most properties removed for brevity):
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border x:Name="BackgroundElement"
Grid.ColumnSpan="2"/>
<Border x:Name="BorderElement"
Grid.ColumnSpan="2"/>
<ScrollViewer x:Name="ContentElement" />
<Button x:Name="RevealButton"
Grid.Column="1"
Visibility="Collapsed" />
</Grid>
The actual Password text is placed into the ScrollViewer named “ContentElement”. This is done by the WinRT control itself when it applies the template. What we need is a TextBlock to be directly above that ScrollViewer displaying our watermark text when the control has no password and does not have focus. As I mentioned before, I’m a big fan of behaviors – so this time I chose to just create a standard attached behavior to accomplish the goal:
using JulMar.Windows.Extensions;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
namespace PwBoxWatermark
{
/// <summary>
/// Simple behavior for the PasswordBox to provide a watermark text element.
/// </summary>
public static class PasswordBoxBehavior
{
private const string WatermarkId = "_pboxWatermark";
/// <summary>
/// Backing storage key for the text property
/// </summary>
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.RegisterAttached("Watermark", typeof (string), typeof (PasswordBoxBehavior),
new PropertyMetadata("", OnWatermarkChanged));
/// <summary>
/// Gets the watermark text
/// </summary>
/// <param name="pbox"></param>
/// <returns></returns>
public static string GetWatermark(PasswordBox pbox)
{
return (string) pbox.GetValue(WatermarkProperty);
}
/// <summary>
/// Sets the watermark text
/// </summary>
/// <param name="pbox"></param>
/// <param name="text"></param>
public static void SetWatermark(PasswordBox pbox, string text)
{
pbox.SetValue(WatermarkProperty, text);
}
/// <summary>
/// Called when the watermark is changed.
/// </summary>
/// <param name="dpo"></param>
/// <param name="e"></param>
private static void OnWatermarkChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
{
var pbox = dpo as PasswordBox;
if (pbox == null)
return;
pbox.PasswordChanged += PboxOnPasswordChanged;
pbox.GotFocus += PboxOnGotFocus;
pbox.LostFocus += PboxOnLostFocus;
pbox.Loaded += PboxOnLoaded;
string text = (e.NewValue ?? "").ToString();
if (string.IsNullOrEmpty(text))
{
RemoveWatermarkElement(pbox);
}
else
{
AddWatermarkElement(pbox, text);
}
}
/// <summary>
/// Called when the PasswordBox is loaded. This adds the watermark if one is present.
/// </summary>
/// <param name="sender"></param>
/// <param name="routedEventArgs"></param>
private static void PboxOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var pbox = (PasswordBox) sender;
string text = GetWatermark(pbox);
if (string.IsNullOrEmpty(text))
{
RemoveWatermarkElement(pbox);
}
else
{
AddWatermarkElement(pbox, text);
}
}
/// <summary>
/// Called when the PasswordBox loses focus - this adds the watermark if necessary.
/// </summary>
/// <param name="sender"></param>
/// <param name="routedEventArgs"></param>
private static void PboxOnLostFocus(object sender, RoutedEventArgs routedEventArgs)
{
var pbox = (PasswordBox) sender;
if (pbox.Password.Length == 0)
{
AddWatermarkElement(pbox, GetWatermark(pbox));
}
}
/// <summary>
/// Called when the PasswordBox gets focus - this removes any watermark.
/// </summary>
/// <param name="sender"></param>
/// <param name="routedEventArgs"></param>
private static void PboxOnGotFocus(object sender, RoutedEventArgs routedEventArgs)
{
var pbox = (PasswordBox) sender;
RemoveWatermarkElement(pbox);
}
/// <summary>
/// This is called when the password is changed in the PasswordBox and removes the watermark.
/// </summary>
/// <param name="sender"></param>
/// <param name="routedEventArgs"></param>
private static void PboxOnPasswordChanged(object sender, RoutedEventArgs routedEventArgs)
{
var pbox = (PasswordBox) sender;
if (pbox.Password.Length > 0)
{
RemoveWatermarkElement(pbox);
}
}
/// <summary>
/// Simple method to add a new TextBlock into the visual tree of the
/// PasswordBox which will present the watermark.
/// </summary>
/// <param name="pbox"></param>
/// <param name="text"></param>
private static void AddWatermarkElement(PasswordBox pbox, string text)
{
var wmTb = pbox.FindVisualChildByName<TextBlock>(WatermarkId);
if (wmTb == null)
{
var fe = pbox.FindVisualChildByName<ScrollViewer>("ContentElement");
if (fe != null)
{
var panelOwner = fe.FindVisualParent<Panel>();
if (panelOwner != null)
{
// Add the TextBlock.
var tb = new TextBlock
{
Name = WatermarkId,
Text = text,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(3, 0, 0, 0),
Foreground = new SolidColorBrush(Colors.Gray)
};
int index = panelOwner.Children.IndexOf(fe);
panelOwner.Children.Insert(index + 1, tb);
}
}
}
}
/// <summary>
/// Simple method to remove the TextBlock from the PasswordBox
/// visual tree.
/// </summary>
/// <param name="pbox"></param>
private static void RemoveWatermarkElement(PasswordBox pbox)
{
var wmTb = pbox.FindVisualChildByName<TextBlock>(WatermarkId);
if (wmTb != null)
{
var panelOwner = wmTb.FindVisualParent<Panel>();
if (panelOwner != null)
{
panelOwner.Children.Remove(wmTb);
}
}
}
}
}
The only attached property is the Watermark property – this activates the behavior and causes the code to attached event handlers to the GotFocus, LostFocus and Loaded events. When the watermark is supposed to be shown (no password, no focus), the code adds a TextBlock into the PasswordBox’s visual tree through the VisualTreeHelper class (I’m actually using some extensions from mvvmhelpers.codeplex.com here – added via Nuget [MVVMHelpers.Metro] to do the lookup, but you could replace it with a loop if you want).
To use it, you just apply the PasswordBoxBehavior.Watermark property to any PasswordBox – it will then display the watermark. Here’s an example:
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<PasswordBox Width="200" local:PasswordBoxBehavior.Watermark="Enter Password..." />
<Button Content="Login" Margin="20" />
</StackPanel>
Here’s the code if you’d like to use it yourself.
Enjoy!
Watermark TextBox in Windows Store apps
A common request for WSAs is to add a “watermark” to TextBox entries so users get a hint as to what is expected in the TextBox. You can see this in many Search Charm implementations as it allows a search hint to be provided via the SearchPane.PlaceHolderText property. However, the built-in TextBox in XAML doesn’t have this feature (HTML does!).
There are a few custom controls out there which allow for this, however, I’m a big fan of behaviors over custom controls – so when I needed this, I created a custom behavior to apply to any existing XAML TextBox to provide a watermark:
The behavior sets the text to the watermark and then watches for focus changes to add/remove the text when there is no existing text entered. It has two forms of activation – either you can add it to the normal Interaction.Behaviors collection:
<TextBox ...>
<si:Interaction.Behaviors>
<i:WatermarkTextBehavior WatermarkColor="Red" WatermarkText="Search Keyword..." />
</si:Interaction.Behaviors>
</TextBox>
Here, the main advantage is you can set the watermark text color. The default is a gray color. Alternatively, you can use an attached property (Text) which will add a new behavior to the TextBox without needing the full syntax:
<TextBox ... i:WatermarkTextBehavior.Text="Search Keyword..."> </TextBox>
The code is part of the latest MVVMHelpers library and is checked in at: WatermarkTextBoxBehavior.cs. The plan is to include it in the next Nuget drop, until then you can download the source and use it in your project directly if desired!
Enjoy!
Variable sized tiles in Windows Store Apps
One of the common requests I hear when training customers on Windows 8 is “How do I create variable sized tiles in a GridView?” The Windows Store app utilize this technique where different tiles have different sizes to promote content.
It turns out this is relatively easy in HTML – which is probably one of the reasons Microsoft chose to use that visual platform to build the app. In XAML however, this is a bit more difficult. There are many posts which detail using a VariableSizeWrapGrid with a GridView to achieve a similar effect – I like Mike Taulty’s blog on the approach. And this does work as long as you don’t want to have different sized rows and columns, and you don’t have too many items. The problem is that the VariableSizeWrapGrid doesn’t support virtualization or incremental loading – both key technologies if you have a lot of items.
An alternative approach to this is to do the layout yourself and use a Canvas as the panel – in this sense, you (the programmer) have to manage the layout and it’s not done automatically as rows and columns, but the upside is you have complete control over the layout and performance is greatly improved.
For a test, I chose to enumerate the available colors and then randomly create rows and columns – here’s a single run:
Notice that in this case the sample has two items side-by-side in the same column, and also has different sized columns and items that span rows – in fact, because I’m using a Canvas as my panel, I can use any sized element I choose – I’m not required to enforce columns and rows at all like I am with the grid-based panels.
The code is relatively straight-forward – I’m using MVVM so I put the Top/Width/Height into properties on the ColorViewModel:
public sealed class ColorViewModel : SimpleViewModel
{
#region Data
private int _heightPercent;
private double _height, _width;
private double _top, _left;
#endregion
/// <summary>
/// Color for this item
/// </summary>
public string Color { get; set; }
/// <summary>
/// Index of the item (used for label)
/// </summary>
public int Index { get; set; }
/// <summary>
/// Row for the item (used for label)
/// </summary>
public int Row { get; set; }
/// <summary>
/// Column for the item (used for label)
/// </summary>
public int Col { get; set; }
/// <summary>
/// Left position relative to (0,0).
/// </summary>
public double Left
{
get { return _left; }
set { SetPropertyValue(ref _left, value); }
}
/// <summary>
/// Top position - changes when the Height of the GridView
/// is altered (i.e. screen orientation)
/// </summary>
public double Top
{
get { return _top; }
set { SetPropertyValue(ref _top, value); }
}
/// <summary>
/// Width of this item
/// </summary>
public double Width
{
get { return _width; }
set { SetPropertyValue(ref _width, value); }
}
/// <summary>
/// Height to use for this item - calculated by the MainViewModel
/// once it knows the actual height of the GridView using the Height %
/// </summary>
public double Height
{
get { return _height; }
set { SetPropertyValue(ref _height, value); }
}
/// <summary>
/// How much of the height to take up as a %
/// </summary>
public int HeightPercent
{
get { return _heightPercent; }
set { SetPropertyValue(ref _heightPercent, value); }
}
/// <summary>
/// This indicates if the cell is shared with another item in the
/// same column/row (side-by-side)
/// </summary>
public bool IsSplitCell { get; set; }
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
public override string ToString()
{
return string.Format("{0}: {1} ({2}x{3}) {4}% [{5},{6}]", Index, Color, Width, Height, HeightPercent, Col, Row);
}
}
These properties are then calculated by the MainViewModel for each of the known colors – the position is randomly determined here just for example purposes, presumably in a real application (such as the Windows Store app) there would be an XML data file pulled from a server to display the data – but in any case there would be an known row/column and size for each element. Note there is a little code at the end of each loop iteration to ensure we always end the height of the column at 100%. The two important bits of code here are the ViewHeight property and the constructor which loads the items.
public sealed class MainViewModel : SimpleViewModel
{
#region Data
private readonly Random _rng = new Random();
private double _viewHeight = Double.NaN, _viewWidth;
private readonly List<ColorViewModel> _backingStore;
#endregion
/// <summary>
/// This is set to the actual height of the panel
/// it then calculates the proper height and top of each item
/// </summary>
public double ViewHeight
{
get { return _viewHeight; }
set
{
SetPropertyValue(ref _viewHeight, value);
double top = 0;
for (int index = 0; index < _backingStore.Count; index++)
{
var cvm = _backingStore[index];
if (cvm.Row == 0)
top = 0;
cvm.Height = _viewHeight*(cvm.HeightPercent/100.0)*.9;
if (index > 0 && _backingStore[index - 1].IsSplitCell)
{
cvm.Top = _backingStore[index - 1].Top;
}
else
{
cvm.Top = top;
top += cvm.Height;
}
}
}
}
/// <summary>
/// The calculated width of the panel - this is required so we get scrollbars in the GridView.
/// </summary>
public double ViewWidth
{
get { return _viewWidth; }
set { SetPropertyValue(ref _viewWidth, value); }
}
/// <summary>
/// The list of colors
/// </summary>
public IList<ColorViewModel> Colors { get; private set; }
/// <summary>
/// Constructor
/// </summary>
public MainViewModel()
{
_backingStore = typeof(Colors).GetTypeInfo().DeclaredProperties
.Select(p => new ColorViewModel { Color = p.Name })
.ToList();
int currentColumn = 0;
double currentWidth = 0;
// Calculate the position of each item.
for (int i = 0; i < _backingStore.Count; )
{
int columnWidth = _rng.Next(200, 500);
int numberOfColors = _rng.Next(1, 7);
int trackPct = 0;
// Create a single column
for (int c = 0; c < numberOfColors && trackPct < 95 && i < _backingStore.Count; i++, c++)
{
ColorViewModel cvm = _backingStore[i];
cvm.Index = i + 1;
cvm.Col = currentColumn;
cvm.Row = c;
cvm.Left = currentWidth;
// Decide the height of this item.
int maxH = Math.Min(100 - trackPct, 100/numberOfColors);
int h = _rng.Next(15, maxH);
// Allow it to share row with second item
if (c > 0 && _backingStore[i-1].IsSplitCell)
{
var previousCell = _backingStore[i - 1];
cvm.Left = previousCell.Left + previousCell.Width;
cvm.Width = previousCell.Width;
cvm.HeightPercent = previousCell.HeightPercent;
c--;
}
else
{
trackPct += h;
cvm.HeightPercent = h;
if (c > 0 && numberOfColors > 2 && _rng.Next(4) == 1) // 1/4 chance
{
cvm.Width = (columnWidth / 2.0) - 5;
cvm.IsSplitCell = true;
}
else
cvm.Width = columnWidth;
}
}
// Make sure we always end on 100%
_backingStore[i - 1].HeightPercent += 100 - trackPct;
_backingStore[i - 1].Width = columnWidth;
_backingStore[i - 1].IsSplitCell = false;
currentColumn++;
currentWidth += columnWidth;
}
Colors = _backingStore;
ViewWidth = currentWidth;
}
}
The last piece of the puzzle is the XAML – in order to properly size the height, we need the actual height of the GridView itself, so the code behind hooks the SizeChanged event on the GridView and then passes the newly calculated size onto the MainViewModel:
private void OnPanelSizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
// Populate DC after first resize so we don't see small-ish items on first render.
if (DataContext == null)
{
DataContext = new MainViewModel();
}
// Set the new actual height
MainViewModel vm = (MainViewModel) DataContext;
vm.ViewHeight = e.NewSize.Height;
}
We also need to respect the Canvas.Left and Canvas.Top properties – this would normally be done in an ItemContainerStyle, but unfortunately setting attached properties is not currently supported. To compensate for this, we override the GridView and set our properties onto each ItemContainer in the PrepareContainerForOverride method:
/// <summary>
/// This is here just to create a binding for the Height/Width on the GridViewItem.
/// WinRT currently doesn't support attached properties in Style setters.
/// </summary>
public class VariableSizedGridView : GridView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
BindingOperations.SetBinding(element, Canvas.LeftProperty, new Binding { Path = new PropertyPath("Left") });
BindingOperations.SetBinding(element, Canvas.TopProperty, new Binding { Path = new PropertyPath("Top") });
BindingOperations.SetBinding(element, WidthProperty, new Binding { Path = new PropertyPath("Width") });
BindingOperations.SetBinding(element, HeightProperty, new Binding { Path = new PropertyPath("Height") });
base.PrepareContainerForItemOverride(element, item);
}
}
And, of course we replace the panel for the derived GridView with a Canvas:
<differentSizedTiles:VariableSizedGridView ItemsSource="{Binding Colors}" Grid.Column="1" Grid.Row="1">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<Canvas SizeChanged="OnPanelSizeChanged" Width="{Binding ViewWidth}" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Grid Margin="5" ToolTipService.ToolTip="{Binding}">
<Rectangle Fill="{Binding Color}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
<Border Background="#30000000" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="40">
<TextBlock Text="{Binding}" Margin="10" Style="{StaticResource ItemTextStyle}" />
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</differentSizedTiles:VariableSizedGridView>
Voila! Note that we still don’t have UI virtualization – it turns out that you currently cannot create a custom virtualized panel, the support isn’t exposed yet in WinRT. But even still, the performance is easily 100x better than the VariableSizeGridPanel. The code is slightly more complex, but not overly so. Here’s the final project if you want to play with it yourself. Have fun!
.NET Bio 1.1 alpha available now
One of the open source projects I’m actively involved in is a bioinformatics library for .NET called, appropriately enough, .NET Bio. You can check it out at bio.codeplex.com. We have just put out the alpha version of the next release for community testing – this has several significant changes in it:
- A new (more standard) implementation of the Smith-Waterman alignment algorithm.
- A new (more standard) implementation of the Needleman-Wunsch alignment algorithm.
- Several improvements to the Parallel DeNovo assembler (PADENA)
- Changes to the NUCmer pairwise aligner to evaluate both the forward and reverse strands of the query sequence(s).
- Better file compatibility with several standardized formats.
- and lots of little performance tuning and bug fixes throughout.
Biology is one of those areas of science which needs high performance algorithms and computing power – we utilize the .NET Parallel Framework (PFx) all over the place to attempt to improve algorithmic performance and provide some great tools for analysis of biological sequences! Check it out at .NET Bio 1.1 Alpha Download



