CRM 2016 + Currency is required if a value exists - dynamics-crm-online

I have javascript on my form wherein onchange of a field, currency is set.
For eg i have a field named 'Field1', on change of a value in 'Field1', currency is set and an another money field (say price) is set based on the currency selected.
So the scenario is, when the value from Field1 is removed, currency and price both are set to blank,after then if a value is selected in Field1, eventhough the currency is set it throws an error "Currency is required if a value exists". My Assumption is it throws the error because i am trying to set the price field also after setting the currency.
Below is the code used to set currency.
var arrLookupData = new Array();
var objLookupItem = new Object();
objLookupItem.typename = "transactioncurrency";
objLookupItem.id = varray.id;
objLookupItem.name = varray.name;
arrLookupData[0] = objLookupItem;
Xrm.Page.getAttribute("transactioncurrencyid").setValue(arrLookupData);
Xrm.Page.getAttribute("transactioncurrencyid").fireOnChange();
//Some code
Xrm.Page.getAttribute("core_price").setValue(value);
Kindly suggest.

Make sure you invoke fireOnChange after you set values in fields from Javascript, otherwise the form won't "see" the new data.
Your code would become:
var arrLookupData = new Array();
//
// omitted
//
Xrm.Page.getAttribute("transactioncurrencyid").setValue(arrLookupData);
Xrm.Page.getAttribute("transactioncurrencyid").fireOnChange();

Related

CRM Dynamics Upsert - Insert OptionSet

I am trying to update/Insert Contact Entity. While simple text fields are saving, having difficulties updating OptionSet. For ex. new_gender field is an option set (Male/Female).
contact["new_gender"] = new OptionSetValue(1); //Does not work
contact["new_gender"] = 1; //Does not work
Error says:
"new_gender should have the Integer value of Enum. Please supply it in the format - <entitysetname>(<attributename>=100000000)"
Any help is appreciated!!
Custom OptionSets have a prefix named Option Value Prefix. I guess in you case it's "10000".
Try it this way:
contact["new_gender"] = new OptionSetValue(100000001);
You can define an Enum something like
public enum Gender
{
Male = 10000001,
Female = 10000002
}
and set the attribute of gender
contact["new_gender"] = new OptionSetValue((int)Gender.Male);
also, you can get the gender value like this;
int value = ((OptionSetValue)contact[new_gender]).Value;

Parameter options for User Defined Functions in PowerQuery

