Multiple DataKeyNames in a GridView - asp.net

I have a GridView populated from an ObjectDataSource with two items in its DataKeyNames field. One is the primary key, ID, the other is a category field (the category field is used to add header rows to delineate categories).
Displaying works fine, but I'm trying to create a Delete action. The object's delete method only needs the ID field and in the ObjectDataSource even if I define the method as only needing an ID field, .net complains because it is looking for a method which has both the fields defined in DataKeyNames.
It works if I add a parameter for the category to the delete method, but it's annoying to have a parameter defined that isn't used for anything.
Can I configure the ObjectDataSource and GridView objects to have two values for DataKeyNames but specific which would should be passed to which methods?
The (simplified) definitions for the two objects are:
<asp:ObjectDataSource ID="ObjDS1" runat="server" SelectMethod="getAllItems"
TypeName="Items" DeleteMethod="deleteItem">
<DeleteParameters>
<asp:Parameter Name="ID" Type="Int32" />
<!-- This shouldn't be necessary: -->
<asp:Parameter Name="Category" Type="String" />
</DeleteParameters>
</asp:ObjectDataSource>
<asp:GridView ID="gvJItems" runat="server" AutoGenerateColumns="False" DataKeyNames="ID,Category"
DataSourceID="ObjDS1">
<Columns>
<asp:BoundField DataField="ID" Visible="false" HeaderText="ID" />
<asp:BoundField DataField="Name" HeaderText="Name" ItemStyle-Width="85%"/>
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="lbDelete" Runat="server"
OnClientClick="return confirm('Are you sure you want to delete this?');"
CommandName="Delete">Delete</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

All of the DataKey values will always be passed to the Delete method because the fields named by DataKeyNames are intended to be the field or fields that uniquely identify that item in the GridView. They're often used for more than that to easily maintain extra fields on a per-row basis in ViewState as you are here, but doing so has the side-effect that you're seeing with the ObjectDataSource methods.
To answer your original question, no, you can't have Category be in DataKeyNames without having it be passed to the Delete method. You could maintain the value in ViewState by putting the value in a hidden input in your Template Column, though. It would be more work to get that value out than to ignore the parameter to the Delete method, though.

I had the same problem with my gridview with an object datasource, how I handled this situation : I use StoredProcs in the SQL end. I added one more parameter into the store procedure, though I did not need that parameter. It worked fine

You can remove them using the ObjectDataSource updating/deleting the event
void ODS_Updating(Object sender, ObjectDataSourceMethodEventArgs e)
{
e.InputParameters.Remove("Type_ID");
e.InputParameters.Remove("Document_ID");
e.InputParameters.Remove("State_ID");
}
I am using the DataKeys for look-up purposes but I don't need them in my update procedure.

When using the default declarative delete method, GridView will pass both values to the ObjectDataSource.
If you really don't like the extra parameter, an alternative is to cancel the declarative method and use your own one (you can remove the unnecessary parameter there), but it requires more code.

Related

Label value read for Insert command is not the value shown in browser

