In this post I will cover the changes required to use a PasswordBox control to display the FTP password.
The following links a quick reference to the topics covered:
- FTP Plug-in Overview – Quick description of the plug-in control created by Tim Heuer.
- FTP Plug-in Changes – Steps to change the plug-in to use a PasswordBox control instead of TextBox.
- Testing The Changes – Describes testing the changes with Expression Encoder and the issues encountered.
- PasswordBox Styles – Describes to changes made to alter the style for the PasswordBox control so that it has consistent look and feel.
- Investigating Default Styles - Investigating the Expression Encoder WPF Styles using Redgate’s .Net Reflector.
- Final Version – Describes the final version incorporating the style changes in the previous sections.
- Review – Describes the step taken to modify the plug-in to make use the of a PasswordBox control.
Technologies Used
- Microsoft Visual Studio 2008 (with .Net 3.5 SP1 installed)
- Microsoft Expression Encoder 2 (with Expression Encoder 2 SP1 installed)
The FTP Publishing Plug-in for Encoder created by Tim Heuer allows the encoded output of to be “published” to a FTP server for hosting. One minor issue from a security point of view is the password is shown in clear text. As a result the password is always visible and it could represent a security risk.
In the next section I will look at replacing the standard WPF text TextBox control with a PasswordBox so that the password is masked from the user.
Looking at the original XAML it is a standard TextBox that makes use of data binding to load and save changes.
<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
<TextBox Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" Text="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
The obvious change to replace this with a PasswordBox and bind to the Password property instead of the Text property.
<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
<PasswordBox Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
This however generates an error. Looking at the problem further, Samuel Jack provides the explanation in his post WPF PasswordBox and Data binding. In the post he discusses how the Password property is a plain CLR property rather than a Dependency Property, so it doesn't support being the target of a data binding. Using Samuel’s solution the code is easily modified to make use of attached properties by adding a attribute namespace reference at the top of the UserControl
xmlns:FtpPublish="clr-namespace:TimHeuer.Expression.FtpPublish"
Combined with additions to the the PasswordBox xaml to make use of the attached properties
<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="0,0,3,0" Content="Password" />
<PasswordBox Name="Password" Grid.Row="2" Grid.Column="1" ToolTip="FTP account password" Margin="0,2.5,0,2.5" FtpPublish:PasswordBoxAssistant.BindPassword="true" FtpPublish:PasswordBoxAssistant.BoundPassword="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
And adding the following C# class to define the implementation of the PasswordBoxAssistant class.
using System.Windows;
using System.Windows.Controls;
namespace TimHeuer.Expression.FtpPublish
{
/// <summary>
/// Class that allows a <see cref="PasswordBox"/> to be included in DataBinding using attached properties
/// </summary>
/// <remarks>
/// Original source http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
/// </remarks>
public static class PasswordBoxAssistant
{
public static readonly DependencyProperty BoundPassword =
DependencyProperty.RegisterAttached("BoundPassword", typeof(string), typeof(PasswordBoxAssistant), new FrameworkPropertyMetadata(string.Empty, OnBoundPasswordChanged));
public static readonly DependencyProperty BindPassword = DependencyProperty.RegisterAttached(
"BindPassword", typeof(bool), typeof(PasswordBoxAssistant), new PropertyMetadata(false, OnBindPasswordChanged));
private static readonly DependencyProperty UpdatingPassword =
DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool), typeof(PasswordBoxAssistant));
private static void OnBoundPasswordChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var box = d as PasswordBox;
// only handle this event when the property is attached to a PasswordBox
// and when the BindPassword attached property has been set to true
if (box == null || !GetBindPassword(d))
{
return;
}
// avoid recursive updating by ignoring the box's changed event
box.PasswordChanged -= HandlePasswordChanged;
var newPassword = (string)e.NewValue;
if (!GetUpdatingPassword(box))
{
box.Password = newPassword;
}
box.PasswordChanged += HandlePasswordChanged;
}
private static void OnBindPasswordChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
{
// when the BindPassword attached property is set on a PasswordBox,
// start listening to its PasswordChanged event
var box = dp as PasswordBox;
if (box == null)
{
return;
}
var wasBound = (bool)(e.OldValue);
var needToBind = (bool)(e.NewValue);
if (wasBound)
{
box.PasswordChanged -= HandlePasswordChanged;
}
if (needToBind)
{
box.PasswordChanged += HandlePasswordChanged;
}
}
private static void HandlePasswordChanged(object sender, RoutedEventArgs e)
{
var box = sender as PasswordBox;
if (box == null)
return;
// set a flag to indicate that we're updating the password
SetUpdatingPassword(box, true);
// push the new password into the BoundPassword property
SetBoundPassword(box, box.Password);
SetUpdatingPassword(box, false);
}
public static void SetBindPassword(DependencyObject dp, bool value)
{
dp.SetValue(BindPassword, value);
}
public static bool GetBindPassword(DependencyObject dp)
{
return (bool)dp.GetValue(BindPassword);
}
public static string GetBoundPassword(DependencyObject dp)
{
return (string)dp.GetValue(BoundPassword);
}
public static void SetBoundPassword(DependencyObject dp, string value)
{
dp.SetValue(BoundPassword, value);
}
private static bool GetUpdatingPassword(DependencyObject dp)
{
return (bool)dp.GetValue(UpdatingPassword);
}
private static void SetUpdatingPassword(DependencyObject dp, bool value)
{
dp.SetValue(UpdatingPassword, value);
}
}
}
Copying the updated dll to the <Program Files>\Microsoft Expression\Encoder 2\Plugins folder, the plug-in now correctly loads but the password box does not have the correct visual styles applied to it because the PasswordBox has not been included in the template styles for the WPF plug-in.
Overall the control is missing the following functionality:
- The correct background colour.
- Rounded corners on the control border,
- Missing a password character to display when characters are typed.
The next sections will look at adding applying styles to the PasswordBox so that it is consistent with the rest of the plug-in.
Adjusting The style of the password box can be performed by altering the UserControl resources of the StandardSettings.xaml. An example of how apply the rounded corners is provided by Tamir Khason on the msdn forums. Tweaking example slightly to only have a border radius of 2 gives the following xaml to be added to the xaml file.
<UserControl.Resources>
<Style TargetType="PasswordBox">
<Setter Property="PasswordChar" Value="●"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border CornerRadius="2" x:Name="Bd" Background="{DynamicResource WindowBackgroundBrush}" BorderBrush="Black" BorderThickness="1" OpacityMask="{x:Null}">
<ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Template="{DynamicResource ScrollViewerControlTemplate1}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
Which gives the following output

