What is the correct way to format SPGridView values being displayed? - sharepoint

Problem
As we know, SharePoint saves data in database in plain text. Some fields even have concatenated strings like <id>;#<value> for user fields. Percents are saved as doubles (1.00000000000000 for 100%) and etc.
Ofcourse, I want to display data as they are displayed in lists.
What should I do?
Should I use derived SPBoundField to format values (Which I actually did and it works fine until you want to filter (probably SPBoundField won't format me values because i use ObjectDataSource not list and with reflector I saw if there are SPListItems in datasource, then it formats correctly. Not my case)
alt text http://img199.imageshack.us/img199/2797/ss20090820110331.png
Or must I loop through all the DataTable and format each row accordingly?
What are Your techniques?
Thank you.

Here is how I solved this issue.
<asp:TemplateField HeaderText="Campaign Members">
<ItemTemplate>
<%# RemoveCharacters(Eval("CampaignMembers").ToString())%>
</ItemTemplate>
</asp:TemplateField>
// Make sure declare using System.Text.RegularExpression;
protected string RemoveCharacters(object String)
{
string s1 = String.ToString();
string newString = Regex.Replace(s1, #"#[\d-];", string.Empty);
newString = Regex.Replace(newString, "#", " ");
return newString.ToString();
}

I normaly use ItemTemplates that inherit from ITemplate. With in the ItemTemplate I use the SPFieldxxxValue classes or some custom formating code. This saves looping through the DataTable and the ItemTemplates can be reused.
The ItemTemplates are attached in Column Binding
E.G
// Normal Data Binding
SPBoundField fld = new SPBoundField();
fld.HeaderText = field.DisplayName;
fld.DataField = field.InternalName;
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
// ItemTemplate Binding
TemplateField fld = new TemplateField();
fld.HeaderText = field.DisplayName;
fld.ItemTemplate = new CustomItemTemplateClass(field.InternalName);
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
An example of a ItemTemplate
public class CustomItemTemplateClass : ITemplate
{
private string FieldName
{ get; set; }
public CustomItemTemplateClass(string fieldName, string formatString)
{
FieldName = fieldName;
}
#region ITemplate Members
public void InstantiateIn(Control container)
{
Literal lit = new Literal();
lit.DataBinding += new EventHandler(lit_DataBinding);
container.Controls.Add(lit);
}
#endregion
void lit_DataBinding(object sender, EventArgs e)
{
Literal lit = (Literal)sender;
SPGridViewRow container = (SPGridViewRow)lit.NamingContainer;
string fieldValue = ((DataRowView)container.DataItem)[FieldName].ToString();
//Prosses Filed value here
SPFieldLookupValue lookupValue = new SPFieldLookupValue(fieldValue);
//Display new value
lit.Text = lookupValue.LookupValue;
}
}

Here are a few options. I don't know the output of all of them (would be a good blog post) but one of them should do what you want:
SPListItem.GetFormattedValue()
SPField.GetFieldValue()
SPField.GetFieldValueAsHtml()
SPField.GetFieldValueAsText()
It may also be handy to know that if you ever want to make use of the raw values then have a look at the SPField*XYZ*Value classes. For example the form <id>;#<value> you mention is represented by the class SPFieldUserValue. You can pass the raw text to its constructor and extract the ID, value, and most usefully User very easily.

I would suggest either to format the values before binding them to the spgridview. Linq and an anonymous type is preffered or to call a code behind function on the field that needs the formatting upon binding.
DataField='<%# FormatUserField(Eval("UserFieldName")) %>'
or...maybe a templated field?

After all, i did have not know any other solution to loop through DataTable rows and format them accordingly.
If your SPGridView's data source is list, try out SPBoundField.

Related

Xpages "filter by category name" with two fields values (Restricting a View to Multiple Categories)

I want to design a view that display only projects of selected customer and services, so I created a view that customer and service columns are categorized and a view panel for this view. If I put
document1.getDocument().getItemValueString("Customer")
it works with a restrict single category. How I can do this with tow categorized columns.
Thanks in advance
Could you implement a repeat within a repeat for this?
The first repeat would capture your first category. Then inside you have a panel and a repeat and the second repeat would further filter the datasource by the second category
You cheat. Have one column that concatenates customer and project. Grab that column in your selection and split it in code.
Then use the selected value for the one column. Lets presume your original columns are customer and project. So your new column would have the formula customer+"~"+project. You then read this values to populate a SSJS object or a variable, so you can retrieve the customers (first dropdown) and the projects (second dropdown). In a dropdown you can use the format Display|Value, so a good approach is to have the value in the format customer~project.
As said you can do that in Java or JavaScript. Since I like Java's collection framework a lot, here's the Java version:
import java.util.Set;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
public class SplitCategoryBean {
private static final String SEPARATOR = "~";
private final Map<String,Set<String>> allCategories = new TreeMap<String, Set<String>>();
public void populate(Database db, String viewName) throws NotesException {
View v = db.getView(viewName);
ViewEntryCollection vec = v.getAllEntries();
ViewEntry ve = vec.getFirstEntry();
while (ve != null) {
ViewEntry nextVE = vec.getNextEntry(ve);
this.addEntry(ve.getColumnValues().get(0).toString());
ve.recycle();
ve = nextVE;
}
vec.recycle();
v.recycle();
}
private void addEntry(String combinedCategory) {
String[] splitCategory = combinedCategory.split(SEPARATOR);
String key = splitCategory[0];
String value = splitCategory[1];
Set<String> thisCategory = (this.allCategories.containsKey(key)) ? this.allCategories.get(key): new TreeSet<String>();
thisCategory.add(value+"|"+combinedCategory);
this.allCategories.put(key, thisCategory);
}
public Set<String> getFirstCategory() {
return this.allCategories.keySet();
}
public Set<String> getSecondCategoryReadyForDropDown(String key) {
return this.allCategories.get(key);
}
}
You would configure that as a managed bean (viewScope) and in the queryOpen you call the populate method. Then you can easily bind your first selection to #{beanName.firstCategory} for the selection and e.g. #{viewScope.curCustomer} for the value. The second drop-down you use the rendered="#{viewScope.curCustomer}" so it only shows when the customer is selected. And you bind the selections to #{javascript:beanName.getSecondCategoryReadyForDropDown(viewScope.curCustomer);
Put refreshs on the change events and render the view only if you have a project selected.
Does that work for you?
Thanks guys
I created a view that the first column is categorized and its value is Customer+Service then put
document1.getDocument().getItemValueString("Customer") + document1.getDocument().getItemValueString(“Service")
in "filter by category name" of view panel it works now.

Binding the style property of all cells in a JavaFX TableView

I have a JavaFX TableView where each row should have a conditional style.
The styling is dependent on whether the source item of of the table row is present in a certain list or not.
This is what I have so far:
1) The data class that holds the data of a table row together with two boolean properties (true if the data is contained in list X) and a string property that should bind to the correct style attributes.
private class WebPageData {
private WebPage page;
private BooleanProperty isReferenced = new SimpleBooleanProperty(false);
private BooleanProperty isReferencing = new SimpleBooleanProperty(false);
private StringBinding style = new When(isReferenced).then("...").otherwise(...);
}
2) A change listener on table selection change that updates each boolean property accordingly, when the table selection changes
tblResultData.getSelectionModel().getSelectedIndices().addListener(new ListChangeListener<Integer>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Integer> arg0) {
if (arg0.getList().size() == 0) {
selectedPage.set(null);
} else {
// for coloring only consider the first selected row
// multi select must be doable for certain other features
WebPage selectedWebPage = tblResultData.getItems().get(arg0.getList().get(0)).page;
selectedPage.set(selectedWebPage);
// tableModel.data holds a list of data for every table row
for (WebPageData data : tableModel.data) {
boolean referenced = selectedWebPage.getReferencedWebPagesList().contains(data.page);
boolean referencing = selectedWebPage.getReferencingWebPagesList().contains(data.page);
data.isReferenced.set(referenced);
data.isReferencing.set(referencing);
}
}
}
});
Now what I want to do is to somehow bind the style property of each table cell to the style property of WebPageData - so that the change listener updates the two boolean properties, therefore the style property of WebPageData is updated and in consequence the style of the table cell changes.
I tried to bind the style during creation phase by using a custom TableCellFactory, but of course this approach fails as there is no WebPageData instance present at this time. As the TableColumn classes don't provide an opportunity to iterate over all cells (so I could bind the style after the table actually gets its data), the only option I currently see is to keep a reference to each created table cell. I don't consider this solution is good practice.
So is there any other option to bind the cell styles? If I don't bind them, I have to set the styles manually each time the table selection changes - which puts me to the "I can't iterate over cells" problem again.
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.scene.control.TreeTableRow;
public class HighlightBoundTreeTableRow extends TreeTableRow<Thing> {
private static final String CHOSEN_STYLE_CLASS = "chosenStyle";
private final ObjectProperty<Boolean> chosen = new SimpleObjectProperty<>();
private final ChangeListener propertyChangeListener = (obs, ov, nv) -> updateHighlight();
#Override
protected void updateItem(Thing item, boolean empty) {
super.updateItem(item, empty);
//cleanup
getStyleClass().remove(CHOSEN_STYLE_CLASS);
chosen.unbind(); // unbinding something that is not bound has no effect
chosen.removeListener(propertyChangeListener); // also ok to remove a listener that was never there
if (empty) {
return;
}
chosen.bind(item.chosenProperty()); //bind will also set the intial value
chosen.addListener(propertyChangeListener);
updateHighlight();
}
private void updateHighlight() {
if (chosen.get()) {
getStyleClass().add(CHOSEN_STYLE_CLASS);
} else {
getStyleClass().remove(CHOSEN_STYLE_CLASS);
}
}
}
I know this was asked forever ago but maybe it'll help someone.
I had a similar issue I wanted to solve. I know you're using a TableCell and this involves a TreeTableRow but I believe the concept is the same: You want to change a field in your data object and have that change update the styling on wherever that object is being displayed in a table.
So I extended TreeTableRow and gave that class its own property field to hold on to. Every time that row is updated I unbind that property and rebind it to the field I want to listen to. (I do the same with the listener.) Since every time updateItem() is called it could be getting a different instance of my data object.
"chosenStyle" is just a class in my style sheet that changes the background color. Using classes instead of calling setStyle() makes it easier to remove the styling.

