Eto TreeGridView cell order issue on scrolling

I use Eto’s TreeGridView and display a single column that uses Eto’s CustomCell. This seems to render correctly until the tree is large enough so that it gets a scroll bar. Once I start scrolling to view the tree, the cells can be rendered in a different order than they exist in the DataStore.

Example:
I created a tree to display two different types of Nodes, A and B. The tree’s datastore is initialized:

public class Root : ITreeGridStore<Node>, ITreeGridItem
{
    List<Node> nodes;
    public Root()
    {
        nodes = new List<Node>();
        for (int i = 0; i < 10; i++)
        {
            nodes.Add(new NodeA(this));
            nodes.Add(new NodeA(this));
            nodes.Add(new NodeB(this));
            nodes.Add(new NodeB(this));
        }
    } 

where the column is set up to display the cell via:

column = new GridColumn
        {
            AutoSize = true,
            Editable = true,
            Resizable = false,
            Sortable = false,
            DataCell = new CustomCell
            {
                CreateCell = (args) => (args.Item as Node).CreateCell(args),
                ConfigureCell = (args, control) => { control.DataContext = args.Item; }
            }

and where the create cell functions are:

public class NodeA : Node
{
    public NodeA(ITreeGridItem p) : base(p) { }
    public override Control CreateCell(CellEventArgs a)
    {
        return new Label { Text = "A  A  A", BackgroundColor = Colors.Blue };
    }
}
public class NodeB : Node 
{
    public NodeB(ITreeGridItem p) : base(p) { }
    public override Control CreateCell(CellEventArgs a)
    {
        return new Label { Text = "B  B  B", BackgroundColor = Colors.Yellow };
    }
}

This shows how the cells can be created/?displayed in a different order than the datastore when the tree is scrolled up and down:

Is there a way to prevent this issue?

Code:
EtoTreeGridViewDemo1.zip (93.6 KB)

Hi @jakemurphy0118,

The problem you are having is because cells are reused, and you are not configuring the cell properly. During your ConfigureCell, you need to change the properties of the existing control (which is passed in as a parameter) to match the new args.Item.

Note that setting the DataContext only works if you are using MVVM bindings with your controls.

Hope this helps!

I have a related question to this post. I have an EtoTreeGridView which uses MVVM bindings.
When I scroll down to view all the cells in the Tree, the cells initially not shown are not loaded until some action occurs such as clicking on the expander button for a parent cell.

Here is what I mean:


Is there an efficient way to keep the cells loaded even when there are lots of cells in the tree?

EtoTreeGrid.zip (37.6 KB)

attempt to bump this

Curtis,

could you be a bit more explicit on what you mean by “…change the properties of the existing control (which is passed in as a parameter) to match the new args.Item …”???

I am having the same issue and no clue on what is going on.

13-01-_2022_18-18-51

This is my current code in OnCreateCell override.

protected override Control OnCreateCell(CellEventArgs args)
{
    string text = "???";
    if (args.Item is TreeGridItem treeGridItem)
    {
        if (treeGridItem.Values[args.Column] is string stringValue)
        {
            if (treeGridItem.Children != null &&
                !(treeGridItem.Values[args.Column + 1] is AFPropertyBaseType baseType))
            {
                text = stringValue + " (" + treeGridItem.Children.Count.ToString() + ")";
            }
            else
            {
                text = stringValue;
            }
        }
        else if (treeGridItem.Values[args.Column] is Tuple<string, int> tuple)
        {
            text = tuple.Item1 + " (" + tuple.Item2.ToString() + ")";
        }
    }
    else if (args.Item is GridItem item)
    {
        if (item.Values[args.Column] is string stringValue)
        {
            text = stringValue;
        }
    }
    if (args.GridColumn.Editable)
    {
        TextBox textBox = new TextBox
        {
            Height = 18,                        
            ShowBorder = false,
            TextAlignment = TextAlignment.Left,                        
        };
        textBox.Text = text;
        textBox.TextChanged += new EventHandler<EventArgs>(this.OnTextChanged);
        return textBox;
    }
    else
    {
        Label label = new Label
        {
            Height = 18,
            Text = text,
            TextAlignment = TextAlignment.Left,
            VerticalAlignment = VerticalAlignment.Center,
        };
        label.MouseDown += new EventHandler<MouseEventArgs>(this.OnLabelMouseDown);
        return label;
    }
}

Kindest
Christian

Hi @Christian_Hartz,

Why are you using a custom cell? From you screen shot, TextBoxCell and ImageViewCell objects should work.

– Dale

Dale,

thanks for getting back so quickly. Well, this is a starting setup and I want to include much more content(controls) into the cells.

I should have had a look at this post link from @curtisw before aksing. My bad. We have a saying in Germany:

Those who are able to read
are at an advantage indeed.

protected override Control OnCreateCell(CellEventArgs args)
{
    if (args.GridColumn.Editable)
    {
        TextBox textbox = new TextBox
        {
            Height = 18,
            ShowBorder = false,
            TextAlignment = TextAlignment.Left,
        };
        textbox.TextChanged += new EventHandler<EventArgs>(this.OnTextChanged);
        return textbox;
    }
    else
    {
        return new Label
        {
            Height = 18,
            TextAlignment = TextAlignment.Left,
            VerticalAlignment = VerticalAlignment.Center,
        };
    }
}

protected override void OnConfigureCell(CellEventArgs args, Control control)
{
    if (control is Label label)
    {
        if (args.Item is TreeGridItem treeGridItem)
        {
            if (treeGridItem.Values[args.Column] is string stringValue)
            {
                if (treeGridItem.Children != null &&
                    !(treeGridItem.Values[args.Column + 1] is AFPropertyBaseType baseType))
                {
                    label.Text = stringValue + " (" + treeGridItem.Children.Count.ToString() + ")";
                }
                else
                {
                    label.Text = stringValue;
                }
            }
            else if (treeGridItem.Values[args.Column] is Tuple<string, int> tuple)
            {
                label.Text = tuple.Item1 + " (" + tuple.Item2.ToString() + ")";
            }
        }
        else if (args.Item is GridItem item)
        {
            if (item.Values[args.Column] is string stringValue)
            {
                label.Text = stringValue;
            }
        }
    }
    if (control is TextBox textBox)
    {
        if (args.Item is TreeGridItem treeGridItem)
        {
            if (treeGridItem.Values[args.Column] is string stringValue)
            {
                if (treeGridItem.Children != null &&
                    !(treeGridItem.Values[args.Column + 1] is AFPropertyBaseType baseType))
                {
                    textBox.Text = stringValue + " (" + treeGridItem.Children.Count.ToString() + ")";
                }
                else
                {
                    textBox.Text = stringValue;
                }
            }
            else if (treeGridItem.Values[args.Column] is Tuple<string, int> tuple)
            {
                textBox.Text = tuple.Item1 + " (" + tuple.Item2.ToString() + ")";
            }
        }
        else if (args.Item is GridItem item)
        {
            if (item.Values[args.Column] is string stringValue)
            {
                textBox.Text = stringValue;
            }
        }
    }
}

Seems to work fine now.

kindest
Christian

Works great. Thanks again.

13-01-_2022_20-33-53