How to add a Hyperlink to a dynamic gridview column - c#

I have an issue hope someone can help.
I have a dynamic Gridview. I need to have a hyperlink on gridview column. These hyperlink should open a popup to display certain data on clicking.
I tried this by having a dynamic template field . But even on binding the data , I'm unable to get the hyper link for the column. I'm able to get the data but not the hyperlink.
This is the HyperLinkTemplate class which is implementing ITemplate.
public class HyperLinkTemplate : ITemplate
{
private string m_ColumnName;
public string ColumnName
{
get { return m_ColumnName; }
set { m_ColumnName = value; }
}
public HyperLinkTemplate()
{
//
// TODO: Add constructor logic here
//
}
public HyperLinkTemplate(string ColumnName)
{
this.ColumnName = ColumnName;
}
public void InstantiateIn(System.Web.UI.Control ThisColumn)
{
HyperLink HyperLinkItem = new HyperLink();
HyperLinkItem.ID = "hl" + ColumnName;
HyperLinkItem.DataBinding += HyperLinkItem_DataBinding;
ThisColumn.Controls.Add(HyperLinkItem);
}
private void HyperLinkItem_DataBinding(object sender, EventArgs e)
{
HyperLink HyperLinkItem = (HyperLink)sender;
GridViewRow CurrentRow = (GridViewRow)HyperLinkItem.NamingContainer;
object CurrentDataItem = DataBinder.Eval(CurrentRow.DataItem, ColumnName);
HyperLinkItem.Text = CurrentDataItem.ToString();
}
}

I'm not entirely sure that I understand what you are trying to accomplish, but I don't think that you should have to build your own template class for this.
You might mean something other than what I'm thinking by the term "dynamic gridview", but if you need to add a hyperlink to each row of a column in a GridView, and if you need to do this in the code-behind, then I would suggest handling the GridView's RowDataBound event and doing something like the following in the event handler:
protected void grdData_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
HyperLink link = new HyperLink();
link.Text = "This is a link!";
link.NavigateUrl = "Navigate somewhere based on data: " + e.Row.DataItem;
e.Row.Cells[ColumnIndex.Column1].Controls.Add(link);
}
}

Related

Change visible state of DataGridView only given its name

I have a form application where multiple DataGridView objects are to be displayed (but not at once). They should be created on top of each other and it should then be possible to toggle the displayed DataGridView using a ComboBox.
I have a function which should create new DataGridView every time its called and then adds the name to the ComboBox:
private void readCSV(string DBname)
{
DataGridView tagDBname = new DataGridView();
tagDBname.Location = new System.Drawing.Point(24, 260);
tagDBname.Name = DBname;
tagDBname.Size = new System.Drawing.Size(551, 217);
tagDBname.TabIndex = 6;
tagDBname.Columns.Add("Column1", "Col1");
tagDBname.Columns.Add("Column2", "Col2");
tagDBname.Visible = false;
comboBoxTag.Items.Add(DBname);
}
Then I would like to change the visibility state of a DataGridView given the selected name from the ComboBox. This should be done in the function called when the index changes:
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
DataGridView tagDatabase = ? // Here the DataGridView should be selected given the name "selectedTagDB"
tagDatabase.Visible = true;
}
In the above, I do not know how to assign the DataGridView only given its name. Any help would be appreciated - even if it means that the selected approach is inappropriate of what I am trying to achieve. If the question is answered elsewhere, feel free to guide me in the right direction :)
I would store the gridviews in a dictionary by using the DB name as key;
private readonly Dictionary<string, DataGridView> _tagDBs =
new Dictionary<string, DataGridView>();
private void readCSV(string DBname)
{
DataGridView tagDBname = new DataGridView();
// Add the gridview to the dictionary.
_tagDBs.Add(DBname, tagDBname);
tagDBname.Name = DBname;
tagDBname.Location = new System.Drawing.Point(24, 260);
tagDBname.Size = new System.Drawing.Size(551, 217);
tagDBname.TabIndex = 6;
tagDBname.Columns.Add("Column1", "Col1");
tagDBname.Columns.Add("Column2", "Col2");
tagDBname.Visible = false;
this.Controls.Add(tagDBname); // Add the gridview to the form ot to a control.
comboBoxTag.Items.Add(DBname);
}
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
foreach (DataGridView dgv in _tagDBs.Values) {
dgv.Visible = dgv.Name == selectedTagDB; // Hide all gridviews except the selected one.
}
}
If you need to do something with the selected gridview, you can get it with:
if (_tagDBs.TryGetValue(selectedTagDB, out DataGridView tagDatabase)) {
// do something with tagDatabase.
}
Note: you must add the gridview to the form or to a container control on the form. E.g.
this.Controls.Add(tagDBname);
You can loop through all DataGridViews of the form to display the expected one using its name, while hidding the others ones.
This solution isn't pretty but works
private void ShowOneDataGridViewAndHideOthers(string name)
{
foreach (var DGV in this.Controls.OfType<DataGridView>())
{
DGV.Visible = DGV.Name == name;
}
}
And call it this way :
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
ShowOneDataGridViewAndHideOthers(selectedTagDB);
}
The method can be made a bit more generic this way :
private void ShowOneControlAndHideOthers<T>(string name, Control controls) where T : Control
{
foreach (var control in controls.Controls.OfType<T>())
{
control.Visible = control.Name == name;
}
}
private void comboBoxTag_SelectedIndexChanged(object sender, EventArgs e)
{
// Get the name of the DataGridView which should be visible:
string selectedTagDB = comboBoxTagDatabases.SelectedItem.ToString();
ShowOneControlAndHideOthers<DataGridView>(selectedTagDB, this);
}

