Vahid
Vahid

Reputation: 5444

SelectedItem doesn't bind in ListView in WPF

I've been trying to Bind the two ListViews to ViewModel. Both lists are loading the items properly. But to my surprise I've encountered a little problem.

The first ListView's SelectedItem binds correctly but the second one doesn't bind! As shown in the image below. What could be the reason?

enter image description here

XAML:

<Window x:Class="Test.Dialogs.BeamElevationsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:converters="clr-namespace:Test.Dialogs.Converters"
        Title="Select Beam Elevation" Height="350" Width="460"
        Style="{StaticResource DialogStyle}"
        WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <converters:ElevationValueConverter x:Key="ElevationValueConverter"/>
    </Window.Resources>

    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <GroupBox>
            <Grid Margin="5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="175"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition Width="215"/>
                </Grid.ColumnDefinitions>
                <GroupBox Header="Typs">
                    <ListView ItemsSource="{Binding TypIds}"
                              SelectedItem="{Binding CurrentTypId}">
                        <ListView.View>
                            <GridView AllowsColumnReorder="False"  
                          ColumnHeaderContainerStyle="{StaticResource DialogsGridViewColumnHeaderStyle}" >
                                <GridViewColumn Header="Typ."/>
                            </GridView>
                        </ListView.View>
                    </ListView>
                </GroupBox>

                <GroupBox Grid.Row="0" Grid.Column="2" Header="Elevations">
                    <ListView ItemsSource="{Binding Elevations}"
                              SelectedItem="{Binding CurrentBeamElevation}">
                        <ListView.View>
                            <GridView AllowsColumnReorder="False"  
                          ColumnHeaderContainerStyle="{StaticResource DialogsGridViewColumnHeaderStyle}" >
                                <GridViewColumn Header="Typ." />
                            </GridView>
                        </ListView.View>
                    </ListView>
                </GroupBox>
            </Grid>
        </GroupBox>
        <Grid Grid.Row="1">
            <Button Content="OK"/>
        </Grid>
    </Grid>
</Window>

Code-Behind:

public partial class BeamElevationsWindow
{
    private BeamElevationsViewModel ViewModel { get; set; }

    public BeamElevationsWindow()
    {
        InitializeComponent();
        ViewModel = new BeamElevationsViewModel();
        DataContext = ViewModel;
    }
}

ViewModel:

namespace Test.Dialogs.ViewModels
{
    public class BeamElevationsViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public BeamElevationsViewModel()
        {
            var frames = Building.Frames
                .GroupBy(f => f.TypId)
                .Select(group => group.First())
                .OrderBy(f => f.TypId)
                .ToList();

            typIds = new List<int>();
            foreach (var frame in frames)
            {
                typIds.Add(frame.TypId);
            }

            TypIds = typIds;
            CurrentTypId = Building.CurrentFrame.TypId;

            GetElevations(CurrentTypId);
            CurrentBeamElevation = Building.CurrentBeamElevation;
        }

        public void GetElevations(int typId)
        {
            var frames = Building.Frames
                .Where(f => f.TypId == typId)
                .OrderByDescending(f => f.Elevation)
                .ToList();

            elevations = new List<Elevation>();
            foreach (var fr in frames)
            {
                foreach (var elevation in Building.Elevations)
                {
                    if (Math.Abs(fr.Elevation - elevation.El) < Arithmetics.Tolerance)
                    {
                        elevations.Add(elevation);
                        break;
                    }
                }
            }

            Elevations = elevations;
        }

        private List<int> typIds;
        public List<int> TypIds
        {
            get { return typIds; }
            private set
            {
                typIds = value;
                RaisePropertyChanged("TypIds");
            }
        }

        private int currentTypId;
        public int CurrentTypId
        {
            get { return currentTypId; }
            private set
            {
                currentTypId = value;
                RaisePropertyChanged("CurrentTypId");
            }
        }

        private List<Elevation> elevations;
        public List<Elevation> Elevations
        {
            get { return elevations; }
            private set
            {
                elevations = value;
                RaisePropertyChanged("Elevations");
            }
        }

        private Elevation currentBeamElevation;
        public Elevation CurrentBeamElevation
        {
            get { return currentBeamElevation; }
            private set
            {
                currentBeamElevation = value;
                RaisePropertyChanged("CurrentBeamElevation");
            }
        }

        private void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Upvotes: 3

Views: 1102

Answers (1)

BradleyDotNET
BradleyDotNET

Reputation: 61349

The binding is actually working fine :)

However, the default comparer for object does a reference comparison. That means that when its trying to find the existing object in the list, it doesn't pick any of them because they aren't the same instance (per your comment).

The solution is to override Object.Equals (and when you override that, you should also override Object.GetHashCode). It should test equality based on some unique property of the object, so you don't get false positives.

Upvotes: 4

Related Questions