Hi i have been trying to make a user defined function that allows the user to select the values which the function will use from a list.
I have tried setting the parameter i want as a list to type list in my function but this only seems to accept columns rather than a list of values a user can select from.
let
ListOfDays = {1.1,0.5,2,3,1},
DayOfTheWeek = (Day as list, HoursWorked ) =>
let
Earnings = Day * HoursWorked
in
Earnings
in
DayOfTheWeek
What i would like is for me to allow the user to select a single value from the ListOfDays list. I used typed list within my function parameters so that it can give the user a dropdown list kind of option.
I believe this is the relevant documentation you are looking for:
github.com/microsoft/DataConnectors/docs/function-docs.md: Adding Function Documentation
In particular, look at the definition for Documentation.AllowedValues:
List of valid values for this parameter. Providing this field will change the input from a textbox to a drop down list. Note, this does not prevent a user from manually editing the query to supply alternate values.
This (and other Documentation fields) are part of the meta typing of the function arguments. Scroll down to the code snippet which shows how to use them:
[DataSource.Kind="HelloWorldWithDocs", Publish="HelloWorldWithDocs.Publish"]
shared HelloWorldWithDocs.Contents = Value.ReplaceType(HelloWorldImpl, HelloWorldType);
HelloWorldType = type function (
message as (type text meta [
Documentation.FieldCaption = "Message",
Documentation.FieldDescription = "Text to display",
Documentation.SampleValues = {"Hello world", "Hola mundo"}
]),
optional count as (type number meta [
Documentation.FieldCaption = "Count",
Documentation.FieldDescription = "Number of times to repeat the message",
Documentation.AllowedValues = { 1, 2, 3 }
]))
as table meta [
Documentation.Name = "Hello - Name",
Documentation.LongDescription = "Hello - Long Description",
Documentation.Examples = {[
Description = "Returns a table with 'Hello world' repeated 2 times",
Code = "HelloWorldWithDocs.Contents(""Hello world"", 2)",
Result = "#table({""Column1""}, {{""Hello world""}, {""Hello world""}})"
],[
Description = "Another example, new message, new count!",
Code = "HelloWorldWithDocs.Contents(""Goodbye"", 1)",
Result = "#table({""Column1""}, {{""Goodbye""}})"
]}
];
HelloWorldImpl = (message as text, optional count as number) as table =>
let
_count = if (count <> null) then count else 5,
listOfMessages = List.Repeat({message}, _count),
table = Table.FromList(listOfMessages, Splitter.SplitByNothing())
in
table;
They also provide a screenshot of what this should look like when invoked:
If the user is able to open up the Query Editor, then they can choose a Day parameter from a dropdown list and have this automatically apply to the query.
You would create the parameter from the Manage Parameters > New Parameter menu
The drop-down at the upper right of the image is how the user would select the choice.
Your User Defined Function fn_DayOfTheWeek would be the following:
let
DayOfTheWeek = (Day as number, HoursWorked as number) =>
let
Earnings = Day * HoursWorked
in
Earnings
in
DayOfTheWeek
Note that Day is a number, not a list. You want to choose from a list, not pass a list into the function.
Now you can invoke your function with your parameter to actually produce a result.
let
Source = fn_DayOfTheWeek(Day, <HoursWorked value here>)
in
Source
This result will update when you change the parameter.
As you can see, whether a user has access to the Query Editor is rather a critical question for this approach. I'm not sure if it's possible to somehow set a parameter directly within a custom connector dialog box or not but this should be equivalent in functionality.

Getting a column by string name

I'm trying to update a record given the customer Id, the row Id, and a dynamic column name.
Thus far I have the following, with the trouble spot marked by ***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
I haven't been able to find a good example of how to do this.
Addition after comments at the end
The reason you couldn't find examples is because it is not a good design.
Your method is very error prone, difficult to test and horrible to maintain. What if someone types the incorrect column name? What if you try to assign a string to the customer's birthday? And even if you would implement some string checking for column names and proposed values, then your program wouldn't work anymore after someone changes the names or the types of the columns.
So let's redesign!
Apparently you have a Customer with an Id and a property Rvals. This property Rvals also has a property Id.
You also have a function GetRValId that can convert a string rval to an int rvalId.
What you want, is given an Id and a string rval, you want to update one of the columns of the first Customer with this Idand rValId.
Side questions: Can there be more than one Customer with Id? In that case: are you sure Id is an ID? What do you want if there are more matching Customers? Update all customers or update only the first one? Which customer do you define as the first customer?
Leaving the side questions aside. We want a function signature that reports errors at compile time if you use non-existing customer properties, or if you try to assign a string to a Birthday. Something like this perhaps?
Update the name of the customer:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
Update the Birthday of the customer:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
This way you can't use any column that does not exist, and you can't assign a string to a DateTime.
You want to change two values in one call? Go ahead:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
Convinced? Let's write the function:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
Simple comme bonjour!
Addition after comments
So what does this function do? As long as you don't call it, it does nothing. But when you call it, it fetches a customer, performs the Action on the Customer you provided in the call, and finally calls SaveChanges.
It doesn't do this with every Customer, no it does this only with the Customer with Id equal to the provided Id and customer.RVals.Id == ... (are you still certain there is more than one customer with this Id? If there is only one, why check for RVals.Id?)
So the caller not only has to provide the Id, and the RVal, which define the Customer to update, but he also has to define what must be done with this customer.
This definition takes the form of:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
Well if you want, you can use other identifiers than customer, but it means the same:
x => {x.Name = X; x.BirthDay = Y;}
Because you put it on the place of the Action parameter in the call to UpdateCustomerRecord, I know that x is of type Customer.
The Acton statement means: given a customer that must be updated, what must we do with the customer? You can read it as if it was a Function:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
In the end it will do something like:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
So in the third parameter, called Action you can type anything you want, even call functions that have nothing to do with Customers (probably not wise). You have an input parameter of which you are certain that it is a Customer.
See my earlier examples of calling UpdateCustomerRecord, one final example:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
So the Action parameter is just a simpler way to say: "once you've got the Customer that must be updated, please perform this function with the Customer
So if you only want to update a given property of the customer write something like:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
Of course this only works if you know which property must be updated. But since you wrote "I am trying to update a specific cell." I assume you know which property the cells in this column represent.
It is not possible to pass the column name as the string value in LINQ. Alternate way to do it, if you have the limited number of the column name which can be passed then it can be achieved as below:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
Here, suppose you have the 5 columns that can be passed while calling the funcion UpdateRecord then you can add the 5 clauses in the WHERE as above.
Other way to do it dynamic LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);

