Solved

WPF DataGrid Binding

Posted on 2011-03-20
12
1,176 Views
Last Modified: 2013-11-12
How can I make the DataGrid autosort when it was changed from outside the control?

To reproduce my problem using the sample code below:
1. Click the header of the FirstName column of DataGrid to sort
2. In the datagrid, change "Jenny" to "Benny".
3. Note that after chaning the FirstName, the DataGrid automatically sorts the items to the correct order.
4. Now again select "Benny" and change the value back to "Jenny" using the bounded textbox control.
5. Note that the DataGrid changes the value of the FirstName but without sorting the items.

XAML Code:
<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid Name="DataGrid1" Grid.Column="0" ItemsSource="{Binding}"/>
        <TextBox Name="TextBox1" Grid.Column="1" Width="100" Height="23" Margin="10"
                 Text="{Binding ElementName=DataGrid1, Path=SelectedItem.FirstName}"/>
    </Grid>
</Window>

Open in new window


VB Code:
Imports System.Collections.ObjectModel

Class MainWindow

    Public Property Persons As ObservableCollection(Of Person)

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
        Persons = New ObservableCollection(Of Person) From
                    {New Person With {.FirstName = "John", .LastName = "Doe"},
                     New Person With {.FirstName = "James", .LastName = "Long"},
                     New Person With {.FirstName = "Albert", .LastName = "Rice"},
                     New Person With {.FirstName = "Jenny", .LastName = "Jordan"}}

        DataContext = Persons
    End Sub

End Class

Public Class Person

    Public Property FirstName As String
    Public Property LastName As String

End Class

Open in new window

0
Comment
Question by:Thomasian
  • 6
  • 5
12 Comments
 
LVL 13

Expert Comment

by:agarwalrahul
ID: 35177859
Please Check the following Link:

http://www.dotnetcurry.com/ShowArticle.aspx?ID=563
0
 
LVL 22

Author Comment

by:Thomasian
ID: 35177868
How do I modify the sample code to make that work?
0
 
LVL 3

Expert Comment

by:politex
ID: 35187557
Hi, your Person class must implement INotifyPropertyChanged, c# sample:
    public class Person : EventBase
    {
        private string firstName;
        public string FirstName 
        {
            get { return firstName; }
            set
            {
                firstName = value;
            PropertyChangedHandler("FirstName");
            }
        }
        public string LastName { get; set; }
    }

    public abstract class EventBase : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;
        protected void PropertyChangedHandler(string propertyName)
        {

            var handler = PropertyChanged;

            if (handler != null)
            {

                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    } 

Open in new window

0
 
LVL 3

Expert Comment

by:politex
ID: 35187588
and one more:        
private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            Dispatcher.BeginInvoke(()=>((Person) DataGrid1.SelectedItem).FirstName = TextBox1.Text);
        }

Open in new window

0
 
LVL 22

Author Comment

by:Thomasian
ID: 35187815
>>Dispatcher.BeginInvoke(()=>((Person) DataGrid1.SelectedItem).FirstName = TextBox1.Text);
I couldn't make this line work so I changed it to

     ((Person)DataGrid1.SelectedItem).FirstName = TextBox1.Text;

Here's the code I tested, but it still doesn't automatically sort the datagrid.

WPF Code:
<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" SizeToContent="WidthAndHeight" Loaded="Window_Loaded">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid Name="DataGrid1" Grid.Column="0" ItemsSource="{Binding}" CanUserAddRows="False" />
        <TextBox Name="TextBox1" Grid.Column="1" Width="100" Height="23" Margin="10"
                 Text="{Binding ElementName=DataGrid1, Path=SelectedItem.FirstName}"/>
    </Grid>
</Window>

Open in new window

C# Code
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public class Person : EventBase
        {
            private string firstName;
            public string FirstName
            {
                get { return firstName; }
                set
                {
                    firstName = value;
                    PropertyChangedHandler("FirstName");
                }
            }
            public string LastName { get; set; }
        }

        public abstract class EventBase : INotifyPropertyChanged
        {

            public event PropertyChangedEventHandler PropertyChanged;
            protected void PropertyChangedHandler(string propertyName)
            {

                var handler = PropertyChanged;

                if (handler != null)
                {

                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ObservableCollection<Person> persons = new ObservableCollection<Person>
            {
                new Person {FirstName = "John", LastName = "Doe"},
                new Person {FirstName = "James", LastName = "Long"},
                new Person {FirstName = "Albert", LastName = "Rice"},
                new Person {FirstName = "Jenny", LastName = "Jordan"}
            };

            DataContext = persons;
        }

        private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            ((Person)DataGrid1.SelectedItem).FirstName = TextBox1.Text;
        } 
    }
}

Open in new window

0
 
LVL 22

Author Comment

