Sitecore 6 WFFM: ListField value? - custom-controls

I am building a complex WFFM user control that extends BaseUserControl. This control has multiple fields that get prepopulated based on some business logic. One of the fields is supposed to be a drop down which shows values from a series of Sitecore items. Here is the definition of my ListField property:
private string myListField;
[VisualProperty("My List Field:", 100),
VisualCategory("Appearance"), VisualFieldType(typeof(ListField))]
public string MyListField{
get { return myListField; }
set { myListField= value; }
}
When I debug this, the content of titleFieldList is a string that contains the following XML in URL encoded format:
%3Cquery%20t%3D%22root%22%20vf%3D%22__ID%22%20tf%3D%22Value%22%3E%3Cvalue%3E%7B814FC177-2750-48D6-B7B7-4EE87012C637%7D%3C%2Fvalue%3E%3C%2Fquery%3E
which, decode, is:
<query t="root" vf="__ID" tf="Value">
<value>{814FC177-2750-48D6-B7B7-4EE87012C637}</value>
</query>
I understand the meaning of this XML. It says that all the children of the item whose ID is that Guid are supposed to be used to populate my list, using the template field "__ID" for the value and the template field "value" for the text.
Can someone help me understand what am I supposed to do to bind an asp:DropDownList to this? Is this a particular sitecore object that has been serialized and encoded?
Is there an sc:control that can handle this?
Thanks!
** EDIT **
So I tried the following piece of code
string encodedQuery = TitleFieldList;
string query = HttpUtility.UrlDecode(encodedQuery);
XDocument xmlQuery = XDocument.Parse(query);
if (xmlQuery.Element("query") != null)
{
Dictionary<string, string> nodesDictionary = new Dictionary<string, string>();
string root = xmlQuery.Element("query").Element("value").Value;
string value = xmlQuery.Element("query").Attribute("vf").Value;
string text = xmlQuery.Element("query").Attribute("tf").Value;
Item rootItem = SitecoreUtility.GetItemWithoutSecurity(new ID(root));
ChildList childList = rootItem.GetChildren();
foreach (Item child in childList)
{
string theValue = (value == "__ID") ? child.ID.ToString() : child.Fields[value].ToString();
string theText = child.Fields[text].ToString();
nodesDictionary.Add(theText, theValue);
}
titleDropDownList.DataSource = nodesDictionary;
titleDropDownList.DataTextField = "key";
titleDropDownList.DataValueField = "value";
titleDropDownList.DataBind();
}
and it works. The dropdownlist is populated with the correct data coming from the fields that were selected in the editor. I just can't believe that there is no easier way to do this. Plus how am I supposed to honor the MultipleSelectedValueField and the EmptyChoiceField if present?

Try to change the return type of your property and add attribute TypeConverter. The type specified in TypeConverter is responsible for converting raw string value to a property's return type.
ListItemCollectionConverter - is a converter provided by WFFM
[VisualProperty("My List Field:", 100)]
[VisualCategory("Appearance")]
[VisualFieldType(typeof(ListField))]
[TypeConverter(typeof(Sitecore.Form.Web.UI.Controls.ListItemCollectionConverter.ListItemCollectionConverter))]
public Sitecore.Form.Web.UI.Controls.ListItemCollection MyListField{
get { return myListField; }
set { myListField= value; }
}

Related

Lite DB not finding inner object query

