Extending AutoCompleteBox

I like the AutoComplete feature for email addresses in hotmail, we get suggestions for any number of email addresses when we start typing. With AutoCompleteBox we get one.
Here is my attempt to implement that feature extending AutoCompleteBox.

I defined a property called ‘ExtText’ instead of overriding the Text property

we will start using the control by declaring like this

<local:ExtAutoCompleteBox Margin=”25″    TextBoxStyle=”{StaticResource textboxStyle}”
                                    x:Name=”eacb”
                                    MinimumPrefixLength=”1″ />

I had to style the TextBox to remove some borders and adjust BorderThickness properties. this style is in Page.xaml and in the codebehind set the ItemsSource Property to an array of string values. once we start typing we get the popup with suggestions like below

 eacb1

After we make a selection, we get the word displayed similar to what hotmail does, you will be able to remove the item by clicking the ‘x’. and the cursor will move to the end.  The button is just to get a message of all the selected words seperated by ‘;’

eacb1

eacb1
eacb1

you can download the code here

Creating DataGridDateColumn for DataGrid

When we want to display/edit date values in DataGrid,   we could add a templatecolumn add a TextBlock to the CellTemplate and DatePicker to CellEditingTemplate. but that gets repetitive, if we are doing the same in multiple places

we can create this custom column by inheriting from DataGridBoundColumn and overriding few methods

Here is the minimal code to create a DataGridDateColumn
 public class DataGridDateColumn : DataGridBoundColumn
    {
        public string DateFormat { get; set; }
        protected override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
            DatePicker dp = editingElement as DatePicker;
            if (dp != null)
            {
                dp.SelectedDate = DateTime.Parse(uneditedValue.ToString());
            }
        }
        protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
        {
            DatePicker dp = new DatePicker();
            Binding b = new Binding();
            b.Path = this.Binding.Path;
            b.Source = this.Binding.Source;
            if (DateFormat != null)
            {
                DateTimeConverter dtc = new DateTimeConverter();
                b.Converter = dtc;
                b.ConverterParameter = DateFormat;
            }
            dp.SetBinding(DatePicker.SelectedDateProperty, this.Binding);

            return dp;
        }

        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            TextBlock txt = new TextBlock();
            Binding b = new Binding();
            b.Path = this.Binding.Path;
            b.Source = this.Binding.Source;
            if (DateFormat != null)
            {
                DateTimeConverter dtc = new DateTimeConverter();              
                b.Converter = dtc;
                b.ConverterParameter = DateFormat;
            }
            txt.SetBinding(TextBlock.TextProperty, b);
            return txt;
        }

        protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
        {
            DatePicker dp = editingElement as DatePicker;
            if (dp != null)
            {
                DateTime? dt = dp.SelectedDate;
                if (dt.HasValue)
                return dt.Value;
            }
            return DateTime.Today ;
        }
    }

This is a converter, in case we want to format the date value

    public class DateTimeConverter : IValueConverter
    {
        public object Convert(object value,
                           Type targetType,
                           object parameter,
                           CultureInfo culture)
        {
            DateTime date = (DateTime)value;
            return date.ToString(parameter.ToString());
        }

        public object ConvertBack(object value,
                                  Type targetType,
                                  object parameter,
                                  CultureInfo culture)
        {
            string strValue = value.ToString();
            DateTime resultDateTime;
            if (DateTime.TryParse(strValue, out resultDateTime))
            {
                return resultDateTime;
            }
            return value;
        }
    }

Once we have these classes and namespace decleration(in my ex. it is mapped to local)
we can just say

<local : DataGridDateColumn DateFormat=”yyyy-MM-dd” Binding=”{Binding HireDate}”/>

Multiple selection ListBox

In a forum post poster wanted to how to do multiple selection. To be honest, I thought the listbox supports it. Anyway, looks like there is a sample implementation using checkbox. The sample involves changing the datasource.

Here is a version using ToggleButton and styles, without changing the datasource

we create a listbox setting the ItemContainer style

<ListBox Height=”200″
VerticalAlignment=”Top”
x:Name=”list1″
ItemContainerStyle=”{StaticResource ListBoxItemStyle}” />

The style is defined like this

<ToggleButton Click=”ToggleButton_Click”
Content=”{TemplateBinding Content}”
ContentTemplate=”{StaticResource dt}”
Style=”{StaticResource ToggleButtonStyle1}” />

the style for the ToggleButton is defined like this

