How do I disable a button cell in a WinForms DataGrid?
I have a WinForms application with a DataGridView control and a column of DataGridViewButtonCell cells within that. When I click on one of these buttons, it starts a background task, and I'd like to disable the buttons until that task completes.
I can disable the DataGridView control, but it gives no visual indication that the buttons are disabled. I want the user to see that the buttons are disabled, and to notice that the task has finished when the buttons are enabled again.
Bonus points for a method that allows me to disable the buttons individually, so I can leave one of the buttons enabled while the task runs. (Note that I can't actually give out bonus points.)
Answers (2)
Here's the best solution I've found so far. This MSDN article gives the source code for a cell class that adds an Enabled property.
It works reasonably well, but there are two gotchas:
- You have to invalidate the grid after setting the Enabled property on any cells. It shows that in the sample code, but I missed it.
- It's only a visual change, setting the Enabled property doesn't actually enable or disable the button. The user can still click on it. I could check the enabled property before executing the click event, but it also seemed to be messing up the appearance when the user clicked on it. Instead, I just disabled the entire grid. That works alright for me, but I'd prefer a method that allows me to disable some buttons without disabling the entire grid.
There's a similar sample in the DataGridView FAQ.
You could give this a try:
When you click on the cell...
- Check to see if the process with the current row identifier is running from a class-level list; if so, exit the cell click event.
- Store the row identifier in the class-level list of running processes.
- Change the button text to "Running..." or something appropriate.
- Attach a basic RunWorkerCompleted event handler to your process (explained shortly).
- Call backgroundWorker.RunWorkerAsync(rowIdentifier).
In the DoWork event handler...
- Set e.Result = e.Argument (or create an object that will return both the argument and your desired result)
In the RunWorkerCompleted event hanlder...
- Remove the row identifier from the running processes list (e.Result is the identifier).
- Change the button text from "Running..." to "Ready"