Search code examples
c#wpfxamlasync-awaitprism

WPF red border unexpectedly flashing around combobox


Encountering an issue in a WPF ComboBox where a red border appears and then quickly disappears as shown below:

enter image description here

The main issue seems to be the timing of the loading of the ViewModel for the ComboBox. I was attempting to use an async/await approach with my MVVM design. I followed an example that was using Prism to publish and subscribe to events through its event aggregator.

ContractEditViewModel.cs

This is the ViewModel that encompasses the data for the dropdown

public class ContractEditViewModel : Observable, IContractEditViewModel
{
    private readonly ICoreRepository _coreRepository;
    private readonly IEventAggregator _eventAggregator;
    private CoreContract _contract;
    private IEnumerable<CoreCarrier> _carriers;

    public CoreContract Contract
    {
        get => _contract;
        set
        {
            _contract = value;
            OnPropertyChanged();
        }
    }

    public IEnumerable<CoreCarrier> CarrierLookup
    {
        get { return _carriers; }
        set
        {
            _carriers = value;
            OnPropertyChanged();
        }
    }

    public ContractEditViewModel(ICoreRepository coreRepository, IEventAggregator eventAggregator)
    {
        _coreRepository = coreRepository;
        _eventAggregator = eventAggregator;
        _eventAggregator.GetEvent<OpenContractEditViewEvent>()
            .Subscribe(OnOpenContractView);
    }

    public async Task LoadAsync(int ContractId)
    {
        CarrierLookup = await _coreRepository.GetAllCarriersAsync();
        Contract = await _coreRepository.FindContractByIdAsync(ContractId);
    }

    private async void OnOpenContractView(int contractId)
    {
        await LoadAsync(contractId);
    }
}

ContractEditView.xaml

<UserControl x:Class="Contracts.Views.ContractEditView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:viewmodels="clr-namespace:Contracts.ViewModels" 
             d:DataContext="{d:DesignInstance Type=viewmodels:ContractEditViewModel}"
             mc:Ignorable="d">

    <ComboBox ItemsSource="{Binding CarrierLookup}"
                  DisplayMemberPath="DisplayValue"
                  SelectedValuePath="Id"
                  SelectedValue="{Binding Contract.CarrierId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  Margin="4" VerticalAlignment="Center"/>
</UserControl>

NavigationViewModel.cs

This view-model represents the left portion which has the datagrid. I've omitted code for the datagrid and its own LoadAsync() to keep the example short.

public class NavigationViewModel : Observable
{
    // other members omitted for brevity 

    private CoreContract _selectedContract;

    public CoreContract SelectedContract
    {
        get { return _selectedContract; }
        set
        {
            _selectedContract= value;
            OnPropertyChanged();
            if (_selectedContract != null)
            {
                _eventAggregator.GetEvent<OpenContractEditViewEvent>()
                    .Publish(_selectedContract.ContractId);
            }
        }
    }
}

I can exacerbate the problem by including a Task.Delay in the LoadAsync() method. Another interesting thing is it only has a red border around the CombBox after selecting a second item, but not the first:

public async Task LoadAsync(int ContractId)
{
    CarrierLookup = await _coreRepository.GetAllCarriersAsync();
    await Task.Delay(TimeSpan.FromSeconds(1));
    Contract = await _coreRepository.FindContractByIdAsync(ContractId);
}

enter image description here

It seems to me this more a problem of the ComboBox control rather than async/await. Anyone have advise on this dilemma I'm facing? Starting to lose my marbles.


Solution

  • After digging into this more, its an issue with the ContractId being an integer. I increased the wait time, and used this answer to help see what's going on in the tooltip, which gave me the error message "Value '' could not be converted.". So the binding is receiving a null value before Task finishes and it receives the selected Contract which has the CarrierId in it.

    If I update the CoreContract model's CarrierId to be a nullable integer int?, the red box goes away. The thing that perplexes me, is why the redbox doesn't show up the first time after clicking the on a datagrid row but before the Contract finishes loading. Can anyone explain?