SubSonic 2.2 and ASP.NET gridview - asp.net

I'm trying to show a custom column in my gridview which displays a content type based on a couple of boolean fields in my database. Everything works fine but it's causing a lot of overhead the way I do it now.. like this:
<ItemTemplate>
<asp:Label ID="lblType" runat="server" Text='<%# GetType((int)DataBinder.Eval(Container.DataItem))%>' />
</ItemTemplate>
This calls a function GetType which queries the database based on the ArticleID. Of course this happens for every item in the gridview. Now I would like to know if it's possible to send the current (subsonic) collection item to this function instead? Because the item is already available but I don't know how to put this in my itemtemplate.
My current item is DAL.Article which contains everything I need.
I hope I made myself clear a little !Thanks for your time.
Kind regards,
Mark

Subsonic generated classes are partial and thus extendable.
Let's say you have a DAL object called Person. You can create a new file Person.cs (in a different folder of course).
namespace Your.Dal.Namespace {
public partial class Person
{
public string DisplayName
{
get
{
return String.Format("{0}, {1}", this.LastName, this.FirstName);
}
}
}
}
Now you can access the DisplayName property of your class:
PersonCollection col = new PersonCollection().Load();
foreach(Person p in col)
Console.WriteLine(p.DisplayName);
I use this technique for binding Subsonic Collections to a Windows.Forms DataGridView a lot.
But it should work for asp.net, too.

Related

How do I declaratively bind 'SelectedValue' to datasource field?

I want to drive a RadioButtonLists SelectedValue property from a property in my datasource, but I'm not managing to make it work.
I have a <asp:RadioButtonList on my page bound to an <asp:ObjectDataSource. This datasource in turn provides the following model class:
public class CollectionWithDefault : Collection<string>
{
CollectionWithDefault(IEnumerable<string> items, string defaultItem)
{
foreach (var item in items)
Add(item);
DefaultItem = defaultItem;
}
public string DefaultItem { get; }
}
Notice that this class is a standard collection of strings that also exposes which one of them is the default option.
Consider that I have the following implementation for a value provider. This is a simple in-memory implementation, but keep in mind that this could be coming from a database or any other source:
public static class ItemProvider
{
public static CollectionWithDefault GetAvailableItems()
{
var items = new [] { "option1", "option2", "option3" };
return new CollectionWithDefault(items, items[1]);
}
}
I tried the following:
<asp:ObjectDataSource runat="server"
ID="ItemSource"
TypeName="MyNamespace.ItemProvider"
SelectMethod="GetAvailableItems" />
<asp:RadioButtonList runat="server"
DataSourceID="ItemSource"
SelectedValue='<%# Eval("DefaultItem") #>' />
I'm getting the following exception in the Eval call:
System.InvalidOperationException: 'Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.'
How can I ensure that the correct radio is preselected based on the field coming from my datasource?
Changing the collection model itself to make it work is acceptable, but I can't set the SelectedValue from codebehind. I wanted to rely on the datasource control to do the heavy lifting.
I managed to make this work seamlessly without requiring manual assignments in codebehind by extending the original RadioButtonList control and modifying the core databinding method to honor ListItem objects.
It goes something like this:
public class MyRadioButtonList : RadioButtonList
{
protected override void PerformDataBinding(IEnumerable dataSource)
{
if (dataSource is IEnumerable<ListItem> listItems)
{
...
foreach (var listItem in listItems)
Items.Add(listItem);
...
}
else
{
base.PerformDataBinding(dataSource);
}
}
}
With this in place, it was just a matter of converting my source model into a IEnumerable<ListItem> on the presentation layer (easy to accomplish with an adapter/proxy implementation) and then feed these ListItems to the control.
Once I got this in place, I could see my selected items reflected correctly in the UI based on the datasource field. Considering how trivial the extension is, I feel it was quite worth it :)
The same inheritance approach can probably be used for similar controls like CheckBoxList, which suffers from the very same limitation.
For the more adventurous folks, one could also make this work by introducing extra DataSelectedField and DataEnabledField properties in the control and using Eval on top of them as part of the original databinding algorithm (which already does this with DataTextField and DataValueField). I felt this would be a little bit more involved for my use case and decided to go with a simpler override, but it is definitely a valid approach that could even live along my proposed solution for an even more robust RadioButtonList.

AjaxControlToolkit AutoCompleteExtender ... how to fill from linq query?