I have two objects.
[DataContract]
public class Record
{
[DataMember]
public string Id { get; set; }
}
And this class:
public class BatteryStatus : Record
{
[DataMember]
public DateTime RetrieveTime { get; set; }
}
I'm using Lite DB as a local NoSQL option to query and save the data. I'm needing to find and delete the values based after some time. Here's my code doing so:
var col = db.GetCollection<BatteryStatus>(CollectionName);
var test = col.FindAll()
.Where(x => x.Id == status.Id).ToList();
var result = col.Find(Query.EQ("Id", status.Id.ToString())).ToList();
Test returns with the with the object, but the result value doesn't. Lite DB only uses the Query or the BSONId as a way to delete an object. I don't have a BSON id attached to it (it's a referenced definition so I can't change it).
How can I use the "Query" function in order to get a nested value so I can delete it?
Classes has properties, BSON documents has fields. By default, LiteDB convert all property names to same name in BSON document except _id field which is document identifier.
If you want query using Linq, you will use properties expressions. If you are using Query object class, you must use field name.
var result = col.FindById(123);
// or
var result = col.FindOne(x => x.Id == 123);
// or
var result = col.FindOne(Query.EQ("_id", 123));
Find using _id always returns 1 (or zero) document.
I figured out the problem with LiteDB, since I was using the property name of "Id", the BSON interpreted that as the "_id" of the JSON object, and merging their two values. I solve the issue by renaming the "Id" property to something else.

How to get ItemIndex in Transferred event of Rad ListBox

I'm trying to get selected Item Text and value of RadListBox in Transferred event.
I tried following code:
protected void listbox_Initial_Transferred(object sender, RadListBoxTransferredEventArgs e)
{
string id = e.Items[0].Value.ToString();
string description = e.Items[0].Text.ToString();
}
here i passed "0" as index. Its working fine.
Now i got stuck here to fetch the selected index of Item.
Looking forward for your comments and replies.
Thanks.
Please try with the below code snippet.
// for single item
string id = RadListBox1.SelectedItem.Value.ToString();
string description = RadListBox1.SelectedItem.Text.ToString();
// for multiple item
foreach (RadListBoxItem item in RadListBox1.SelectedItems)
{
string _id = item.Value.ToString();
string _description = item.Text.ToString();
}
AS per your sample code, please also try with the below code snippet.
// for multiple item
foreach (RadListBoxItem item in e.Items)
{
string _id = item.Value.ToString();
string _description = item.Text.ToString();
}

Using an list in a query in entity framework

I am trying to find a way to pass in an optional string list to a query. What I am trying to do is filter a list of tags by the relationship between them. For example if c# was selected my program would suggest only tags that appear in documents with a c# tag and then on the selection of the next, say SQL, the tags that are linked to docs for those two tags together would be shown, whittling it down so that the user can get closer and closer to his goal.
At the moment all I have is:
List<Tag> _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
select t).ToList();
This is in a method that would be called repeatedly with the optional args as tags were selected.
I think I have been coming at it arse-backwards. If I make two(or more) queries one for each supplied tag, find the docs where they all appear together and then bring out all the tags that go with them... Or would that be too many hits on the db? Can I do it entirely through an entity context variable and just query the model?
Thanks again for any help!
You can try this.
First collect tag to search in a list of strings .
List<string> tagStrings = new List<string>{"c#", "sql"};
pass this list in your query, check whether it is empty or not, if empty, it will return all the tags, else tags which matches the tagStrings.
var _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
&& (tagStrings.Count ==0 || tagStrings.Contains(t.tagName))
select t).ToList();
You can also try this, Dictionary represents ID of a document with it's tags:
Dictionary<int, string[]> documents =
new Dictionary<int, string[]>();
documents.Add(1, new string[] { "C#", "SQL", "EF" });
documents.Add(2, new string[] { "C#", "Interop" });
documents.Add(3, new string[] { "Javascript", "ASP.NET" });
documents.Add(4, new string[] { });
// returns tags belonging to documents with IDs 1, 2
string[] filterTags = new string[] { "C#" };
var relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to document with ID 1
filterTags = new string[] { "C#", "SQL" };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to all documents
// since no filtering tags are specified
filterTags = new string[] { };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
public static string[] GetRelatedTags(
Dictionary<int, string[]> documents,
string[] filterTags)
{
var documentsWithFilterTags = documents.Where(o =>
filterTags
.Intersect(o.Value).Count() == filterTags.Length);
string[] relatedTags = new string[0];
foreach (string[] tags in documentsWithFilterTags.Select(o => o.Value))
relatedTags = relatedTags
.Concat(tags)
.Distinct()
.ToArray();
return relatedTags;
}
Thought I would pop back and share my solution which was completely different to what I first had in mind.
First I altered the database a little getting rid of a useless field in the allocateDocumentTag table which enabled me to use the entity framework model much more efficiently by allowing me to leave that table out and access it purely through the relationship between Tag and Document.
When I fill my form the first time I just display all the tags that have a relationship with a document. Using my search filter after that, when a Tag is selected in a checkedListBox the Document id's that are associated with that Tag(s) are returned and are then fed back to fill the used tag listbox.
public static List<Tag> fillUsed(List<int> docIds = null)
{
List<Tag> used = new List<Tag>();
if (docIds == null || docIds.Count() < 1)
{
used = (from t in frmFocus._context.Tags
where t.Documents.Count >= 1
select t).ToList();
}
else
{
used = (from t in frmFocus._context.Tags
where t.Documents.Any(d => docIds.Contains(d.id))
select t).ToList();
}
return used;
}
From there the tags feed into the doc search and vice versa. Hope this can help someone else, if the answer is unclear or you need more code then just leave a comment and I'll try and sort it.

