开发者

WPF - View model updating property via background worker, but view is not updating some elements until focused

View model is loading data asynchronously using background worker thread in model. All properties in the model and view model raise the property changed events, and all properties are being updated in the view as expected, except 2 buttons whose IsEnabled state depends on the outcome of some properties that are loaded.

The frustrating part is that as soon as I focus on any part of the view, or set a breakpoint after the properties are updated (create a delay), then the buttons IsEnabled state is updated as expected. So it appears to be a timing issue.

Any clues as to how to the best way to solve this? I'm using mvvm-light framework, but that shouldn't matter.

I've tried binding IsEnabled to the button instead of just relying on the Command property, but that made no difference. I've confirmed via logging that view model properties are set and the PropertyChanged event is being raised for the properties associated with the buttons.

Considering sending a message using mvvm-light messenger from the view model to the view on the async completed event and then somehow? triggering a view refresh, but that seems like a kludge.

Update

Thanks to blindmeis' answer, I tested the button behaviour without the Command binding set, i.e. just binding IsEnabled property, and it works as expected!

<Button 
    Grid.Column="2" Content="{Binding LoadProject开发者_StackOverflow社区sLabel}"
    VerticalAlignment="Top" HorizontalAlignment="Right" 
    IsEnabled="{Binding CanLoadProjects}" />

Obviously it's not great because I can no longer execute the command :) but as soon as I add the command back, it stops behaving:

<Button 
    Grid.Column="2" Content="{Binding LoadProjectsLabel}"
    VerticalAlignment="Top" HorizontalAlignment="Right" 
    Command="{Binding LoadProjectsCommand}" />

Leaving IsEnabled binding doesn't solve the problem, but that seems like a good clue.

The view model command code:

public ICommand LoadProjectsCommand
{
    get
    {
        if (_loadProjectsCommand == null)
        {
            _loadProjectsCommand = new RelayCommand(loadProjects, () => CanLoadProjects);
        }
        return _loadProjectsCommand;
    }            
}

Workaround

Wire up the Click event and avoid Command. Would be nice to solve it from the view model, but this works:

<Button 
    Grid.Column="2" Content="{Binding LoadProjectsLabel}"
    VerticalAlignment="Top" HorizontalAlignment="Right" 
    IsEnabled="{Binding CanLoadProjects}" 
    Click="loadProjects_Click"/>

Code behind:

void loadProjects_Click(object sender, RoutedEventArgs e)
{
    SettingsViewModel vm = (SettingsViewModel)DataContext;
    vm.LoadProjectsCommand.Execute(null);
}


Answer from other thread:

When your BackgroundWorker completes, call CommandManager.InvalidateRequerySuggested();

By default, Commands are only requeried occasionally by WPF. Otherwise, there would be a huge amount of overhead in constantly calling "CanExecute" on every ICommand implementation. Calling the above method forces the CommandManager to update immediately.

This will force the Commands to re-enable/disable appropriately.

EDIT:

i use a simpler but not so beautiful workaround. i simply call OnPropertyChanged("MyICommand") for my commands in my BackgroundWorker Completed Event.

EDIT:

here is another nice solution.


You should bind command parameter property to any updatable property on viewmodel and can execute must use this command parameter to enable button. If command parameter's target is updated, binding will enable/disable based on return value of can excute.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