<Style x:Key=”ToggleButtonStyle1″
               TargetType=”ToggleButton”>
            <Setter Property=”Padding”
                    Value=”3″ />
            <Setter Property=”HorizontalContentAlignment”
                    Value=”Left” />
            <Setter Property=”VerticalContentAlignment”
                    Value=”Top” />
            <Setter Property=”Background”
                    Value=”Transparent” />
            <Setter Property=”BorderThickness”
                    Value=”1″ />
            <Setter Property=”TabNavigation”
                    Value=”Local” />
            <Setter Property=”Template”>
                <Setter.Value>
                    <ControlTemplate TargetType=”ToggleButton”>
                        <Grid x:Name=”grid1″>
                            <vsm:VisualStateManager.VisualStateGroups>
                                <vsm:VisualStateGroup x:Name=”CommonStates”>
                                    <vsm:VisualState x:Name=”Normal” />
                                    <vsm:VisualState x:Name=”MouseOver”>
                                        <Storyboard>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”fillColor”
                                                                           Storyboard.TargetProperty=”Opacity”>
                                                <SplineDoubleKeyFrame KeyTime=”0″
                                                                      Value=”.35″ />
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </vsm:VisualState>
                                    <vsm:VisualState x:Name=”Pressed”>
                                        <Storyboard />
                                    </vsm:VisualState>
                                    <vsm:VisualState x:Name=”Disabled”>
                                        <Storyboard>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”contentPresenter”
                                                                           Storyboard.TargetProperty=”Opacity”>
                                                <SplineDoubleKeyFrame KeyTime=”0″
                                                                      Value=”.55″ />
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </vsm:VisualState>
                                </vsm:VisualStateGroup>
                                <vsm:VisualStateGroup x:Name=”CheckStates”>
                                    <vsm:VisualState x:Name=”Checked”>
                                        <Storyboard>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName=”fillColor2″
                                                                           Storyboard.TargetProperty=”Opacity”>
                                                <SplineDoubleKeyFrame KeyTime=”0″
                                                                      Value=”.75″ />
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </vsm:VisualState>
                                    <vsm:VisualState x:Name=”Unchecked”>
                                        <Storyboard>

                                        </Storyboard>
                                    </vsm:VisualState>
                                </vsm:VisualStateGroup>
                                <vsm:VisualStateGroup x:Name=”FocusStates”>
                                    <vsm:VisualState x:Name=”Focused”>
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Duration=”0″
                                                                           Storyboard.TargetName=”FocusVisualElement”
                                                                           Storyboard.TargetProperty=”Visibility”>
                                                <DiscreteObjectKeyFrame KeyTime=”0″>
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </vsm:VisualState>
                                    <vsm:VisualState x:Name=”Unfocused” />
                                </vsm:VisualStateGroup>
                            </vsm:VisualStateManager.VisualStateGroups>
                            <Rectangle x:Name=”fillColor”
                                       IsHitTestVisible=”False”
                                       Opacity=”0″
                                       Fill=”#FFBADDE9″
                                       RadiusX=”1″
                                       RadiusY=”1″ />
                            <Rectangle x:Name=”fillColor2″
                                       IsHitTestVisible=”False”
                                       Opacity=”0″
                                       Fill=”#FFBADDE9″
                                       RadiusX=”1″
                                       RadiusY=”1″ />
                            <ContentPresenter HorizontalAlignment=”Left”
                                              Margin=”{TemplateBinding Padding}”
                                              x:Name=”contentPresenter”
                                              Content=”{TemplateBinding Content}”
                                              ContentTemplate=”{TemplateBinding ContentTemplate}” />
                            <Rectangle x:Name=”FocusVisualElement”
                                       Visibility=”Collapsed”
                                       Stroke=”#FF6DBDD1″
                                       StrokeThickness=”1″
                                       RadiusX=”1″
                                       RadiusY=”1″ />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
Thats all for the styles. When the ToggleButton is clicked, we will have code similar to the following
where we will use couple of helper functions

private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
ListBoxItem lbi = GetParent(sender as DependencyObject);
if ((sender as ToggleButton).IsChecked.Value)
lbi.Tag = (sender as ToggleButton).DataContext;
else
lbi.Tag = null;
}

When it is time to find all the Items that are selected in the listbox. we get the stackpanel and get its children

StackPanel sp = GetChild(list1);
List mylist = sp.Children.Cast().Where(ele => ele.Tag != null).ToList();

you can see a demo or download the code here

Tip:Changing Foreground on row selection in Datagrid

When we want to change the foreground of the cells in datagrid when the row is selected it seems easier to write a few lines of code than messing with the templates
In the selectionchanged handler, we can loop throgh the columns and set the foreground of each cell like below

void datagrid2_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (DataGridColumn col in datagrid2.Columns)
{
FrameworkElement ele = col.GetCellContent(e.AddedItems[0]);
(ele as TextBlock).Foreground = new SolidColorBrush(Colors.Blue);
if (e.RemovedItems.Count > 0)
{
ele = col.GetCellContent(e.RemovedItems[0]);
(ele as TextBlock).Foreground = new SolidColorBrush(Colors.Black);
}
}
}