by:Thomasian
ID: 35187825
I forgot to include

         TextChanged="TextBox1_TextChanged"

on the TextBox xaml code. But it still doesn't work. The data changes but no sorting
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 3

Expert Comment

by:politex
ID: 35189161
sorry, forgot about main:
<UserControl.Resources>
        <CollectionViewSource x:Name="viewSource"></CollectionViewSource>
 </UserControl.Resources>

Open in new window

private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Persons = new ObservableCollection<Person>
                              {
                                  new Person {FirstName = "John", LastName = "Doe"},
                                  new Person {FirstName = "James", LastName = "Long"},
                                  new Person {FirstName = "Albert", LastName = "Rice"},
                                  new Person {FirstName = "Jenny", LastName = "Jordan"}
                              };

            viewSource.Source = Persons;
            viewSource.View.SortDescriptions.Clear();
            viewSource.View.SortDescriptions.Add(new SortDescription("FirstName",ListSortDirection.Ascending));
            DataGrid1.DataContext = viewSource;
            TextBox1.DataContext = DataGrid1;
        }

Open in new window

private string last;
        private int selection;
        private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (last != TextBox1.Text)
            {
                last = TextBox1.Text;
                selection = TextBox1.SelectionStart;
                ((Person)DataGrid1.SelectedItem).FirstName = TextBox1.Text;
                viewSource.View.Refresh();
                TextBox1.SelectionStart = selection;
            }
        }

Open in new window

0
 
LVL 22

Author Comment

by:Thomasian
ID: 35189272
I changed the xaml code to:

    <Window.Resources>
        <CollectionViewSource x:Name="viewSource" />
    </Window.Resources>

When I added the resource to the xaml code, it is giving me errors:

All objects added to an IDictionary must have a Key attribute or some other type of key associated with them. Line 6 Position 10.


Have you tried the code? If you did, can you post the whole xaml and csharp code?

Thanks
0
 
LVL 3

Expert Comment

by:politex
ID: 35189469
Not my day. previous code was for Silverlight,  this is for WPF:
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="UserControl_Loaded">
    <Grid x:Name="LayoutRoot" VerticalAlignment="Top">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid Height="300" Width="300" Name="DataGrid1" Grid.Column="0" ItemsSource="{Binding}" />
        <TextBox Name="TextBox1" Grid.Column="1" Width="100" Height="23" Margin="10" TextChanged="TextBox1_TextChanged"
                 Text="{Binding ElementName=DataGrid1, Path=SelectedItem.FirstName, Mode=TwoWay}"/>
    </Grid>
</Window>

Open in new window

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;


namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public ObservableCollection<Person> Persons = new ObservableCollection<Person>
                              {
                                  new Person {FirstName = "John", LastName = "Doe"},
                                  new Person {FirstName = "James", LastName = "Long"},
                                  new Person {FirstName = "Albert", LastName = "Rice"},
                                  new Person {FirstName = "Jenny", LastName = "Jordan"}
                              };

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            DataGrid1.ItemsSource = Persons;
            TextBox1.DataContext = DataGrid1;
        }

        CollectionView viewSource = null;

        private string last;
        private int selection;
        private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
            
            if (last != TextBox1.Text)
            {
                last = TextBox1.Text;
                selection = TextBox1.SelectionStart;
                ((Person)DataGrid1.SelectedItem).FirstName = TextBox1.Text;
                TextBox1.SelectionStart = selection;
            }
        }
    }

    public class Person : EventBase
    {
        private string firstName;
        public string FirstName 
        {
            get { return firstName; }
            set
            {
                firstName = value;
            PropertyChangedHandler("FirstName");
            }
        }
        public string LastName { get; set; }
    }

    public abstract class EventBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void PropertyChangedHandler(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {

                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    } 
    }

Open in new window

0
 
LVL 22

Author Comment

by:Thomasian
ID: 35189509
I copied and pasted the exact codes you posted, but still no auto sort..

To replicate:
1. Click the header button of FirstName column to sort
2. Click on item "Jenny"
3. Change the name to "Benny" using the textbox.
4. The data on the datagrid is updated but no sorting occurs

Note that if you follow the same steps but edit the name using the datagrid control, the control automatically sorts the data in the correct order.
0
 
LVL 3

Accepted Solution

by:
politex earned 500 total points
ID: 35190175
xml - need to rename to xaml:
MainWindow.xaml.cs
MainWindow.xml
0
 
LVL 22

Author Comment

by:Thomasian
ID: 35190793
I need to be able to let the user select the sort column and direction.

i.e. The user can click the header for Last Name to sort by the last name. So changing the first name should not affect the sort order.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Expando 4 36
Create a form which is copy of a form in vb.net 2 18
Hide Tab Page 3 20
COnsume rest client 6 16
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now