DataGridView not loading data programmatically

I am working on a Windows application where I get an input from a TextBox and add it to the DataGridView when user clicks on a Button (Add).
My problem is that when I add the text for the first time, it works fine and its added to the grid.
When I add new text, it's not added to the DataGridView. Once the Form is closed and reopened with the same object then I am able to see it.
Code:
private void btnAddInput_Click(object sender, EventArgs e)
{
if (Data == null)
Data = new List<Inputs>();
if (!string.IsNullOrWhiteSpace(txtInput.Text))
{
Data.Insert(Data.Count, new Inputs()
{
Name = txtInput.Text,
Value = string.Empty
});
}
else
{
MessageBox.Show("Please enter parameter value", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
txtInput.Text = "";
gridViewInputs.DataSource = Data;
}
I am not sure what is causing the record not to be added to grid on second add button click.
You could set the DataGridView.DataSource to null before setting a new one.
This would cause the DataGridView to refresh its content with the new data in the source List<Inputs>:
the underlying DataGridViewDataConnection is reset only when the DataSource reference is different from the current or is set to null.
Note that when you reset the DataSource, the RowsRemoved event is raised multiple times (once for each row removed).
I suggest to change the List to a BindingList, because any change to the List will be reflected automatically and because it will allow to remove rows from the DataGridView if/when required: using a List<T> as DataSource will not allow to remove a row.
BindingList<Inputs> InputData = new BindingList<Inputs>();
You can always set the AllowUserToDeleteRows and AllowUserToAddRows properties to false if you don't want your Users to tamper with the grid content.
For example:
public class Inputs
{
public string Name { get; set; }
public string Value { get; set; }
}
internal BindingList<Inputs> InputData = new BindingList<Inputs>();
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = InputData;
}
private void btnAddInput_Click(object sender, EventArgs e)
{
string textValue = txtInput.Text.Trim();
if (!string.IsNullOrEmpty(textValue))
{
InputData.Add(new Inputs() {
Name = textValue,
Value = "[Whatever this is]"
});
txtInput.Text = "";
}
else
{
MessageBox.Show("Not a valid value");
}
}
If you want to keep using a List<T>, add the code required to reset the DataGridView.DataSource:
private void btnAddInput_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(textValue))
{
//(...)
dataGridView1.DataSource = null;
dataGridView1.DataSource = InputData;
txtInput.Text = "";
}
//(...)

Pass additional variable to button_click() method