How can I access the PostOfficeBox of a contact using Exchange Webservices (EWS)?

We exported information from a customer's Address book using outlook.
Now he wants that information maintained and updated using Exchange.
We decided to use the Exchange Webservices API for this purpose.
I can retrieve the Contacts, and within those Contacts is the PhysicalAddresses enumeration that contains Work, Home and Other addresses.
Those addresses are instances of the PhysicalAddressEntry class.
This class contains Street address, Postal code, City, State and Region/Country. (See: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.physicaladdressentry_members(v=exchg.80).aspx )
It does not however contain the PostOfficeBox fields which Outlook address items have:
BusinessAddressPostOfficeBox
HomeAddressPostOfficeBox
MailingAddressPostOfficeBox
OtherAddressPostOfficeBox
(See: https://msdn.microsoft.com/en-us/vba/outlook-vba/articles/contactitem-object-outlook )
The Exchange Webservices API does have a PersonaPostalAddress Class which is more detailed than the PhysicalAddressEntry Class ( See: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.personapostaladdress_properties(v=exchg.80).aspx ). That one does have a PostOfficeBox member.
I have not succeeded in obtaining PersonaPostalAddress entries out of my Contacts (See: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.contact_properties(v=exchg.80).aspx )
Is there a way to read and possibly write the PostOfficeBox field that belongs to a certain address ?
There is a 'user defined' property that we are already reading as an extended property, those are in the PublicStrings namespace. (See: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.defaultextendedpropertyset(v=exchg.80).aspx )
I tried creating an extended property called "OtherAddressPostOfficeBox" as PublicStrings, Address and Common, but I haven't succeeded in getting it returned.
Forgive the openedge / progress code but here is what I tried:
DEFINE VARIABLE continent-regio AS CLASS Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition NO-UNDO.
DEFINE VARIABLE OtherAddressPostOfficeBox AS CLASS Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition NO-UNDO.
DEFINE VARIABLE properties AS CLASS "Microsoft.Exchange.WebServices.Data.PropertyDefinitionBase[]" NO-UNDO.
ASSIGN continent-regio = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet:PublicStrings,"Continent/Regio",Microsoft.Exchange.WebServices.Data.MapiPropertyType:String).
ASSIGN OtherAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet:Common,"OtherAddressPostOfficeBox",Microsoft.Exchange.WebServices.Data.MapiPropertyType:String).
ASSIGN properties = NEW "Microsoft.Exchange.WebServices.Data.PropertyDefinitionBase[]"(3).
properties:SetValue(continent-regio,0).
properties:SetValue(Microsoft.Exchange.WebServices.Data.ContactSchema:PostalAddressIndex,1).
properties:SetValue(OtherAddressPostOfficeBox,2).
contactlijst:PropertySet = NEW Microsoft.Exchange.WebServices.Data.PropertySet(
Microsoft.Exchange.WebServices.Data.BasePropertySet:FirstClassProperties,
properties).
DELETE OBJECT properties.
ASSIGN contacten = service:FindItems(Microsoft.Exchange.WebServices.Data.WellKnownFolderName:Contacts, contactlijst).
DO iX = 0 TO contacten:Items:COUNT - 1:
IF STRING(contacten:Items:Item[iX]:GetType()) EQ "Microsoft.Exchange.WebServices.Data.Contact" THEN DO: /* Mailinglijsten overslaan */
ASSIGN contactpersoon = CAST(contacten:Items:Item[iX], Microsoft.Exchange.WebServices.Data.Contact).
DO iY = 0 TO contactpersoon:ExtendedProperties:COUNT - 1:
CASE contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:NAME:
WHEN continent-regio:NAME THEN ASSIGN tt-cp.continent-regio = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
WHEN OtherAddressPostOfficeBox:NAME THEN MESSAGE contactpersoon:ExtendedProperties:ITEM[iY]:VALUE
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END CASE.
END.
DELETE OBJECT contactpersoon.
END.
END.
We can read the custom Continent/Regio field this way, but not the PostOfficeBox fields.
I probably need the numeric ID's of these fields to use one of the other constructors of the ExtendedPropertyDefinition class (See: https://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.extendedpropertydefinition_members(v=exchg.80).aspx ) but I can't seem to find them.
I tried this, after finding that 0x3A64 (14948) might be the ID for OtherAddressPostOfficeBox but that didn't work either.
ASSIGN OtherAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet:Address,14948,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String).
Did I use the correct Property Identifier ? Would it work this way or do I need to do something entirely different ?
Edit: I'm running a brute force attack on these Address fields on an Exchange Contact Item for which I filled in every available field with the name of the field. For most of them Exchange responds with an internal server error, however, from 32768 onwards, the fields start to appear periodically.
I can as of now confirm that ID 0x804A / 32842 corresponds with BusinessAddressPostOfficeBox, so expect an answer to be added shortly once the others have popped up. Do feel free to comment if you happen to know how to pull a PersonaPostalAddress directly from the server.
We have Microsoft Exchange Server 2010 SP3 (14.3.123.4002)
It wasn't as simple as I thought, as only the BusinessAddressPostOfficeBox is a field that can be retrieved out of the Address DefaultExtendedPropertySet.
However, having this one field I managed to find the following list on the internet that explains how to get the others: http://j-integra.intrinsyc.com/support/kb/article.aspx?id=53135
Eventually our code now looks like this:
ASSIGN BusinessAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet:Address,32842,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
HomeAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14942,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressCity = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14887,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressCountry = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14886,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressPostalCode = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14890,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14891,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressState = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14888,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
MailingAddressStreet = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14889,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
OtherAddressPostOfficeBox = NEW Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(14948,Microsoft.Exchange.WebServices.Data.MapiPropertyType:String)
ASSIGN properties = NEW "Microsoft.Exchange.WebServices.Data.PropertyDefinitionBase[]"(10).
properties:SetValue(continent-regio,0).
properties:SetValue(BusinessAddressPostOfficeBox,1).
properties:SetValue(HomeAddressPostOfficeBox,2).
properties:SetValue(MailingAddressCity,3).
properties:SetValue(MailingAddressCountry,4).
properties:SetValue(MailingAddressPostalCode,5).
properties:SetValue(MailingAddressPostOfficeBox,6).
properties:SetValue(MailingAddressState,7).
properties:SetValue(MailingAddressStreet,8).
properties:SetValue(OtherAddressPostOfficeBox,9).
contactlijst:PropertySet = NEW Microsoft.Exchange.WebServices.Data.PropertySet(
Microsoft.Exchange.WebServices.Data.BasePropertySet:FirstClassProperties,
properties).
ASSIGN contacten = service:FindItems(Microsoft.Exchange.WebServices.Data.WellKnownFolderName:Contacts, contactlijst).
DO iX = 0 TO contacten:Items:COUNT - 1:
IF STRING(contacten:Items:Item[iX]:GetType()) EQ "Microsoft.Exchange.WebServices.Data.Contact" THEN DO: /* Mailinglijsten overslaan */
ASSIGN contactpersoon = CAST(contacten:Items:Item[iX], Microsoft.Exchange.WebServices.Data.Contact).
CREATE tt-cp.
ASSIGN tt-cp.naam = contactpersoon:CompleteName:FullName
tt-cp.firma = contactpersoon:CompanyName
tt-cp.initialen = contactpersoon:Initials
tt-cp.functie = contactpersoon:JobTitle
tt-cp.afdeling = contactpersoon:Department
/* ... */
DO iY = 0 TO contactpersoon:ExtendedProperties:COUNT - 1:
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(continent-regio) THEN
ASSIGN tt-cp.continent-regio = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(BusinessAddressPostOfficeBox) THEN
ASSIGN tt-cp.werkadres-postbus = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(HomeAddressPostOfficeBox) THEN
ASSIGN tt-cp.thuisadres-postbus = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressCity) THEN
ASSIGN tt-cp.postadres-stad = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressCountry) THEN
ASSIGN tt-cp.postadres-land = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressPostalCode) THEN
ASSIGN tt-cp.postadres-postcode = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressPostOfficeBox) THEN
ASSIGN tt-cp.postadres-postbus = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressState) THEN
ASSIGN tt-cp.postadres-staat = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(MailingAddressStreet) THEN
ASSIGN tt-cp.postadres-straat = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(OtherAddressPostOfficeBox) THEN
ASSIGN tt-cp.anderadres-postbus = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
IF contactpersoon:ExtendedProperties:ITEM[iY]:PropertyDefinition:Equals(Webpage) THEN
ASSIGN tt-cp.webstek = contactpersoon:ExtendedProperties:ITEM[iY]:VALUE.
END.
So basically you just look up what the tagged id is for the field you are looking for and create an extended property for it, then it will be returned.
When going over the returned resultset, you can test the propertydefinition using Equals to see which one was returned.