Go easy... I'm a newbie at this.
Ok, I've added the AutoCompleteExtender to my webpage. The user will add search tags to a project, and I want the textbox to autocomplete with tags that already exist in the database.
I don't have a "registry" for tags; only a table with a tagName and a projectID. So, a tagName might be repeated many times in the table. So I just want to return distinct results in my query. (That's easy.)
But how do I tie it to the AutoCompleteExtender? I'm not well versed in WebServices, etc...
I'm using entity framework, fyi...
Here's my autocomplete code on the aspx page:
<asp:TextBox ID="TagNameTextBox" runat="server"></asp:TextBox>
<ajaxToolkit:AutoCompleteExtender ID="TagNameTextBox_AutoCompleteExtender"
runat="server"
ServiceMethod="GetCompletionList"
MinimumPrefixLength="2"
EnableCaching="false"
DelimiterCharacters=""
Enabled="True"
TargetControlID="TagNameTextBox">
</ajaxToolkit:AutoCompleteExtender>
And here's my linq query:
var qrygettags = (from t in db.TagTables
select new { t.TagName }).Distinct().ToString();
I found a few examples of a jquery solution, too, but I don't know how to get my query into that format. Any help or ideas would be greatly appreciated!
Well, I kept hunting around and found this seemingly easy solution, and it works for me.
in the aspx page, in the control, I have
ServiceMethod="GetTagNames"
and in the cs page I added this in the page load:
[System.Web.Services.WebMethod]
public static string[] GetTagNames(string prefixText, int count)
{
mydatabase db = new mydatabase();
return db.TagTables.Where(n => n.TagName.StartsWith(prefixText)).OrderBy(n => n.TagName).Select(n => n.TagName).Distinct().Take(count).ToArray();
}
Hopefully it will help someone else out there!

Repeater template control with custom events

I'm writing item template for repeater in separate control, and then I'm using following code:
HospitalRepeater.DataSource = LocationsList;
HospitalRepeater.ItemTemplate = Page.LoadTemplate("~/UserControls/HospitalDetails.ascx");
HospitalRepeater.DataBind();
This code worked fine, however now we want to add custom events to HospitalDetails control.
We created following event with custom event args:
public class HospitalItemEventArgs : EventArgs
{
public Int32 HospitalID { get; set; }
public HospitalItemEventArgs() { }
public HospitalItemEventArgs(Int32 hID)
{
this.HospitalID = hID;
}
}
public event EventHandler<HospitalItemEventArgs> HospitalAction;
protected virtual void OnHospitalAction(HospitalItemEventArgs e)
{
if (HospitalAction!= null)
this.HospitalAction(this, e);
}
Now here is a problem - I can't access this custom event from my code after loading this control as template, because it returns object of System.Web.UI.ITemplate.
I assumed that this is wrapper above my exact control, but this assumtion is wrong.
Cast to my control type fails with following error message:
Unable to cast object of type 'SimpleTemplate' to type 'UserControls.HospitalDetails'.
I've tried reverse action - load control using
Page.LoadControl("~/UserControls/HospitalDetails.ascx");
It returns object of correct HospitalDetails type, but It does not implement ITemplate interface.
Whan I tried to do that I've received error message:
Unable to cast object of type 'ASP.usercontrols_hospitaldetails_ascx' to type 'System.Web.UI.ITemplate'.
Can anyone help me to deal with this cast, or find another solution which matches following requrements:
Repeater should be bound to list of HospitalItems to display details.
Template for displaying details should hide it's UI interactions and expose few simple events like HospitalAction with hospital ID.
Separate object must have ability to subscribe to this events.
Your best bet is probably to create some <asp:Button /> controls specifying the CommandName and CommandArgument attributes. You can then handle the bubbled even on the repeater itself, interrogate the arguments for CommandName and CommandArgument.
I'm not using a separate control as a template but the same thing can be accomplished like this.
<asp:Repeater ID="myRepeater" runat="server">
<ItemTemplate>
<asp:Button ID="SaveButton"
CommandName="Save"
CommandArgument="{insert hospitol ID here}"
Text="Save"
runat="server" />
</ItemTemplate>
</asp:Repeater>
void myRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Save")
{
int id = int.Parse(e.CommandArgument.ToString());
//Do some saving
}
}
Update
The purpose for the button's CommandArgument and CommandName attributes is to allow you to provide the user with individual actions to take on databound ui content. The CommandArgument attribute is to allow you a point of entry back into the data to retrieve the relevant information for the event. I have never tried to put multiple values in this attribute, but I do not see a reason why it would not work.
Another option is to create a separate list that contains HospitalID and DoctorID associations, create a unique ID for each association and store that list in a database (if you need to persist it), cache (if used by multiple users), session (if used by multiple pages by same user) or Viewstate (if used by a single page and the list is relatively short).

How do I get many property values from View to Presenter in WebFormsMvp?