Good afternoon.
I have a simple gridview with a field like this:
<asp:TemplateField HeaderText="USD Full Load Machine">
<ItemTemplate>
<asp:Label ID="lblUSDFullLoad" runat="server" Text='<%# Bind("USDFullyLoadMachine") %>' />
</ItemTemplate>
</asp:TemplateField>
The InsertCommand and Insertparameters are set up like this:
<asp:SqlDataSource (code omitted)
InsertCommand="Insert Into [xxxxTable]
([SubmissionID],USDFullyLoadMachine]) Values(#newSubID,#usdflm)">
<InsertParameters>
<asp:ControlParameter Name="newSubID" ControlID="lblNewSubmissionID" />
<asp:ControlParameter Name="usdflm" ControlID="gridConversionTotals$ctl02$lblUSDFullLoad" />
</InsertParameters>
</asp:SqlDataSource>
When I change the gridConversionTotals$ctl02$lblUSDFullLoad label's text using client-side javascript, and I execute the insert method on the sql datasource control in code behind, the value sent to SQL is NOT the new value of the label, i.e. is not the value shown on the browser, it is the value the label had when it was last databound.
I initially thought that this was because the server, upon executing the insert command, could not possibly know what the client was showing, but then this is not true, because if I change the label to a textbox and leave all the code intact, the value sent to the database is indeed the one shown on the client.
Can someone please explain why the insert method, in this instance, treats a label's and a textbox's client-side content differently?
And does this mean that generally when I want to change values client-side and then send them to the database I will need to always use textboxes?
Thank you.
After some further research I found this post https://stackoverflow.com/a/10324721/1266732, which explains that the label will not post the value back to the webserver, and therefore it is recommended to use textboxes and styling them as labels for this purpose.
At a technical level, form values are sent using <input /> elements or cookies. If your javascript is not editing an <input /> element or a cookie (or in some way modifying a GET URL), then your value will not make it to the server.

Accessing Gridview row data inside fields

I have a Gridview bound to a linqdatasource. The gridview has a FK. i want to display a name/text field instead of the key field for this column.
I have created a method in my ASP.NET page that basically GetLookupForKey that returns the string when provided the key. However, I don't know how to send the column data for the specific row in the data declaration.
This should make it clear:
<asp:TemplateField>
<ItemTemplate>
<asp:Literal ID="RoleName" runat="server" Text='<%#
GetRoleName(* I need to send in the RoleId Here*) %>' />
RoleId is both a boundfield and a DataKeyName. How can I send the RoleId to my method? Additionally, how can I achieve this without using any codebehind?
Thanks
I can see two ways right now, maybe there are even more.
1) With code behind
<asp:Literal ID="RoleName" runat="server" Text='<%# GetRoleName(Eval("RoleId")) %>'/>
This one will require a protected method GetRoleName(object roleId) in the page's code behind class.
2) Without code behind
I assume that objects Role and whatever object is referencing it are both declared in the Linq context. If so, Linq can (and even does it by default behavour) generate properties for referenced objects. That is, when you have a table with FK to Role, the corresponding object will have both RoleID and Role properties. Therefore everything can be accomplished declaratively:
<asp:Literal ID="RoleName" runat="server" Text='<%# Eval("Role.Name") %>'/>

DotNetNuke: ObjectDataSource for GridView not being found

