Question

In Josh Smith's MVVM demo he's uses a ListView with a ListViewItem style like this:

<Style x:Key="CustomerItemStyle" TargetType="{x:Type ListViewItem}">
    <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="ItemsControl.AlternationIndex" Value="1" />
                <Condition Property="IsSelected" Value="False" />
                <Condition Property="IsMouseOver" Value="False" />
            </MultiTrigger.Conditions>
            <Setter Property="Background" Value="#EEEEEEEE" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

It binds the IsSelected property nicely. The style is applied to the ListView like so:

<ListView  
  ItemContainerStyle="{StaticResource CustomerItemStyle}"
  ItemsSource="{Binding}"
  >

My Version

I have tried binding IsSelected in a similar way with a DataGrid through DataGridRow. However it is causing issues when items are selected through the collection of item ViewModels, where the IsSelected property is defined.

As it is using a two way binding I would have thought that items could be selected through the UI and the collection of item ViewModels.

Let's say I select items through the UI, this works fine. I can select a singular item and then use [shift] to select a range, then using [ctrl] select some more items. Deselecting items works correctly as well.

However I select a bunch via the collection. Let's say upon the click of a button (as I do in the code below), a bunch of items get selected. When I scroll down the DataGrid then some are selected as they should be some are not. If I select one item through the UI then only some of the items are deselected and some remain selected, it's all a bit funky. Even the Select All button in the top left doesn't perform quite right.

Code All code is below, at the bottom is the view the key piece there is the DataGridRow style with IsSelected binding.

Here is my user class:

using System.ComponentModel;

namespace WpfAppDataGrid.Model
{
    public class User : INotifyPropertyChanged
    {
        public static User CreateNewUser()
        {
            return new User();
        }

        public User() { }

        public int User_ID { get; set; }
        public string Username { get; set; }
        public string Name { get; set; }
        public string Job_Title { get; set; }
        public string Department { get; set; }
        public string Company { get; set; }
        public string Phone_Office { get; set; }
        public string Phone_Mobile { get; set; }
        public string Email { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

        }
    }
}

Here is the UserViewModel, where IsSelected resides:

using System;
using System.ComponentModel;
using WpfAppDataGrid.DataAccess;
using WpfAppDataGrid.Model;

namespace WpfAppDataGrid.ViewModel
{
    class UserViewModel : INotifyPropertyChanged
    {
        readonly User _user;
        readonly UserRepository _userRepository;
        bool _isSelected;

        public UserViewModel(User user, UserRepository userRepository)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            if (userRepository == null)
                throw new ArgumentNullException("userRepository");

            _user = user;
            _userRepository = userRepository;
        }
        public UserViewModel()
        {
        }

        public int User_ID
        {
            get { return _user.User_ID; }
            set
            {
                if (value == _user.User_ID)
                    return;

                _user.User_ID = value;

                RaisePropertyChanged("User_ID");
            }
        }

        public string Username
        {
            get { return _user.Username; }
            set
            {
                if (value == _user.Username)
                    return;

                _user.Username = value;

                RaisePropertyChanged("Username");
            }
        }
        public string Name
        {
            get { return _user.Name; }
            set
            {
                if (value == _user.Name)
                    return;

                _user.Name = value;

                RaisePropertyChanged("Name");
            }
        }
        public string Job_Title
        {
            get { return _user.Job_Title; }
            set
            {
                if (value == _user.Job_Title)
                    return;

                _user.Job_Title = value;

                RaisePropertyChanged("Job_Title");
            }
        }
        public string Department
        {
            get { return _user.Department; }
            set
            {
                if (value == _user.Department)
                    return;

                _user.Department = value;

                RaisePropertyChanged("Department");
            }
        }
        public string Company
        {
            get { return _user.Company; }
            set
            {
                if (value == _user.Company)
                    return;

                _user.Company = value;

                RaisePropertyChanged("Company");
            }
        }
        public string Phone_Office
        {
            get { return _user.Phone_Office; }
            set
            {
                if (value == _user.Phone_Office)
                    return;

                _user.Phone_Office = value;

                RaisePropertyChanged("Phone_Office");
            }
        }
        public string Phone_Mobile
        {
            get { return _user.Phone_Mobile; }
            set
            {
                if (value == _user.Phone_Mobile)
                    return;

                _user.Phone_Mobile = value;

                RaisePropertyChanged("Phone_Mobile");
            }
        }
        public string Email
        {
            get { return _user.Email; }
            set
            {
                if (value == _user.Email)
                    return;

                _user.Email = value;

                RaisePropertyChanged("Email");
            }
        }

