I am using ASP.NET MVC 4 Runtime, and writing encrypted data to my database using a function.
public string Encrypt(string encryptMe)
{
return Helpers.Encryptor.Encrypt(encryptMe);
}
On return the data is displayed like so:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Phone)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
I want to decrypt the data before displaying it here using the following function:
public static string Decrypt(string decryptMe)
{
return Helpers.Encryptor.Decrypt(decryptMe); ;
}
How can I decrypt my data?
Add a Property in the model called for example "DecryptedPhone".
This will only have a Get and will call the decrypting function:
public string DecryptedPhone
{
get
{
return Decrypt(Phone);
}
}
private string Phone;
The phone will be private so that it cannot be accessed.
In your controller's method, before the return View(model); line, you can decrypt the properties. It would be something like this:
foreach (var item in model)
{
item.Phone = Decrypt(item.Phone);
item.Name = Decrypt(item.Name);
}
return View(model);
I need to populate a set of repeater drop down list items from a custom class but cannot seem to figure it out. It works perfectly when I use just a standard ListItem collection, but for some reason my custom class only returns a bunch of empty drop down lists. I'm new to repeaters and am trying to understand them the best I can, but this one kind of has me beat. Code is shown below. I have a base class that contains a child class, the child class is used to populate the drop down lists. When ListItemOptions is set to just a List<ListItem>', the actual type ofListItem', it works perfect. Why doesn't my repeater like my class?
// My base class. FYI: there are about 20 other properties in this class. I'm not just aimlessly jumping to a child class for fun!
public class ConfiguratorOption
{
private List<ListItemOptions> _listItemOptions = new List<ListItemOptions>();
public ConfiguratorOption() { }
public List<ListItemOptions> ListItemOptions { get { return _listItemOptions; } set { _listItemOptions = value; } }
public void AddListItemOption(string nodeId, string value, string displayName)
{
ListItemOptions lioNew = new ListItemOptions();
NodeID = nodeId;
Value = value;
DisplayName = displayName;
ListItemOptions.Add(lioNew);
}
}
// The child class.
public class ListItemOptions : IEnumerable
{
private string _nodeID = string.Empty;
private string _displayName = string.Empty;
private string _value = string.Empty;
public ListItemOptions() { }
public string NodeID { get { return _nodeID; } set { _nodeID = value; } }
public string Value { get { return _value; } set { _value = value; } }
public string DisplayName { get { return _displayName; } set { _displayName = value; } }
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
}
// The infamous repeater...
<asp:Repeater runat="server" ID="rptConfigurationOptions">
<ItemTemplate>
<div class="ConfigurationBlock">
<asp:DropDownList ID="ddnOption" runat="server" CssClass="DropDownStandard" style="width: 500px;" DataTextField="DisplayName" DataValueField="Value" AutoPostBack="true"
DataSource='<%# Eval("ListItemOptions") %>' OnSelectedIndexChanged="ddnOption_SelectedIndexChanged" ></asp:DropDownList>
</div>
</ItemTemplate>
</asp:Repeater>
// And the repeater code..
private void BindRepeater<T>(Repeater rpt, List<T> cOp)
{
rpt.Controls.Clear();
rpt.DataSource = cOp;
rpt.DataBind();
}
You can't use Eval this way. Eval will essentially evaluate the property and return a string. In your case that could render out looking like:
<asp:dropdown DataSource='List<ListItemOptions>'/>
Don't set the datasource via Eval. Handle the repeater's ItemDataBound event and set the DropDownList's datasource manually to the actual property.
I am using MVC 4 Visual Studio 2012 with Razor.
I am generating a table based off of several tables pulled into a dataset from a remote call to a SQL server.
I want to be able to output these tables onto the webpage and then create two columns of checkboxes beside them in order to assign them to one area or another (it essentially sorts data into accepted and not accepted, while allowing some to continue pending if a decision has not been made).
I currently have all the tables in the dataset being assigned to datatables in the controller and then exported to the razor page. I do not have a model set up for this as of yet and I'm not sure what I would require in one if I did.
This is my current View:
#{
ViewBag.Title = "Requisitions";
}
<table class="table">
<thead>
<tr>
#foreach (System.Data.DataColumn col in Model.Columns)
{
<th class ="td">#col.Caption</th>
}
</tr>
</thead>
<tbody>
#foreach(System.Data.DataRow row in Model.Rows)
{
<tr>
#foreach (var cell in row.ItemArray)
{
<td class="td">#cell.ToString()</td>
}
</tr>
}
</tbody>
</table>
This is my current controller:
DataTable R = new DataTable();
public void GetData()
{
string connString = "Data Source=.;database=dataBase;Integrated Security=SSPI";
DataSet dataset = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter("dbo.procApprovalSelectPending", connString);
using (adapter)
{
adapter.SelectCommand.CommandType = CommandType.StoredProcedure;
adapter.Fill(dataset);
}
int count = dataset.Tables.Count;
for (int i = 0; i < dataset.Tables.Count; i++)
{
// Do something for each recordset (11 recordsets)
if (i == 0)
{
R = dataset.Tables[i];
}
}
dataset.Dispose();
adapter.Dispose();
}
public ActionResult Rs()
{
GetData();
return View(R);
}
I have more datatables than shown but they were removed to conserve space.
To sum it up, I would like two checkbox columns that are aligned with each row that is created from the datatable that allow me to choose that portion of data to send back to the server what has been changed with it, and creating a dynamic checkbox for each row was giving me an error such that :
#foreach(System.Data.DataRow row in Model.Rows)
{
<tr>
#foreach (var cell in row.ItemArray)
{
<td class="td">#cell.ToString()</td>
}
<td class="td">#Html.CheckBoxFor(m => m.Checkbox)</td>
</tr>
}
"An expression tree may not contain a dynamic operation"
Thanks in advance!
You should create a view model to represent the properties you want to display, including 2 additional boolean properties for 'accepted' and 'not accepted'. Assuming your columns are ID and Name
public class MyModel
{
public int ID { get; set; }
public string Name { get; set; }
public bool Accepted { get; set; }
public bool NotAccepted { get; set; }
}
and create a collection based on each row in your table that you want to display
public ActionResult Rs()
{
List<MyModel> items = new List<MyModel>();
// Populate items from your datatable
return View(items);
}
then in your view
#model List<MyModel>
#using (Html.BeginForm()
{
<table>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m[i].ID)</tr>
<td>#Html.TextBoxFor(m => m[i].Name)</tr>
<td>#Html.CheckBoxFor(m => m[i].Accepted)</tr>
<td>#Html.CheckBoxFor(m => m[i].NotAccepted)</tr>
</tr>
}
</table>
<input type="submit" value="Save" />
}
then in your post method
[HttpPost]
public ActionResult Rs(List<MyModel> model)
{
foreach(MyModel item in model)
{
if(item.Accepted) {.. // do something
else if (item.NotAccepted) { .. // do something else
else {.. // do another thing
}
}
Use Following pattern
I have Created one static Class that is called as CommonUtilities it is giving me FormCollection,Checkboxname and valueofKey
public static class CommonUtilities
{
public static List<string> GetCheckboxSelectedValue(FormCollection frm, string checkboxname, string value)
{
List<string> retls = new List<string>();
var fileIds = frm[value].Split(',');
var selectedIndices = frm[checkboxname].Replace("true,false", "true").Split(',').Select((item, index) =>
new {
item= item,
index= index
}).Where(row=>row.item =="true")
.Select(row => row.index).ToArray();
if (selectedIndices.Count() > 0)
{
retls.AddRange(selectedIndices.Select(index => fileIds[index]));
}
return retls;
}
}
the above Function will be applied as below
Controller
List<string> selectedKeypair = CommonUtilities.GetCheckboxSelectedValue(frm, "m.BoolSelectedVal", "m.Key");
view
#foreach (MVCCareerModule.Models.Requirement m in Model.RequirementSearchResult)
{
<tr>
<td>
#Html.CheckBoxFor(modelitem => m.Apply)
#Html.HiddenFor(modelitem => m.Req_ID, new { Value = m.Req_ID })
</td>
</tr>
}
You will receive all Selected IDs in selectedKeypair
I want to create a reusable way to use the excellent jQuery datatables plugin with .Net MVC for all my views that contain tables. Now, I already worked out how server side processing with this plugin works and how I need to write my controller logic to provide the appropriate response.
The problem starts with rendering html though. Say for example that I need to render a picture next every person's name in a 'people table'. However, when I type in the search box, it should still correctly search by the names of those people.
Now, with the Razor view engine this is easy as pie, you could write something like this:
#foreach (var person in people)
{
<tr>
<td>
<span>#person.Name</span>
<img src="#person.ImgUrl" title="#person.Name"/>
</td>
</tr>
}
The problem is that this needs to be rendered to JSON on the server side. You would have a Controller that handles the jQuery datatables AJAX requests and returns a valid JSON object containing everything the datatable needs. However, you can't access snippets from your view in your controller. The only option I see here is to create partial views for every column that needs to render some specific HTML. (For common use cases, like action buttons, I could write HTML helpers)
And there's the problem. I'm hesitant to create partial views for every table for every HTML column.
The issue here is that the controller needs to somehow access the display logic to create JSON, while the display logic is typically written in the view.
My options are limited to using partial views and rendering those to a string in the controller, or storing my display logic (which are lambda expressions) in some sort of server session or cache and fetching it from the controller. The big downside to this last approach is that sessions or cache seem too volatile to depend on.
So, to reiterate:
I want to show a table of entities using the jQuery datatable plugin with AJAX server side processing.
I want to write my display logic in my view, using the Razor view engine
I want to handle the jQuery datatable AJAX requests in my controller
I want to somehow ** access the display logic per column** written in my view from within my controller to construct the JSON response. This is because some columns can have custom HTML, load partials, etc.
I'm really quite stuck on this, if there are similar questions on Stackoverflow about this please point me to them because I haven't found them.
Thank you in advance for your help.
I'm not sure if I entirely follow the issue but I think you can solve this very easily. Instead of trying your controllers to reference the views which doesn't work, you should have jQuery pass the parameters that you need for the controller back in the Ajax request. Then the controller can use those parameters to send proper JSON response.
I think you are getting stuck on number 2:
I want to write my display logic in my view, using the Razor view engine
This is difficult to do with datatables.net because the display logic is in javascript. You could use a JS template plugin (like mustache) and write that in your view. Then in your datatables.net js you can plug the JSON into the templates.
This question is old, but it may help someone else who will also fight with this problem. I have very nice solution.
I have html helpers (AjaxTable, Column), which create an object describing the table (AjaxTable) while page load. This object is stored in session. All ajax requests are handled in common controller, which uses the stored object to create response. It supports paging, sorting and filtering.
Example usage:
in Controller:
public ActionResult Odeslane()
{
return View(db.Dotaz.Where( ... /* select all data which should be displayed in the table */ ));
}
in View:
#model IQueryable<Dotaz>
#using (Html.AjaxTable(Model, new { id = "dotazTable" }))
{
Html.Column(Model, #<th>#Html.DisplayNameFor(model => item.Autor.JmenoPrijmeniAUzivatelskeJmeno)</th>,
#<td>
<input type="hidden" name="id" value="#item.DotazId" />
#if (item.AutorId != null) {
#Html.DisplayFor(model => item.Autor.JmenoPrijmeniAUzivatelskeJmeno)
}
else
{
#Html.DisplayFor(model => item.AnonymMail)
}
</td>,
d => d.AutorId != null ? d.Autor.Jmeno + " " + d.Autor.Prijmeni + " (" + d.Autor.UserName + ")" : d.AnonymMail);
Html.Column(Model, #<th>#Html.DisplayNameFor(model => item.Nazev)</th>, #<td>#Html.DisplayFor(model => item.Nazev)</td>, d => d.Nazev );
Html.Column(Model, #<th>#Html.DisplayNameFor(model => item.DatumVzniku)</th>, #<td>#Html.DisplayFor(model => item.DatumVzniku)</td>, d => d.DatumVzniku );
}
Implementation:
the helpers:
public static IDisposable AjaxTable<T>(this HtmlHelper htmlHelper, IQueryable<T> items, object htmlAttributes) where T : new()
{
var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
// TODO: replace CommonAjaxActionUrl with url of the common ajax action
return new AjaxTableDisposable<T>(htmlHelper, items, CommonAjaxActionUrl, attrs);
}
public static MvcHtmlString Column<T, TKey>(this HtmlHelper htmlHelper, IEnumerable<T> items, Func<T, HelperResult> th, Func<T, HelperResult> td, Expression<Func<T, TKey>> keySelector) where T : new()
{
var obj = htmlHelper.ViewContext.TempData["AjaxTable"];
AjaxTableDisposable<T> table = obj as AjaxTableDisposable<T>;
table.Column(th, td, keySelector);
return MvcHtmlString.Empty;
}
AjaxTableDisposable:
public class AjaxTableDisposable<T> : IDisposable where T : new()
{
private HtmlHelper htmlHelper;
private ViewContext viewContext;
private AjaxTable<T> table;
public AjaxTableDisposable(HtmlHelper htmlHelper, IQueryable<T> items, string ajaxUrl, RouteValueDictionary attrs)
{
// akce na zacatku
this.htmlHelper = htmlHelper;
viewContext = htmlHelper.ViewContext;
viewContext.TempData["AjaxTable"] = this;
if (!attrs.ContainsKey("id")) attrs["id"] = "AjaxTable" + Guid.NewGuid().ToString("N");
table = new AjaxTable<T>() { AjaxUrl = ajaxUrl, Attrs = attrs, Items = items };
}
public void Column<TKey>(Func<T, HelperResult> th, Func<T, HelperResult> td, Expression<Func<T, TKey>> keySelector)
{
AjaxTableColumn<T> col = new AjaxTableColumn<T>() { Th = th, Td = td, KeySelector = keySelector, KeyType = typeof(TKey) };
col.OrderData = (IQueryable<T> data, bool asc) => asc ? data.OrderBy(keySelector) : data.OrderByDescending(keySelector);
table.Columns.Add(col);
}
// When the object is disposed (end of using block), write "end" function
public void Dispose()
{
// akce na konci
viewContext.TempData.Remove("AjaxTable");
viewContext.Writer.Write(htmlHelper.Partial("DisplayTemplates/AjaxTable", table));
string tableId = table.Attrs["id"].ToString();
StoreInSession(table); // TODO: you have to implement the StoreInSession method
}
}
AjaxTable template:
#model IAjaxTable
#{
RouteValueDictionary attrs = Model.GetAttrs();
string tableId = attrs["id"].ToString();
string cls = attrs.ContainsKey("class") ? " " + attrs["class"] : "";
}
<table id="#tableId" class="#{ ViewContext.Writer.Write(cls); }"#{
foreach (var attr in attrs)
{
if (attr.Key != "id" && attr.Key != "class")
{
ViewContext.Writer.Write(" "+attr.Key+"="+"\""+attr.Value+"\"");
}
}
}>
<thead>
<tr>
#for (int i = 0; i < Model.GetColumnsCount(); i++ )
{
Model.GetTh(i).WriteTo(ViewContext.Writer);
}
</tr>
</thead>
<tbody></tbody>
</table>
<script type="text/javascript">
$(document).ready(function () {
var dt = $('##tableId').DataTable({
serverSide: true,
ajax: function (data, callback, settings) {
data["tableId"] = "#tableId";
$.ajax({
dataType: 'json',
url: "#Model.GetAjaxUrl()",
type: "POST",
success: callback,
data: data
});
},
...
});
});
</script>
AjaxTable class:
public class AjaxTable<T> : IAjaxTable where T : new()
{
public List<AjaxTableColumn<T>> Columns { get; set; }
public T Row { get; set; }
public String AjaxUrl { get; set; }
public RouteValueDictionary Attrs { get; set; }
public IQueryable<T> Items { get; set; }
public AjaxTable()
{
Columns = new List<AjaxTableColumn<T>>();
Row = new T();
}
public HelperResult GetTh(int column)
{
return Columns[column].Th.Invoke(Row);
}
public string GetAjaxUrl()
{
return AjaxUrl;
}
public RouteValueDictionary GetAttrs()
{
return Attrs;
}
public int GetColumnsCount()
{
return Columns.Count;
}
public object DefaultAjaxAction()
{
var total = Items.Count();
IQueryable<T> data = Search(Items, ParamSearchValue, ParamSearchRegex);
var filtered = data.Count();
data = Columns[ParamOrderColumn].OrderData(data, ParamOrderDirAscending);
data = data.Skip(ParamStart).Take(ParamLength);
return CreateAjaxResponse(data, total, filtered);
}
public IQueryable<T> Search(IQueryable<T> data, string search, bool regex)
{
if (search == null || search == "") return data;
Expression orExpression = null;
IReadOnlyCollection<ParameterExpression> parameters = null;
foreach (var col in Columns)
{
if (col.KeyType == typeof(string))
{
Expression<Func<T, string>> keySelector = (Expression<Func<T, string>>) col.KeySelector;
Expression compare = Expression.Call(keySelector.Body, typeof(String).GetMethod("Contains"), Expression.Constant(search));
if (orExpression == null)
{
orExpression = compare;
parameters = keySelector.Parameters;
}
else
{
orExpression = Expression.OrElse(compare, orExpression);
}
}
}
if (orExpression != null)
{
Expression<Func<T, bool>> whereExpr = Expression.Lambda<Func<T, bool>>(orExpression, parameters);
UnifyParametersVisitor visitor = new UnifyParametersVisitor();
whereExpr = visitor.UnifyParameters(whereExpr);
data = data.Where(whereExpr);
}
return data;
}
public object CreateAjaxResponse(IQueryable<T> data, int recordsTotal, int recordsFiltered)
{
Dictionary<string,object> obj = new Dictionary<string,object>();
obj.Add("draw", HttpContext.Current.Request.Params["draw"]);
obj.Add("recordsTotal", recordsTotal);
obj.Add("recordsFiltered", recordsFiltered);
List<T> dataList = data.ToList();
String[][] cell = new String[dataList.Count()][];
int rowIndex = 0;
foreach (T row in dataList)
{
cell[rowIndex] = new String[Columns.Count];
int colIndex = 0;
foreach (var column in Columns)
{
StringWriter sw = new StringWriter();
column.Td.Invoke(row).WriteTo(sw);
cell[rowIndex][colIndex++] = sw.ToString();
sw.Dispose();
}
rowIndex++;
}
obj.Add("data", cell);
return obj;
}
public int ParamStart { get { return Int32.Parse(HttpContext.Current.Request.Params["start"]); } }
public int ParamLength { get { return Int32.Parse(HttpContext.Current.Request.Params["length"]); } }
public int ParamOrderColumn { get { return Int32.Parse(HttpContext.Current.Request.Params["order[0][column]"]); } }
public bool ParamOrderDirAscending { get { return HttpContext.Current.Request.Params["order[0][dir]"] == "asc"; } }
public string ParamSearchValue { get { return HttpContext.Current.Request.Params["search[value]"]; } }
public bool ParamSearchRegex { get { return HttpContext.Current.Request.Params["search[regex]"] == "true"; } }
}
AjaxTableColumn class:
public class AjaxTableColumn<T>
{
public Func<T, HelperResult> Th { get; set; }
public Func<T, HelperResult> Td { get; set; }
public Expression KeySelector { get; set; } // typ: Expression<Func<T,?>>, typicky neco jako: (T t) => t.Key
public Type KeyType { get; set; }
public OrderDataDelegate OrderData { get; set; }
public delegate IQueryable<T> OrderDataDelegate(IQueryable<T> data, bool asc);
}
IAjaxTable interface:
public interface IAjaxTable
{
HelperResult GetTh(int column);
string GetAjaxUrl();
RouteValueDictionary GetAttrs();
int GetColumnsCount();
object DefaultAjaxAction();
}
UnifyParametersVisitor:
public class UnifyParametersVisitor : ExpressionVisitor
{
private IEnumerable<ParameterExpression> parameters;
public Expression<TDel> UnifyParameters<TDel>(Expression<TDel> expression)
{
parameters = expression.Parameters;
return (Expression<TDel>) Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression node)
{
foreach (var param in parameters)
{
if (param.Name == node.Name)
{
return param;
}
}
return base.VisitParameter(node);
}
}
Controller for creating ajax response - idea: you have to find the AjaxTable instance in the session by tableId, then return Json(ajaxTable.DefaultAjaxAction()). Ensure that session is cleared when user logs out.
UPDATE:
Today I encountered an limitation of my approach: you can't use some html helpers inside a table cell (working with templates, render partial view...), but it can be solved! You have to create new instance of htmlHelper for each http request and use it instead of standard "Html", e.g. with this html helper:
public static HtmlHelper<object> CurrentHtml(this HtmlHelper html)
{
var ch = (HtmlHelper<object>)HttpContext.Current.Items["currentHtml"];
if (ch == null)
{
var context = new HttpContextWrapper(HttpContext.Current);
var routeData = new RouteData();
routeData.Values["controller"] = "Home"; // you can use any controller here
var controllerContext = new ControllerContext(new RequestContext(context, routeData), new HomeController());
var view = ViewEngines.Engines.FindView(controllerContext, "Index", null);
ch = new HtmlHelper<object>(new ViewContext(controllerContext, view.View, new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null), new ViewPage());
HttpContext.Current.Items["currentHtml"] = ch;
}
return ch;
}
I have created a UserControl with an Inner Property called "Actions", which is a List of "Action" objects. The code looks like this:
[ParseChildren(true)]
public class MyLink : UserControl
{
readonly List<Action> _actions = new List<Action>();
[PersistenceMode(PersistenceMode.InnerProperty)]
public List<Action> Actions
{
get { return _actions; }
}
public string Text { get;set; }
public string Url { get;set; }
public string MenuName { get; set; }
protected override void Render(HtmlTextWriter writer)
{
//Build link
StringBuilder sb = new StringBuilder();
sb.Append(#"
<table class=""myLink"">
<tr>
<td class=""myLinkLeft"">" + Text + #"</td>
<td class=""myLinkRight " + MenuName + #"_trigger""> </td>
</tr>
</table>
");
//Build actions
sb.Append("<ul id=\"" + MenuName + "_actions\" class=\"contextMenu\">");
foreach (Action action in _actions)
{
sb.Append("<li class=\"" + action.CssClass + "\">" + action.Text + "</li>");
}
sb.Append("</ul>");
writer.Write(sb.ToString());
}
}
public class Action : UserControl
{
public string Url { get; set; }
public string Text { get; set; }
public string ImageUrl { get; set; }
public string CssClass { get; set; }
}
If I then put this code in my aspx inside a DataRepeater, it works fine:
<uc1:MyLink runat="server" Url="/" Text='<%#DataBinder.Eval(Container.DataItem,"Text") %>' MenuName="contextMenu" id="contextMenu">
<Actions>
<uc1:Action runat="server" Url="http://mysite.com" Text="MyUrl" />
<uc1:Action runat="server" Url="http://google.com" Text="Google" />
</Actions>
</uc1:MyLink>
However, if I try to bind data to the attributes of the Action elements like so:
<uc1:MyLink runat="server" Url="/" Text='<%#DataBinder.Eval(Container.DataItem,"Text") %>' MenuName="contextMenu" id="contextMenu">
<Actions>
<uc1:Action runat="server" Url='<%#DataBinder.Eval(((RepeaterItem)Container.Parent).DataItem,"Url") %>' Text="MyUrl" />
<uc1:Action runat="server" Url="http://google.com" Text="Google" />
</Actions>
</uc1:MyLink>
I merely get the actual text "<%#DataBinder.Eval(((RepeaterItem)Container.Parent).DataItem,"Url") %>" assigned to the Url property, and not the evaluated server expression as I expected.
I've googled this for hours but cannot seem to find anybody else trying to do this. Any ideas why this isn't working and how to get around it?
Thanks,
Bjoern
you set the DataRepeater.Datasource in your aspx to a collection or a list static or get from database ...
instead of using DataRepeater try to make a loop inside that list you re already must create it and create new dynamic Action and that in page_load or page_init
Action a;
foreach(object x in objects)
{
a= new Action();
a.Url = ... ;
a.Text = ... ;
MyLink.Actions.Add(a);
}
Regards
I ended up using tokens for the innermost databinding and then handling the replacement in my control on bind. So the ASPX code looks like this:
<uc1:MyLink runat="server" Url="/">
<Actions>
<uc1:Action Url="/Page.aspx?cpr=##cpr##&opgaveId=##id##" />
<uc1:Action Url="/Test.aspx" />
</Actions>
</uc1:MyLink>
And the added CS code like this:
protected override void OnInit(EventArgs e)
{
DataBinding += BindData;
}
public void BindData(object sender, EventArgs e)
{
MyLink pl = (MyLink) sender;
IDataItemContainer container = (IDataItemContainer) pl.NamingContainer;
foreach (Action action in _actions)
{
action.Url = ReplaceTokens(action.Url, container);
action.Text = ReplaceTokens(action.Text, container);
}
}
private static string ReplaceTokens(string text, IDataItemContainer container)
{
Regex re = new Regex("##.*?##", RegexOptions.Compiled | RegexOptions.Singleline);
StringBuilder sb = new StringBuilder(text);
foreach (Match m in re.Matches(text))
{
string tokenValue = DataBinder.GetPropertyValue(container.DataItem, m.Value.Substring(2, m.Value.Length - 4), "{0}");
sb.Replace(m.Value, tokenValue);
}
return sb.ToString();
}