As a follow-up to a previous question about GridView and DotNetNuke, I'm having a little more trouble getting things to act correctly. Right now I have a simple GridView in my ascx file and I bind the data to the GridView in my .cs file like so:
DiscoveryController objDiscoverys = new DiscoveryController();
List<DiscoveryInfo> lstDiscoveries = objDiscoverys.GetDiscoverys(ModuleId);
grdDiscoverys.DataSource = lstDiscoveries;
grdDiscoverys.DataBind();
This works. However, I have seen an alternate method in a tutorial which instead defines an <asp:ObjectDataSource> in the controller, and this seems to allow the Designer to do more intelligent things, such as add functioning Delete buttons through a checkbox. Later on in the tutorial, I see inline editing being done as well, which is functionality I desire.
So I decided to give it a shot. To wit:
<asp:ObjectDataSource ID="objDataSource" runat="server" TypeName="MyCompany.Modules.Discovery.DiscoveryController" />
As my dll file in the bin folder is named MyCompany.Modules.Discovery (which matches the assembly name and default namespace I have set up in my C# project), this makes perfect sense. As the tutorial says, I then used the Designer to attempt to bind the Data Source to the GridView.
However, I get an error message that it can't be loaded. The namespace names and class name match up, and I can clearly bind it from the codebehind, so what gives?
EDIT: A follow up. After some experimentation, I have discovered that while the Designer cannot see my module, the .ascx template itself can. Putting this in my .ascx file seems to work...for the most part:
<asp:ObjectDataSource ID="objDataSource" runat="server" TypeName="MyCompany.Modules.Discovery.DiscoveryController" SelectMethod="GetDiscoverys" UpdateMethod="UpdateDiscovery" DeleteMethod="DeleteDiscovery">
<SelectParameters>
<asp:QueryStringParameter Name="ModuleId" QueryStringField="mid" />
</SelectParameters>
<UpdateParameters>
<asp:QueryStringParameter Name="ModuleId" QueryStringField="mid" />
</UpdateParameters>
<DeleteParameters>
<asp:QueryStringParameter Name="ModuleId" QueryStringField="mid" />
</DeleteParameters>
</asp:ObjectDataSource>
<asp:GridView ID="grdDiscoverys" runat="server" DataSourceID="objDataSource" EnableModelValidation="True" AutoGenerateColumns="false" AutoGenerateEditButton="true" AutoGenerateDeleteButton="true" DataKeyNames="ItemId">
<Columns>
<asp:BoundField DataField="ItemId" HeaderText="#" ReadOnly="true" />
<asp:BoundField DataField="Title" HeaderText="Title" />
<asp:BoundField DataField="Image" HeaderText="Image URL" />
<asp:BoundField DataField="Link" HeaderText="Link" />
</Columns>
</asp:GridView>
Fantastic...it looks like I've mirrored most of the functionality of what the Designer would have added...except for one teensy little thing. The automatic update doesn't update.
More specifically, I get this message when I try to update a field:
ObjectDataSource 'objDataSource' could not find a non-generic method 'UpdateDiscovery' that has parameters: ModuleId, Title, Image, Link, ItemId.
Of course it doesn't work! The method signature goes like this:
public void UpdateDiscovery(DiscoveryInfo objDiscovery)
At this point, I'm so close to getting something working that I can taste it, and DAL-be-damned I'm about to change the function so it takes those five exact params instead of a data object. However, the tutorial that I referenced above seemed to somehow convince the automatic update to pass a data object, so I'm kind of curious to know how it got away with it.
Change your object data source declaration to match your update method:
public void UpdateDiscovery(DiscoveryInfo objDiscovery)
Use single parameter in update parameter declaration:<UpdateParameters>
<asp:Parameter Name="objDiscovery" />
</UpdateParameters>
use object data source's updating event to create an object from the existing control set and assign it to parameter's default value.
Good luck

Question: Regarding GridView boundfield ReadOnly property

I have a Gridview boundfield where i set ReadOnly to true because i don't want user to change its value. However on the objectdatasource control's update method that boundfield became null when i try to use it as parameter in update method. Is there a way to set that value during updating?
No, just add the field name you need to the DataKeyNames attribute of the GridView. Then the value will be sent to the Update command.
When you mark a field as read-only on the GridView it renders on the page as a span element, not an input. Therefore the value is not available on PostBack. If you can construct the update statement so that it doesn't expect this field, that would be the best way to deal with this. If the update statement is autogenerated and you can't get around having the value to update, then you can either read the value from the database before doing the update (so that you have it) or include a HiddenField bound to this column and use a literal that obtains the value via Eval instead of binding (if necessary). This will require using a template.
<asp:TemplateField>
<InsertItemTemplate>
<asp:TextBox runat="server" ID="itemTextBox" />
</InsertItemTemplate>
<EditItemTemplate>
<asp:HiddenField runat="server" ID="itemHF" Value='<% Bind("Item") %>' />
<asp:Label runat="server" ID="itemLabel" Text='<% Eval("Item") %>' />
</EditItemTemplate>
<ItemTemplate>
<asp:Label runat="server" ID="itemLabel" Text='<% Bind("Item") %>' />
</ItemTemplate>
</asp:TemplateField>
Another approach is to add a new query to your tableadapter. Create an update query that just uses the fields desired to be updated. When selecting the update method on the ODS pick the update query. The BoundFields that are not part of the update query can now be turned to readonly=true and it should work.
I had a similar problem and solved it in a slightly different way. But in my case the parameter I wanted to use in my Update method was my primary key and was available in a Query string. So in my DataSource definition I defined the UpdateParameters section to use instead of . Then I was able to remove the parameter completely from my table and it would revert to the Query string parameter.

Dynamic Columns and Data Sources with .NET Grid View

I am working on a multi-purpose page and rather than adding multiple grids to the same page we wanted to use a single GridView to the page, and on Page_Init add the needed columns, and set the respective DataSourceID.
So to do this, we have something like the following in the aspx, the codebehind in the Page_Init is very simple adding a few columns then setting the DataSourceID property of the GridView.
ASPX:
<asp:GridView ID="gvDisplay" runat="server" AutoGenerateColumns="false" CellPadding="5"
width="100%" AllowPaging="true" PageSize="200" DataSourceID="wuProcessLogDataSource">
<RowStyle CssClass="RowStyle" />
<AlternatingRowStyle CssClass="AlternatingRowStyle" />
<HeaderStyle CssClass="HeaderStyle" />
</asp:GridView>
<asp:ObjectDataSource id="wuProcessLogDataSource" runat="server" EnablePaging="True"
SelectMethod="GetWUProcessLog" TypeName="Project.Objects.WUProcessLogDal"
SelectCountMethod="GetWUProcessLogTotalRecords">
<SelectParameters>
<asp:QueryStringParameter QueryStringField="w" DefaultValue="0" Name="workunitId" />
</SelectParameters>
</asp:ObjectDataSource>
The object data source is there and working as the first page load triggers without any issues at all. However, as soon as you click on a page button, the grid disappears from the page? Any ideas?
I would just use a DataGrid but it doesn't have the desired dynamic display abilities for the HyperLinkColumn.
It sounds like you're doing something like
If (!Page.IsPostBack)
{
//create + add columns - set datasource etc
}
If that's the case - then you need to remove the check and always generate the columns (I'd also suggest disabling viewstate for the datagrid)
try the page load event instead of page init

Resources