I am creating a dynamic table for listing files.
It shows filename, filesize, dateModified columns. In addition, I have added one more columns: Delete.
The method which lists files of a folder in a table.
public void listFile()
{
var dir = new DirectoryInfo(selectedFolder);
Table fileTable = new Table();
foreach (var file in dir.GetFiles())
{
TableRow tr = new TableRow();
TableCell td1 = new TableCell();
TableCell td2 = new TableCell();
TableCell td3 = new TableCell();
TableCell td4 = new TableCell();
Label name = new Label();
Label size = new Label();
Label dateMod = new Label();
LinkButton btn_delete = new LinkButton();
name.Text = file.Name;
size.Text = (file.Length / 1024) + " KB";
dateMod.Text = file.LastWriteTime.ToLongTimeString();
btn_delete.Text = "Delete";
btn_delete.Click += new EventHandler(btn_delete_Click);
td1.Controls.Add(name);
td2.Controls.Add(size);
td3.Controls.Add(dateMod);
td4.Controls.Add(btn_delete);
tr.Controls.Add(td1);
tr.Controls.Add(td2);
tr.Controls.Add(td3);
tr.Controls.Add(td4);
}
filePanel.Controls.Add(fileTable);
}
protected void btn_delete_Click(object sender, EventArgs e)
{
//Delete file
}
Now I want to delete the file when I click on the corresponding delete button. But problem is how will computer know which file to be deleted? I must pass the filename to the delete method.
You can pass command argument to the link button as mentioned below:
btn_delete.CommandArgument = [ID of the file]
and on click event of the link button you can access it as mentioned below:
protected void btn_delete_Click(object sender, EventArgs e)
{
LinkButton btn = (LinkButton)sender;
var id = btn.CommandArgument;
}
Reference: LinkButton.CommandArgument Property
The answer from SpiderCode and RePierre about using the CommandArgument is a correct. It might however help you to understand that the CommandArgument is nothing more than a thin wrapper around the ViewState concept.
ViewState is one way how you can transport data between client and server. If you ever run into a similar problem and you don't have commandargument available you can resort to viewstate.
Take a look at the LinkButton.cs
public string CommandArgument {
get {
string s = (string)ViewState["CommandArgument"];
return((s == null) ? String.Empty : s);
}
set {
ViewState["CommandArgument"] = value;
}
So you can either use the CommandArgument or use ViewState directly.
You can use the CommandArgument property of btn_delete to pass the path to the file:
btn_delete.CommandArgument = file.Name;
And in the event handler you just have to get the value as following:
protected void btn_delete_click(object sender, EventArgs e)
{
var button = sender as Button;
var filename = button.CommandArgument;
}
You can do this by creating a UserControl which is inheritated from the main Control.
In my example I extended a Textbox with an address variable.
In that way you bind a value to the control, which you can use later.
public class ExtendedTextBox : System.Windows.Forms.TextBox
{
private int? _Address = null;
[Description("Address of variable")]
[DefaultValue(null)]
public int? Address
{
get { return _Address; }
set { _Address = value; }
}
}

Accessing dynamically generated dropdownlist in Gridview_RowUpdating event

I am using Gridview with AutoGenerateColumns="True", so gridview columns are generated dynamically. Now in case of edit, I am adding dropdownlist dynamically for one of the field in the gridview. Please see following code:
protected void grdViewConfig_RowEditing(object sender, GridViewEditEventArgs e)
{
grdViewConfig.EditIndex = e.NewEditIndex;
BindGridView();
clientBAL = new TMIWsBALClient();
var lstAppIds = clientBAL.GetDistinctApplicationIds();
GridViewRow grdRow = grdViewConfig.Rows[e.NewEditIndex];
for (int i = 0; i < grdRow.Cells.Count; i++)
{
if (grdRow.Cells[i].GetType().Equals(typeof(DataControlFieldCell)))
{
DataControlFieldCell dcField = (DataControlFieldCell )grdRow.Cells[i];
if (dcField.ContainingField.HeaderText.ToLower().Equals("applicationid"))
{
DropDownList drpDwnAppIds = new DropDownList();
drpDwnAppIds.ID = "drpDwnAppIds";
drpDwnAppIds.DataSource = lstAppIds;
drpDwnAppIds.DataBind();
var tb = dcField.GetAllControlsOfType<TextBox>(); ;// grdRow.Cells[i].GetAllControlsOfType<TextBox>();
TextBox firstTb = (TextBox)tb.First();
foreach (ListItem lstItem in drpDwnAppIds.Items)
{
if (firstTb.Text.Equals(lstItem.Text, StringComparison.CurrentCultureIgnoreCase))
{
lstItem.Selected = true;
}
}
dcField.Controls.Remove(firstTb);
dcField.Controls.Add(drpDwnAppIds);
}
}
}
}
Now in Gridview_RowUpdating event, I am trying to fetch the dropdownlist in similar way, but I am unable to get it. GetAllControlsOfType() is an extension method, which will return all the child controls under selected parent. In this case, parent is gridview cell and child control is dropdownlist. But it is returning null.
protected void grdViewConfig_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
strTableName = txtTable.Text.Trim();
string strAppId;
GridViewRow grdRow = grdViewConfig.Rows[grdViewConfig.EditIndex];
for (int i = 0; i < grdRow.Cells.Count; i++)
{
if (grdRow.Cells[i].GetType().Equals(typeof(DataControlFieldCell)))
{
DataControlFieldCell dcField = (DataControlFieldCell)grdRow.Cells[i];
if (dcField.ContainingField.HeaderText.ToLower().Equals("applicationid"))
{
var drpDwn = dcField.GetAllControlsOfType<DropDownList>();
DropDownList drpDwnAppIds = (DropDownList)drpDwn.First();
strAppId = drpDwnAppIds.SelectedValue;
}
}
}
}
What am I missing? Please help. Also let me know if more information is needed.
Thank you in advance.
Dynamically generated controls need to be recreated on every postback. In your case the DropDownList controls you created no longer exist when you hit the grdViewConfig_RowUpdating handler.
Generally in this sort of case you would set AutoGenerateColumns to false and manually define your columns which would allow you to define a TemplateField which contains an ItemTemplate for read only mode and an EditItemTemplate for edit mode which could then contain your DropDownList.

