WPF Datagrid : On load, selection on current item (highlighting)
Asked Answered
L

5

8

I have a WPF Datagrid binded to some properties in my ViewModel

<DataGrid AutoGenerateColumns="False" Name="dataGrid" SelectionMode="Single"
          ItemsSource="{Binding ItemList}" SelectedItem="{Binding SelectedItem}">
...
</DataGrid>

When my window load and the Datagrid too, I set the SelectedItem and it binds fine but the row isn't highlighted. The moment I click a row, the row highlight and the problem is resolved.

How can I set/trigger the highlighting of the SelectedItem in the DataGrid on load/initialization ?

EDIT:

It's actually selected because I have the little selection cell. It's just the rendering of the Highlighting that does not trigger.

enter image description here

Leafstalk answered 4/3, 2011 at 21:53 Comment(0)
H
7

When using a Model as the DataContext for a WPF Window, the DataGrid's SelectionChanged event doesn't get called until after the Window is loaded which is why the row is never highlighted and you only see the first row with the partial highlight. There may be a more elegant way, but here's a work-around.

In the Window's loaded event or the DataGrid's loaded event, reset the SelectedItem binding:

public MainWindow()
{
    InitializeComponent(); 
    this.Loaded += new RoutedEventHandler( OnLoaded );
}

// could also be placed in the DataGrid's loaded event handler
private void OnLoaded( object sender, RoutedEventArgs e )
{
    if( dataGrid != null && Model.SelectedItem != null )
    {
        var selected = Model.SelectedItem;
        Model.SelectedItem = null;
        Model.SelectedItem = selected;
    }
}

Here's a complete working sample.

XAML

<Window x:Class="WpfDataGridHighlightOnLoad.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:model="clr-namespace:WpfDataGridHighlightOnLoad" 
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <model:MainWindowModel x:Name="Model" />
    </Window.DataContext>

    <Grid>
        <DataGrid AutoGenerateColumns="True" SelectionMode="Single"
                  HorizontalAlignment="Stretch" 
                  Name="dataGrid" 
                  VerticalAlignment="Top"
                  ItemsSource="{Binding ItemList}"
                  SelectedItem="{Binding SelectedItem}">
        </DataGrid>

        <Button Content="Cycle Selection" Click="OnCycleClick" 
                Height="23" 
                HorizontalAlignment="Right" 
                Name="button1" 
                VerticalAlignment="Bottom" Width="125" />

        <Button Content="Reset Grid" Click="OnResetClick" 
                Height="23" 
                HorizontalAlignment="Left" 
                Name="button2" 
                VerticalAlignment="Bottom" Width="125" />

    </Grid>
</Window>

