Altinet
  • Home
  • Blog
  • Contact





AutoCompleteBox 2-way binding to Text property

On 13 Oct, 2011
Uncategorized
By : Bruno Samardzic
With 2 Comments
Views : 7443

Source: AutoCompleteBox with Text 2-way binding

Ok, so basically you have a following problem:
Say you have a PartnerName property on your Task entity, but you also have a Partner property. You want a PartnerName to be set, but if there’s a Partner in your database with the same name, you also want your Partner property to be set with that particular partner through async call to database.
I could bind the Text field of the out-of-box AutoCompleteControl but it acted up on me really weird, and basically it’s unusable in this type of scenario. Basically the main problem was that highlighting an item from a dropdown would overwrite what was written in the text box, and there ware a few other side-effects.
Anyway, I made my own assembly for AutoCompleteBox, you can find it if you follow the link. There are a few key changes here:

IsTextDetachedFromCollection property basically disables that a selection in the dropdown affects the text. It gets turned on the moment the Text property gets a two-way binding. It has a possibility to get disabled by setting the backing field to false

private bool _isTextDetachedFromCollection = true;
        public bool IsTextDetachedFromCollection
        {
            get
            {
                var e = GetBindingExpression(TextProperty);
                return _isTextDetachedFromCollection && 
                 e != null && e.ParentBinding.Mode == BindingMode.TwoWay;
            }
        }

It’s main usage is here, it basically disables this function.

private void OnSelectedItemChanged(object newItem)
        {
            if (IsTextDetachedFromCollection) return;
.
.
.

Additionaly, i added that after selecting one of the list items and closing the dropdown, text property does get updated.

protected virtual void OnDropDownClosed(RoutedPropertyChangedEventArgs<bool> e)
        {
            RoutedPropertyChangedEventHandler<bool> handler = DropDownClosed;
            if (handler != null)
            {
                handler(this, e);
            }
            //Added part
            if (IsTextDetachedFromCollection)
            {
                _isTextDetachedFromCollection = false;
                OnSelectedItemChanged(SelectedItem);
                _isTextDetachedFromCollection = true;
            }
        }

Now comes the ugly part…
I had a few strange behaviours coming up during use and in the end, what i had to do is heavily alter the following function

/// <summary>
        /// Updates both the text box value and underlying text dependency 
        /// property value if and when they change. Automatically fires the 
        /// text changed events when there is a change.
        /// </summary>
        /// <param name="value">The new string value.</param>
        /// <param name="userInitiated">A nullable bool value indicating whether
        /// the action was user initiated. In a user initiated mode, the 
        /// underlying text dependency property is updated. In a non-user 
        /// interaction, the text box value is updated. When user initiated is 
        /// null, all values are updated.</param>
        private void UpdateTextValue(string value, bool? userInitiated)
        {
            // Update the Text dependency property
            var triggered = false;
            if ((userInitiated == null || userInitiated == true) && Text != value)
            {
                _ignoreTextPropertyChange++;
                Text = value;
                OnTextChanged(new RoutedEventArgs());
                triggered = true;
            }

            // Update the TextBox's Text dependency property
            if (
                TextBox != null && TextBox.Text != value)
            {
                _ignoreTextBoxTextPropertyChange++;
                TextBox.Text = value ?? string.Empty;
                // Text dependency property value was set, fire event
                if ((Text == value || Text == null) && !triggered)
                {
                    OnTextChanged(new RoutedEventArgs());
                }
            }
        }

It’s basically about proper synchronization between a AutoCompletBox.Text property and the underlying TextBox.Text property. Very complicated stuff but i finally sorted it out.
But that’s not all, i also had to modify the following method


/// <summary>
        /// Handle the update of the text for the control from any source, 
        /// including the TextBox part and the Text dependency property.
        /// </summary>
        /// <param name="newText">The new text.</param>
        /// <param name="userInitiated">A value indicating whether the update 
        /// is a user-initiated action. This should be a True value when the 
        /// TextUpdated method is called from a TextBox event handler.</param>
        private void TextUpdated(string newText, bool userInitiated)
        {
            // Only process this event if it is coming from someone outside 
            // setting the Text dependency property directly.
            if (_ignoreTextBoxTextPropertyChange > 0 && userInitiated)
            {
                _ignoreTextBoxTextPropertyChange--;
                //hotfix sa if-om dogadja se situacija da pri pejstanju nove vrijednosti na prazno polje nema sadrzaja
                if (!string.IsNullOrEmpty(Text) || string.IsNullOrEmpty(newText))
                    return;
            }

            // Only process this event if it is coming from someone outside 
            // setting the Text dependency property directly.
            if (_ignoreTextPropertyChange > 0 && !userInitiated)
            {
                _ignoreTextPropertyChange--;
                return;
            }

This method gets called by the previous one, and i had to redo some flags because of asynchronous call to routed events. Messy stuff i tell ya!
Though i think the latter changes definitely SHOULD go into the autocompletebox generally, it does the same thing as a previous solution but it’s more robust.

In the end i disabled TextCompletion in when using Text binding:

        private void UpdateTextCompletion(bool userInitiated)
        {
            //Added
            if (IsTextDetachedFromCollection)
                return;
.
.
.

So i guess that’s about it! It’s a few changes here and there, but now i have it working pretty straightforward and clean. Oh, and there’s an added bonus of trimming the input if you need it (it’s in the code in few places, you’ll have no problems finding it. If you don’t then just disable it. Happy coding!



Previous Post Next Post 

About The Author

Bruno Samardzic


Number of Posts : 45
All Posts by : Bruno Samardzic

Comments ( 2 )

  • Mictian Jan 17 , 2012 at 7:04 pm / Reply

    Hi man!, I’d like to contact you, I want to know if you could provide an example of your dataform, I’m looking for some features that maybe you already have implemented.
    Anyway a “Contact me” would be gr8.
    thx in advance!

  • Bruno Jan 29 , 2012 at 1:15 pm / Reply

    Hey there!

    My contact info is on the about page, so you can use any one of those means, i’ll be glad to help


Leave a Comment

Click here to cancel reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>





Recent Posts

  • Angular vs React: round 2
  • Why programming is hard
  • Orchard Workflows, undocumented gem
  • The React Redux
  • React.js invoicing CRUD application, part 2: Description

Recent Comments

  • Angshuman on Animation for starred grid rows and columns using xaml
  • Advanced Excel Training in Lucknow on Angular vs React: round 2
  • Reginald Sophomore on My example of Angularjs directive
  • Slobodan on Angular and Breeze – story so far.
  • Bruno Samardzic on Reaction to Ember-forged Durandal Angularly Knocking out my Backbone, or my JS MVC framework research conclusion.

Contact

Altinet d.o.o.

OIB: 97429091194

Čulinečka cesta 146, Zagreb

Tel: +385 (1) 2946 819

Mob: +385 (98) 210 756

IBAN: HR4323400091110406470

Latest tweets