I often find Firebug lacking in certain areas, such as finding where a certain html tag is used. Jacob Gube came out with a list of add ons for Firebug (which itself is a Firefox add on), that I find very useful.
Sunday, June 21, 2009
Wednesday, May 20, 2009
To support long file name (>260 char), get a wider (>260 columns across) monitor
Sunday, May 17, 2009
WinForms cue/prompt/watermark/placeholder TextBox and other controls
I thought writing the placeholder text box was much easier in WPF than WinForms, since WPF allows graphical elements to be overlaid easily. I was wrong! For WinForms, a P/Invoke can achieve that function since XP and Vista had it built-in. Thanks to Aaron for the source.
Sunday, May 03, 2009
The “Coolest” DHTML Calendar
Searching for a JavaScript calendar returned a lot of results. However, most calendar implementations out there are buggy. They are also written in the old JavaScript ways without all the prototyping stuff, causing conflicts with my own variables and functions.
After trying out some of the calendars, I found this calendar to be the best—stable, prototyped and has few side-effects. Only worry is that it uses browser detection rather than feature detection, potentially breaking in future versions of browsers. If you know of an even better calendar, please tell me =). Thanks.
Tuesday, April 28, 2009
Start Menu - Enable Folder and Library Pin to Start Menu
Dragging the Computer link in the start menu to the Pin area creates a pin for Computer. However, while it is easy to pin, it is impossible to remove! Luckily, by just adding a new registry key, the context menu will show the “Unpin from Start Menu” item.
Instructions at Start Menu - Enable Folder and Library Pin to Start Menu - Windows 7 Forums
Thursday, April 23, 2009
Why no more 16-bit support in Windows 64-bit?
It seems kind of weird that Microsoft removed support for running 16-bit programs on 64-bit Windows. Even in Windows 7 32-bit, 16-bit programs are still supported. How different can the code be? Why is the 32-bit version written but not ported to 64-bit?
The following article explains why
It is AMD and Intel who removed support for 16-bit on 64-bit. Code for 16-bit support on 32-bit must be re-written in order not to use the Virtual 8086 mode.
Does this mean that my QuickBasic programs will never see the light of the day again? Or will Windows 64-bit one day support 16-bit again (by bundling DOSBox maybe)?
Tuesday, April 21, 2009
Confused by news titles
Microsoft: Walls will become computers of the future
So are there Walls or no Walls?
Saturday, April 04, 2009
Saturday, March 21, 2009
Mod your Visual Studio Professional
Here's my list of developer tools for Visual Studio Professional. Dreamspark only provides Professional Edition, so I'll be listing the
alternatives for the missing features that are only available for Team Foundation. All software listed are free unless otherwise stated.
Client-side
Code templates and refactoring
- SlickEdit Gadgets generates methods to retrieve data from clipboard, clipboard analyzer and calculates Source Lines of Codes.
- Enterprise Library contains various application blocks for Validation, Logging and others. (I had not used this before)
- Coderush Xpress declare classes, interface structs, extract method and others with ease.
- Refactor! for ASP.NET extract to user control, rename, move style to style sheets, surround with update panels and many more with ease.
- OR
- Coderush (Trial, best to buy as it is really worth it. For the broke, download a new version, uninstall and install when it expires). Generates templates for classes, exceptions, for/foreach loops and many more. Identifies syntax errors, suggests alternative shorter statements. Also provides the DXCore library (free) which some of the plugins depend on. (Requires a powerful computer)
- Refactor Pro! (Trial, best to buy as it is really worth it. For the broke, download a new version, uninstall and install when it expires). Rename, move a piece of code into a new method, extract interface, simplify logic, extract html styles to style sheets, remove end tags in XML and many more with ease. (Requires a powerful computer)
Source control (SVN)
- Slik SVN for SVN functionalities and command line.
- TortoiseSVN to have GUI in Windows Explorer.
- AnkhSVN to have GUI in Visual Studio.
Code compliance/best practices
- FxCop to check for performance, globalization, signing and other issues.
- StyleCop to check for missing XML comments, standardize code and other issues.
- RightHand plugin spell-check your XML comments, comments and strings. (Requires DXCore)
Database connectivity
- NHibernate O/R mapper. Generates classes for communicating to database. Simpler and more mature than Linq to SQL. (I had not use this before)
- OR
- LLBLGen Pro (Buy) O/R mapper. Generates classes for communicating to database. Popular and more mature that Linq to SQL. (I had not use this before)
Unit Testing
- xUnit.net provides method for asserting exceptions, rather than the more restrictive ExpectedException attribute used in nUnit.
- TestDriven.Net Personal Version to have GUI in Visual Studio. (I had not use this before)
Documentation
- GhostDoc to generate default XML comments.
- Sandcastle to generate help files (in html/chm) from the XML comments. (Warning: a memory and processor hog)
JavaScript tools
- Script# generate JavaScript from C#. Native support for Windows Gadgets and Silverlight. Takes advantage of intellisense, static-typing and others of C#.
- IE 8 for JavaScript debugging and testing IE specific methods.
- Firefox with Firebug and Tamper Data add-ons for JavaScript debugging, testing Firefox specific methods and checking POST data.
WPF tools
- Mole Visualizer presents your WPF classes graphically during debugging. (Warning: Crashes often the last time I used it)
- ViewerSvg (Trial). Converts SVG into XAML.
- Xceed DataGrid for WPF Express provides the missing data grid, masked text box, date time picker and other controls in WPF. (Warning: The last time that I’ve used it, there are some properties that you need to leave it as default, if not the data grid will crash. Test extensively.)
Regular expression tools
- Regex Builder to see results of regular expression instantly.
Reverse engineering
- .NET Reflector reverse engineer any .NET generated dll or exe files and see the source code in C#.
- ILDASM and ILASM (installed with VS) for editing .NET dll and exe files.
- Spy++ (installed with VS) to find the Windows Handles of applications.
Interoperability
- PInvoke Visual Studio Add-in provides GUI to search PInvoke.net. Community-contributed C# and VB signatures to the Windows 32 API functions.
- Connector/NET for database connection to MySQL database (if you happen to inherit one, like me).
Blogging
- CopySourceAsHtml copies the syntax highlighting in your C# code as html or rtf for posting/printing nicely formatted code.
- Window Clippings (trial) for capturing screen shots with translucent non-client area (the title bar, border etc).
Server-side
Source control (SVN)
- VisualSVN Server to host SVN on the web.
Continuous Integration
- CruiseControl.NET check-out source code, run scripts, unit tests, source analysis whenever there is a check-in to source control. (Warning: hard to configure. I am now using it only to check-out source code)
- NAnt run build scripts. (I had not used this before)
Bug Tracking
- BugTracker.NET website for filing tasks, bugs. Sends email for new entries. Very easy to install and configure, though look and feel leaves much to be desired.
Friday, March 20, 2009
CSS Centering: Auto-width Margins
<div align=”center”> has been deprecated in XHTML 1.1. Replacing it with <div style=”text-align:center”> did not work.
The correct replacement is <div style=”width;1000px; margin:0 auto”> on the child element and a text-align:center on the parent element. Credit to CSS Centering: Auto-width Margins for example.
Wednesday, March 11, 2009
Windows 7 and Life Without Walls
A demonstration of web and networking functionalities in Windows 7.
Monday, February 23, 2009
ASP.NET Supervising Controller (Model View Presenter) From Schematic To Unit Tests to Code
A very practical way of implementing MVP for ASP.NET:
ASP.NET Supervising Controller (Model View Presenter) From Schematic To Unit Tests to Code
Tuesday, January 20, 2009
Useful blogs about installing Windows 7 Beta 1
Saturday, January 17, 2009
Fujitsu T5010 Drivers for Windows 7 64-bit
Tuesday, January 06, 2009
How to recover from an accidental deletion of linux boot partition (Part 3 of 3)
Recover process
Boot into Fedora DVD. Choose the Install or Upgrade option. Upgrade the existing partition. The installation should proceed smoothly now. Fedora recovered!How to recover from an accidental deletion of linux boot partition (Part 2 of 3)
Recovery Process
Boot into Vista, install VirtualBox (VirtualPC didn't work. No USB support). All the steps below are done in VirtualBox. Create new virtual machine. Install Fedora 10. (Default installation) Boot into Fedora. swapoff -a, delete the swap partition and edit fstab. (this is to create a configuration that matches my Fedora installation on my hard disk before I deleted my boot partition) Insert thumb drive and capture it. Open Terminal. Switch to root (su). Copy everything in /boot into my thumb drive, which is mounted on /mnt/TOSHIBA Shut down Fedora. Exit out of VirtualBox. Shut down Vista. Boot into Fedora DVD. Go into Rescue Installed System. Select English, US keyboard, no network. To find which device is the thumb drive, type #blkid My system showed /dev/sdb1: LABEL="TOSHIBA" UUID=... Also, note down the UUID of the root partition, we will need it later. /dev/dm-0: UUID="<note down this long string>" TYPE="ext3" Mount the thumb drive as follows #mkdir /mnt/thumb #mount /dev/sdb1 /mnt/thumb Mount the boot partition #mkdir /mnt/boot #mount /dev/sda7 /mnt/boot Copy everything from the thumb drive to the boot partition. Next is to edit grub.conf #nano /mnt/boot/grub/grub.conf Change the timeout=0 to timeout=5 After hiddenmenu add the following to create the Windows Vista boot option.title Windows Vista
rootnoverify (hd0,1)
chainloader +1
For the line beginning with kernel /vmlinuz, replace the UUID in the root=UUID=<replace this what you note down just now>
To save, Ctrl+O
To exit, Ctrl+X
#reboot
Fedora recovered!
How to recover from an accidental deletion of linux boot partition (Part 1 of 3)
6 partitions? Hmm, I thought it could have been that the recovery partitions were special and does not take up entries in the partition table.
Then I wanted to free up one partition to make (partition table) space for Windows 7 Beta 1. So Linux swap partition shall go. As I had tried out Fedora 2 long time ago, I thought the 3rd partition was Linux swap and 4th the Linux root.
Booted into Linux, swapoff and edited the /etc/fstab file to exclude the swap partition.
Booted back to Vista, deleted the 3rd partition. Boom! The truth revealed...
I have in fact only 4 partitions! The 2 Linux partitions were on the extended partition. Rebooted my computer. All is not well...
At start-up
To boot into Vista, I had to type
rootnoverify (hd0,1)
chainloader +1
boot
After looking through a number of websites, it finally dawned on me that the default Fedora partitioning had changed. The 3rd partition is a boot partition and the 4th is an LVM partition, which contains both swap and root. (both are totally new to me)
No boot partition means no Fedora kernel, and that means my Fedora will never boot! Oh no!
Recovery process
Boot from the Fedora DVD. Go into Rescue Installed System. Select English, US keyboard, no network. Next step is to re-create the boot partition. #fdisk /dev/sda n p 1 <enter> <enter> w #reboot Fedora recovered!Saturday, October 25, 2008
Placeholder text using Adorner, improved
//-----------------------------------------------------------------------
// <copyright file="Placeholder.cs" company="Jeow Li Huan">
// Copyright (c) Jeow Li Huan. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Media;
namespace TetonWhitewaterKayak.WinUI
{
/// <summary>
/// Represents an adorner that adds placeholder text to a <see cref="T:System.Windows.Controls.TextBox"/>,
/// <see cref="T:System.Windows.Controls.RichTextBox"/> or <see cref="T:System.Windows.Controls.PasswordBox"/>.
/// </summary>
public class Placeholder : Adorner
{
#region Dependency Property
/// <summary>
/// Identifies the <see cref="T:Huan.Windows.Documents.Placeholder" />.Text attached property.
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(Placeholder),
new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
/// <summary>
/// Identifies the <see cref="T:Huan.Windows.Documents.Placeholder" />.HideOnFocus attached property.
/// </summary>
public static readonly DependencyProperty HideOnFocusProperty = DependencyProperty.RegisterAttached(
"HideOnFocus",
typeof(bool),
typeof(Placeholder),
new FrameworkPropertyMetadata(true, OnHideOnFocusChanged));
#endregion
/// <summary>
/// <see langword="true"/> when the placeholder text is visible, <see langword="false" /> otherwise.
/// Used to avoid calling <see cref="M:System.Windows.UIElement.InvalidateVisual"/> unnecessarily.
/// </summary>
private bool isPlaceholderVisible;
#region Constructors
/// <summary>
/// Initializes static members of the <see cref="T:Huan.Windows.Documents.Placeholder"/> class.
/// </summary>
static Placeholder()
{
IsHitTestVisibleProperty.OverrideMetadata(typeof(Placeholder), new FrameworkPropertyMetadata(false));
ClipToBoundsProperty.OverrideMetadata(typeof(Placeholder), new FrameworkPropertyMetadata(true));
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Huan.Windows.Documents.Placeholder"/> class.
/// </summary>
/// <param name="adornedElement">
/// The element to bind the adorner to.
/// </param>
/// <exception cref="T:System.ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
public Placeholder(PasswordBox adornedElement)
: this((Control)adornedElement)
{
if (!(adornedElement.IsFocused && (bool)adornedElement.GetValue(HideOnFocusProperty)))
adornedElement.PasswordChanged += this.AdornedElement_ContentChanged;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Huan.Windows.Documents.Placeholder"/> class.
/// </summary>
/// <param name="adornedElement">
/// The element to bind the adorner to.
/// </param>
/// <exception cref="T:System.ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
public Placeholder(TextBoxBase adornedElement)
: this((Control)adornedElement)
{
if (!(adornedElement.IsFocused && (bool)adornedElement.GetValue(HideOnFocusProperty)))
adornedElement.TextChanged += this.AdornedElement_ContentChanged;
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Huan.Windows.Documents.Placeholder"/> class.
/// </summary>
/// <param name="adornedElement">
/// The element to bind the adorner to.
/// </param>
/// <exception cref="T:System.ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
protected Placeholder(Control adornedElement)
: base(adornedElement)
{
if ((bool)adornedElement.GetValue(HideOnFocusProperty))
{
adornedElement.GotFocus += this.AdornedElement_GotFocus;
adornedElement.LostFocus += this.AdornedElement_LostFocus;
}
}
#endregion
#region Attached Property Getters and Setters
/// <summary>
/// Gets the value of the <see cref="T:Huan.Windows.Documents.Placeholder" />.Text attached property for a specified element.
/// </summary>
/// <param name="adornedElement">
/// The element from which the property value is read.
/// </param>
/// <returns>
/// The placeholder text property value for the element.
/// </returns>
/// <exception cref="T:ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
public static string GetText(Control adornedElement)
{
if (adornedElement == null)
throw new ArgumentNullException("adornedElement");
return (string)adornedElement.GetValue(TextProperty);
}
/// <summary>
/// Sets the value of the <see cref="T:Huan.Windows.Documents.Placeholder" />.Text attached property to a specified element.
/// </summary>
/// <param name="adornedElement">
/// The element to which the attached property is written.
/// </param>
/// <param name="placeholderText">
/// The needed placeholder text value.
/// </param>
/// <exception cref="T:ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
/// <exception cref="T:InvalidOperationException">
/// Raised when adornedElement is not a <see cref="T:System.Windows.Controls.TextBox"/>,
/// <see cref="T:System.Windows.Controls.RichTextBox"/> or <see cref="T:System.Windows.Controls.PasswordBox"/>.
/// </exception>
public static void SetText(Control adornedElement, string placeholderText)
{
if (adornedElement == null)
throw new ArgumentNullException("adornedElement");
adornedElement.SetValue(TextProperty, placeholderText);
}
/// <summary>
/// Gets the value of the <see cref="T:Huan.Windows.Documents.Placeholder" />.HideOnFocus attached property for a specified element.
/// </summary>
/// <param name="adornedElement">
/// The element from which the property value is read.
/// </param>
/// <returns>
/// A value indicating whether the control will be hidden when the element is in focus.
/// </returns>
/// <exception cref="T:ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
public static bool GetHideOnFocus(Control adornedElement)
{
if (adornedElement == null)
throw new ArgumentNullException("adornedElement");
return (bool)adornedElement.GetValue(HideOnFocusProperty);
}
/// <summary>
/// Sets the value of the <see cref="T:Huan.Windows.Documents.Placeholder" />.HideOnFocus attached property to a specified element.
/// </summary>
/// <param name="adornedElement">
/// The element to which the attached property is written.
/// </param>
/// <param name="hideOnFocus">
/// A value indicating whether to hide the placeholder text when the element is in focus.
/// </param>
/// <exception cref="T:ArgumentNullException">
/// Raised when adornedElement is null.
/// </exception>
/// <exception cref="T:InvalidOperationException">
/// Raised when adornedElement is not a <see cref="T:System.Windows.Controls.TextBox"/>,
/// <see cref="T:System.Windows.Controls.RichTextBox"/> or <see cref="T:System.Windows.Controls.PasswordBox"/>.
/// </exception>
public static void SetHideOnFocus(Control adornedElement, bool hideOnFocus)
{
if (adornedElement == null)
throw new ArgumentNullException("adornedElement");
adornedElement.SetValue(HideOnFocusProperty, hideOnFocus);
}
#endregion
/// <summary>
/// Draws the content of a <see cref="T:System.Windows.Media.DrawingContext" /> object during the render pass of a <see cref="T:Huan.Windows.Documents.Placeholder"/> element.
/// </summary>
/// <param name="drawingContext">
/// The <see cref="T:System.Windows.Media.DrawingContext" /> object to draw. This context is provided to the layout system.
/// </param>
protected override void OnRender(DrawingContext drawingContext)
{
Control adornedElement = this.AdornedElement as Control;
string placeholderText;
bool hideOnFocus = (bool)adornedElement.GetValue(HideOnFocusProperty);
if (adornedElement == null ||
(adornedElement.IsFocused && hideOnFocus) ||
!this.IsElementEmpty() ||
string.IsNullOrEmpty(placeholderText = (string)adornedElement.GetValue(TextProperty)))
this.isPlaceholderVisible = false;
else
{
this.isPlaceholderVisible = true;
Size size = adornedElement.RenderSize;
double maxHeight = size.Height - adornedElement.BorderThickness.Top - adornedElement.BorderThickness.Bottom - adornedElement.Padding.Top - adornedElement.Padding.Bottom;
double maxWidth = size.Width - adornedElement.BorderThickness.Left - adornedElement.BorderThickness.Right - adornedElement.Padding.Left - adornedElement.Padding.Right - 4.0;
if (maxHeight <= 0 || maxWidth <= 0)
return;
TextAlignment computedTextAlignment = this.ComputedTextAlignment();
// Foreground brush does not need to be dynamic. OnRender called when SystemColors changes.
Brush foreground = SystemColors.GrayTextBrush.Clone();
foreground.Opacity = adornedElement.Foreground.Opacity;
Typeface typeface = new Typeface(adornedElement.FontFamily, FontStyles.Italic, adornedElement.FontWeight, adornedElement.FontStretch);
FormattedText formattedText = new FormattedText(
placeholderText,
CultureInfo.CurrentCulture,
adornedElement.FlowDirection,
typeface,
adornedElement.FontSize,
foreground);
formattedText.TextAlignment = computedTextAlignment;
formattedText.MaxTextHeight = maxHeight;
formattedText.MaxTextWidth = maxWidth;
double left;
double top = 0.0;
if (adornedElement.FlowDirection == FlowDirection.RightToLeft)
left = adornedElement.BorderThickness.Right + adornedElement.Padding.Right + 2.0;
else
left = adornedElement.BorderThickness.Left + adornedElement.Padding.Left + 2.0;
switch (adornedElement.VerticalContentAlignment)
{
case VerticalAlignment.Top:
case VerticalAlignment.Stretch:
top = adornedElement.BorderThickness.Top + adornedElement.Padding.Top;
break;
case VerticalAlignment.Bottom:
top = size.Height - adornedElement.BorderThickness.Bottom - adornedElement.Padding.Bottom - formattedText.Height;
break;
case VerticalAlignment.Center:
top = (size.Height + adornedElement.BorderThickness.Top - adornedElement.BorderThickness.Bottom + adornedElement.Padding.Top - adornedElement.Padding.Bottom - formattedText.Height) / 2.0;
break;
}
if (adornedElement.FlowDirection == FlowDirection.RightToLeft)
{
// Somehow everything got drawn reflected. Add a transform to correct.
drawingContext.PushTransform(new ScaleTransform(-1.0, 1.0, RenderSize.Width / 2.0, 0.0));
drawingContext.DrawText(formattedText, new Point(left, top));
drawingContext.Pop();
}
else
drawingContext.DrawText(formattedText, new Point(left, top));
}
}
/// <summary>
/// Adds a <see cref="T:Huan.Windows.Documents.Placeholder"/> to the adorner layer.
/// </summary>
/// <param name="adornedElement">
/// The adorned element.
/// </param>
private static void AddAdorner(Control adornedElement)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
if (adornerLayer == null)
return;
Adorner[] adorners = adornerLayer.GetAdorners(adornedElement);
if (adorners != null)
foreach (Adorner adorner in adorners)
if (adorner is Placeholder)
{
adorner.InvalidateVisual();
return;
}
TextBox textBox = adornedElement as TextBox;
if (textBox != null)
{
adornerLayer.Add(new Placeholder(textBox));
return;
}
RichTextBox richTextBox = adornedElement as RichTextBox;
if (richTextBox != null)
{
adornerLayer.Add(new Placeholder(richTextBox));
return;
}
PasswordBox passwordBox = adornedElement as PasswordBox;
if (passwordBox != null)
{
adornerLayer.Add(new Placeholder(passwordBox));
return;
}
// TextBox is hidden in template. Search for it.
TextBox templateTextBox = null;
templateTextBox = FindTextBox(adornedElement);
if (templateTextBox != null)
Placeholder.SetText(templateTextBox, (string)Placeholder.GetText(adornedElement));
}
/// <summary>
/// Finds a <see cref="T:System.Windows.Controls.TextBox"/> in the visual tree of the adorned element using a breadth first search.
/// </summary>
/// <param name="adornedElement">The adorned element which visual tree is searched for a <see cref="T:System.Windows.Controls.TextBox"/>.</param>
/// <returns>The <see cref="T:System.Windows.Controls.TextBox"/> if one is found, <see langword="null"/> if none exists.</returns>
private static TextBox FindTextBox(Control adornedElement)
{
TextBox templateTextBox = null;
Queue<DependencyObject> queue = new Queue<DependencyObject>();
queue.Enqueue(adornedElement);
while (queue.Count > 0)
{
DependencyObject element = queue.Dequeue();
templateTextBox = element as TextBox;
if (templateTextBox != null)
break;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); ++i)
queue.Enqueue(VisualTreeHelper.GetChild(element, i));
}
return templateTextBox;
}
/// <summary>
/// Event handler for AdornedElement.Loaded.
/// </summary>
/// <param name="sender">
/// The AdornedElement where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private static void AdornedElement_Loaded(object sender, RoutedEventArgs e)
{
Control adornedElement = (Control)sender;
adornedElement.Loaded -= AdornedElement_Loaded;
AddAdorner(adornedElement);
}
/// <summary>
/// Invoked whenever <see cref="T:Huan.Windows.Documents.Placeholder" />.Text attached property is changed.
/// </summary>
/// <param name="sender">
/// The object where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private static void OnTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (((string)e.OldValue).Equals((string)e.NewValue, StringComparison.Ordinal))
return;
Control adornedElement = sender as Control;
if (adornedElement == null)
return;
if (adornedElement.IsLoaded)
AddAdorner(adornedElement);
else
adornedElement.Loaded += AdornedElement_Loaded;
}
/// <summary>
/// Invoked whenever <see cref="T:Huan.Windows.Documents.Placeholder" />.HideOnFocus attached property is changed.
/// </summary>
/// <param name="sender">
/// The object where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private static void OnHideOnFocusChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
bool hideOnFocus = (bool)e.NewValue;
if ((bool)e.OldValue == hideOnFocus)
return;
Control adornedElement = sender as Control;
if (!(adornedElement is TextBox || adornedElement is PasswordBox || adornedElement is RichTextBox))
{
if (!adornedElement.IsLoaded)
adornedElement.Loaded += ChangeHideOnFocus;
else
{
TextBox templateTextBox = FindTextBox(adornedElement);
if (templateTextBox != null)
Placeholder.SetHideOnFocus(templateTextBox, (bool)e.NewValue);
}
return;
}
Placeholder placeholder = null;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(adornedElement);
if (adornerLayer == null)
return;
Adorner[] adorners = adornerLayer.GetAdorners(adornedElement);
if (adorners != null)
foreach (Adorner adorner in adorners)
{
placeholder = adorner as Placeholder;
if (placeholder != null)
break;
}
if (placeholder == null)
return;
if (adornedElement.IsLoaded)
{
if (hideOnFocus)
{
adornedElement.GotFocus += placeholder.AdornedElement_GotFocus;
adornedElement.LostFocus += placeholder.AdornedElement_LostFocus;
if (adornedElement.IsFocused && placeholder.isPlaceholderVisible)
placeholder.InvalidateVisual();
}
else
{
adornedElement.GotFocus -= placeholder.AdornedElement_GotFocus;
adornedElement.LostFocus -= placeholder.AdornedElement_LostFocus;
placeholder.AdornedElement_LostFocus(adornedElement, new RoutedEventArgs(UIElement.LostFocusEvent, placeholder));
}
}
else
adornedElement.Loaded += AdornedElement_Loaded;
}
/// <summary>
/// Changes the HideOnFocus property of the text box in the visual tree of the sender.
/// </summary>
/// <param name="sender">The object where the event handler is attached.</param>
/// <param name="e">Provides data about the event.</param>
private static void ChangeHideOnFocus(object sender, RoutedEventArgs e)
{
Control adornedElement = sender as Control;
if (adornedElement == null)
return;
TextBox templateTextBox = FindTextBox(adornedElement);
if (templateTextBox != null)
Placeholder.SetHideOnFocus(templateTextBox, Placeholder.GetHideOnFocus(adornedElement));
}
#region Event Handlers
/// <summary>
/// Event handler for AdornedElement.GotFocus.
/// </summary>
/// <param name="sender">
/// The AdornedElement where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private void AdornedElement_GotFocus(object sender, RoutedEventArgs e)
{
TextBoxBase textBoxBase = AdornedElement as TextBoxBase;
if (textBoxBase != null)
textBoxBase.TextChanged -= this.AdornedElement_ContentChanged;
else
{
PasswordBox passwordBox = AdornedElement as PasswordBox;
if (passwordBox != null)
passwordBox.PasswordChanged -= this.AdornedElement_ContentChanged;
}
if (this.isPlaceholderVisible)
this.InvalidateVisual();
}
/// <summary>
/// Event handler for AdornedElement.LostFocus.
/// </summary>
/// <param name="sender">
/// The AdornedElement where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private void AdornedElement_LostFocus(object sender, RoutedEventArgs e)
{
TextBoxBase textBoxBase = AdornedElement as TextBoxBase;
if (textBoxBase != null)
textBoxBase.TextChanged += this.AdornedElement_ContentChanged;
else
{
PasswordBox passwordBox = AdornedElement as PasswordBox;
if (passwordBox != null)
passwordBox.PasswordChanged += this.AdornedElement_ContentChanged;
}
if (!this.isPlaceholderVisible && this.IsElementEmpty())
this.InvalidateVisual();
}
/// <summary>
/// Event handler for AdornedElement.ContentChanged.
/// </summary>
/// <param name="sender">
/// The AdornedElement where the event handler is attached.
/// </param>
/// <param name="e">
/// Provides data about the event.
/// </param>
private void AdornedElement_ContentChanged(object sender, RoutedEventArgs e)
{
if (this.isPlaceholderVisible ^ this.IsElementEmpty())
this.InvalidateVisual();
}
#endregion
/// <summary>
/// Checks if the content of the adorned element is empty.
/// </summary>
/// <returns>
/// Returns <see langword="true" /> if the content is empty, <see langword="false" /> otherwise.
/// </returns>
private bool IsElementEmpty()
{
UIElement adornedElement = AdornedElement;
TextBox textBox = adornedElement as TextBox;
if (textBox != null)
return string.IsNullOrEmpty(textBox.Text);
PasswordBox passwordBox = adornedElement as PasswordBox;
if (passwordBox != null)
return string.IsNullOrEmpty(passwordBox.Password);
RichTextBox richTextBox = adornedElement as RichTextBox;
if (richTextBox != null)
{
BlockCollection blocks = richTextBox.Document.Blocks;
if (blocks.Count == 0)
return true;
if (blocks.Count == 1)
{
Paragraph paragraph = blocks.FirstBlock as Paragraph;
if (paragraph == null)
return false;
if (paragraph.Inlines.Count == 0)
return true;
if (paragraph.Inlines.Count == 1)
{
Run run = paragraph.Inlines.FirstInline as Run;
return run != null && string.IsNullOrEmpty(run.Text);
}
}
return false;
}
return false;
}
/// <summary>
/// Computes the text alignment of the adorned element.
/// </summary>
/// <returns>
/// Returns the computed text alignment.
/// </returns>
private TextAlignment ComputedTextAlignment()
{
Control adornedElement = AdornedElement as Control;
TextBox textBox = adornedElement as TextBox;
if (textBox != null)
{
if (DependencyPropertyHelper.GetValueSource(textBox, TextBox.HorizontalContentAlignmentProperty)
.BaseValueSource != BaseValueSource.Local ||
DependencyPropertyHelper.GetValueSource(textBox, TextBox.TextAlignmentProperty)
.BaseValueSource == BaseValueSource.Local)
// TextAlignment dominates
return textBox.TextAlignment;
}
RichTextBox richTextBox = adornedElement as RichTextBox;
if (richTextBox != null)
{
BlockCollection blocks = richTextBox.Document.Blocks;
TextAlignment textAlignment = richTextBox.Document.TextAlignment;
if (blocks.Count == 0)
return textAlignment;
if (blocks.Count == 1)
{
Paragraph paragraph = blocks.FirstBlock as Paragraph;
if (paragraph == null)
return textAlignment;
return paragraph.TextAlignment;
}
return textAlignment;
}
switch (adornedElement.HorizontalContentAlignment)
{
case HorizontalAlignment.Left:
return TextAlignment.Left;
case HorizontalAlignment.Right:
return TextAlignment.Right;
case HorizontalAlignment.Center:
return TextAlignment.Center;
case HorizontalAlignment.Stretch:
return TextAlignment.Justify;
}
return TextAlignment.Left;
}
}
}