Code Behind

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace WpfDataGridHighlightOnLoad
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();   
            this.Loaded += new RoutedEventHandler( OnLoaded );
        }

        // could also be placed in the DataGrid's loaded event handler
        private void OnLoaded( object sender, RoutedEventArgs e )
        {
            if( dataGrid != null && Model.SelectedItem != null )
            {
                var selected = Model.SelectedItem;
                Model.SelectedItem = null;
                Model.SelectedItem = selected;
            }
        }

        private void OnCycleClick( object sender, RoutedEventArgs e )
        {
            int index = Model.ItemList.IndexOf( Model.SelectedItem );
            index = index == Model.ItemList.Count - 1 ? 0 : index + 1;
            Model.SelectedItem = Model.ItemList[index];
        }

        private void OnResetClick( object sender, RoutedEventArgs e )
        {
            Model.Reset();
        }
    }

    public class MainWindowModel : INotifyPropertyChanged
    {
        public MainWindowModel()
        {
            Reset();
        }

        public void Reset()
        {
            ItemList = new List<Person>
                           {
                               new Person("Joe", 20),
                               new Person("John", 30),
                               new Person("Jane", 40),
                               new Person("Jill", 50),
                               new Person("Fido", 7),
                           };

            SelectedItem = ItemList[2];
        }

        private Person _selectedItem;
        public Person SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                NotifyPropertyChanged( "SelectedItem" );
            }
        }

        private List<Person> _itemList;
        public List<Person> ItemList
        {
            get { return _itemList; }
            set
            {
                _itemList = value;
                NotifyPropertyChanged( "ItemList" );
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged( String info )
        {
            if( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( info ) );
            }
        }

        #endregion
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public Person( string name, int age )
        {
            Name = name;
            Age = age;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
Houchens answered 5/3, 2011 at 18:42 Comment(4)
There is no Loaded event on UIElementLeafstalk
DataGrid inherits from Controls which does have a Loaded event: MSDN DataGrid EventsHouchens
@Smurf - resetting SelecteItem has solved an issue i have been trying to understand for a couple of days. Thanks!!!Bibliogony
this works the first time on loading. but when I change the viewmodel value, it still doesn't highlight the correct row. allthough it is selected.Diva
T
8

I had the same "problem" and finally found a pretty good solution to the problem. As you already said is not that the row isn't selected but that it does not highlight the row. If you watch carefully you will notice that when you click anywhere in the row (using the mouse) it still doesn't highlight the row, only the cells inside it.

So 2 options;

  • Create code to select the cells of the row
  • or create a Style.Trigger to highlight the row (which is the best option in my mind).

To do this, add something like this to the datagrid in the xaml-file:

            <DataGrid.RowStyle>
                <Style TargetType="DataGridRow">
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="Background" Value="DodgerBlue"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.RowStyle>

Hope it helps!

Cheers, LTB

Tallyman answered 24/5, 2011 at 7:16 Comment(0)
H
7

When using a Model as the DataContext for a WPF Window, the DataGrid's SelectionChanged event doesn't get called until after the Window is loaded which is why the row is never highlighted and you only see the first row with the partial highlight. There may be a more elegant way, but here's a work-around.

In the Window's loaded event or the DataGrid's loaded event, reset the SelectedItem binding:

public MainWindow()
{
    InitializeComponent(); 
    this.Loaded += new RoutedEventHandler( OnLoaded );
}

// could also be placed in the DataGrid's loaded event handler
private void OnLoaded( object sender, RoutedEventArgs e )
{
    if( dataGrid != null && Model.SelectedItem != null )
    {
        var selected = Model.SelectedItem;
        Model.SelectedItem = null;
        Model.SelectedItem = selected;
    }
}

Here's a complete working sample.

XAML

<Window x:Class="WpfDataGridHighlightOnLoad.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:model="clr-namespace:WpfDataGridHighlightOnLoad" 
        Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <model:MainWindowModel x:Name="Model" />
    </Window.DataContext>

    <Grid>
        <DataGrid AutoGenerateColumns="True" SelectionMode="Single"
                  HorizontalAlignment="Stretch" 
                  Name="dataGrid" 
                  VerticalAlignment="Top"
                  ItemsSource="{Binding ItemList}"
                  SelectedItem="{Binding SelectedItem}">
        </DataGrid>

        <Button Content="Cycle Selection" Click="OnCycleClick" 
                Height="23" 
                HorizontalAlignment="Right" 
                Name="button1" 
                VerticalAlignment="Bottom" Width="125" />

        <Button Content="Reset Grid" Click="OnResetClick" 
                Height="23" 
                HorizontalAlignment="Left" 
                Name="button2" 
                VerticalAlignment="Bottom" Width="125" />

    </Grid>
</Window>

Code Behind

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace WpfDataGridHighlightOnLoad
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();   
            this.Loaded += new RoutedEventHandler( OnLoaded );
        }

        // could also be placed in the DataGrid's loaded event handler
        private void OnLoaded( object sender, RoutedEventArgs e )
        {
            if( dataGrid != null && Model.SelectedItem != null )
            {
                var selected = Model.SelectedItem;
                Model.SelectedItem = null;
                Model.SelectedItem = selected;
            }
        }

        private void OnCycleClick( object sender, RoutedEventArgs e )
        {
            int index = Model.ItemList.IndexOf( Model.SelectedItem );
            index = index == Model.ItemList.Count - 1 ? 0 : index + 1;
            Model.SelectedItem = Model.ItemList[index];
        }

        private void OnResetClick( object sender, RoutedEventArgs e )
        {
            Model.Reset();
        }
    }

    public class MainWindowModel : INotifyPropertyChanged
    {
        public MainWindowModel()
        {
            Reset();
        }

        public void Reset()
        {
            ItemList = new List<Person>
                           {
                               new Person("Joe", 20),
                               new Person("John", 30),
                               new Person("Jane", 40),
                               new Person("Jill", 50),
                               new Person("Fido", 7),
                           };

            SelectedItem = ItemList[2];
        }

        private Person _selectedItem;
        public Person SelectedItem
        {
            get { return _selectedItem; }
            set
            {
                _selectedItem = value;
                NotifyPropertyChanged( "SelectedItem" );
            }
        }

        private List<Person> _itemList;
        public List<Person> ItemList
        {
            get { return _itemList; }
            set
            {
                _itemList = value;
                NotifyPropertyChanged( "ItemList" );
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged( String info )
        {
            if( PropertyChanged != null )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( info ) );
            }
        }

        #endregion
    }

    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public Person( string name, int age )
        {
            Name = name;
            Age = age;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}
