2

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.

  • What is `CarrierLookup`? Does it contain the value you are trying to select at all times? A minimal example would be helpful. – mm8 Jun 02 '22 at 14:45
  • @mm8 Apologies, its in the ContractEditViewModel which is now included. I also posted the project to my Github, a link has been added in the post. The ContractViewModel is loaded asynchronously each time a user selects a row in the grid, which also asynchronously gets the list of Carriers for the dropdown. – Shandy Sawyer Jun 02 '22 at 16:21
  • I mean `async void` says literally everything about how your code is broken, doesn't it? Since it can't be awaited (only tasks can be awaited) and your code still compiles, it means whatever is calling it (you don't show that part) doesn't handle async methods at all, so the non-async code runs first, then the gui update, then the async part. – Blindy Jun 03 '22 at 19:17
  • @Blindy see [here](https://stackoverflow.com/questions/19415646/should-i-avoid-async-void-event-handlers) and [here](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming). That async void method is used the same way a button click event handler would be used. I can only make assumptions as to why the author of Prism has [declined to include the feature](https://github.com/PrismLibrary/Prism/issues/832#issuecomment-257886188). – Shandy Sawyer Jun 06 '22 at 16:26

1 Answers1

2

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?