I am trying to get the display name of an item in a droplink in the back-end C# code. I am using Sitecore 6.6, not using MVC, and am setting a droplink control in the CMS for clients called Address. The droplink source goes to /sitecore/Templates/User Defined/WAC/Address, and the individual items have an SEO-compliant name and a readable display name.
For example:
Item ID: {9E60F5F8-FBF2-4CBD-BB13-6A93397AAC87}
Name: 100-main-street
Display Name: 100 Main Street, Sample Town, 10011
My code:
protected void Page_Load(object sender, EventArgs e)
{
String sl = "";
Sitecore.Data.Items.Item currentItem = Sitecore.Context.Item;
// BEGIN main class list
Sitecore.Collections.ChildList classList = currentItem.Children;
foreach (Sitecore.Data.Items.Item mainPage in classList)
{
if (mainPage.TemplateID.ToString() == "{27A9692F-AE94-4507-8714-5BBBE1DB88FC}")
{
sl += "<span class=\"address\">" + mainPage.Fields["Address"] +"</span>";
}
else
{
}
}
// END main class list
classSessionList.Text = sl;
}
This code will give me the ID of the Item. If I use mainPage.Fields["Address"].DisplayName, I get "Address".
How can I get the Display Name of the item from the droplink?
Use LookupField for getting reference item below are the sample code:
LookupField address= (LookupField)mainPage.Fields["Address"];
Item addressItem = address.TargetItem;
string displayName = addressItem.Fields["DisplayName"].Value;
If you want it in one line then use below code:
((LookupField)mainPage.Fields["Address"]).TargetItem.DisplayName
Type cast the field to a ReferenceField. Then access the TargetItem property:
sl += "<span class=\"address\">" + ((ReferenceField)mainPage.Fields["Address"]).TargetItem.DisplayName +"</span>";
The thomas answer would work based on your code. But I would suggest you also to try to stick with ASP.Net and Sitecore server components.
That will avoid Null Reference errors, will support Page Editor better and also makes your code easier to maintain.
You can have a repeater in your markup like this:
<asp:Repeater ID="rptAdresses" runat="server">
<ItemTemplate>
<span class="address">
<sc:Text id="scAddress" runat="server" Field="__Display Name" Item="<%#(Sitecore.Data.Items.Item)Container.DataItem%>"></sc:Text>
</span>
</ItemTemplate>
</asp:Repeater>
And then bind the address on your code behind:
private void BindRepeater()
{
if (mainPage.TemplateID.ToString() != "{27A9692F-AE94-4507-8714-5BBBE1DB88FC}")
{
rptAdresses.Visible = false;
}
else
{
rptAdresses.DataSource = Sitecore.Context.Item.GetChildren();
rptAdresses.DataBind();
}
}
Another point that I notice was the line mainPage.TemplateID.ToString() != "{27A9692F-AE94-4507-8714-5BBBE1DB88FC}". That is a point that you also could improve. Hard coded IDs are not a good pratice. You could create a class to hold those things or, even better, you could think more about your design to avoid it.
cheers
All other solutions are perfect, but you can also use the code below:
var itemID=mainPage.Fields["Address"].value;
Item targetItem=Sitecore.Context.Database.GetItem(itemID);
if (mainPage.TemplateID.ToString() == "{27A9692F-AE94-4507-8714-5BBBE1DB88FC}")
{
sl += "<span class=\"address\">" + targetItem.DisplayName +"</span>";
}
else
{
}
With this approach you can use every field of target item.
Related
If i have method like this to Draw my side Menu Dynamically :
private void DrawSideMenu()
{
LinkButton x;
TaskDTO TaskList = new TaskDTO();
List<TaskDTO> List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
HtmlGenericControl myDIV = new HtmlGenericControl("div");
myDIV.ID = "menu8";
HtmlGenericControl myOrderedList = new HtmlGenericControl("ul");//css clss for <ul>
myOrderedList.ID = "orderedList";
myOrderedList.Attributes.Add("class", "task");
HtmlGenericControl listItem1;
string count = "";
foreach (TaskDTO i in List)
{
count = AdjustMenuCount1(i.TaskCode);
x = new LinkButton();
x.ID = i.TaskCode.ToString();
x.Text = i.TaskName + " " + count;
x.Click += new EventHandler(TaskC);
x.Style["FONT-FAMILY"] = "tahoma";
listItem1 = new HtmlGenericControl("li");
listItem1.Attributes.Add("class", "normal");
if (count != "0")
{
listItem1.Controls.Add(x);
myOrderedList.Controls.Add(listItem1);
}
}
myDIV.Controls.Add(myOrderedList);
MenuTD.Controls.Add(myDIV);
Session["SideMenu"] = myDIV;//Save to redraw when page postbacks
}
This Method takes long time to draw my menu.so i call it one time in (!IsPostBack) and save it in session so that i could redraw it like that :
MenuTD.Controls.Add( ((System.Web.UI.Control)(Session["SideMenu"])));
It redraws it successfully but when i click on any link it doesn't hit the event because i thought it's not possible to save the x.Click += new EventHandler(TaskC); in the session ,so i want to know how to loop through my session content to resetting the delegate of my link ?
That idea won't work because if you're not wiring up the Event Handler every time the page is loaded, it won't run.
If we come back to the original issue, you said it's slow. Creating controls at runtime cannot be slow and it's most likely the way you create your list of items:
List<TaskDTO> List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
Instead of storing complete menu, try to store in the Session only List and create all controls as usual. If menu is required on one page only, then use ViewState instead of Session.
Also it makes sense to change the entire code as currently you hardcode all style and layout settings in the code. Create all layout (div, ul, li) in aspx, move all styles in css (for example, you use "task" class but still set "tahoma" in the code). This would simplify the code and bring more flexibility.
List<TaskDTO> List = null;
void Page_Load(object sender, EventArgs e)
{
if (ViewState["List"] != null) {
List = (List<TaskDTO>)ViewState["List"];
} else {
// ArrayList isn't in view state, so we need to load it from scratch.
List = TaskList.DrawMenu(int.Parse(Session["emp"].ToString()));
}
// Code to create menu, e.g.
if (!Page.IsPosBack) {
Repeater1.DataSource = List;
Repeater1.DataBind();
}
}
void Page_PreRender(object sender, EventArgs e)
{
// Save PageArrayList before the page is rendered.
ViewState.Add("List", List);
}
...
<ul id="orderedList">
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<li><%# Eval("TaskName") %></li>
</ItemTemplate>
</asp:Repeater>
</ul>
Maybe save it in application level so it only gets built once, then just put the menu into an object and loop through it to re-add the clicks.
I'm afraid that in order for it to work you are going to have to rebind the Click handler on every Page_Load.
Based on your code, and assuming your TaskC is available, you can make this method:
private void RebindMenuHandlers() {
if(Session["SideMenu"] == null)
return; // Your menu has not been built yet
var menu = ((System.Web.UI.Control)(Session["SideMenu"]));
var orderedList = menu.Controls[0];
foreach(var listItem in orderedList){
foreach(var control in listItem){
var linkButton = control as LinkButton;
if(linkButton != null){
linkButton.Click += new EventHandler(TaskC);
}
}
}
}
Then call it on your Page_Load event:
void Page_Load(object sender, EventArgs e)
{
RebindMenuHandlers();
// .... etc
}
I just typed this directly here, so please forgive any silly compilation mistakes, this should be enough to give you the general idea. Hope that helps.
I am trying to filter an listbox with text from a textbox, realTime.
Here is the code:
private void SrchBox_TextChanged_1(object sender, EventArgs e)
{
var registrationsList = registrationListBox.Items.Cast<String>().ToList();
registrationListBox.BeginUpdate();
registrationListBox.Items.Clear();
foreach (string str in registrationsList)
{
if (str.Contains(SrchBox.Text))
{
registrationListBox.Items.Add(str);
}
}
registrationListBox.EndUpdate();
}
Here are the issues:
When I run the program i get this error: Object reference not set to an instance of an object
If I hit backspace, my initial list is not shown anymore. This is because my actual list of items is now reduced, but how can I achieve this?
Can you point me in the right direction?
It's hard to deduct just from the code, but I presume your filtering problem born from the different aspects:
a) You need a Model of the data shown on ListBox. You need a colleciton of "Items" which you hold somewhere (Dictionary, DataBase, XML, BinaryFile, Collection), some kind of Store in short.
To show the data on UI you always pick the data from that Store, filter it and put it on UI.
b) After the first point your filtering code can look like this (a pseudocode)
var registrationsList = DataStore.ToList(); //return original data from Store
registrationListBox.BeginUpdate();
registrationListBox.Items.Clear();
if(!string.IsNullOrEmpty(SrchBox.Text))
{
foreach (string str in registrationsList)
{
if (str.Contains(SrchBox.Text))
{
registrationListBox.Items.Add(str);
}
}
}
else
registrationListBox.Items.AddRange(registrationsList); //there is no any filter string, so add all data we have in Store
registrationListBox.EndUpdate();
Hope this helps.
Something like this might work for you:
var itemList = registrationListBox.Items.Cast<string>().ToList();
if (itemList.Count > 0)
{
//clear the items from the list
registrationListBox.Items.Clear();
//filter the items and add them to the list
registrationListBox.Items.AddRange(
itemList.Where(i => i.Contains(SrchBox.Text)).ToArray());
}
Yes that was the answer to filtering. (modified a bit). I had the info in a text file. This is what worked for me
FileInfo registrationsText = new FileInfo(#"name_temp.txt");
StreamReader registrationsSR = registrationsText.OpenText();
var registrationsList = registrationListBox.Items.Cast<string>().ToList();
registrationListBox.BeginUpdate();
registrationListBox.Items.Clear();
if (!string.IsNullOrEmpty(SrchBox.Text))
{
foreach (string str in registrationsList)
{
if (str.Contains(SrchBox.Text))
{
registrationListBox.Items.Add(str);
}
}
}
else
while (!registrationsSR.EndOfStream)
{
registrationListBox.Items.Add(registrationsSR.ReadLine());
}
registrationListBox.EndUpdate();
It seems that the error:
Object reference not set to an instance of an object
is from somewhere else in my code, can't put my finger on it.
If able, store everything in a dictionary and just populate it from there.
public partial class myForm : Form
{
private Dictionary<string, string> myDictionary = new Dictionary<string, string>();
//constructor. populates the items. Assumes there is a listbox (myListbox) and a textbox (myTextbox), named respectively
public myForm()
{
InitializeComponent();
myDictionary.Add("key1", "item1");
myDictionary.Add("key2", "My Item");
myDictionary.Add("key3", "A Thing");
//populate the listbox with everything in the dictionary
foreach (string s in myDictionary.Values)
myListbox.Add(s);
}
//make sure to connect this to the textbox change event
private void myTextBox_TextChanged(object sender, EventArgs e)
{
myListbox.BeginUpdate();
myListbox.Items.Clear();
foreach (string s in myDictionary.Values)
{
if (s.Contains(myListbox.Text))
myListbox.Items.Add(s);
}
myListbox.EndUpdate();
}
}
I would do it like this:
private List<string> registrationsList;
private void SrchBox_TextChanged_1(object sender, EventArgs e)
{
registrationListBox.BeginUpdate();
registrationListBox.Items.Clear();
var filteredList = registrationList.Where(rl => rl.Contains(SrchBox.Text))
registrationListBox.Items.AddRange();
registrationListBox.EndUpdate();
}
Just remember to populate registrationsList the first time you fill your listbox.
Hope this helps.
it was a very hard issue for me, but I found a workaround (not so simple) that works fine for me.
on aspx page:
<input id="ss" type="text" oninput="writeFilterValue()"/>
<asp:HiddenField ID="hf1" runat="server" Value="" ClientIDMode="Static" />
I need HTML input type because of "oninput" function, that is not availiable on classic asp.net controls. The writeFilterValue() function causes a postback that filters values of a given ListBox (in code-behind).
I've defined this two javascript function:
<script type="text/javascript">
function writeFilterValue() {
var bla = document.getElementById("ss").value;
$("#hf1").val(bla)
__doPostBack();
}
function setTboxValue(s) {
document.getElementById('ss').value = s;
document.getElementById('ss').focus();
}
</script>
You can now use postback on code-behind to capture hf1 value, every time some single Character is typed on inputbox.
On code-behind:
If IsPostBack Then
FiltraLbox(hf1.Value)
End If
The function FiltraLbox(hf1.Value) changes datasource of Listbox, and rebind it:
Public Sub FiltraLbox(ByVal hf As String)
If hf <> "" Then
' change datasource here, that depends on hf value,
ListBox1.DataBind()
ScriptManager.RegisterStartupScript(Page, Page.GetType(), "text", setTboxValue('" + hf + "');", True)
End If
End Sub
At the end I call the function setTboxValue(), that rewrites the input text value lost on postback, and puts the focus on it.
Enjoy it.
The above is the code in my .aspx page.
How this can be added from code behind dyanmically?
<ul runat="server" id="1">
<li>abc
<ul runat="server" id="2">
<li>3</li>
<li>2</li>
</ul>
</li>
</ul>
you can take the analogy of the tutorial given in this link :
http://neimke.blogspot.com/2011/01/create-delicious-user-interface-for.html
it worked for me - It dynamically adds the list items using the given code below using jquery .. check it pout ...
<li id="tagInputListItem"><input class="tagInput" id="tagInput" /></li>
You can put a PlaceHolder in your .aspx and give it an id, then use that id in code behind page and add controls to that placeholder.
For more information you can see in here.
And if you're really sure about "runat=server" attribute maybe this post of mine it's useful (here)
If you need clarifications give me a feedback.
You must use the "InnerHtml" property of "sidebarmenu1" control.
protected void Page_Load(object sender, EventArgs e)
{
this.loadHtml();
}
So you can generate every list item code and add it to the InnerHtml:
private loadHtml()
{
this.sidebarmenu1.InnerHtml = GetListHtml().ToHtmlString();
}
And a little example for this GetListHtml:
public string GetListHtml()
{
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.AppendLine("<li>Flat");
htmlBuilder.Append("<ul runat="server" id="sidebarmenu2">");
htmlBuilder.AppendLine("<li>Flat 1`enter code here`</li>");
htmlBuilder.Append("<li>Flat 2</li></ul>");
return htmlBuilder.ToString();
}
This GetListHtml method can call to a DAL or load data from any other place... use a foreach to load every item...
You can use ASP Literal to populate data from back-end code
eg. if you have literal with id ltrNavigation
protected void Page_Load(object sender, EventArgs e)
{
ltrNavigation.text = "";
if (!IsPostBack)
{
ltrNavigation.text += "<ul id='sidebarmenu1'>";
ltrNavigation.text += "<li><a href='#'>Flat</a></li>";
ltrNavigation.text += "</ul>";
}
}
I am working on claim expenses application for the staff where I work. Part of the process contains a listview, part of a new requirement is that if an expense type is mileage the user will not be able to edit the item, only delete and resubmit as part of business rules and UK tax reasons etc.
Anyway, I want to be able to find a control in each item of the listview that has a certain text value.
I thought something like the following but this is not correct and I know why.
Label ExpenseTypeLabel = (Label)Expenses.FindControl("ExpenseTypeLabel");
string ExpenseType = (ExpenseTypeLabel.Text.ToString());
if (ExpenseType == "Mileage")
{
foreach (ListViewDataItem thisItem in Expenses.Items)
{
ImageButton btnEdit = (ImageButton)thisItem.FindControl("btnEdit");
btnEdit.Enabled = false;
}
}
The expenses are based on weekending and as the page loads it throws my excepion as It cannot bind to a particular individual control as there are many ExpenseTypeLabels associated with the expense for the current weekending (which loads first).
What I am trying to accomplish here is to find all ExpenseTypeLabels in both the item template and the alternating item template and disable the edit function of that expense item. FYI incase you're wondering the weekending is the expense, and the children are the individual expense items.
Could one of you lovely people please educate me on the best way to accomplish this?
Thanks
Matt
Binding order, and timing for accessing bound items, is extremely important; this is especially true when you have sub controls that have binding items also.
If you want to affect the the display for these bound controls, you can usually do it from the aspx end.
Create a link from the front end to a function on the server end, then pass it all the necessary parameters:
<asp:listview id='lstExpense'>
...
<asp:button id='btnEdit' enabled='<%#= isEnabled(((Expense)Container.DataItem).ExpenseType) %>' ...
...
<asp:listview>
On the server end, make a public function to return that value:
public boolean IsEnabled(string ExpenseType) {
return ('Mileage' != ExpenseType);
}
Best solution though, is to use jQuery. Not exaggerating, but you can accomplish all of that with something as simple as:
$('.rowClass').each(function() {
if ($(this).find('.expenseTypeClass').val() == 'Mileage'))
$(this).find('.btnEditClass').attr('disabled','disabled');
})
use OnItemDataBound event as follows
OnItemDataBound="Expenses_ItemDataBound"
protected void Expenses_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Label ExpenseTypeLabel = (Label)e.Item.FindControl("ExpenseTypeLabel");
string ExpenseType = (ExpenseTypeLabel.Text.ToString());
if (ExpenseType == "Mileage")
{
// disable button
}
}
}
I have in many places in my ASP.NET project used the Session variable for storing data. I usually write something like this:
public uint MyPropery
{
get
{
object o = Session["MyProperty"];
if (o != null)
return (uint)o;
else
return 0;
}
set
{
Session["MyProperty"] = value;
}
}
However, this time I get a NullReferenceException in the setter. As far as I know, it is valid to assign the Session variable in the manner above. Also, Session is not null and neither is value.
Any ideas on this?
Edit:
Adding the code for the UserControl in which the property exists. I am using ext.net but that shouldn't have anything to do with this. One thought that crossed my mind:
The UserControl (seen below) is added dynamically in code-behind of a page. Can that have anything to do with it?
I am adding UserControls like this (on a Page):
foreach(CoreCommons.System.Comment c in cg.Reply_Comments)
{
WebApplicationExtNetTest.Secure.UserControls.CoreComment cc = new UserControls.CoreComment();
cc._Comment = c; // here is where i get the NullRef
this.Panel1.ContentControls.Add(cc);
}
Markup:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="CoreComment.ascx.cs" Inherits="WebApplicationExtNetTest.Secure.UserControls.CoreComment" %>
<%# Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<ext:Panel runat="server" ID="CoreCommentOuterPanel" BodyStyle="background: #FFFDDE">
<Items>
<ext:ColumnLayout runat="server">
<Columns>
<ext:LayoutColumn ColumnWidth="0.8">
<ext:Image runat="server" ImageUrl="/Resources/bullet_triangle_green_16x16.png" Align="AbsMiddle"></ext:Image>
<ext:Label runat="server" ID="lblCommentInfo"></ext:Label>
</ext:LayoutColumn>
<ext:LayoutColumn ColumnWidth="0.2"><ext:Button runat="server" ID="btnDelete" Icon="Delete"></ext:Button></ext:LayoutColumn>
</Columns>
</ext:ColumnLayout>
<ext:Label runat="server" ID="lblComment"></ext:Label>
</Items>
</ext:Panel>
Code-behind:
namespace WebApplicationExtNetTest.Secure.UserControls
{
public partial class CoreComment : System.Web.UI.UserControl
{
public CoreCommons.System.Comment _Comment
{
get
{
object o = Session["CoreComment_ObjectId"];
if (o != null)
return (tWorks.Core.CoreCommons.System.Comment)o;
else
return null;
}
set
{
Session["CoreComment_ObjectId"] = value;
SetComment();
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
private void SetComment()
{
if (_Comment == null)
{
lblCommentInfo.Text = "";
lblComment.Text = "";
}
else
{
lblCommentInfo.Text = _Comment.Author + ", " + _Comment.TimeStamp.ToString("g");
lblComment.Text = _Comment.Text;
}
}
}
}
I'm almost completely sure the NullReferenceException is thrown in SetComment() because none of the CoreComment's child controls (lblComment, lblCommentInfo) are properly instantiated at the point you set the _Comment property.
The reason these child controls are not instantiated is indeed the way you currently add the CoreComment controls. For dynamically adding UserControls, you must use Page.LoadControl() (see: here) to create a new instance of the control, as it does some behind-the-scenes magic to ensure it is properly initialized, which includes the instantiation of the child controls.
On a sidenote, personally I'd change SetComment() to SetComment(CoreCommons.System.Comment comment) and use the parameter instead of repeatedly calling the getter, or, if staying with the original, at least call the getter only once and store the result in a local variable. With what I assume is probably InProc session storage it won't make much of a difference, but in any other storage mode you'd repeatedly deserialize the Comment object for no reason.
You need to use the Page.LoadControl() method instead , please look here
BTW:the problem is in adding the control programatically with that way.
Use:
return Session["MyProperty"] as uint? ?? 0;
and post somewhere full exception stack trace with inner exception(s)