I am trying to customize DataGridView class to have a button beneath all rows.
So far I've added a button to a DataGridView.Controls.
The position of this button is calculated on each add/remove row, DataGridView resize and scroll.
This works, however there is a one problem with that. On DataGridView resize or scroll, when bottom edge of the DataGridView is directly below last row, the button is not visible at all or just partially.
Is there a way to make the button always visible?
I've tried setting scrollbar position and FirstDisplayedScrollingRowIndex. This does not work.
Unfortunatelly adding a whole new row isn't possible for this project.
Adding button:
buttonAddRow.Height = 17;
buttonAddRow.Text = "+";
buttonAddRow.FlatStyle = FlatStyle.System;
buttonAddRow.Font = new Font(buttonAddRow.Font.FontFamily, 6.75F);
buttonAddRow.Click += ButtonAddRow_Click;
dataGridView.Controls.Add(buttonAddRow);
And the location:
private void setLocation()
{
if (dataGridView.FirstDisplayedCell != null)
{
int positionY = 0;
positionY += dataGridView.ColumnHeadersHeight;
var visibleRowsCount = dataGridView.DisplayedRowCount(true);
var firstDisplayedRowIndex = dataGridView.FirstDisplayedCell.RowIndex;
var lastvisibleRowIndex = (firstDisplayedRowIndex + visibleRowsCount) - 1;
for (int rowIndex = firstDisplayedRowIndex; rowIndex <= lastvisibleRowIndex; rowIndex++)
{
positionY += dataGridView.Rows[rowIndex].Height;
}
buttonAddRow.Location = new Point(dataGridView.ClientRectangle.X, dataGridView.ClientRectangle.Y + positionY);
buttonAddRow.Visible = true;
}
}
Below is some code that creates a “button Row” I described earlier and adds this button row to the top, bottom and row 4 of the DataGridView. As the picture shows this button is only in the first column. If you want to display the button across all columns then you will have to implement the OnPaint method to adjust this row. However, looking at the picture, if the user scrolls down then the top button row will not be visible, this is where you would have to implement keeping the button row at the top as the user scrolled down. If this is what you are looking for, then what you end up with is a STATIC button that is always displayed at the top of the grid. Again putting this button ABOVE and OUTSIDE the grid would accomplish the same thing, with much less effort.
The code below uses a DataGridView with 3 text columns.
private void Form1_Load(object sender, EventArgs e) {
FillGrid();
InsertButtonRow(0);
InsertButtonRow(4);
InsertButtonRow(dataGridView.Rows.Count -1);
}
private void FillGrid() {
for (int i = 1; i < 15; i++) {
dataGridView.Rows.Add("Row" + i + "C1", "Row" + i + "C2", "Row" + i + "C3");
}
}
private void InsertButtonRow(int rowIndex) {
if (rowIndex >= 0 && rowIndex < dataGridView.Rows.Count) {
DataGridViewButtonCell buttonCell = new DataGridViewButtonCell();
buttonCell.Value = "+";
buttonCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
DataGridViewRow row = (DataGridViewRow)dataGridView.Rows[0].Clone();
row.Cells[0] = buttonCell;
dataGridView.Rows.Insert(rowIndex, row);
}
}
Related
I have a DataGrid bound to EmployeeDataTable property. Let’s consider that count of rows of EmployeeDataTable is 50 . That is, DataGrid has 50 rows.
private DataTable employeeDataTable;
public DataTable EmployeeDataTable
{
get { return employeeDataTable; }
set {
employeeDataTable = value;
OnPropertyChanged("EmployeeDataTable");
}
}
What I want is when user scrolls to the bottom or to the top of the DataGrid(50 rows) and there are just 5 rows to see(at the top or at the top), then I would like to add the next 20 items to the DataGrid(to the top of Datagrid if user scrolls to Topor to the bottom if user scrolls to the bottom).
That is, I would like to run a method called GetNewData(), when user sees the forty fifth (45) row or the fifth(5) of DataGrid. Is it possible to do without SelectedItem property?(Just by using Scroll)
private void GetNewData()
{
for (int i = 0; i < 20; i++)//Adding 20 DataRows
{
var theRow = employeeDataTable.NewRow();
for (int j = 0; j < 10; j++)
{
theRow[j] = "o";
}
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
employeeDataTable.Rows.Add(theRow);
}));
}
}
It looks like when user scrolls down or scroll up in DataGrid and my method GetNewData() loads new data in background thread. It is like paging but without buttons “Next” and “Previous” (I cannot use the buttons).
I’ve tried to handle many events such as MouseWheel or MouseMove of DataGrid but there is no result. Any help would be greatly appreciated.
What you need to do is wrap your data grid in a ScrollableView and register to the ScrollChanged event:
<ScrollViewer VerticalScrollBarVisibility="Visible"
PreviewMouseWheel="UIElement_OnPreviewMouseWheel"
ScrollChanged="ScrollViewer_OnScrollChanged">
<DataGrid x:Name="dg" VerticalScrollBarVisibility="Disabled"></DataGrid>
</ScrollViewer>
Next, you implement the ScrollChanged event to check if you are at the bottom of the ScrollView and if so then add some rows.
private void ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
var sv = sender as ScrollViewer;
if (sv != null && !_addingData)
{
if (sv.ScrollableHeight - e.VerticalOffset == 0)
{
_addingData = true;
GetNewData();
_addingData = false;
}
}
}
private void UIElement_OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollViewer scv = (ScrollViewer)sender;
scv.ScrollToVerticalOffset(scv.VerticalOffset - e.Delta);
e.Handled = true;
}
You don't need to add any observable stuff to your EmployeeDataTable if you register the datagrid like this:
dg.AutoGenerateColumns = true;
dg.ItemsSource = EmployeeDataTable.DefaultView;
EDIT: Updated to enable mouse wheel scrolling.
In the details view of ListView control, I need additional functionality of editing the entry pointed to by the cursor so I simply added a button overlay onto the ListView control.
The following code was intended to show the button on the rightmost part of each cell of the details view of ListView control when the cursor is on the ListView control.
To determine which cell was hovered, HitTest method was used.
The problem is, the button show correctly only when the cursor is on the rectangle area the button is supposed to show up. In other words, when the cursor is on non-rightmost area of any field, redraw occurs in ListView control and erases the button (and the content of the cell). Also, BringToFront method doesn't work.
How to correct this behavior?
class DocumentView {
Button btn = new Button();
ListView lv; // designer-generated
public DocumentView(Document doc)
{
btn.AutoSize = true;
btn.AutoSizeMode = AutoSizeMode.GrowAndShrink;
btn.AutoEllipsis = false;
btn.Hide();
this.Controls.Add(btn);
InitializeComponent();
}
ListViewItem mouseMoveHitTestItem = null;
ListViewItem.ListViewSubItem mouseMoveHitTestSubItem = null;
int iCol_MouseMove = -1;
int iRow_MouseMove = -1;
private void lv_MouseMove(object sender, MouseEventArgs e)
{
var mousePos = lv.PointToClient(Control.MousePosition);
var hitTest = lv.HitTest(mousePos);
mouseMoveHitTestItem = hitTest.Item;
mouseMoveHitTestSubItem = hitTest.SubItem;
if (mouseMoveHitTestItem != null)
{
var iCol = hitTest.Item.SubItems.IndexOf(hitTest.SubItem);
var iRow = hitTest.Item.Index;
if (iCol != iCol_MouseMove || iRow != iRow_MouseMove)
{ //Reposition button if the cursor moved to a different cell
var bounds = hitTest.SubItem.Bounds;
btn.SetBounds(
bounds.Right - btn.Width + lv.Left,
bounds.Top + lv.Top,
bounds.Width, bounds.Height);
if (!btn.Visible)
{
btn.Show();
btn.BringToFront();
}
btn.Text = "" + iRow + ", " + iCol; //test hittest row, col calc.
}
}
btn.BringToFront(); //takes no effect
}
}
Missed status update so invalidatation occurred on every MouseMove:
btnAddName.SetBounds(
bounds.Right - btnAddName.Width + lvVertices.Left,
bounds.Top + lvVertices.Top,
bounds.Width, bounds.Height);
//-->
iColBtnAdd = iCol;
iRowBtnAdd = iRow;
//<--
if (!btnAddName.Visible)
{
Warnings help. (CS0169)
I have a program that uses dataGridView for showing data that updates automatically every second by adding rows to dataGridView.
When I want to read something around the beginning, I scroll up, and even when data updates, the scroll bar doesn't go down, it is good. But I want the scroll bar to go down only when it is at the bottom of dataGridView.
The behavior I want when a new row is added to the text:
if the scrollbar is at the bottom, scroll down automatically.
if the scrollbar is elsewhere, don't scroll.
The code I have written for this and unfortunately doesn't work is:
private void liveDataTable_Scroll(object sender, ScrollEventArgs e)
{
ScrollPosition = liveDataTable.FirstDisplayedScrollingRowIndex;
if (ScrollPosition == liveDataTable.RowCount - 1)
{
IsScrolledToBottom = true;
}
else
{
IsScrolledToBottom = false;
}
}
public void AddRowToDataGridMethod()
{
dataTable.Rows.Add();
if (dataWin.IsScrolledToBottom == true)
dataWin.LiveDataTable.FirstDisplayedScrollingRowIndex = (dataWin.ScrollPosition + 1);
else
dataWin.LiveDataTable.FirstDisplayedScrollingRowIndex = dataWin.ScrollPosition;
}
You can try this:
int firstDisplayed = liveDataTable.FirstDisplayedScrollingRowIndex;
int displayed = liveDataTable.DisplayedRowCount(true);
int lastVisible = (firstDisplayed + displayed) - 1;
int lastIndex = liveDataTable.RowCount - 1;
liveDataTable.Rows.Add(); //Add your row
if(lastVisible == lastIndex)
{
liveDataTable.FirstDisplayedScrollingRowIndex = firstDisplayed + 1;
}
So basically check if the last row is visible and if it is scroll 1 row down after adding the new row.
Just wanted to add, another method to keep it scrolling (but with the new row at the bottom) is ...
Basically what this does is say you have 10 rows displayed and your are processing each row. When it gets to the 11th row, it scrolls up by 1 row so your row in now displayed but at the bottom. You could for example add 1 and now it will stay your row + 1 so it is next to the last row from the bottom.
if (myRow.Displayed == false)
{
int intDisplayRows = myRow.Index - dataView_Database.DisplayedRowCount(false);
dataView_Database.FirstDisplayedScrollingRowIndex = intDisplayRows;
}
private void dgZavod_RowsAdded_1(object sender, DataGridViewRowsAddedEventArgs e) {
dgZavod.FirstDisplayedScrollingRowIndex = dgZavod.Rows[dgZavod.Rows.Count - 1].Index;
}
All, I want to build and display multiple DataGridView seperated by horizontol Splitters at runtime. To test out doing this I have created a test application with the following code
private void button1_Click(object sender, EventArgs e)
{
int i = 1;
List<DataGridView> DgvList = new List<DataGridView>()
{
new DataGridView(), new DataGridView()
};
foreach (DataGridView Dgv in DgvList)
{
Dgv.Parent = this.panelMain;
int verticalSize = (int)(panelMain.Height / DgvList.Count);
Dgv.Height = verticalSize;
Dgv.Dock = DockStyle.Top;
if (DgvList.Count > 1 && i < DgvList.Count)
{
Splitter tmpSplitter = new Splitter();
tmpSplitter.Parent = this.panelMain;
tmpSplitter.Dock = DockStyle.Top;
tmpSplitter.BringToFront();
tmpSplitter.Height = 8;
}
i++;
}
}
However, this is not displaying the Splitter
Can someone highlight the error of my ways?
Thanks for your time.
Drop this line:
tmpSplitter.BringToFront();
and splitter will show.
Note that you are displaying grids in reversed order - the first in list will be at bottom of the screen.
I need to enable the end user to tick several checkboxes in the datagridview of my C# application at the same time, using the checkbox_checkChanged event, when they have used the mouse to first highlight the cells they require.
So basically the end user will use their mouse to highlight cells in the datagridview, then click on the checkbox2, which should then tick the checkbox in each row that they have highlighted a cell.
Here is my code which displays a window that states how many rows/cells have been selected. I then need to tick each checkbox in the datagridview which has a highlighted cell in that row. Currently it will display the window recording which rows/cells have been selected, then when clicking the OK button it ticks every checkbox in the datagridview instead of just the checkboxes that have a highlighted cell in that row.
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
Int32 selectedCellCount = dgv2.GetCellCount(DataGridViewElementStates.Selected);
if (selectedCellCount > 0)
{
if (dgv2.AreAllCellsSelected(true))
{
MessageBox.Show("All cells are selected", "Selected Cells");
}
else
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0;
i < selectedCellCount; i++)
{
sb.Append("Row: ");
sb.Append(dgv2.SelectedCells[i].RowIndex.ToString());
sb.Append(", Column: ");
sb.Append(dgv2.SelectedCells[i].ColumnIndex.ToString());
sb.Append(Environment.NewLine);
}
sb.Append("Total: " + selectedCellCount.ToString());
// confirmation
MessageBox.Show(sb.ToString(), "Selected Cells");
foreach (DataGridViewRow row in dgv2.Rows)
{
row.Cells[2].Value = checkBox2.Checked && String.IsNullOrEmpty(row.Cells[0].ErrorText);
}
}
}
}
Please help...
I see your code is from here: http://msdn.microsoft.com/en-us/library/x8x9zk5a.aspx but instead of this:
foreach (DataGridViewRow row in dgv2.Rows)
{
row.Cells[2].Value = checkBox2.Checked && String.IsNullOrEmpty(row.Cells[0].ErrorText);
}
You need to follow what the article says, here this will point you in the right direction:
for (int i = 0; i < selectedCellCount; i++)
{
dataGridView1.SelectedCells[i].Value = true;
}