Gridview auto load data when scroll bar reach the bottom - c#

Hello guys I have a sample gridview
Here's what i want to happen.
When I open the form i load top 100 of data from the server in to the gridview. When I scroll down and reach the end of the scroll bar I want to load another data from 101 - 200.
so the data in the gridview is 1 to 200.
How do I determine if the scroll bar reach the end?

Depending on the version of XtraGrid you are using - perhaps you should check out InstantFeedback
It's a datasource that dynamicly get rows, when they come into view.
The great thing about this is - that it's a standard DevExpress component - so you don't have to invent anything youself.
OR:
You could force that behavior with some thing like this:
private bool _working = false;
private void view_RowStyle(object sender, RowStyleEventArgs e)
{
if(_working) return;
var view = sender as GridView;
if (view != null)
{
int lastRowIndex = (view.GridControl.DataSource as BindingSource).Count;
if (view.IsRowVisible(lastRowIndex) == RowVisibleState.Visible)
{
_working = true;
//go get more rows.
_working = false;
}
}
}
This assumes that you are using a BindingSource (if not, the you must change the cast type).
I handle the RowStyle event because the code in this event are executed "all time time".

You cah handle the scrolling to end (and any another conditions) via handling the Scroll event of GridControl's embedded scrollbar.
Here is the approach details:
var sb = GetScrollBar(gridControl1, ScrollBarType.Vertical);
sb.Scroll += new ScrollEventHandler(sb_Scroll);
//...
void sb_Scroll(object sender, ScrollEventArgs e) {
var scrollBar = sender as DevExpress.XtraEditors.ScrollBarBase;
if(e.NewValue == (scrollBar.Maximum - scrollBar.LargeChange)) {
MessageBox.Show("Last row is reached!");
}
}
ScrollBarBase GetScrollBar(GridControl gridControl, ScrollBarType type) {
foreach(Control c in gridControl.Controls) {
var scrollBar = c as ScrollBarBase;
if(scrollBar != null && scrollBar.ScrollBarType == type)
return scrollBar;
}
return null;
}

You can get the displayed rows count and calculate based on the total loaded rows and check in the TopRowChanged event and see if you need to load any more rows.
This is a more manually approach for this situation.
Also you can use the grid in server mode which does this for you.
Regards,
Mishu

Related

How to freeze first row when I clicked the header of column for sort in DataGridView?

I have a desktop app and I am using DataGridView on form. I froze the first row. But when I clicked the header of column in DataGridView The location of the first row is also changing. How to ignore the first row while I am sorting.
Within the SortCompare event handler for the SortCompare event of the DataGridView, try this:
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
if (e.RowIndex1 == 0)
{
e.Handled = true;
}
else
{
e.Handled = false;
}
}
Essentially, what you're asking is to be able to sort only a portion of the DataGridView. To my knowledge, there's no easy or standard way to do this, echoed by Filip's response to this question: Sorting selected rows in DataGridView. This doesn't mean it's impossible to achieve the behavior that you want.
Filip offers an implementation, and in this question, Justin offers code that you would have to modify slightly, but that may suit your needs. The question asks how to move a row up or down in a DataGridView, but I think you could modify his implementation to move the row you want to keep static all the way to the top after you sort. Depending on the volume of rows you're sorting, the first row might disappear and reappear. However, if your data set is relatively small, I can't imagine the brief flash of the first row being sorted to some random position and then added back to the top would cause any major problems.
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
int so = 1;
if (dataGridView1.SortOrder == SortOrder.Ascending)
{
so = -1;
}
if (e.RowIndex1 == 0 )
{
e.SortResult = so;
e.Handled = true;
}
if (e.RowIndex2 == 0)
{
e.SortResult = -so;
e.Handled = true;
}
}
This code puts the first line always on top of the sorted list, by setting the sort result of the first row accordingly.

Delete multiple rows from devexpress gridcontrol

