This little class shows you how you can control the binding updates and make inner TextBox properties accessible:
//-----------------------------------------------------------------------
// <copyright file="EditableComboBox.cs" company="DockOfTheBay">
// http://www.dotbay.be
// </copyright>
// <summary>Defines the EditableComboBox class.</summary>
//-----------------------------------------------------------------------
namespace DockOfTheBay
{
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
/// <summary>
/// Editable combo box which updates its data source on the following events:
/// - Selection changed (e.g. via the DropDown)
/// - Lost focus (e.g. after Tab Key)
/// - Enter or Return pressed.
/// </summary>
/// <remarks>
/// To work properly, this EditableComboBox assumes/requires the following about its binding:
/// - The data source should be bound to the Text property of the ComboBox (not SelectedValue)
/// - The binding expression UpdateSourceTrigger property should be set to LostFocus
/// </remarks>
public class EditableComboBox : ComboBox
{
/// <summary>
/// The Maximum Length of the TextBox's content.
/// </summary>
/// <remarks>
/// It's implemented as a Dependency Property, so you can set it in XAML
/// </remarks>
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register(
"EditableTextBox",
typeof(int),
typeof(EditableComboBox),
new UIPropertyMetadata(int.MaxValue));
/// <summary>
/// Initializes a new instance of the <see cref="EditableComboBox"/> class.
/// </summary>
public EditableComboBox()
{
// Avoid non-intuitive behavior
this.IsTextSearchEnabled = false;
this.IsEditable = true;
}
/// <summary>
/// Gets or sets the maximum length of the text in the EditableTextBox.
/// </summary>
/// <value>The maximum length of the text in the EditableTextBox.</value>
[Description("Maximum length of the text in the EditableTextBox.")]
[Category("Editable ComboBox")]
[DefaultValue(int.MaxValue)]
public int MaxLength
{
[System.Diagnostics.DebuggerStepThrough]
get
{
return (int)this.GetValue(MaxLengthProperty);
}
[System.Diagnostics.DebuggerStepThrough]
set
{
this.SetValue(MaxLengthProperty, value);
this.ApplyMaxLength();
}
}
/// <summary>
/// Gets a reference to the internal editable textbox.
/// </summary>
/// <value>A reference to the internal editable textbox.</value>
/// <remarks>
/// We need this to get access to the real MaxLength.
/// </remarks>
protected TextBox EditableTextBox
{
get
{
return this.GetTemplateChild("PART_EditableTextBox") as TextBox;
}
}
/// <summary>
/// Makes sure that the design time settings are applied when the control
/// becomes operational. If the MaxLength setter is called too early, we miss
/// the assignment.
/// </summary>
/// <remarks>
/// This feels like a hack, but
/// - OnInitialized is fired too early
/// - OnRender in fired too often
/// </remarks>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.ApplyMaxLength();
}
/// <summary>
/// Updates the Text property binding when the user presses the Enter key.
/// </summary>
/// <remarks>
/// KeyDown is not raised for Arrows, Tab and Enter keys.
/// They are swallowed by the DropDown if it is open.
/// So use the KeyUp instead.
/// </remarks>
/// <param name="e">A Key EventArgs.</param>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.Key == Key.Return || e.Key == Key.Enter)
{
this.UpdateDataSource();
}
}
/// <summary>
/// Updates the Text property binding when the selection changes.
/// </summary>
/// <param name="e">A SelectionChanged EventArgs.</param>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
this.UpdateDataSource();
}
/// <summary>
/// Applies the MaxLength value to the EditableTextBox.
/// </summary>
private void ApplyMaxLength()
{
if (this.EditableTextBox != null)
{
this.EditableTextBox.MaxLength = this.MaxLength;
}
}
/// <summary>
/// Updates the data source.
/// </summary>
private void UpdateDataSource()
{
BindingExpression expression = GetBindingExpression(ComboBox.TextProperty);
if (expression != null)
{
expression.UpdateSource();
}
}
}
}
You have now access to the MaxLength property, even from XAML:
<local:EditableComboBox x:Name="EditableComboBox1"
Text="{Binding Value, UpdateSourceTrigger=LostFocus}"
MaxLength="15" />
Hi, I have a question - can I use this (somehow) to have editable combobox in my properties window (in visual studio) of MyElement? You know - I have for example MyRadioButton and it has property OtherElements. This property should be editable combobox with names of all other elements on page.
ReplyDeleteHello,
ReplyDeleteWhat should I bind the Text property to?
My first try:
<local:EditableComboBox
Text="{Binding ID, UpdateSourceTrigger=LostFocus"
IsEditable="True" ItemsSource="{Binding FieldList}" DisplayMemberPath="Name" SelectedValuePath="ID" IsSynchronizedWithCurrentItem="True" />
does not give any good results.
I can't get this to work either, the example usage is far to terse or either it just doesnt work, The example provided doesnt even include an itemssource binding. Please help and provide a full example on how to use this control from Xaml.
ReplyDeleteworks fine, thanks!
ReplyDeletebinding and editing is ok but It doesnt pop up or pop down combobox? It looks more like a textbox for me.
ReplyDeleteTo use this to add to your ComboBox ItemsSource you must add a KeyUp handler so you can add the entered text into the ItemsSource.
ReplyDeleteWhat this does accomplish is it works around the nagging "bug" of the WPF ComboBox which happens when you select an item but are bound to the Text property, the ComboBox will clear the selected text from the TextProperty.
Thanks for this, saved me many hours of scratching my head!
ReplyDeleteI was searching online for a way to set MaxLength on a ComboBox and found several posts that were not very helpful. Yours, however, is GREAT! Thanks for posting it!
ReplyDeleteThis: return this.GetTemplateChild("PART_EditableTextBox") as TextBox;
ReplyDeletealways returns NULL for me. Anyone know why?
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThank you! This worked great for me. Here's an example than complements the use of ItemsSource to show a list of users and allows to enter domain user accounts and adds them to the list:
ReplyDelete< helpers:EditableComboBox ItemsSource="{Binding CurrentUsers, NotifyOnSourceUpdated=True}" Text="{Binding AccountName, UpdateSourceTrigger=LostFocus}" SelectedIndex="{Binding UserSelectedIndex}" />
Add the following properties to your ViewModel class. (Model is my model class and Users is a class that basically contains a List Names, an int Index and a string Selected to keep track of the users list and the currently selected user, you can simplify as needed):
public List CurrentUsers
{
get { return Model.Users.Names; }
}
public string AccountName
{
get { return Model.AccountName; }
set
{
Model.AccountName = value;
Model.Users.Add(value);
OnPropertyChanged(() => UserSelectedIndex);
OnPropertyChanged(() => CurrentUsers);
}
}
public int UserSelectedIndex
{
get { return Model.Users.Index; }
set
{
Model.Users.Index = value;
Model.AccountName = Model.Users.Selected;
OnPropertyChanged(() => AccountName);
}
}
Are you looking to make money from your visitors by popup ads?
ReplyDeleteIn case you are, have you ever used EroAdvertising?
best solution I found with "Take Value with Enter".
ReplyDeleteTHX - works for me perfect!
Stephan