        /// <summary>
        /// Gets/sets whether this customer is selected in the UI.
        /// </summary>
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (value == _isSelected)
                    return;

                _isSelected = value;

                RaisePropertyChanged("IsSelected");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));

        }
    }
}

Here is my AllUsersViewModel:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Input;
using WpfAppDataGrid.DataAccess;

namespace WpfAppDataGrid.ViewModel
{
    class AllUsersViewModel : INotifyPropertyChanged
    {
        readonly UserRepository _userRepository;

        public AllUsersViewModel()
        {
            _userRepository = new UserRepository();
            _userRepository.LoadUsers();
            CreateAllUsers();
        }

        void CreateAllUsers()
        {
            List<UserViewModel> all =
                (from usr in _userRepository.GetUsers()
                 select new UserViewModel(usr, _userRepository)).ToList();

            foreach (UserViewModel uvm in all)
            {
                uvm.PropertyChanged += this.OnUserViewModelPropertyChanged;
            }
            this.UserCollection = new ObservableCollection<UserViewModel>(all);
            this.UserCollection.CollectionChanged += this.OnCollectionChanged;
        }

        private ObservableCollection<UserViewModel> userCollection;
        public ObservableCollection<UserViewModel> UserCollection
        {
            get
            {
                return userCollection;
            }
            set
            {
                userCollection = value;
                RaisePropertyChanged("UserCollection");
            }
        }

        RelayCommand selectItemsCommand;

        public ICommand SelectItemsCommand
        {
            get
            {
                if (selectItemsCommand == null)
                    selectItemsCommand = new RelayCommand(SelectItemsCommandExecute, CanSelectItemsCommand);
                return selectItemsCommand;
            }
        }

        private void SelectItemsCommandExecute(object parameter)
        {
            for (int i = 4; i <= 49; i++)
            {
                UserCollection[i].IsSelected = true;
            }
        }

        private bool CanSelectItemsCommand(object parameter)
        {
            return true;
        }

        void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.Count != 0)
                foreach (UserViewModel userVM in e.NewItems)
                    userVM.PropertyChanged += this.OnUserViewModelPropertyChanged;

            if (e.OldItems != null && e.OldItems.Count != 0)
                foreach (UserViewModel userVM in e.OldItems)
                    userVM.PropertyChanged -= this.OnUserViewModelPropertyChanged;

        }

        void OnUserViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            string IsSelected = "IsSelected";

            if (e.PropertyName == IsSelected)
                this.RaisePropertyChanged("TotalSelectedUsers");
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class RelayCommand : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        /// <summary>
        /// Creates a new command that can always execute.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }
    }
}

Here is the user repository where I create the users:

using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfAppDataGrid.Model;

namespace WpfAppDataGrid.DataAccess
{
    public class UserRepository
    {
        ObservableCollection<User> _users = new ObservableCollection<User>();

        public UserRepository()
        {
        }

        public ObservableCollection<User> GetUsers()
        {
            return _users;
        }

        public void LoadUsers()
        {
            int i = 0;
            while (i < 1000)
            {
                i++;
                var user = new User();
                user.User_ID = i;
                user.Username = RandomString(8, true);
                user.Name = user.Username + " " + RandomString(8, true);
                user.Job_Title = RandomString(8, true);
                user.Department = RandomString(8, true);
                user.Company = RandomString(10, true);
                user.Phone_Office = "07 " + RandomNumber(5200, 6700) + " " + RandomNumber(1000, 9999);
                user.Phone_Mobile = "04 " + RandomNumber(2800, 4500) + " " + RandomNumber(1000, 9999);
                user.Email = user.Username + "@gmail.com";
                _users.Add(user);
            }
        }