I have a wpf application in which one of the user controls uses a devexpress grid control. I have given the user functionality to select a particular row and delete it by handling the keydown event as mentioned in the code below:
private void m_gridA_KeyDown(object sender, KeyEventArgs e)
{
Tableview view = sender as Tableview;
if(e.Key == Key.Delete)
{
IList<GridCell> celllist = null;
celllist = (sender as TableView).GetSelectedCells();
if(cellist.Count < 10)
return;
view.DeleteRow(view.FocusedRowHandle)
}
}
This works perfectly fine when the user selects a single row. However how do I implement the same functionality if the user needs to delete multiple rows at the same time.
This is the approach I tried:
int[] rows = m_gridA.GetSelectedRowHandles();
Then loop over each row handle and delete them. The problem with this approach that I am facing is that in my application, there is a lot of data in the grid control. So in order to make sense of the data a lot of filtering is done using the grid control filter editor. Due to this the function GetSelectedRowHandles returns the row handles selected and visible in the current filtered view. So when I call view.DeleteRow(row[i]) it deletes some other row in the grid control whose rowhandle matches that of the grid control in the unfiltered condition.
How do I overcome this?
Wrap your code like this. It prevents the grid from updating its internal state (including the filter) while you're doing multiple deletes.
view.Grid.BeginDataUpdate();
//Delete multiple rows here
view.Grid.EndDataUpdate();
gridView1.BeginUpdate();
Int32[] selectedRowHandles = gridView1.GetSelectedRows();
int t = -1;
foreach(var item in selectedRowHandles) {
if(item >= 0 && t == -1)
t = item;
if(t >= 0)
gridView1.DeleteRow(t);
}
gridView1.EndUpdate();

Remove blue colored row from DataGridView WinForms

When I fill a DataGridView row by row (calling it's add function), the top row gets blue colored.
It's not selected because I tried ClearSelection() also but it didnt work.
It's giving the wrong illusion that the first row is selected.
How to get rid of it?
void FillDGV()
{
dataGridViewParties.Rows.Clear();
for (int i = 0; i < dataGridViewParties.Columns.Count; i++)
{
dataGridViewParties.Columns[i].Width = this.Width / dataGridViewParties.Columns.Count;
}
DataTable partyTbl = UtilityClass.GetDataTable(#"SELECT [PartyID]
,[PartyName]
,[PartyAddress]
,[PartyState]
,[PartyCity]
,[PartyPhone]
FROM [VegiManager].[dbo].[Parties]
WHERE [PartyName] LIKE '" + textBoxPartySearch.Text + "%' ");
foreach (DataRow dr in partyTbl.Rows)
{
dataGridViewParties.Rows.Add(1);
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[0].Value = dr["PartyID"].ToString();
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[1].Value = dr["PartyName"].ToString();
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[2].Value = dr["PartyAddress"].ToString();
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[3].Value = dr["PartyState"].ToString();
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[4].Value = dr["PartyCity"].ToString();
dataGridViewParties.Rows[dataGridViewParties.Rows.Count - 1].Cells[5].Value = dr["PartyPhone"].ToString();
}
if (dataGridViewParties.Rows.Count > 0)
{
dataGridViewParties.ClearSelection();
dataGridViewParties.CurrentCell = null;
}
}
In the debugger I found that CurrentCell is already null before DataGridViewParties.CurrentCell = null; executes.
This question http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/c440c4f6-6dfc-47b6-97c0-1ce49c105b64/ is also related to it but does not offer a solution.
EDIT: Its weird but it works for Load event, I was doing it in constructor.
I want that when the first row is selected and when the user presses the UP arrow key, the focus moves to a certain textbox. But in this case it does not work (first row appears blue)
private void dataGridViewInwards_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up && dataGridViewParties.SelectedRows.Count > 0 && dataGridViewParties.SelectedRows[0].Index == 0)
{
textBoxPartySearch.Focus();
dataGridViewParties.ClearSelection();
dataGridViewParties.CurrentCell = null;
}
else if (e.KeyCode == Keys.Up && dataGridViewParties.SelectedRows.Count == 0)
{
textBoxPartySearch.Focus();
}
}
To achieve this along with the ClearSelection you will need to set one more property
Try this in the DataBindingComplete
dataGridView1.ClearSelection();
dataGridView1.CurrentCell = null;
EDIT
Based on your comments you can modify the code as
if (e.KeyCode == Keys.Up && dataGridView1.CurrentCell.RowIndex == 0)
{
this.ActiveControl = textBoxPartySearch;
dataGridView1.Refresh();
dataGridView1.ClearSelection();
dataGridView1.CurrentCell = null;
e.Handled = true;
}
I tried the above methods but nothing worked for me.
Finally, I just decided to set the default colors for selected cells to be the same as non-selected cells.
Then, in the cell click method, I set them back. That way nothing appears selected until its clicked, which was sufficient for my application. Here's the code I used in the click method to set everything back to normal:
dataGridView.DefaultCellStyle.SelectionBackColor = SystemColors.Highlight;
dataGridView.DefaultCellStyle.SelectionForeColor = SystemColors.Window;
Annoyingly, the ClearSelection method actually works just fine if I put it in a button, but if I create the control which contains the datagrid, load some data into it, and then try to clear it right then, it doesn't work. I wish I knew why...
Try adding ClearSelection() to VisibleChanged event as follows:
private void datagridview1_VisibleChanged(object sender, EventArgs e)
{
datagridview1.ClearSelection();
}
In Form.Shown Event Just do a:
dataGridView1.ClearSelection();
dataGridView1.CurrentCell = null;
After three hours of troubleshooting this, I've figured it out: Your DataGridView needs to be displayed on the screen before calling dataGridView.ClearSelection().
How I know this works:
I have two DataGridViews, each in a different panel. Only one panel is visible at a time. In the panel that's initially visible, the DataGridView has never ignored my call to ClearSelection(), so I've never seen a row selected right away.
I used to populate the other DataGridView and clear its selection before showing the panel that contains it, but that top row was always still highlighted. I changed my code to display the other panel before clearing the DataGridView's selection, and the call to ClearSelection() worked as it should.