Trying to use [Description] data annotation attribute with existing code

SLIGHT UPDATE BELOW
I am trying to use the [Description] data annotation attribute with enums in order to display a friendly name. I've searched around a lot and cannot get anything implemented. Right now I have code that will display an enum as a string (using an extension), but I am not liking ThisIsAnEnum as an enum name (which is spaced out by the string extension) and it prohibits me from having longer names (which I need to maintain) such as for a radio button item. My goal is to have longer descriptions for radio button items without having to write really long enums. An extension/helper will probably be the right way to go, but I need to "fit" it into the code I am using, which is where I failed using the many examples out there.
The code I am using is generic, in that depending upon some logic either a radio button list, check box list, drop down list, select list or regular text boxes are displayed. For multi-item lists enum's are used, and the enum name is what is displayed (after using the string extension).
Here is the particular code that displays the enum:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = name.ProperCase(),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
ProperCase is the class that changes the enum to something readable.
I found something that almost worked:
public static string GetEnumDescription<TEnum>(TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if ((attributes != null) && (attributes.Length > 0))
return attributes[0].Description;
else
return value.ToString();
}
in which case I changed code from Text = name.ProperCase(), to Text = name.GetEnumDescription(...) but if I put value in the parenthesis I get a "does not exist in the current context" message (which I tried fixing but just made the problem worse). If I leave it blank I get the "No overload for ... takes 0 arguments" (again, understandable - but I don't know how to fix). And if I put name in the parenthesis the code compiles but upon viewing the page I get the "Object reference not set..." error on this line:
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes
(typeof(DescriptionAttribute), false);
I've spent a lot of time on this and know that my stumbling block is the
Text = name.ProperCase(),
code. Any ideas/help? Thanks in advance.
UPDATE:
If I do:
Text = GetEnumDescription(selectedValue),
I actually DO get the [Description] text, however, it just displays for the first enum. So, if I have 5 enums all with different [Description]'s the code just repeats the [Description] for the first enum 5 times instead of displaying differently for each. I hope that makes sense and gets to narrow down the problem.
I'd recommend you the Display attribute:
public static IEnumerable<SelectListItem> GetItemsFromEnum<T>(T selectedValue = default(T)) where T : struct
{
return
from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = name == selectedValue.ToString()
};
}
public static string GetEnumDescription(string value, Type enumType)
{
var fi = enumType.GetField(value.ToString());
var display = fi
.GetCustomAttributes(typeof(DisplayAttribute), false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
if (display != null)
{
return display.Name;
}
return value;
}
and then you could have:
public enum Foo
{
[Display(Name = "value 1")]
Value1,
Value2,
[Display(Name = "value 3")]
Value3
}
And now you could have:
var foo = Foo.Value2;
var values = GetItemsFromEnum(foo);
Also notice that I have modified the Selected clause in the LINQ expression as yours is not correct.
This being said, personally I would recommend you staying away from enums on your view models as they don't play nicely with what's built-in ASP.NET MVC and you will have to reinvent most of the things.

How to access data into IQueryable?

I have IQueryable object and I need to take the data inside the IQueryable to put it into Textboxs controls. Is this possible?
I try something like:
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.????
}
Update:
I'm doing this:
public IQueryable getData(String tableName, Hashtable myparams)
{
decimal id = 0;
if (myparams.ContainsKey("id") == true)
id = (decimal)myparams["id"];
Type myType= Type.GetType("ORM_Linq." + tableName + ", ORM_Linq");
return this.GetTable(tableName , "select * from Articu where id_tipo_p = '" + id + "'");
}
public IQueryable<T> GetTable<T>(System.Linq.Expressions.Expression<Func<T, bool>> predicate) where T : class
{
return _datacontext.GetTable<T>().Where(predicate);
}
This returns a {System.Data.Linq.SqlClient.SqlProvider+OneTimeEnumerable1[ORM_Linq.Articu]}`
I don't see any method like you tell me. I see Cast<>, Expression, ToString...
EDIT: Updated based on additional info from your other posts...
Your getData method is returning IQueryable instead of a strongly typed result, which is why you end up casting it. Try changing it to:
public IQueryable<ORM_Linq.Articu> getData(...)
Are you trying to query for "Articu" from different tables?
With the above change in place, your code can be rewritten as follows:
ORM_Linq.Articu result = mydata.SingleOrDefault();
if (result != null)
{
TextBoxCode.Text = result.id.ToString();
TextBoxName.Text = result.descrip;
}
If you have a single result use SingleOrDefault which will return a default value if no results are returned:
var result = mydata.SingleOrDefault();
if (result != null)
{
textbox1.text = result.ProductName; // use the column name
}
else
{
// do something
}
If you have multiple results then loop over them:
foreach (var item in mydata)
{
string name = item.ProductName;
int id = item.ProductId;
// etc..
}
First, you should be using a strongly-typed version of IQueryable. Say that your objects are of type MyObject and that MyObject has a property called Name of type string. Then, first change the parameter mydata to be of type IQueryable<MyObject>:
public void setdata (IQueryable<MyObject> mydata)
Then we can write a body like so to actually get some data out of. Let's say that we just want the first result from the query:
public void setdata (IQueryable<MyObject> mydata) {
MyObject first = mydata.FirstOrDefault();
if(first != null) {
textbox1.Text = first.Name;
}
}
Or, if you want to concatenate all the names:
public void setdata(IQueryable<MyObject> mydata) {
string text = String.Join(", ", mydata.Select(x => x.Name).ToArray());
textbo1.Text = text;
}
Well, as the name suggests, an object implementing IQueryable is... Queryable! You'll need to write a linq query to get at the internal details of your IQueryable object. In your linq query you'll be able to pull out its data and assign bits of it where ever you'd like - like your text box.
Here's a great starting place for learning Linq.
I think you find the same mental struggle when coming from FoxPro and from DataSet. Really nice, powerful string-based capabilities(sql for query, access to tables and columns name) in these worlds are not available, but replaced with a compiled, strongly-typed set of capabilities.
This is very nice if you are statically defining the UI for search and results display against a data source known at compile time. Not so nice if you are trying to build a system which attaches to existing data sources known only at runtime and defined by configuration data.
If you expect only one value just call FirstOrDefault() method.
public void setdata (IQueryable mydata)
{
textbox1.text = mydata.FirstOrDefault().PropertyName;
}

Resources