picoe / Eto

Cross platform GUI framework for desktop and mobile applications in .NET
Other
3.67k stars 335 forks source link

Cannot use BeginEdit from within the grid #1246

Open msasso69 opened 6 years ago

msasso69 commented 6 years ago

I'm trying to implement a grid with a button that when pressed lets me edit the content of a cell in the same row, something like this:

image

The problem is that when I click on the image with the pencil and execute the BeginEdit for the cell on the left, somehow also the event CellEdited for the same cell gets fired. This is probably because I'm doing it from another cell, so Eto understands that I already left the cell being edited.

In order to achieve what I wanted, in the code I execute when I click the cell with the button, I had to execute the BeginEdit in a code like this:

Task.Run(() =>
{
    Application.Instance.Invoke(() =>
    {
        this.colProjectName.Editable = true;
        this.gridRecentProjects.BeginEdit(rowIndex1, cellIndex1);
    });
});

but it's an ugly workaround and doesn't look to me very efficient, though it works.

Expected Behaviour

If I execute the BeginEdit for a cell from the code executed when clicking on another cell the CellEdited event for the cell being edited should not be fired, the CellEdited should be fired only after editing (leaving) the cell subject of the BeginEdit.

Actual Behavior

If I execute the BeginEdit for a cell from the code executed when clicking on another cell the CellEdited event is also fired, makeing it impossible to edit the desired cell.

Steps to Reproduce the Problem

  1. Launch the code below
  2. click on a row of the second column with "Click here to edit the text to the left"

you get this: image

instead of this: image

Code that Demonstrates the Problem

using System;
using Eto;
using Eto.Forms;
using System.Collections.ObjectModel;

namespace VisualSEO.EtoFormGui
{
    public class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
            new Application(Platform.Detect).Run(new MyMainForm());
        }

        public class MyMainForm : Form
        {
            public MyMainForm()
            {
                GridColumn col;
                GridView grid = new GridView() { Width = 370, Height = 100 };
                grid.Columns.Add(col = new GridColumn { DataCell = new TextBoxCell("Col1"), Editable = false });
                grid.Columns.Add(new GridColumn { DataCell = new TextBoxCell("Col2"), Editable = false });
                ObservableCollection<Data> model = new ObservableCollection<Data>();
                model.Add(new Data { Col1 = "Text to be edited", Col2 = "Click here to edit the text to the left" });
                model.Add(new Data { Col1 = "Text to be edited", Col2 = "Click here to edit the text to the left" });
                model.Add(new Data { Col1 = "Text to be edited", Col2 = "Click here to edit the text to the left" });
                model.Add(new Data { Col1 = "Text to be edited", Col2 = "Click here to edit the text to the left" });
                grid.DataStore = model;
                grid.CellClick += (sender, e) =>
                {
                    if (e.Column == 1)
                    {
                        col.Editable = true;
                        grid.BeginEdit(e.Row, 0);
                    }
                };
                grid.CellEdited += (sender, e) => { col.Editable = false; };
                Content = grid;
            }

            public class Data
            {
                public string Col1 { get; set; }
                public string Col2 { get; set; }
            }
        }
    }
}

Specifications

cwensley commented 5 years ago

Hey @msasso69, thanks for reporting the issue.

I think doing an async invoke might be the right solution here, since all platforms behave identically (except winforms).

However, instead of Task.Run(() => Application.Instance.Invoke(() => { ... }));, you can just simply use Application.Instance.AsyncInvoke(() => { ... });, which is essentially the same thing but without spawning a background thread.