Home Forums WPF controls Xceed DataGrid for WPF Rebinding a DataCell?

Viewing 3 posts - 1 through 3 (of 3 total)
  • Author
    Posts
  • User (Old forums)
    Member
    Post count: 23064
    #24361 |

    We have grid bound to a read-only collection where users select records to edit in a separate details view. The user clicks a line in the grid to select it, and the details view fills with data for that record. The grid shows only the ID field from the record, which corresponds to one of the editable fields in the details area.

    To make the UI more responsive, we’d like the selected line item in the grid to update real-time, a keystroke at a time, if the user types changes to the ID field in the details view. Because the grid is bound, our first thought was to have the keypresses trigger changes to the appropriate item in the grid’s underlying bound list. But it appears that to see the changes in the grid, we need to call Refresh on the grid’s DataGridCollectionView with each keystroke, and with potentially thousands of records in the grid it’s too slow.

    Our next thought was to handle the CurrentChanging event in the grid, and
    use data binding. When a grid line becomes current, we use GetContainerFromItem to get a DataRow for the current item, then dataRow.Cells[“Id”] to get the cell displaying the ID. We then re-bind this cell to the ID textbox in the details area:

    Binding binding = new Binding(“Text”); // Binding to the Text property of the textbox
    binding.Source = idTextBox;
    binding.Mode = BindingMode.OneWay;
    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
    cell.SetBinding(Cell.ContentProperty, binding);

    This seems to work well; as one types in the textbox, the grid line item updates immediately. To make sure only the current grid line is bound to the textbox, there’s more code to reestablish the previous cell binding. Right before the SetBinding call we do this to save the original binding:

    oldBinding = BindingOperations.GetBinding(cell, Cell.ContentProperty);

    And next time the current item changes we use this to restore the original binding on the cell. We’re careful not to store explicit references to the datarows and cells, realizing the previously-current row may not be realized anymore, etc.

    We’re running into a weird problem with this approach. If we fire up the application and just select different rows in the grid, it works fine. The current item binds and unbinds and it looks great. We then change an id in the details view, and see the changes reflected in the grid — still OK. But then when we click to a different grid item, it goes haywire. Not only does the new current item get bound to the textbox, but apparently also the first line in the grid! Both the current item and the first grid item show the contents of the textbox. If we keep clicking from item to item, the first line updates in tandem with the current line. Weird.

    We’ve checked our logic carefully and can’t find an obvious problem. We’re assuming it has something to do with virtualization, and perhaps it’s just not “legal” to try to rebind an individual data cell.

    One more approach we tried was to handle the KeyPress event on the details textbox, and when a key is pressed, acquire the current datarow, get the cell with the id, and directly set the Content property to match the details textbox. In other words, manually do what binding does automatically. We see the same problem: As soon as the cell’s Content property is manually manipulated, if you then switch to a different current item, when you try to set the new cell’s content property, the first grid line also changes.

    Any ideas? Is there a better overall approach to keeping the grid in synch with the details view without having to refresh the DataGridCOllectionView with each change?

    Thanks.

    Imported from legacy forums. Posted by Mark (had 850 views)

    User (Old forums)
    Member
    Post count: 23064

    Using a simple test, I think I’m doing what you want to do with data binding alone.

    In the simple example below, I can select a current row in the grid. I can change the TextBox name field outside of the grid and see the changes immediately reflected in the grid.

    Note my test class implements INotifyPropertyChanged and you’ll need to add that implementation if you don’t have it already. Additionally the simple example is binding to People[]. I’m not sure if the underlying array class implements INotifyCollectionChanged or if it’s needed in this case. I believe INotifyCollectionChanged only comes into play when your are adding, deleting and re-ordering.

    Note I also checked that the example works with a DataGridCollectionViewSource defined as follows:

    <Window.Resources>
    <xcdg:DataGridCollectionViewSource x:Key=”dgcv_People” Source=”{x:Static local:PersonData.People}”/>
    </Window.Resources>

    <xcdg:DataGridControl Name=”m_xcdg_people” ItemsSource=”{Binding Source={StaticResource dgcv_People}, Path=View}” AutoCreateColumns=”False” CellEditorDisplayConditions=”CellIsCurrent”>

    ** In my case a Path=View is needed for VS2008 builds

    ====================================
    Simple Example Snippets:
    ====================================

    <xcdg:DataGridControl Name=”m_xcdg_people” ItemsSource=”{x:Static local:PersonData.People}” AutoCreateColumns=”False” CellEditorDisplayConditions=”CellIsCurrent”>

    <Border BorderBrush=”Blue” BorderThickness=”2″>

    <Grid DataContext=”{Binding ElementName=m_xcdg_people, Path=CurrentItem}”>
    <Grid.RowDefinitions>
    <RowDefinition/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <TextBlock>Name</TextBlock>

    <!–

    *** Changes to this TextBox outside of the Xceed grid are reflected immediately in it.
    *** Changes in the Xceed grid are not reflected in the text box until the grid commits the edit, *** then it shows up.
    –>
    <TextBox Text=”{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}” Grid.Column=”1″/>

    </Grid>
    </Border>

    ….
    meanwhile elsewhere in the code PeopleData is defined as follows:
    ….
    <code>
    public class Person : INotifyPropertyChanged
    {
    static int s_count = 0;

    private Person()
    {
    }

    public Person(string name, int age, Address currentAddress)
    {
    m_name = name;
    m_age = age;
    m_currentAddress = currentAddress;
    m_oid = s_count;
    s_count++;
    }

    private int m_oid;
    public int Oid
    {
    get { return m_oid; }
    set
    {
    m_oid = value;
    }
    }
    private string m_name;
    public string Name
    {
    get { return m_name; }
    set
    {
    if (m_name != value)
    {
    m_name = value;
    OnPropertyChanged(“Name”);
    }
    }
    }

    private int m_age;
    public int Age
    {
    get { return m_age; }
    set
    {
    if (m_age != value)
    {
    m_age = value;
    OnPropertyChanged(“Age”);
    }
    }
    }

    private Address m_currentAddress;
    public Address CurrentAddress
    {
    get { return m_currentAddress; }
    set
    {
    if (m_currentAddress != value)
    {
    m_currentAddress = value;
    OnPropertyChanged(“CurrentAddress”);
    }
    }
    }

    private string m_description;
    public string Description
    {
    get { return m_description; }
    set
    {
    m_description = value;
    OnPropertyChanged(“Description”);
    }
    }

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

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
    }

    class PersonData
    {
    static private Person[] s_people;
    static public Person[] People
    {
    get
    {
    if (s_people == null)
    {
    s_people = new Person[] {
    new Person(“Johnny Foo”,15, AddressData.Addresses[0]),
    new Person(“Sarah Sweet”,42, AddressData.Addresses[1])
    };
    }

    return s_people;
    }
    set
    {
    s_people = value;
    }
    }

    }

    </code>

    Imported from legacy forums. Posted by Richard (had 373 views)

    User (Old forums)
    Member
    Post count: 23064

    Thanks, that helped. Although our details view was already using an editable object that supported INotificationChanged, the items in the grid collection were using a different, stripped-down, read-only version of the object that didn’t support INotificationChanged. We added support for the interface into our readonly class and fired PropertyChanged at the appropriate time, and now the grid updates when we edit an item.

    It’s still not an ideal solution, because when the grid has a large number of items (thousands), there’s a slight but noticeable delay when the property changes and the grid redraws. In our original attempt that directly manipulated the cell contents, it was instantaneous, I assume because the XCeed code was only working with the realized data instead of the whole collection. But the code now is a lot cleaner, and it works, so thanks again!

    Imported from legacy forums. Posted by Mark (had 3752 views)

Viewing 3 posts - 1 through 3 (of 3 total)
  • You must be logged in to reply to this topic.