RowCommand not getting fired on Dynamically created GridView

I am creating a gridview dynamically with a ButtonField and several BoundFields. ButtonField button type is LinkButton. If i run it the buttonclick triggers a post back but rowCommand is not triggered. It is not triggered even if i use AutogenerateSelectButton. The event is dyanamically bound. Code As follows:
protected void B_Search_Click(object sender, EventArgs e) //Search buttonclick that creates and displays the gridview
{
gd = getGridView(); //defines the gridview with columns and buttonfield
gd.DataSource = executeAdvanceSearch(); //retrieves data from DB as Dataset
gd.DataBind();
gd.RowCommand += new GridViewCommandEventHandler(gdView_RowCommand); //Rowcommand event binding
PlaceHolder1.Controls.Add(gd);
}
protected void gdView_RowCommand(object sender, GridViewCommandEventArgs e) //not getting triggered on postback
{
int index = Convert.ToInt32(e.CommandArgument);
GridView gdView = (GridView)sender;
if (e.CommandName == "IDClick")
{
//Do something
}
}
private GridView getGridView()
{
GridView gdView = new GridView();
gdView.AutoGenerateColumns = false;
gdView.AutoGenerateSelectButton = true;
string name;
string[] field = ZGP.BLL.Search.getResultFormat(Convert.ToInt32(DDL_ResultView.SelectedValue), out name); //Ignore. This jst gets columnNames
if (field.Count() != 0)
{
gdView.Columns.Add(getSelectButton()); //Adds linkbutton
foreach (string cName in field) //ignore. This adds columns.
if (!String.IsNullOrEmpty(cName))
{
gdView.Columns.Add(GV_DataColumn.getGridViewColumn((DataColumnName)Enum.Parse(typeof(DataColumnName), cName))); //Ignore. adds columns
}
}
return gdView;
}
private ButtonField getSelectButton()
{
ButtonField _bf = new ButtonField();
_bf.ButtonType = ButtonType.Link;
_bf.HeaderText = "ID";
_bf.DataTextField = "ID";
_bf.CommandName = "IDClick";
return _bf;
}
Thanks for the help.
if (e.CommandName=="CommandName")
{
int index = Convert.ToInt32(e.CommandArgument);
GridViewRow row = GridView1.Rows[index];
string boundFieldText= row.Cells[0].Text;
}
You'll need to create the grid view on each postback.

Categories