How to detect the vertical scrollbar in a DataGridView control

Using winforms in vs2008. I have a DataGridView and I would like to detect when the vertical scroll bar is visible. What event should I register for?
I am adding the summing the each cell value in the last column of the grid and displaying that value in a textbox at the bottom of the DataGridView.
I would like this textbox to stay lined up with the cell values (I have made them right aligned since it is $$ values) even after the scroll bar is present.
var vScrollbar = dataGridView1.Controls.OfType<VScrollBar>().First();
if (vScrollbar.Visible)
{
}
Overriding DGV behavior is usually a huge pain in the neck. Got this going pretty quickly though. Add a new class to your form and paste the code shown below. Compile. Drop the new control from the top of the toolbar onto a form. Implement the ScrollbarVisibleChanged event.
using System;
using System.Windows.Forms;
class MyDgv : DataGridView {
public event EventHandler ScrollbarVisibleChanged;
public MyDgv() {
this.VerticalScrollBar.VisibleChanged += new EventHandler(VerticalScrollBar_VisibleChanged);
}
public bool VerticalScrollbarVisible {
get { return VerticalScrollBar.Visible; }
}
private void VerticalScrollBar_VisibleChanged(object sender, EventArgs e) {
EventHandler handler = ScrollbarVisibleChanged;
if (handler != null) handler(this, e);
}
}
Set the DGV last column's "AutoSizeMode" property to "Fill" and set the TextBox's Width property equal to dgv.Columns["lastcolumn"].Width.
Instead of using Linq (Adam Butler) you can just iterate through the controls and sign up an event handler that will be called each time the scrollbar visibility changes. I implemented it that way and it works pretty smoothly:
private System.Windows.Forms.DataGridView dgCounterValues;
private Int32 _DataGridViewScrollbarWidth;
// get vertical scrollbar visibility handler
foreach (Control c in dgCounterValues.Controls)
if (c.GetType().ToString().Contains("VScrollBar"))
{
c.VisibleChanged += c_VisibleChanged;
}
do this somewhere after the InitializeComponent()
In the handler, do whatever you need to do in response to the visibility change of the vertical scrollbar. Same works for the horizontal scrollbar (replace VScrollBar with HScrollBar):
void c_VisibleChanged(object sender, EventArgs e)
{
VScrollBar vb = sender as VScrollBar;
if (vb.Visible) _DataGridViewScrollbarWidth = vb.Width;
else _DataGridViewScrollbarWidth = 0;
}
I think thereĀ“s no event for that... but you can try with something like this in all the places where the grid can grow:
Obtain the number of actual rows in the gridview + the header
Multiply that number by the height of each row
If the result is greater than the height of the DataGrid, there must be a vertical scrollbar.
I gave Hans Passant the Check mark since he answered the question asked... However, I went another route for the solution. Since the dialog box is modal, the list of items will not change from the time it is created. So I am able to call the code below to ensure that the textboxes are in the correct location when the dialog first displays.
/// <summary>
/// Horizontally shifts the label and text boxes that display the total
/// values so that the totals remain aligned with the columns.
/// </summary>
private void ShiftTotalsDisplay(DataGridView grid, Label firstLabel,
TextBox secondTextBox, TextBox thirdTextBox)
{
//Note if you have a rowheader add the width here also.
int nameRightLoc = grid.Location.X +
grid.Columns[0].Width;
int fpRightLoc = nameRightLoc +
grid.Columns[0].DividerWidth +
grid.Columns[1].Width;
int dlRightLoc = fpRightLoc +
grid.Columns[1].DividerWidth +
grid.Columns[2].Width;
Point loc = firstLabel.Location;
loc.X = nameRightLoc - firstLabel.Width - 2;
firstLabel.Location = loc;
loc = secondTextBox.Location;
loc.X = fpRightLoc - secondTextBox.Width - 2;
secondTextBox.Location = loc;
loc = thirdTextBox.Location;
loc.X = dlRightLoc - thirdTextBox.Width - 2;
thirdTextBox.Location = loc;
}
If your dgv is inside a panel then you can compare panel and dgv height properties. If dgv's is bigger than panel's then there must be a scrollbar, right?
Like :
int panel_height = pnl.Height;
int dgv_height = (dgv.RowCount + 1) * 24; // You can play around with this 24 according to your cell styles
if (dgv_height > panel_height) MessageBox.Show("Voila!");

