开发者

Databinding falls behind event notification - discussion

开发者 https://www.devze.com 2023-02-14 22:18 出处:网络
Found an interesting problem that I first found in WinForms, and found again in Silverlight, and more than likely WPF as well when it comes to databinding.

Found an interesting problem that I first found in WinForms, and found again in Silverlight, and more than likely WPF as well when it comes to databinding.

I have a tab control with several tabs. As users click across the tabs, each time should be valid before allowing the user to switch from the tab.

For example, user is in a text box which is updated. Binding of text boxes is not flushed until the control loses focus. Loss of focus occurs when the cursor is moved from the control, and focus is given to another control.

In this scenario, the user tabs into a control (let's use开发者_Go百科 text box for this example), and updates the text box. At this point the databinding has not flushed the control, and hence the VM has not yet seen the change. The user then uses their mouse to click the next tab of the control.

At this point things get interesting. I used the PreviewSelectionChanged (Telerik RadTabControl), as I want to check things out before the jump to the next tab occurs, and it also gives me the ability to cancel the event.

However, when I look at the VM, in this event, it still does not have the updated data. I see the VM is clean, and go ahead and allow the jump to the next tab.

As soon as this event is over however, the databindings flush, and the VM gets updated. what now? The events are out of sync! When the mouse was used to click the next tab, the textbox should have lost focus, flushed it's bindings, before the Preview of the Tab click! It's to late to jump back and say oops we didn't catch that in time!

I think I found an interesting work around to this issue - but I'm not 100% sure it will work 100% of the time. I cancel the current event, but then I use the Dispatcher and create a delegate pointing to another method with the same signature as the current event. The Dispatcher will add this message to the message pump, which by this time will now (hopefully?) be behind the messages of the VM updating...

Databinding falls behind event notification - discussion

My two questions are: 1) I'm assuming that the textbox control either didn't flush when the mouse left the control, or the process that was fired was too slow and hence the preview message was on the pump before the databinding - either way I see this to be a major issue.

2) Is the workaround a good solution?


Ok, first to answer question 1:

Just because the mouse left the textbox area, doesn't mean that the textbox lost focus. It only loses focus once something else gets focus. For example, if you moved the mouse out of the textbox and click on some other control on your page (it can be anything from a scroll viewer to another textbox, etc.) then your textbox will lose focus.

Now, based on that, the events do not happen in the wrong order. What happens is; your click event on the other tab triggers both the textbox to lose focus (and the data binding to take place) and the move to the next frame, and based on that, you basically get a race condition where the moving to the next tab happens before the databinding takes place.

About question 2:

What you can do is, set the UpdateSourceTrigger to Explicit, you will however be forced to then have some kind of text_changed event and manually update the binding.

You can read more about that here. It might not be the most complete explanation but is a good place to start.

On the other hand, you can associate some events to the textbox and force the textbox to lose focus on those events (e.g. mouse out).


Just an idea: Why not do everything in the VM's PropertyChanged event?

protected override void OnThisViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
            if(e.PropertyName == "WhateverProperty") {
                //Do your magic here for whatever you want to set
            }
        }

Have your TabItems bound to a collection that will control is being disabled or not.

<sdk:TabControl>
   <sdk:TabItem IsEnabled="{Binding SomeProperty, Converter={AmIDisabledOrWhatConverter}}" />    
</sdk:TabControl>

That way, everything is triggered whenever a property is chaned in the vm. No more timing issues since everything is on the vm.

Just my two cents.


There's a design defect here, and you're trying to work around the defect instead of fixing it. You shouldn't have to figure out how to cancel the Click event on the tab. The tab shouldn't be processing Click events in the first place.

Generally speaking, if it's not legal for the user to click on a control, the control shouldn't be enabled. The tab should be disabled until the state of the view model is valid.

Your view model should be exposing a command for navigating to the next tab, and the tab should be bound to the command. The command's CanExecute method should only return true when the state of the view model on the current tab is valid.

This doesn't fix your other problem, which is that Silverlight doesn't support UpdateSourceTrigger="PropertyChanged" out of the box. But that's a solved problem (here is one example).

Note that if you implement commands to handle this wizard-like navigation in your application, you can, down the road, change the view to use something other than a tab control (e.g. to use navigation buttons like an actual wizard, or something like Telerik's PanelBar) without having to screw around with event handlers.


Change your bindings to include UpdateSourceTrigger="PropertyChanged".

This will ensure that your data sources are updated on every key stroke, not just LostFocus.


    MyOwnTextBox()
    {
        this.TextChanged += (s, e) => UpdateText();
    }

    private void UpdateText()
    {
        BindingExpression be = GetBindingExpression(TextProperty);
        if (be != null && be.ParentBinding.Mode == BindingModes.TwoWay)
        {
            be.UpdateSource();
        }
    }

I am using this class it updates my binding on the fly, however there is issue with empty string and null values, if your destination property is nullable string then this will update empty string in your destination property. You can get workaround by using some sort of string converter which will use null instead of empty string in case of nullable strings.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号