Tridion: Dreamweaver doesn't resolves the HTML code field

We have a compound CT, which outputs the code field of one of the component.
The dream-weaver part of CT is as follows:
<!-- TemplateBeginRepeat name="Component.HTMLCode" -->
##Component.HTMLCode##
<!-- TemplateEndRepeat -->
However this CT displays the code field on the page, instead of converting into HTML.
For eg: If the code field has a value as ->
<div align="center" id="loginapp"></div>
Then this same value is displayed on page instead of parsing.
In the page source, we get output as "&lt ;div align=&quot ;center" id=&quot ;loginapp&quot ;&gt ;&lt ;/div&gt ;"
I know this can be resolved if we use C#.
But is there any way using dreamweaver to stop the conversion of special characters?
You should use dwt to publish the code to server, I mean create new dwt for every code and just paste the code in the dwt. you can use this dwt with emply component or resource type component.
or if you want to use text field, try following tbb code. add this tbb at the end of the template.
public override void Transform(Engine engine, Package package)
{
Regex objExp = new Regex(#"&#\d+;", RegexOptions.IgnoreCase);
Regex objDecExp = new Regex(#"[^0-9]", RegexOptions.IgnoreCase);
this.Initialize(engine, package);
string strPackage = package.GetValue("Output");
strPackage = unescapeHTML(strPackage);
strPackage = objExp.Replace(strPackage, delegate (Match match)
{
string strInput = match.ToString();
strInput = objDecExp.Replace(strInput, "");
int intValue = Convert.ToInt32(strInput);
char strChar = (char)intValue;
return strChar.ToString();
});
strPackage = strPackage.Trim();
Item objOutput = package.CreateStringItem(ContentType.Html, strPackage);
package.PushItem("Output", objOutput);
}
private string unescapeHTML(string strInput)
{
StringBuilder strOutput = new StringBuilder(strInput);
strOutput.Replace(""", """);
strOutput.Replace(" ", " ");
strOutput.Replace("&", "&");
strOutput.Replace("&apos;", "'");
strOutput.Replace("<", "<");
strOutput.Replace(">", ">");
strOutput.Replace("¡", "&#161");
strOutput.Replace("¢", "&#162");
strOutput.Replace("£", "&#163");
strOutput.Replace("¤", "&#164");
strOutput.Replace("¥", "&#165");
strOutput.Replace("¦", "&#166");
strOutput.Replace("§", "&#167");
strOutput.Replace("¨", "&#168");
strOutput.Replace("©", "&#169");
strOutput.Replace("ª", "&#170");
strOutput.Replace("¬", "&#172");
strOutput.Replace("­", "&#173");
strOutput.Replace("®", "&#174");
strOutput.Replace("¯", "&#175");
strOutput.Replace("°", "&#176");
return strOutput.ToString();
}
}
If I recall correctly it is depending on your fieldtype, if in your Schema you use a normal text field, then HTML is escaped, if you use a rich text field, it will be resolved.
An option would perhaps be to write a Dreamweaver Custom function which allows you to unescape the field (represent it as an HTML field rather than a text field). As you mentioned you could also do it in a TBB, but the Dreamweaver Custom Functions are directly callable from the DWT Template. Either way I think you indeed need to do some coding yourself.
RenderComponentField has two parameters: bool htmlEncodeResult, and bool resolveHtmlAsRTFContent. Are you using this built in function?
Thanks for your help. After lots of trials with dreamweaver code, we decided to use C# TBB instead which solved the purpose.
Also reading the multiline field as a textfield was one of the mistake we committed. This caused the field value to be displayed on page instead of rendering as a code behind.
We finally solved the issue using "MultilineTextField".

SharePoint 2010 - how to make multi-column a unique value

i'm implementing a sand box solution where is should have more than one column a unique key, i have to use the item adding event receiver but how to get the current adding item field values to know if this item is occurred within the list.
thanks
Create UniqueID column and make it unique.
Create an event receiver as follows:
public override void ItemAdding(SPItemEventProperties properties)
{
string Name = properties.AfterProperties["Name"].ToString();
string Title = properties.AfterProperties["Title"].ToString();
StringBuilder StringBuilder = new StringBuilder(Name);
StringBuilder.Append("-");
StringBuilder.Append(Title);
properties.AfterProperties["UniqueID0"] = StringBuilder.ToString();
base.ItemAdding(properties);
}

SubSonic How to Execute a SQL Statement?

My site is using Subsonic 2.2 on my site.
I have 1 weird situation where I need to run some ad-hoc SQL statements.
public IList<string> GetDistincList(string TableName, string FieldName)
{
string sqlToRun = string.Format("SELECT DISTINCT {0} FROM {1} ORDER BY {0}", FieldName, TableName);
Query query = new Query(TableName);
query.PleaseRunThis(sqlToRun);
query.ExecuteReader();
}
Can anyone help me here? As it appears, I just want to return a generic list of strings.
Thanks!
Subsonic has a great method called ExecuteTypedList() so you can do somethink like this.
List<int> result = DB.Select(Table.Columns.Id)
.Distinct()
.From<Table>()
.OrderBy(Table.Columns.Id)
.ExecuteTypedList<int>();
or even with pocos:
public class UserResult
{
public int Id {get;set;}
public string Name {get;set;}
}
List<UserResult> users = DB.Select(
User.Columns.UserId + " as Id", // the as ... is only needed if your
User.Columns.UserName + " as Name" // column name differs from the
).From<User>() // property name of your class
.ExecuteTypedList<UserResult>();
Unfortunately this method doesn't work for string since it requires
a) a valuetype
b) a class with a parameterless constructor since the method uses reflection to map the columns from the result to the properties of the class
However I wrote an extension method a while ago that works for string:
Use the Subsonic.Select() ExecuteTypedList Method with String
Look at my own answer in the link.
If you add the extensionmethod to your code you can do:
List<String> result = DB.Select(User.Columns.UserName)
.From<User>()
.ExecuteTypedList();
Use the CodingHorror class.
Here's the SubSonic 3 way of doing it: http://www.subsonicproject.com/docs/CodingHorror
The SubSonic 2 way is similar:
Dim ch As SubSonic.CodingHorror
ch.Execute("delete from #tablename", table)

Resources