Julien N
Julien N

Reputation: 3920

Listview scrolling triggers Combobox SelectedValue and throws exception

I have a really strange issue in an app in WPF with a lot of screens working really well with bindings and Comboboxes. But one of them is causing me issues.

I created a screen to define a profile to each user defined in the app. So it's a Listview with each row being a label (the user name) and a combobox with the list of profiles. Everything is defined through binding.

Here is the XAML for the ListView (I removed styles) :

<ListView Name="lv_UserProfils" ItemsSource="{Binding ListeEntites}" AlternationCount="2"
ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Nom d'utilisateur" Width="250">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <Border Height="25">
                            <TextBlock Text="{Binding UserLogin}" Width="Auto" />
                        </Border>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Profil" Width="Auto">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox 
                                        ItemsSource="{Binding DataContext.ListeProfils, ElementName=lv_UserProfils}" 
                                        DisplayMemberPath="LibProfil" SelectedValuePath="IdProfil" 
                                        SelectedValue="{Binding Profil.IdProfil}" 
                                        SelectedItem="{Binding Profil}" Width="200" />
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

The DataContext is an instance of a custom ViewModel class, that provides an ObservableCollection<UserBE> named ListeEntites

UserBE is more or less :

public sealed class UserBE
{
public bool IsAdmin { get; set; }
public bool IsUpdateGranted { get; set; }

private string _userLogin;
public string UserLogin
{
    get { return _userLogin; }
    set { _userLogin = value; OnPropertyChanged("UserLogin"); }
}

private ProfilBE _profil;
public ProfilBE Profil
{
    get { return _profil; }
    set
    {
        _profil = value;
        OnPropertyChanged("Profil");
    }
}
}

And ProfilBE is

public sealed class ProfilBE
{
    public long IdProfil { get; set; }

    private string _codProfil;
    public string CodProfil
    {
        get { return _codProfil; }
        set { _codProfil = value; OnPropertyChanged("CodProfil"); }
    }

    private string _libProfil;
    public string LibProfil
    {
        get { return _libProfil; }
        set { _libProfil = value; OnPropertyChanged("LibProfil"); }
    }
}

Here is my problem :
The list of users is quite long so there is a scrollbar. I can scrolldown as much as I want but as soon as I scroll up (but only if I scrolled down enough), all comboboxes that were not displayed start to be cleared as soon as they appear on screen.

Interesting facts :

.

System.Windows.Data Error: 23 : Cannot convert 'BanquePrivee.AssuranceVie.Net.BE.ProfilBE' from type 'ProfilBE' to type 'System.Int64' for 'fr-FR' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: Int64Converter cannot convert from BanquePrivee.AssuranceVie.Net.BE.ProfilBE.
   at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
   at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at System.ComponentModel.BaseNumberConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'

System.Windows.Data Error: 7 : ConvertBack cannot convert value 'BanquePrivee.AssuranceVie.Net.BE.ProfilBE' (type 'ProfilBE'). BindingExpression:Path=Profil.IdProfil; DataItem='UserBE' (HashCode=59629589); target element is 'ComboBox' (Name=''); target property is 'SelectedValue' (type 'Object') NotSupportedException:'System.NotSupportedException: Int64Converter cannot convert from BanquePrivee.AssuranceVie.Net.BE.ProfilBE.
   at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
   at MS.Internal.Data.ObjectTargetConverter.ConvertBack(Object o, Type type, Object parameter, CultureInfo culture)
   at System.Windows.Data.BindingExpression.ConvertBackHelper(IValueConverter converter, Object value, Type sourceType, Object parameter, CultureInfo culture)'

This is obvious that the SelectedValue="{Binding Profil.IdProfil}" is the issue but I don't understand why.
I don't understand why at some point it tries to cast the IdProfil to an ProfilBE. I shouldn't have to use a Converter there.
I did a lot of testing and the data seem to be fine (no null value where there shouldn't be, etc.)

Can someone point me where I did something wrong ?

Upvotes: 1

Views: 1065

Answers (2)

Julien N
Julien N

Reputation: 3920

WPF is virtualizing objects that are not currently displayed. But when I try to scroll up, it seems to try to do something that doesn't work on the items that were virtualized and are about to appear on the screen again.

The solution I used is to disable virtualization for the ListView using VirtualizingStackPanel.IsVirtualizing="False". It has a small performance hit, but now it works.

<ListView Name="lv_UserProfils" ItemsSource="{Binding ListeEntites}" AlternationCount="2" VirtualizingStackPanel.IsVirtualizing="False"
        ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
    <ListView.View>
        [...]
    </ListView.View>
</ListView>

Upvotes: 0

Rachel
Rachel

Reputation: 132588

I think it has to do with setting both the SelectedValue and the SelectedItem. Both properties do the same thing: they set the selected item. But one sets it by Value based on what the SelectedValuePath is, and the other simply sets it to an item in the ItemsSource.

I am guessing that WPF is getting confused somewhere and trying to set the SelectedValue (which is an int) to the SelectedItem, which is of type ProfilBE, and exception is thrown because a ProfilBE cannot be cast as an int.

But anyways, to fix it try removing the SelectedItem binding in your ComboBox

<ComboBox ItemsSource="{Binding DataContext.ListeProfils, ElementName=lv_UserProfils}" 
    DisplayMemberPath="LibProfil" SelectedValuePath="IdProfil" 
    SelectedValue="{Binding Profil.IdProfil}" 
    Width="200" />

Upvotes: 1

Related Questions