        private static Random randomSeed = new Random();
        public static string RandomString(int size, bool lowerCase)
        {
            StringBuilder RandStr = new StringBuilder(size);
            int Start = (lowerCase) ? 97 : 65;

            for (int i = 0; i < size; i++)
                RandStr.Append((char)(26 * randomSeed.NextDouble() + Start));

            return RandStr.ToString();
        }

        private int RandomNumber(int min, int max)
        {
            return randomSeed.Next(min, max);
        }
    }
}

And finally here is the view for all users:

<Window x:Class="WpfAppDataGrid.View.AllUsersView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewmodel="clr-namespace:WpfAppDataGrid.ViewModel"
        Title="AllUsersView" Height="450" Width="820">

    <Window.DataContext>
        <viewmodel:AllUsersViewModel />
    </Window.DataContext>

    <Window.Resources>
        <Style x:Key="UserRowStyle" TargetType="{x:Type DataGridRow}">
            <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
            <Setter Property="BorderBrush" Value="DarkGray" />
            <Setter Property="BorderThickness" Value="0,0,1,0"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridRow}">
                        <Grid>
                            <Border x:Name="DGR_BackingBorder" BorderBrush="Orange" BorderThickness="1,2,1,2" Background="Transparent">
                            </Border>
                            <Border x:Name="DGR_Border" 
                                    BorderBrush="{TemplateBinding BorderBrush}" 
                                    BorderThickness="1,2,1,2" 
                                    Background="{TemplateBinding Background}"
                                    SnapsToDevicePixels="True">

                                <SelectiveScrollingGrid>
                                    <SelectiveScrollingGrid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="*" />
                                    </SelectiveScrollingGrid.ColumnDefinitions>
                                    <SelectiveScrollingGrid.RowDefinitions>
                                        <RowDefinition Height="*" />
                                        <RowDefinition Height="Auto" />
                                    </SelectiveScrollingGrid.RowDefinitions>
                                    <DataGridCellsPresenter x:Name="DGR_CellsPresenter" Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                    <DataGridDetailsPresenter x:Name="DGR_DetailsPresenter" Grid.Column="1" Grid.Row="1"
                                                          SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                                                          Visibility="{TemplateBinding DetailsVisibility}" />
                                    <DataGridRowHeader Foreground="White" Grid.RowSpan="2" SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                                   Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                                </SelectiveScrollingGrid>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="DGR_Border" Property="BorderBrush" Value="Transparent" />
                                <Setter TargetName="DGR_Border" Property="BorderThickness" Value="1,2,1,2" />
                            </Trigger>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter TargetName="DGR_Border" Property="BorderBrush" Value="DarkOrange"/>
                                <Setter TargetName="DGR_Border" Property="Background" Value="Orange"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="{x:Type DataGridCell}" >
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Foreground" Value="Black" />
        </Style>
    </Window.Resources>

    <Grid Name="gridUsers" Background="Transparent">

        <DockPanel Background="Transparent" Margin="2,10,2,2" >
            <Grid DockPanel.Dock="Bottom" Margin="0,2,4,2">
                <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center">
                    <Button Content="Select rows 5 to 50" Command="{Binding SelectItemsCommand}"/>
                    <TextBlock Text=" Total: " />
                    <ContentPresenter Content="{Binding  ElementName=GenericDataGrid, Path=ItemsSource.Count}" ContentStringFormat="0" />
                </StackPanel>
            </Grid>
            <DataGrid Name="GenericDataGrid" Background="Transparent"  
                      RowStyle="{StaticResource UserRowStyle}"
                      BorderThickness="0" 
                      CanUserReorderColumns="True" 
                      AutoGenerateColumns="False" 
                      ItemsSource="{Binding UserCollection}" 
                      HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
                      CanUserAddRows="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="ID" CanUserReorder="True" IsReadOnly="True" Binding="{Binding Path=User_ID,NotifyOnTargetUpdated=True}" />
                    <DataGridTextColumn Header="Name" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Name}"/>
                    <DataGridTextColumn Header="Username" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Username}"/>
                    <DataGridTextColumn Header="Job Title" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Job_Title}"/>
                    <DataGridTextColumn Header="Department" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Department}"/>
                    <DataGridTextColumn Header="Company" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Company}"/>
                    <DataGridTextColumn Header="Phone" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Phone_Office}"/>
                    <DataGridTextColumn Header="Mobile" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Phone_Mobile}"/>
                    <DataGridTextColumn Header="eMail" CanUserReorder="True" CanUserSort="True" Binding="{Binding Path=Email}"/>
                </DataGrid.Columns>
            </DataGrid>
        </DockPanel>
    </Grid>