What is the best way to get a number of property values of a business object from the View to the Presenter in a WebFormsMvp page?
Bearing in mind this issue with DataSources.
Here is what i propose:
The scenario is, I have a business object called Quote which i would like to load form the database, edit and then save. The Quote class has heaps of properties on it. The form is concerned with about 20 of these properties. I have existing methods to load/save a Quote object to/from the database. I now need to wire this all together.
So, in the View_Load handler on my presenter i intend to do something like this:
public void View_Load(object sender, EventArgs e)
{
View.Model.Quote = quoteService.Read(quoteId);
}
And then bind all my controls as follows:
<asp:TextBox ID="TotalPriceTextBox" runat="server"
Text="<%# Model.Quote.TotalPrice %>" />
All good, the data is on the screen.
The user then makes a bunch of changes and hits a "Submit" button. Here is where I'm unsure.
I create a class called QuoteEventArgs exposing the 20 properties the form is able to edit. When the View raises the Submit button's event, I set these properties to the values of the controls in the code behind. Then raise the event for the presenter to respond to. The presenter re-loads the Quote object from the database, sets all the properties and saves it to the database.
Is this the right way to do this? If not, what is?
"A nicer way" (/alternative) is to make use of the 2-way binding, therefore what will be passed back to the Presenter for processing will be your Quote object.
This can be achieved through the use of an asp:FormView in conjunction with the mvp:PageDataSource that specifies an UpdateMethod and the Bind() method.
The WebFormsMVP sample project demonstrates this via the 'EditWidgetControl', including the methods required on the View code-behind file.
As an option your view can simply implement only the EditItemTemplate for asp:FormView making use of DefaultMode="Edit" on the FormView.
Sample Structure:
<asp:FormView DataSourceID="theSource" DefaultMode="Edit">
<EditItemTemplate>
<fieldset>
<asp:TextBox id="totp" value='<%# Bind("TotalPrice") %>' runat="server" />
</fieldset>
</EditItemTemplate>
</asp:FormView>
<mvp:PageDataSource ID="theSource" runat="server"
DataObjectTypeName="Your.NameSpace.Quote"
UpdateMethod="UpdateQuote">
</mvp:PageDataSource>
Code-behind:
public void UpdateQuote(Quote q, Quote ori)
{
OnUpdatingQuote(q, ori);
}
public event EventHandler<UpdateQuoteEventArgs> UpdatingQuote;
private void OnUpdatingQuote(Quote q, Quote ori)
{
if (UpdatingUserGroup != null)
{
UpdatingUserGroup(this, new UpdateQuoteEventArgs(q, ori));
}
}
How to use the GridView inside a FormView.
Because I have list to populate the grid in a entity.

ASP.NET MVC. No idea how to use Url.action to pass an object to the controller

I am new to asp.net MVC. I was able to create my view and display the data (Gridview). Additionally, I was able to create a hyperlink (using Url.Action) passing string and int types. However, I want to create a hyperlink that it is referencing a more complex type. The class associated with my view has a reference to a List. What I want is to create an additional ActionResult in my controller that gets as a parameter List (See below)
public ActionResult ViewItems(List<Items> c)
{
return View(c);
}
My idea is when is to be able to pass that List to the controller and then the controller will call the corresponding view. I tried (See below) but I just get blank.
<asp:HyperLink ID="LinkContractID" runat="server" NavigateUrl='<%#Url.Action("ViewItems", new {c = **((Contract)Container.DataItem).ContractItems.ToList<Items>(**)}) %>'
Text='<%# Eval("ContractId") %>'></asp:HyperLink>
Like in the previous answer, you don't use asp controls. There are pros and cons with Html.ActionLink however, it isn't so good if you want to put a link around an image for instance. In this case the syntax would be
<a href="<%= Url.Action(
"ShowListPage", "MyController", new { modelId = 101 }) %>">
<img src="img.gif" />
</a>
Also with your action in the controller, you would ideally be looking to have this go and get the model to pass to a view strongly typed to this model. So you have a model object with a constructor taking an id, for instance
public MyModel(int modelId)
{
this.TheListThatHoldsTheGridData = MyDataLayerProc(modelId);
}
This way you can have your action in the MyController controller, return the view ShowListPage (associated with a MyModel instance) like so
public ActionResult ShowListPage(int modelId)
{
return View(new MyModel(modelId));
}
Hope this helps,
Mark
If you are looking for a grid, this tutorial shows how to create a grid with MVC.
With MVC, you shouldn't use Gridview and asp: controls. If you want to generate a link, just use <%=Html.ActionLink(...) %> with the necessary parameters.

Resources