Datepicker Watermark

by Sottje 1. April 2010 07:10

On my current project, we make use of the WPF Toolkit. One of the things we use is the Datepicker. A nice usefull control. We wanted to set the watermark, but the control doesn't support setting the watermark with a public property and we didn't want to go into the control's source itself.

On Codeplex, we found a forum topic with a solution (thnx Spud). Derive from the Datepicker control and override the OnRender method. This works and sets the watermark correctly.

But after a date is selected, the value was cleared and the control loses the focus, the default watermark returns again. I fixed this by using the LostFocus event on the textbox. The result is this:

 
public class SottjeDatePicker : DatePicker
{
    private FieldInfo _textboxFieldInfo;
    private DatePickerTextBox _dateTextBox;

    /// 
    /// Overrides the OnRender methods
    /// 
    /// 
    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        _textboxFieldInfo = typeof(DatePicker).GetField("_textBox"
            , BindingFlags.Instance | BindingFlags.NonPublic);

        if (_textboxFieldInfo != null)
        {
            _dateTextBox = (DatePickerTextBox)_textboxFieldInfo.GetValue(this);

            // Add event to update watermark when needed on lostfocus.
            _dateTextBox.LostFocus += OnDateTextBoxLostFocus;

            UpdateWatermark();
        }
    }

    /// 
    /// Called when textbox loses focus.
    /// 
    /// The sender.
    /// The instance containing the event data.
    private void OnDateTextBoxLostFocus(object sender
                    , System.Windows.RoutedEventArgs e)
    {
        if (_dateTextBox != null && string.IsNullOrEmpty(_dateTextBox.Text))  
        {
            UpdateWatermark();
        }
    }

    /// 
    /// Updates the watermark.
    /// 
    private void UpdateWatermark()
    {
        PropertyInfo watermarkPropertyInfo = 
            typeof(DatePickerTextBox).GetProperty("Watermark"
                , BindingFlags.Instance | BindingFlags.NonPublic);

        if (watermarkPropertyInfo != null)
        {
            string watermark = Properties.Resources.DatePickerWatermark;
            watermarkPropertyInfo.SetValue(_dateTextBox, watermark, null);
        }
    }
}

Have fun!

kick it on DotNetKicks.com

Tags: , , , ,

Professional | c# | WPF

WPF autocomplete textbox/combobox

by Sottje 28. February 2010 18:38

You know these nice ajax/javascript textboxes where you start typing somes text and the website looks up the data in the database and returns a list of items starting with the text.

There is no default control for this in WPF as well. So, time to build one.

First we need to create a WPF User Control.

This control uses a canvas in stead of a grid so the control doen't resize when the results are shown. The ListBox is placed under the TextBox using a Margin. The Panel.ZIndex is used to make sure the results are shown over the other control in the window.

<UserControl x:Class="AutoCompleteComboBox.Controls.AutoCompleteComboBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Panel.ZIndex="10">
    <Canvas>
        <TextBox x:Name="autoTextBox" 
                 Height="25" MinWidth="150" 
                 Margin="0,0,10,0" TextWrapping="NoWrap" />
        <ListBox x:Name="suggestionListBox"
                 SelectionChanged="suggestionListBox_SelectionChanged"
                 Background="LightYellow" 
                 Visibility="Collapsed" 
                 MinWidth="150" 
                 Margin="0,25,0,-25"/>
    </Canvas>
</UserControl>

In the constructor of the control whe attach a number of events. The TextChanged event on the TextBox is keep looking up the data based on the currect value in the TextBox.
The PreviewKeyDown handles the navigation in the controls and the SelectionChanged on the ListBox updates the text in the TextBox when a item is selected. 

    /// 
    /// Interaction logic for AutoCompleteComboBox.xaml
    /// 
    public partial class AutoCompleteComboBox : UserControl
    {
        #region Constructor
        /// 
        /// Initializes a new instance of the  class.
        /// 
        public AutoCompleteComboBox()
        {
            InitializeComponent();

            // Attach events to the controls
            autoTextBox.TextChanged += 
                new TextChangedEventHandler(autoTextBox_TextChanged);
            autoTextBox.PreviewKeyDown += 
                new KeyEventHandler(autoTextBox_PreviewKeyDown);
            suggestionListBox.SelectionChanged += 
                new SelectionChangedEventHandler(suggestionListBox_SelectionChanged);
        }
        #endregion
    }

