1

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

enter image description here

Complete code here: https://github.com/shandysawyer/WPF-ComboIssue

Revised Below


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 loose 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
  • Not sure if it means its broken. The Prism event aggregator subscription requires an action. And there is no plans to support Tasks See: https://github.com/PrismLibrary/Prism/issues/832 And I do show that part... Subscriber: `_eventAggregator.GetEvent().Subscribe(OnOpenContractView);` Publisher is the setter in the property SelectedNavigationItem XAML: `SelectedItem="{Binding Path=SelectedNavigationItem}"` – Shandy Sawyer Jun 03 '22 at 20:30

1 Answers1

1

I believe I am running into timing issues between the data being retrieved from the database asynchronously after the binding source is bound to the control. So the UI observes no data initially, and has a binding error, but then the Task finishes and the data is now available. This seems to make sense as sometimes the Task finishes before the binding resolves and you don't see the red border as I click between rows. (This is just an assumption, so if someone can explain better please feel free.)

My main issue appears to be related to trying to load data asynchronously in my ViewModel Load() methods.

Some potential solutions to this problem from what I've found:

Will update once I find a solution that works.