CheckedListBox Control - Only checking the checkbox when the actual checkbox is clicked

I'm using a CheckedListBox control in a small application I'm working on. It's a nice control, but one thing bothers me; I can't set a property so that it only checks the item when I actually check the checkbox.
What's the best way to overcome this?
I've been thinking about getting the position of the mouseclick, relative from the left side of the checkbox. Which works partly, but if I would click on an empty space, close enough to the left the current selected item would still be checked. Any ideas regarding this?
I know this thread's a bit old, but I don't think it's a problem to offer another solution:
private void checkedListBox1_MouseClick(object sender, MouseEventArgs e)
{
if ((e.Button == MouseButtons.Left) & (e.X > 13))
{
this.checkedListBox1.SetItemChecked(this.checkedListBox1.SelectedIndex, !this.checkedListBox1.GetItemChecked(this.checkedListBox1.SelectedIndex));
}
}
(With the value of CheckOnClick = True).
You could use that thingy with the rectangle, but why make it more complex the it needs to.
Well, it is quite ugly, but you could calculate mouse hit coordinates against rectangles of items by hooking on CheckedListBox.MouseDown and CheckedListBox.ItemCheck like the following
/// <summary>
/// In order to control itemcheck changes (blinds double clicking, among other things)
/// </summary>
bool AuthorizeCheck { get; set; }
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if(!AuthorizeCheck)
e.NewValue = e.CurrentValue; //check state change was not through authorized actions
}
private void checkedListBox1_MouseDown(object sender, MouseEventArgs e)
{
Point loc = this.checkedListBox1.PointToClient(Cursor.Position);
for (int i = 0; i < this.checkedListBox1.Items.Count; i++)
{
Rectangle rec = this.checkedListBox1.GetItemRectangle(i);
rec.Width = 16; //checkbox itself has a default width of about 16 pixels
if (rec.Contains(loc))
{
AuthorizeCheck = true;
bool newValue = !this.checkedListBox1.GetItemChecked(i);
this.checkedListBox1.SetItemChecked(i, newValue);//check
AuthorizeCheck = false;
return;
}
}
}
Another solution is to simply use a Treeview.
Set CheckBoxes to true, ShowLines to false, and ShowPlusMinus to false and you have basically the same thing as a CheckedListBox. The items are only checked when the actual CheckBox is clicked.
The CheckedListBox is much more simplistic, but the TreeView offers a lot of options that can potentially be better suited for your program.
I succesfully used this property:
CheckedBoxList.CheckOnClick
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.checkedlistbox.checkonclick?view=netframework-4.7.2
The text for a checkbox in a CheckedListBox is rendered by default is to place an HTML label after the checkbox input and set the label's "for" attribute to the ID of the checkbox.
When a label is denoting an element that it is "for," clicking on that label tells the browser to focus on that element, which is what you're seeing.
Two options are to render your own list with separate CheckBox controls and text (not as the Text property of the CheckBox, as that does the same thing as the CheckBoxList) if the list is static or to use something like a Repeater if the list is dynamic.
Try this. Declare iLastIndexClicked as a form-level int variable.
private void chklst_MouseClick(object sender, MouseEventArgs e)
{
Point p = chklst.PointToClient(MousePosition);
int i = chklst.IndexFromPoint(p);
if (p.X > 15) { return; } // Body click.
if (chklst.CheckedIndices.Contains(i)){ return; } // If already has focus click anywhere works right.
if (iLastIndexClicked == i) { return; } // native code will check/uncheck
chklst.SetItemChecked(i, true);
iLastIndexClicked = i;
}
Just checking to see if the user clicked in the leftmost 15 pixels of the checked list (the check box area) works at all times except re-checking a currently selected item. Storing the last index and exiting without changing lets the native code handle that properly, trying to set it to checked in that case turns it on and it just turns back off when the "ItemCheck" code runs.

Categories