</Window>
Was it helpful?

Solution 4

Hank

Disabling virtualization, will definitely fix your problem at a performance cost.

you could disable recycling of the VirtualizingStackPanel as @nit suggested, but that will not resolve the issue fully.

A different approach is to use Attached behaviours.

there is a tested working solution by @Samuel Jack (used it before), it is for listbox and MVVM and it works great. selected items are bindable in two way mode.

http://blog.functionalfun.net/2009/02/how-to-databind-to-selecteditems.html

As it's for ListBox, I uploaded a modified solution for DataGrid, It is a basic DataGrid with the desired functionality.

https://www.sugarsync.com/pf/D6837746_80955217_331798

Enjoy

OTHER TIPS

Please, do not disable virtualization!
If this had been a bug, Microsoft would have fixed it long time ago as a data table is one of the most common data views in general.
The WPF framework is not a joke that targets amateurs and hobby programmers. It is designed to be used by professional developers to tailor commercial rich desktop application. Such a critical bug, unfixed (i.e. not serviced) for that period of time, would instantly disqualify WPF as a serious and professional technology.

Everything is working fine.

You must set the Binding.UpdateSourceTrigger property of the Binding on the DataGridRow.IsSelected property explicitly to UpdateSourcetrigger.PropertyChanged.

The DataGridRow has multiple visual selection states including unfocused selected. Unfocused selected is the state when the row is selected without focus, for example when being selected via the data model.

By default, the DataGridRow is actually selected, but the style defines no (satisfying) visual feedback for the unfocused state.

This "problem" is artificially introduced by customizing the visual states using simple triggers. It is another very good example why using the VisualStateManager is always preferred over a simple property Trigger (when visualizing the visual states of a Control).
Later frameworks like UWP enforce the use of the VisualStateManager (and VisualState).
The point is, that a control can have multiple states that are usually not expressed via properties. States are mutual exclusive which would require some extra effort when expressing this constraint via properties. VisualState is the convenient way to express visual states.

The following fully working example Style targets DataGridRow to show how to bind the DataGridRow.IsSelected property to a IsEnabled property of the row item (data model) and how to change the visual Unfocused_Selected state.

It is fully working, although incomplete, to proof the fact that the DataGrid is working as expected - with UI virtualization enabled. Just adjust the DataGrid.ItemsSource binding and ensure your data model has a IsEnabled property that raises the INotifyPropertyChanged.PropertyChanged event:

<DataGrid EnableRowVirtualization="True"
          ItemsSource="{Binding DataItems}"
          Height="200">
  <DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
      <Setter Property="IsSelected"
              Value="{Binding IsEnabled, UpdateSourceTrigger=PropertyChanged}" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type DataGridRow}">
            <Border x:Name="DGR_Border"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    SnapsToDevicePixels="True"
                    Background="{TemplateBinding Background}">
              <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                  <VisualState x:Name="Normal" />

                  <VisualState x:Name="Normal_Selected">
                    <Storyboard>
                      <ColorAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border"
                                                    Storyboard.TargetProperty="(Panel.Background).
              (SolidColorBrush.Color)">
                        <EasingColorKeyFrame KeyTime="0"
                                              Value="DarkRed" />
                      </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>

                  <!-- Unfocused related states -->
                  <VisualState x:Name="Unfocused_Selected">
                    <Storyboard>
                      <ColorAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border"
                                                    Storyboard.TargetProperty="(Panel.Background).
              (SolidColorBrush.Color)">
                        <EasingColorKeyFrame KeyTime="0"
                                              Value="Orange" />
                      </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="MouseOver_Unfocused_Selected">
                    <Storyboard>
                      <ColorAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border"
                                                    Storyboard.TargetProperty="(Panel.Background).
              (SolidColorBrush.Color)">
                        <EasingColorKeyFrame KeyTime="0"
                                              Value="GreenYellow" />
                      </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="MouseOver_Unfocused_Editing">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="Unfocused_Editing">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>

                  <!-- Focused states -->
                  <VisualState x:Name="MouseOver">
                    <Storyboard>
                      <ColorAnimationUsingKeyFrames Storyboard.TargetName="DGR_Border"
                                                    Storyboard.TargetProperty="(Panel.Background).
              (SolidColorBrush.Color)">
                        <EasingColorKeyFrame KeyTime="0"
                                              Value="CornflowerBlue" />
                      </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="MouseOver_Selected">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="Normal_Editing">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="MouseOver_Editing">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>

                  <VisualState x:Name="Normal_AlternatingRow">
                    <Storyboard>
                      <!-- TODO::Define ColorAnimation -->
                    </Storyboard>
                  </VisualState>
                </VisualStateGroup>
              </VisualStateManager.VisualStateGroups>

              <SelectiveScrollingGrid>
                <SelectiveScrollingGrid.ColumnDefinitions>
                  <ColumnDefinition Width="Auto" />
                  <ColumnDefinition Width="*" />
                </SelectiveScrollingGrid.ColumnDefinitions>
                <SelectiveScrollingGrid.RowDefinitions>
                  <RowDefinition Height="*" />
                  <RowDefinition Height="Auto" />
                </SelectiveScrollingGrid.RowDefinitions>
                <DataGridCellsPresenter Grid.Column="1"
                                        ItemsPanel="{TemplateBinding ItemsPanel}"
                                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                <DataGridDetailsPresenter Grid.Column="1"
                                          Grid.Row="1"
                                          Visibility="{TemplateBinding DetailsVisibility}"
                                          SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, 
        ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical},
        Converter={x:Static DataGrid.RowDetailsScrollingConverter}, 
        RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                <DataGridRowHeader Grid.RowSpan="2"
                                    SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                    Visibility="{Binding HeadersVisibility, 
        ConverterParameter={x:Static DataGridHeadersVisibility.Row}, 
        Converter={x:Static DataGrid.HeadersVisibilityConverter}, 
        RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
              </SelectiveScrollingGrid>
            </Border>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </DataGrid.RowStyle>
</DataGrid>

I remember having this problem with my DataGrid when scrolling. The reason why it was causing weird problem was due to virtualization. Try setting virtualization to False in your DataGrid.

<DataGrid EnableRowVirtualization="False">

Hank, your problem is that the Grid will only bind the visible Rows of the DataGrid and doesn't refresh the Binding as soon as another Row gets visible.

This issue is cause by the Data Virtualization.

Please try to setting Virtualization to Standard

VirtualizingStackPanel.VirtualizationMode="Standard"

I think this will solve your problem.

You will need to catch the Scroll event and then attach to its items container generator to get the item generated while scrolling. When Item is loaded you can refresh the Binding (using BingingExpression) to the item so that it will reflect the state of your object i.e. IsSelected

This is the problem with the Recycling VirtualizingStackpanel. Setting VirtualizingStackPanel.VirtualizationMode="Standard" on Datagrid will solve this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top