Houchens answered 5/3, 2011 at 18:42 Comment(4)
There is no Loaded event on UIElementLeafstalk
DataGrid inherits from Controls which does have a Loaded event: MSDN DataGrid EventsHouchens
@Smurf - resetting SelecteItem has solved an issue i have been trying to understand for a couple of days. Thanks!!!Bibliogony
this works the first time on loading. but when I change the viewmodel value, it still doesn't highlight the correct row. allthough it is selected.Diva
M
0

This is a bit of an old one, but none of the answers in any post seem to get it quite right. What you want is it to work the way it was supposed to work: i.e. the highlighting is the same whether or not the control ever had focus (which seems like something that is incidental).

This can be done with a DataGridRow style, but the trick is not to specify the colors yourself, but to use the default colors, so everything just works. An extra annoyance comes from the fact that it is the cells that get highlighted, not the rows, so you basically need to duplicate the cell highlighting style:

<Style
    x:Key="DataGridRowStyle"
    TargetType="{x:Type DataGridRow}">
    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition
                    Property="IsKeyboardFocusWithin"
                    Value="False" />
                <Condition
                    Property="IsSelected"
                    Value="True" />
            </MultiTrigger.Conditions>
            <Setter
                Property="Background"
                Value="{DynamicResource
                    {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}" />
            <Setter
                Property="Foreground"
                Value="{DynamicResource
                    {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}" />
        </MultiTrigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition
                    Property="IsKeyboardFocusWithin"
                    Value="True" />
                <Condition
                    Property="IsSelected"
                    Value="True" />
            </MultiTrigger.Conditions>
            <Setter
                Property="Background"
                Value="{DynamicResource
                    {x:Static SystemColors.HighlightBrushKey}}" />
            <Setter
                Property="Foreground"
                Value="{DynamicResource
                    {x:Static SystemColors.HighlightTextBrushKey}}" />
        </MultiTrigger>
    </Style.Triggers>
</Style>

Note there is a gotcha, that to set the row context menu you need to override the DataGridRow style on the DataGrid, so if you do this globally and it isn't working, check that your RowStyle hasn't been overridden.

Martyrize answered 26/5, 2017 at 7:7 Comment(0)
E
0

Ran across same issue, when inserting dummy data into a WPF DataGrid, then trying to alter row order. The row hightlighting bunked out (image below).

Cause was inserting the exact "same" record object multiple times.

enter image description here

//Ex: Messes up highlighting.    
grid.Items.Add(rowObj);
grid.Items.Add(rowObj);
grid.Items.Add(rowObj);

//Ex: Highlighting OK.  Create a new object each time.  Even if all columns have exact same values.  
rowobj = new .....
grid.Items.Add(rowObj);

rowobj = new .....
grid.Items.Add(rowObj);
Exocrine answered 2/7, 2017 at 22:0 Comment(0)
A
0

I know this thread is super old, but none of the solutions helped me.

If someone is having the same sort of issue, a very simple solution I've found was to simply set focus to the datagrid before setting its selected item on load.

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    dataGrid.Focus();
    // Set selected item in datagrid
}
Antlia answered 23/7, 2018 at 9:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.