This style is much closer, but it gives the impression that the password box is not editable by the user. In the next section I will make use Redgate’s .Net Reflector to determine the styles used by the Expression Encoder UI to provide a more consistent look and feel.
The expression encoder is written using Microsoft.Net and WPF. As result the styles and user interface as stored within the application assemblies and binary files. Using Redgates .Net Reflector together with the BAML viewer addin available on codeplex we can look inside these file and discover the styles used in the application.
The main WPF application is located in <Program Files>\Microsoft Expression\Encoder 2\EncoderUI.exe and the english resource files that include the xaml for the application is located in <Program Files>\Microsoft Expression\Encoder 2\en\EncoderUI.resources.dll. Opening these files in reflector we are able to look at the implementation of the application.

Looking at the xaml the style for a password box within the resources assembly the dynamic resource BackgroundBrush should be applied for the BorderBrush and the Background properties of the PasswordBox.
<Style x:Uid="Style_84" TargetType="{x:Type PasswordBox}">
<Setter x:Uid="Setter_1349" Property="BorderBrush" Value="{DynamicResource BackgroundBrush}" />
<Setter x:Uid="Setter_1350" Property="Foreground" Value="{DynamicResource Text1Brush}" />
<Setter x:Uid="Setter_1351" Property="Background" Value="{DynamicResource BackgroundBrush}" />
<Setter x:Uid="Setter_1146" Property="FontFamily" Value="{DynamicResource {x:Static MessageFontFamily}}" />
<Setter x:Uid="Setter_1147" Property="FontSize" Value="{DynamicResource {x:Static MessageFontSize}}" />
<Setter x:Uid="Setter_1148" Property="FontWeight" Value="{DynamicResource {x:Static MessageFontWeight}}" />
</Style>
Applying the style changes in the last section provides the following UserControl.Resources section.
<UserControl.Resources>
<Style TargetType="PasswordBox">
<Setter Property="PasswordChar" Value="●"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="PasswordBox">
<Border CornerRadius="2" x:Name="Bd" Background="{DynamicResource BackgroundBrush}" BorderBrush="{DynamicResource BackgroundBrush}" BorderThickness="1" OpacityMask="{x:Null}">
<ScrollViewer SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" x:Name="PART_ContentHost" Template="{DynamicResource ScrollViewerControlTemplate1}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
With an output of

So there we have it, the plug-in look and feel was updated by altering the XAML to make use of Password box. The changes required the use of WPF attached properties to ensure the data binding continues to work with he original code. To make the user interface look consistent with rest of the plug-in UserControl resources where used to apply the correct styles and resources.