i am programming a piece of software for a loyalty scheme. I have basically finished all of it. The only thing i am stuck on for ages is programming the buttons for the datagridview which i am using for my redeem offers page. I have gotten it to add a button to the end of each row automatically when you add a new offer but i am clueless on how to program it to recognize each row.
for e.g.)
Row1) Offer 1 is X name and costs Y
Row2) offer 2 is Z Name and costs F
I can only seem to find the ability to program every single button at once and not individual and even so, i need it to work automatically so when i can add a offer i can immediately purchase it. How would i get it to recognize which row i am on?
here is what my form and database looks like.
Picture Of Form - (picture link)
What my database looks like - (picture link)
below ur class
make public int id;
//adding button
DataGridViewButtonColumn col = new DataGridViewButtonColumn();
col.UseColumnTextForButtonValue = True;
col.Text = "ADD";
col.Name = "Btn_add";
DataGridView1.Columns.Add(col);
//pass id values here to do functions..
private void Btn_add_Click(object sender, EventArgs e)
{
}
//this gets selected index of selected row value
private void DataGridView1_SelectionChanged(object sender, EventArgs e)
{
int id = DataGridView1.SelectedRows(0).Cells(0).Value;
}
In Datagrid please bind OfferId in buy button. When press button then automatically gives id onclick event.
You can simply hook into the RowDataBound Event. This event is called, each time a new entry is put into the table. At this point, you can add a button to the tabe and add a custom action/url to the url.
Related
I need to implement some kind of accordion effect in a Windows Form DataGridView. When the user selects a row, the row is expanded to display some more information and if possible some buttons or other controls. The problem is, I have absolutely no clue, how to do this. I tried to search the web, but I found nothing that can lead me in the right direction in creating this. I hope someone can tell me how to do this? (does not have to be a code sample)
I created the below mockup to display what I want to do.
I thought about adjusting the columns height and override the OnPaint method. I only need to display some text in the first version. If that is possible that would be great. I know that in the future I would need to place some buttons or other controls to do various things with the selected item. If that is very complicated to achieve, I will skip that part for now. I know I could use tooltips for text and have button columns etc. but in my case, I need to do it as an accordion.
Best regards
Hans Milling…
This is not really hard to do. The best approach would be to create a dedicated UserControl and overlay it at the right spot.
To make room for it simply change the Row's Height, keeping track of the row, so you can revert it when it looses selection.
You will also have to decide if more than one row can be expanded and just what the user is supposed to do to expand and to reset the row..
The UserControl could have a function displayRowData(DataGridViewRow row) which you could call to display the fields you are interested in in its Labels etc..
You should also have a plan on how the Buttons shall interact with the DataGridView..
If you only want one Row to be expanded at a time, you can create the UC up front, make the DGV its Parent and hide it. Later upon the user interaction, like clicking at the row, or a certain cell you would move it to the right row and show it..
If more than one row can be expanded you will need to create several UCs and keep track of them in a List..
Here is a minimal example to encourage you..:
int normalRowHeight = -1;
UcRowDisplay display = new UcRowDisplay();
DataGridViewRow selectedRow = null;
public Form1()
{
InitializeComponent();
// create one display object
display = new UcRowDisplay();
display.Visible = false;
display.Parent = DGV;
display.button1Action = someAction;
}
After you have filled the DGV
// store the normal row height
normalRowHeight = DGV.Rows[0].Height;
You need at least these events:
private void DGV_SelectionChanged(object sender, EventArgs e)
{
if (selectedRow != null) selectedRow.Height = normalRowHeight;
if (DGV.SelectedRows.Count <= 0)
{
selectedRow = null;
display.Hide();
return;
}
// assuming multiselect = false
selectedRow = DGV.SelectedRows[0];
// assuming ColumnHeader show with the same height as the rows
int y = (selectedRow.Index + 1) * normalRowHeight;
display.Location = new Point(1, y);
// filling out the whole width of the DGV.
// maybe you need more, if the DGV is scrolling horizontally
// or less if you show a vertical scrollbar..
display.Width = DGV.ClientSize.Width;
// make room for the display object
selectedRow.Height = display.Height;
// tell it to display our row data
display.displayRowData(selectedRow);
// show the display
display.Show();
}
private void DGV_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
// enforce refresh on the display
display.Refresh();
}
Here is a test action triggered by the button in the display object:
public void someAction(DataGridViewRow row)
{
Console.WriteLine(row.Index + " " + row.Cells[2].Value.ToString());
}
And of course you need a UserControl. Here is a simple one, with two Labels, one Button and one extra Label to close the display:
public partial class UcRowDisplay : UserControl
{
public UcRowDisplay()
{
InitializeComponent();
}
public delegate void someActionDelegate(DataGridViewRow row);
public someActionDelegate button1Action { get; set; }
DataGridViewRow myRow = null;
public void displayRowData(DataGridViewRow row)
{
myRow = row;
label1.Text = row.Cells[1].Value.ToString();
label2.Text = row.Cells[0].Value.ToString();
rowDisplayBtn1.Text = row.Cells[2].Value.ToString();
}
private void rowDisplayBtn1_Click(object sender, EventArgs e)
{
button1Action(myRow);
}
private void label_X_Click(object sender, EventArgs e)
{
myRow.Selected = false;
this.Hide();
}
}
It contains tthree Labels and a Button. In the Designer it looks like this:
Note that in order to make it a little easier on me I have modified the DGV to
have no RowHeader; modify the location if you have one.
assumed the column header to have the same height as a row.
all (normal) rows to have the same height.
set the DGV to multiselect=false and read-only
Update
Here is a quick example for scrolling:
private void DGV_Scroll(object sender, ScrollEventArgs e)
{
DGV.PerformLayout();
var ccr = DGV.GetCellDisplayRectangle(0, selectedRow.Index, true);
display.Top = ccr.Top + normalRowHeight; // **
display.Visible = (ccr.Top >= 0 && ccr.Height > 0);
}
This (**) assumes the that the selected row shall remain visible. The same calculation should happen in the SelectionChanged event! It is up to you to decide what should happen when horizontal scrolling occurs..
Note that similar updates are necessary when rows are added or removed before the current row. So it should be moved to a function you call on those occurrances..
consider the following:
code for buttonclick fn in .cs file :
protected void additembtnClick(object sender, EventArgs e)
{
DataTable dt = createTemptable();
dt = (DataTable)Session["dfdtemptable"];
this.DFDLOVlst.DataSource = dt;
this.DFDLOVlst.DataBind();
this.txtLOVCode.Visible = true;
this.txtLOVvalue.Visible = true;
MDIngrdientsCode.Show();
}
this is the on_row clicked fn of the datagrid
protected void OnFindSelect(int Value)
{
}
code for findbtn click:
protected void btnFind_Click(object sender, EventArgs e)
{
this.FindLookup.SetLookup();
this.FindLookup.SearchLookup();
this.MdFindLookup.Show();
}
When the find button is clicked,the lookup control gets loaded with values from database.On selecting any row the selected row values will be added to corresponding text boxs and data grid.
The additembtnclick is to add new entry to the datagrid.The user can add new values or can click the find and select a row and update its values in the datagrid.
Theres no problem when the user adds fresh values .When the user click the find and select a particular row.The values gets add to correspondind fieldls..And now if the user clicks the additem button to add new values to already existing values in datagrid then the fn onfindselected(fn for the onrowclicked event for the lookup control containg datagrid) is called automatically,but the call is not in the button click fn..
Can't figure out whats wrong..?
It appears that theOnFindSelect is not an event handler itself. It is probably being called from another event handler (or more than 1). Try looking for all calls to it and see if that makes sense
I have a master-detail layout with a section of popup menus (the Details) and a section with a DataGridView which holds the rows.
The popup-menu state is updated when the selected row in the DataGridView changes and the state in the DGV's selected row should update when the popup-menu changes.
All of this works except the row in the DataGridView doesn't immediately update when I change the value of the popup-menu. I have to select a different row in order to see my edits.
I'm assuming this is because the edit hasn't been committed until the selection changes.
My question is: How do I make the change to the popup become immediately reflected in the DataGridView?
I have experimented with calling EndEdit() in the SelectionChangeCommitted handler for the popup-menu, but this has no effect. I'm interested in a technique that would allow me to create a DataGridView that would behave as if there were no Undo mechanism to begin with. Ideally the solution would be generic and transplantable to other projects.
It looks like existing answers work well with BindingSource. In my case, where DataTable was directly used as a DataSource, they didn't work for some reason.
// Other answers didn't work in my setup...
private DataGridView dgv;
private Form1()
{
var table = new DataTable();
// ... fill the table ...
dgv.DataSource = table;
}
After some hair-pulling, I got it work without adding BindingSource indirection:
// Add this event handler to the DataGridView
private void dgv_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
dgv.BindingContext[dgv.DataSource].EndCurrentEdit();
}
private Form1()
{
dgv.CellEndEdit += dgv_CellEndEdit;
// ...
}
Here's what was going on. The answer was in the properties of the ComboBox instances. I needed to change their DataSourceUpdateMode from OnValidation to OnPropertyChanged. This makes sense. The DataGridView was very likely showing the current state of the data. It was just that the data hadn't been edited yet because focus had not left the ComboBox, validating the input.
Thanks to everyone for your responses.
this works well for me:
private void CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var dgw = (DataGridView) sender;
dgw.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
Use this extension method. It works for all columns types, not just ComboBoxes:
public static void ChangeEditModeToOnPropertyChanged(this DataGridView gv)
{
gv.CurrentCellDirtyStateChanged += (sender, args) =>
{
gv.CommitEdit(DataGridViewDataErrorContexts.Commit);
if (gv.CurrentCell == null)
return;
if (gv.CurrentCell.EditType != typeof(DataGridViewTextBoxEditingControl))
return;
gv.BeginEdit(false);
var textBox = (TextBox)gv.EditingControl;
textBox.SelectionStart = textBox.Text.Length;
};
}
This method commits every change just after the change is made.
When we have a text column, after typing a character, its value will commit to the DataSource and the editmode of the cell will end.
Therefore current cell should return to edit mode, and position of the cursor set to end of text in order to enable user to continue typing reminder of the text.
Call the DataGridView.EndEdit method.
following will work
_dataGrid.EndEdit()
s fine once you set the value.
I have a textbox and a button. On page load I select one column from one row and put its value in the textbox. I have a button click method that updates the same column/row with the value in the textbox.
The problem i'm having is that when I clear the text in the text box, type in new data and hit submit the new text value is not being saved, it uses the old one.
I put a breakpoint at the end of my button click method and it appears that asp.net is sending the old value of the textbox rather than the new one I put in. I'm totally stumped.
This problem persists even if I have viewstate false on the textbox.
Both of my LINQ queries are wrapped in a using statement.
Any Ideas?
Edit: Here is the full code:
protected void Page_Load(object sender, EventArgs e)
{
using (StoreDataContext da = new StoreDataContext())
{
var detail = from a in da.Brands
where a.BrandID == 5
select new
{
brand = a,
};
foreach (var d in detail)
{
txtEditDescription.Text = d.brand.Description;
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
using (StoreDataContext dk = new StoreDataContext())
{
Brand n = dk.Brands.Single(p => p.BrandID == 5);
n.Description = txtEditDescription.Text;
dk.SubmitChanges();
}
}
As you said, in Page_Load you are retrieving the original value into the text box which overwrites any changes.
Yes, Page_Load DOES execute before Button Click (or any other control events) is executed. I'm going to guess you have done Windows Forms development before this, the ASP.Net web forms event model is quite different. Check out the ASP.NET Page Life Cycle Overview.
I figured it out. I should be be using if(!IsPostBack) on the code that originally fills the textbox with its value.
However this makes no sense to me. If the page is loaded and the textbox text gets a value, then you clear this value and try to insert it into the database, it should insert this value and then when the page post back it will fetch the new value from the database and put the value in the textbox.
The way it's actually working makes it seem like it is executing the page load code before the button click code is executed on post back.
just to trace the error,please try to put a label =( lblDescription.Text ) and leave the rest of code as is,put the new value in the textbox (editdescription.text) try it and tell me what you see
foreach (var d in detail)
{
lblDescription.Text = d.brand.Description;
}
Similar to this question here ASP.Net Dynamic Command Button Event Not Firing but with a slightly different problem.
Provided below is a (very) condensed version of my code.
protected void Page_Load(object sender, EventArgs e)
{
RenderDataItems();
}
private void RenderDataItems()
{
pnlDataItems.Controls.Clear()
DataTable dt = MyClass.GetAllData();
foreach (DataRow dr in dt.Rows)
{
Button b = new Button();
b.Command += new CommandEventHandler(SelectItem);
b.CommandArgument = dr["ID"].ToString();
b.ID = "btnData" + dr["ID"].ToString();
if (hdnDataListID.Value == dr["ID"].ToString())
{
b.Text = "Selected Item";
}
else
{
b.Text = "Pick This Item";
}
pnlDataItems.Controls.Add(b);
}
}
private void SelectItem(object sender, CommandEventArgs e)
{
hdnDataListID.Value = e.CommandArgument.ToString();
RenderDataItems();
}
private void EditSelectItem(int id)
{
MyClass mc = new MyClass(id);
hdnDataListID.Value = mc.ID.ToString();
RenderDataItems();
}
The method SelectItem is only called by the button controls rendered within the RenderDataItems method. The EditSelectItem is called by a separate control that is created dynamically, but does not require the altering that the buttons in the RenderDataItems method requires.
I've run the debugger and stepped through the code to see what happens. When the page is loaded, the RenderDataItems is called from the PageLoad and populates the panel with all of the buttons having "Pick This Text" (because the HiddenField control's value (hdnDataListID) has not been set).
The first time I click one of the buttons, the RenderDataItems from PageLoad fires, followed by the initial population of the buttons, the HiddenField's value is set to the ID, and the second RenderDataItems call happens from within the SelectItem method. The buttons are cleared, and recreated. The correct button has the "Selected Item" text.
The second time I click one of the buttons, the RenderDataItems from PageLoad fires, followed by the initial population of the buttons, but the SelectItem method never fires.
The third time I click one of the buttons, the same functionality happens as the 1st time. The 4th mimics the 2nd. The 5th mimics the 1st. And so on, and so on.
When using the EditSelectItem method from the controls not contained within the panel (it's a DataSource bound GridView row with buttons that calls this method), it does exactly as I'd expect and properly sets the selected / unselected buttons, with calls to both the RenderDataItems and the EditSelectItem method every time.
Any ideas?
P.S. I've already removed all of my AJAX on this page.
You should give your Button b an ID.