Get IGrouping data in Repeater ItemDataBound

I am wanting to group news articles by year in a repeater. The format would be:
2010
list of articles
2011
List of Articles
My access layer returns a flat list of news articles, specifically List. Therefore, I am grouping them and binding them to the Repeater as follows:
events = DAL.GetEvents();
var groupedNewsList = from e in events
group e by e.StoryDate.Year
into g
select new {
Year = g.Key
, Events = g
};
rptEvents.DataSource = groupedNewsList;
rptEvents.DataBind();
The problem is trying to get the List from within the ItemDataBound event. So far, I have the following:
var data = e.Item.DataItem;
System.Type type = data.GetType();
// getting the year works fine
string year = (string)type.GetProperty("Year").GetValue(data, null).ToString();
// this returns something, but I can't access any properties. I need to get
//access to the contained List<News>
var newsList = type.GetProperty("Events").GetValue(data, null);
Any ideas?
Thanks in advance!
You don't have a List<News> - you just have a grouping. If you want a List<News>, you'll need to change your query, e.g.
var groupedNewsList = from e in events
group e by e.StoryDate.Year into g
select new { Year = g.Key, Events = g.ToList() };
Note that if you're using C# 4 you could do reflection rather more easily using dynamic typing:
dynamic data = e.Item.DataItem;
string year = data.Year.ToString();
List<News> newsList = data.Events;
Alternatively, you could avoid using an anonymous type in the first place - create your own GroupedNewsList type with Year and Events properties, populate that in your query, and then cast to it in your event handler.
The "sender" object in the ItemDataBound event is the repeater -- use it to get to the data-source. If the data-source has been grouped before binding, you can compare the current value to the previous value & hide the year-field if they are equal. Like this:
MyObject item = (MyObject)item.DataItem;
Repeater repeater = (sender as Repeater);
List<MyObject> items = repeater.DataSource as List<MyObject>;
Label lblGrouping = (Label)item.FindControl("lblGrouping");
if (item.ItemIndex == 0 || item.DateField.Year != items[item.ItemIndex - 1].DateField.Year) {
lblGrouping.Text = item.DateField.Year.ToString();
}
This worked for me, as I used a table with each row being one item, and the left-most column contained the "lblGrouping" control.

Resources