The Control had 2 public properties. The ItemsSource and SelectedValue. They are both dependency properties becaus we do want to Bind to them.

 
        #region Properties

        /// 
        /// Gets or sets the items source.
        /// 
        /// The items source.
        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ItemsSource.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource"
                                , typeof(IEnumerable)
                                , typeof(AutoCompleteComboBox)
                                , new UIPropertyMetadata(null));

        /// 
        /// Gets or sets the selected value.
        /// 
        /// The selected value.
        public string SelectedValue
        {
            get { return (string)GetValue(SelectedValueProperty); }
            set { SetValue(SelectedValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedValue.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedValueProperty =
            DependencyProperty.Register("SelectedValue"
                            , typeof(string)
                            , typeof(AutoCompleteComboBox)
                            , new UIPropertyMetadata(string.Empty));

        #endregion

The magic happens in the TextChanged event. If the TextBox has a value, we use LinQ to query the ItemsSource and bind the resultset to the ListBox. Remark, the TextBox freezes while querying the database/source.

 
        /// 
        /// Handles the TextChanged event of the autoTextBox control.
        /// 
        /// The source of the event.
        /// The instance containing the event data.
        void autoTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            // Only autocomplete when there is text
            if (autoTextBox.Text.Length > 0)
            {
                // Use Linq to Query ItemsSource for resultdata
                string condition = string.Format("{0}%", autoTextBox.Text);
                IEnumerable results = ItemsSource.Where(
                    delegate(string s) { return s.ToLower().StartsWith(autoTextBox.Text.ToLower()   ); });

                if (results.Count() > 0)
                {
                    suggestionListBox.ItemsSource = results;
                    suggestionListBox.Visibility = Visibility.Visible;
                }
                else
                {
                    suggestionListBox.Visibility = Visibility.Collapsed;
                    suggestionListBox.ItemsSource = null;
                }
            }
            else
            {
                suggestionListBox.Visibility = Visibility.Collapsed;
                suggestionListBox.ItemsSource = null;
            }
        }

To scroll through the resultset, select and item of to cancel results, we use the PreviewKeyDown event.

 
        /// 
        /// Handles the PreviewKeyDown event of the autoTextBox control.
        /// 
        /// The source of the event.
        /// The instance containing the event data.
        void autoTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
            {
                if (suggestionListBox.SelectedIndex < suggestionListBox.Items.Count)
                {
                    suggestionListBox.SelectedIndex = suggestionListBox.SelectedIndex + 1;
                }
            }
            if (e.Key == Key.Up)
            {
                if (suggestionListBox.SelectedIndex > -1)
                {
                    suggestionListBox.SelectedIndex = suggestionListBox.SelectedIndex - 1;
                }
            }
            if (e.Key == Key.Enter || e.Key == Key.Tab)
            {
                // Commit the selection
                suggestionListBox.Visibility = Visibility.Collapsed;
                e.Handled = (e.Key == Key.Enter);
            }
                
            if (e.Key == Key.Escape)
            {
                // Cancel the selection
                suggestionListBox.ItemsSource = null;
                suggestionListBox.Visibility = Visibility.Collapsed;
            }
        }

When the selected item has changed, the SelectedValue needs to be set. Make sure to disable the TextChanged event to avoid an update on the list.

 
        /// 
        /// Handles the SelectionChanged event of the suggestionListBox control.
        /// 
        /// The source of the event.
        /// The instance containing the event data.
        private void suggestionListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (suggestionListBox.ItemsSource != null)
            {
                autoTextBox.TextChanged 
                    -= new TextChangedEventHandler(autoTextBox_TextChanged);
                if (suggestionListBox.SelectedIndex != -1)
                {
                    autoTextBox.Text = suggestionListBox.SelectedItem.ToString();
                }
                autoTextBox.TextChanged 
                    += new TextChangedEventHandler(autoTextBox_TextChanged);
            }
        }

The solution can be downloaded here: AutoCompleteComboBox.zip (17,55 kb)

kick it on DotNetKicks.com

Tags: , , ,

Professional | c# | WPF

Const versus Readonly

by Sottje 22. February 2010 21:43

Developers often make use of constants to hold static values to use through the class or even through the application.
Sometimes we use de const and sometimes the static readonly. They seem to do the same but aware of the differences.

Const
The const field is a defined and evaluated at compile time and cannot be changed at runtime.
So therefore a const member can only be of a value type, an enumeration a string literal or null.
The initialization of classes and structures is done at runtime with the new keyword os cannot be used with const.

It behaves like a static member but cannot be decalered like one.

public const string OwnerName = "Sottje";

Readonly
The value of a readonly fields can be initialized at runtime by declaration of using the constructor.
When having multiple constructors, readonly fields have different values.
A readonly member is not static by default. Make it explicit by using the static keyword.

public readonly string OwnerName = "Sottje";

or

public class MyClass 
{ 
     public readonly string OwnerName; 

     public MyClass() 
     { 
          OwnerName = "Sottje"; 
     } 
}

Tags: , ,

Professional | c#

Preserve user settings upon installation

by Sottje 21. February 2010 20:00

When a new version of your application is deployed, you need to make sure that the user settings are preserved.
The best way of doing this is to make use of a boolean setting "IsFirstTime" with a initial value of 'True' which is checked at the startup of your application.

// Check if it is the first time the application starts
if (Settings.Default.IsFirstTime)
{
     // Upgrade the settings from the previous version
     Settings.Default.Upgrade();
  
     // Do other initial suff
  
     // Set the IsFirstTime to this code won't run anymore
     Settings.Default.IsFirstTime = false;
}

An alternative could be using the application version instead of the IsFirstTime. This way you can seperate the first time use ever process from the update application version process.

Tags: , , ,

Professional | c#

About me

Jeroen van Gent is a Professional Web, Windows and Surface developer at Qurius.

Currently coordinating 3rd line international support for an accounting company active in over a 100 countries for a WPF/Groove .NET application used by more than 20.000 users on a daily basis.

Husband and Parent with passion for Reading, Star Wars and Lego!

Follow me on twitter: twitter.com/sottje