Get Property Value From Nested Object List - c#

I am not sure how to even title or ask this question, so apologies for any confusion.
I need to get the value of the Id from the first list in Model.Content.GetPropertyValue("SlidePanel"). I've tried many, many, many thing with my troubles usually being that "You can't do this because it's an Object". In the image below, I want to get the Id Value of "1092" as a String.
-----EDIT-------
I was able to get the value with the code below. Casted the list, grabbed the first list since it would always be that option (I wrapped it in an if, but removed it in this example), then I was able to specify the property I needed and converted as needed.
If I sound like I don't speak this language fluently, it's because I'm still fresh to development. Thanks for everyone who helped.
dynamic slidePanelObject = Model.Content.GetProperty("SlidePanel").Value;
List<object> slidePanelCast = ((IEnumerable<object>)slidePanelObject).Cast<object>().ToList();
dynamic slidePanelFirst = slidePanelCast.First();
var slidePanelId = slidePanelFirst.Id;
string slidePanelString = slidePanelId.ToString();

Model.Content.GetPropertyValue is probably returning an System.Object, so you'll need to cast your temp var to a List<T> type of some kind before you can access it like a list.
Without knowing all the types involved, here's some code that you could modify:
var temp = Model.Content.GetPropertyValue("SlidePanel") as List<TYourType>;

Related

Obtaining the value of an option in a picklist

I'm creating a phony instance of an entity beep. It has a required field of type picklist called pickaboo. First I omitted it but then the application started to lament throwing error messages at me due to some business logic demanding all the newly created instances of beep to have that field assigned.
Entity entity = new Entity { LogicalName = "beep" };
Guid guid = proxy.Create(entity);
proxy.Delete("beep", guid);
I don't give a rodent's tail section about that demand because right after I've created the instance, I'm removing it. However CRM gives a huge rodent and doesn't let me do my magic. So, I went clever and added an attribute for the missing attribute.
OptionSetValue option = new OptionSetValue(0);
Entity entity = new Entity { LogicalName = "beep" };
entity.Attributes.Add("pickaboo", option);
Guid guid = proxy.Create(entity);
proxy.Delete("beep", guid);
Of course, it didn't work because zero isn't a valid value. Apparently, CRM adds a hash number based on the solution so the actual "zero" has the numeric value like "846000000000", the actual "one" has "846000000001" etc.
How can I obtain that value programmatically?
Right now I have an ugly workaround obtaining all the beeps and getting the value from the first of them. Don't even get me started on how much sleep I'm loosing knowing how embarrassing it looks, would anybody take time to give me some feed-back. :(
You've got two options.
You can use the CrmSrvcUtil to generate your OptionSetValues as enums... This would create a pickaboo enum that you could then reference in your code
entity.Attributes.Add("pickaboo", new OptionSetValue((int)pickaboo.YourEnumValue);
You could also use the RetrieveOptionSetRequest message to get a list of all values for the particular option set you're interested in. See this SO question
Since I know that all CRM programmers are lazy pigs (own experience, haha), I know that you'd prefer a short and comprehensive solution. I realize that you're looking for a quick access to just a single valid value. If I'm mistaken, stop reading - use the suggestion of #Daryl - he's got a good answer for ya.
If I'm right, though, use this code to get the first valid option value (provided it exists). Just in case, remember to surround it with try/catch so if you misspell or such, you won't end up scratching your head.
RetrieveAttributeRequest request = new RetrieveAttributeRequest
{
EntityLogicalName = "beep",
LogicalName = "pickaboo",
RetrieveAsIfPublished = true
};
RetrieveAttributeResponse response
= proxy.Execute(request) as RetrieveAttributeResponse;
PicklistAttributeMetadata metaData
= response.AttributeMetadata as PicklistAttributeMetadata;
OptionSetValue option
= new OptionSetValue(metaData.OptionSet.Options[0].Value ?? -1);
NB - I'm assuming that you've got a working connection via a proxy called proxy.
Set a try/catch around the whole code, just in case out of bound exception occurs.
Make sure to handle the option of -1, since Value returned is a nullable integer.
This question has inspired me to blog.
If you have a look at my post below its got a function that can be used to find the int values of picklists (global and local optionsets), statecode, statuscode and the boolean (two option) fields.
CRM 2011 Programatically Finding the Values of Picklists, Optionsets, Statecode, Statuscode and Boolean (Two Options)

A Copy vs. a Reference

Why does this code result in both shipment.Items.Count and combinedShipment.Items.Count equal to zero?
private static InboundShipment CombineLikeIsbns(InboundShipment shipment)
{
// shipment.Items has a count of 3
var distinctIsbns = shipment.Items.Select(i => i.ISBN).Distinct().ToList();
var combinedShipment = shipment;
combinedShipment.Items = new List<InboundShipmentItem>();
// Now both combinedShipment and shipment have an empty List in the .Items property
...
return combinedShipment;
}
[EDIT]
And what can I do to avoid having shipment.Items set to new List when I set combinedShipment.Items to the same?
This statement:
var combinedShipment = shipment;
copies the value of shipment into combinedShipment. Assuming InboundShipment is a class, the value of shipment is a reference - not an object itself.
So now we have two variables which both refer to the same object. It doesn't matter which variable you use to make a change to the object - the change will be visible via both variables.
If you want to create a new "copy" of the original object, you'll have to do that explicitly. It's hard to know exactly what you'd need to do here, as you haven't given us much information about the InboundShipment type.
See my article on value types and reference types for more details. Note that this is a vital part of C# and .NET, and you should become confident on it before going further - advanced topics such as LINQ (with its lambda expressions, deferred execution etc) will be hard to understand until you've got a good handle on the basics.
The line
var combinedShipment = shipment;
sets the combinedShipment reference to point at the same instance as shipment. So when you clear the items on combinedShipment, it clears them for that one single instance.
The assignment var combinedShipment = shipment; causes both combinedShipment and shipment to refer to exactly the same object. It's a little bit like sticking two different labels on the same cardboard box.
So the following line where you take all the items out of the box labeled combinedShipment also causes the box labeled shipment to be emptied. Because they're just the same box with two different labels.
If you want to create a new shipment object that has different items, you'll need to start by doing exactly that: Create a new shipment object.
var combinedShipment = new InboundShipment();
The actual code might not be able to look exactly like that. Assuming you want some of combinedShipment's properties to be the same as shipment's, you'll have to manually make sure that happens. Depending on how InboundShipment is designed, that will require either passing the desired values into the constructor, setting the desired values via properties, or a mix of both.

Understanding how a property of type List<> works

I got to think I don't understand how it works.
My specific question is: Why am I allowed to set the value of a list property element when I have no setter and no backing list variable?
Let me explain. Let's say I have a CustomerTable class with:
public List<string> Name
{
get
{
var names = new List<string>();
foreach (CustomerRow row in Rows)
{
name.Add(row.Name);
}
return names;
}
}
The idea is to have a read-only property show the contents of a column without duplicating data in my class, since I already have a list of rows.
Anyway, my surprise comes when pieces of code like the following one are accepted by Visual Studio without claiming any kind of error (and it even allows me to compile without errors):
Name[0] = "John";
I can't understand why this is legal. My property has no set { }, and it doesn't even have a backing list to modify. What is this piece of code supposed to do?
Shouldn't it work like a method? Is there really a stored list other than the one I generate each time someone "gets" it?
(I can give more details on demand and will also be grateful for any other remarks)
You are not setting the property, rather you are getting the property (which is a list) and then operating on it (in your example, changing its first member). If you were to try:
Name = new List<string>();
You would get the compilation error you were expecting to get. Note that since you are creating a new list every time, your Rows property remains read-only (assuming it is not exposed somewhere else). If you want to make it clear that changes to your returned collection are meaningless, you can change the type of the Name property to IEnumerable<string>:
public IEnumerable<string> Name
{
get
{
return Rows.Select(row => row.name); //LINQ is more elegant here
}
}
In your example you don't set Name (which is read-only, indeed), but you set the first list element contained in Name, which is Name[0], and there's no reason why you could not do that since List<string> is an object type which allows to set elements.

C#, calling an instance of a class from a string

I've been struggling for a while to find a way of calling an instance of a class from a user defined input.
Essentially I want to call an instance from my StandardVehicle class that has already been defined, however the problem is that the name of the instance that I'm calling is defined by a users selection on a combo box on a form.
This is how I assumed it would be formatted, however I get an error when I try calling the instance saying that I cannot complicitly convert from string to StandardVehicle.
StandardVehicle VauxhallB = new StandardVehicle();
VauxhallB.Model = "B";
VauxhallB.Manufacturer = "Vauxhall";
VauxhallB.Doors = 5;
VauxhallB.HorsePower = 200;
VauxhallB.Transmission = "Manual";
VauxhallB.Year = 2009;
VauxhallB.Cylinders = 6;
VauxhallB.Seats = 7;
VauxhallB.Price = 17000;
VauxhallB.Registration = "abc1243";
StandardVehicle objname = comboBox1.Text;
I'm assuming it's just a case of formatting the string, but I've been searching for hours and can't figure out what format it needs to be in to define it as an instance.
Any help would be appreciated.
Regards,
S.
No, it's definitely not a case of just formatting the string. It sounds like you want a
Dictionary<string, StandardVehicle>
Populate that to map from strings to objects, then look up the object corresponding to the name given by the user.
VauxhallB is just a symbol to the computer. The user shouldn't reference your variable names -- although it is probably possible to do so via reflection, but that's a complicated aspect of programming.
A better way to do this would be to associate each instance with a string "name" in a Dictionary<string, StandardVehicle>, and look up with that. Something like:
Dictionary<string, StandardVehicle> nameMap = new Dictionary<string, StandardVehicle>();
nameMap["VauxhallB"] = VauxhallB;
StandardVehicle objname = nameMap[comboBox1.Text];
Also, it's generally more readable if you only use lower case names for local variables, to better distinguish them from classes, public methods, and properties.
You cannot convert string to an object unless you have
Constructor that takes string and creates required object
An explicit/implicit cast operator
and another option
Use Activator.CreateInstace
3rd option is only valid if string contains valid type name, for your example you can do
Type type = Type.GetType(comboBox1.Text);
StandardVehicle objname = Activator.CreateInstace(type);
this will work if comboBox1.Text == "StandardVehicle".
More information here:
http://msdn.microsoft.com/en-us/library/w3f99sx1.aspx
http://msdn.microsoft.com/en-us/library/wccyzw83.aspx
Try this:
StandardVehicle selectedObject = (StandardVehicle)comboBox1.SelectedItem;
Of course, your comboBox should be bound to a collection of StandardVehicles... If it's not, then go for Dictionary as was already suggested.
First I also thought you mean reflection but you want to use your class StandardVehicle always. Just the properties should be set by a single string you receive from the user.
Write yourself a constructor. You define the format of the single string, for example fixed position within that string:
public StandardVehicle(String data)
{
this.Model = data.Substring(0, 1);
this.HorsePower = Int32.Parse(data.Substring(1, 4);
}
Not very comfortable but might work. Add some error handling. Or XML loading or other formats.
It's a little more complicated than your sample. I'm going to start with the quick and easy approach, which, while IS quick and easy, is kind of an anti-pattern, but I 'll explain that at the end:
IBritishAuto car;
switch
{
case "Vauxhall":
{
car = new Vauxhall(standardVehicle);
break;
}
case "Jaguar":
{
car = new Jaguar(standardVehicle);
}
...etc.
}
The reason why this is sub-optimal is that every time you want to add a new car type, you have to modify the original code block, and risk regression. If you're happy with this solution, and don't care, you can stop reading now.
Still there?
This pattern is called a "conditional dispatcher". There are two main strategies to improve it: refactor to a Command pattern (that's capped because it's a Gang of Four pattern), or a broader implementation of a concept called 'inversion of control'. If you're interested, here are your Google terms:
"refactor conditional dispatcher"
"inversion of control".
Good luck.

How do you get the proper mapping name from a binding source bound to a List<T>, or an anonymous type, to use on a DataGridTableStyle?

I'm trying to create a DataGridTableStyle object so that I can control the column widths of a DataGrid. I've created a BindingSource object bound to a List. Actually it's bound to an anonymous type list created though Linq in the following manner (variable names changed for clarity of what I'm doing):
List<myType> myList = new List<myType>(someCapacity);
.
...populate the list with query from database...
.
var query = from i in myList
select new
{
i.FieldA,
i.FieldB,
i.FieldC
};
myBindingSource.DataSource = query;
myDataGrid.DataSource = myBindingSource;
Then I create a DataGridTableStyle object and add it to the datagrid. However, it never applies my table style properties I set up because I can't seem set the proper myDataGridTableStyle.MappingName property.
I've searched Google for about 1/2 an hour and keep seeing links to the same question throughout a bunch of different forums (literally the same text, like someone just copied and pasted the question... I hate that...). Anyway, none of the suggestions work, just like the guy says on all the other sites.
So does anybody here know what I need to set the MappingName property to in order to have my TableStyle actually work properly? Where can I grab the name from? (It can't be blank... that only works with a BindingSource that is bound to a DataTable or SqlCeResultSet etc.).
I'm thinking it could be an issue with me using Linq to create an anonymous, more specialized version of the objects with only the fields I need. Should I just try to bind the BindingSource directly to the List object? Or maybe even bind the DataGrid directly to the List object and skip the binding source altogether.
Thanks
PS - C#, Compact Framework v3.5
UPDATE:
I've posted an answer below that solved my problem. Whether or not it's the best approach, it did work. Worth a peek if you're having the same issue I had.
I've found the way to make this work. I'll break it out into sections...
List<myType> myList = new List<myType>(someCapacity);
.
...populate the list with query from database...
.
DataGridTableStyle myDataGridTableStyle = new DatGridtTableStyle();
DataGridTextBoxColumn colA = new DataGridTextBoxColumn();
DataGridTextBoxColumn colB = new DataGridTextBoxColumn();
DataGridTextBoxColumn colC = new DataGridTextBoxColumn();
colA.MappingName = "FieldA";
colA.HeaderText = "Field A";
colA.Width = 50; // or whatever;
colB.MappingName = "FieldB";
.
... etc. (lather, rinse, repeat for each column I want)
.
myDataGridTableStyle.GridColumnStyles.Add(colA);
myDataGridTableStyle.GridColumnStyles.Add(colB);
myDataGridTableStyle.GridColumnStyles.Add(colC);
var query = from i in myList
select new
{
i.FieldA,
i.FieldB,
i.FieldC
};
myBindingSource.DataSource = query.ToList(); // Thanks Marc Gravell
// wasn't sure what else to pass in here, but null worked.
myDataGridTableStyle.MappingName = myBindingSource.GetListName(null);
myDataGrid.TableStyles.Clear(); // Recommended on MSDN in the code examples.
myDataGrid.TablesStyles.Add(myDataGridTableStyle);
myDataGrid.DataSource = myBindingSource;
So basically, the DataGridTableStyle.MappingName needs to know what type of object it is mapping to. Since my object is an anonymous type (created with Linq), I don't know what it is until runtime. After I bind the list of the anonymous type to the binding source, I can use BindingSource.GetListName(null) to get the string representation of the anonymous type.
One thing to note. If I just bound the myList (which is type "myType") directly to the binding source, I could have just used the string "myType" as the value for DataGridTableStyle.MappingName.
Hopefully this is useful to other people!
Just to add to the collection of answers already on this page....
I was just frustrated with this same issue trying to develop my fist application using windows forms and compact framework (For Windows Mobile 6.5).
What I found out, through Marc Gravell's comment above is that indeed is possible to get the run time MappingName inspecting the properties of the DataGrid. Doing this I found out that when binding my List<MyType> directly to the DataSource property of the DataGrid, the DataGrid was actually looking for a DataGridTableStyle with the MappingName of
"List`1"
instead of any combination of List<MyType> or MyType...
So... by putting "List`1" in the Mapping name on the DataGridTableStyle Collection Editor (at design time), I was able to customize the columns and other properties without having to create them all at run time.
I just hope this adds some more to the answers already provided. Thank you all for providing me with the guidelines.
The query returns IEnumerable<T> for some T, but most binding sources (except ASP.NET) require IList (such as any IList<T> implementation) - try adding .ToList() - i.e.
myBindingSource.DataSource = query.ToList();
A BindingList<T> might work even better (if it is supported in CF 3.5) since it has better support for some of the common binding scenarios; if you need this (and assuming BindingList<T> exists on CF 3.5), you can add an extension method:
static BindingList<T> ToBindingList<T>(this IEnumerable<T> data)
{
return new BindingList<T>(new List<T>(data));
}
then call:
myBindingSource.DataSource = query.ToBindingList();
For completeness, an alternative to an IList is IListSource (or even Type for purely-metadata scenarios), which is why DataSource is commonly typed as object; if it wasn't for this issue, the compiler probably would have been able to tell you the problem (i.e. if DataSource was defined as IList).
I followed this answer and found that the MappingName always came out to be the underlying class name (myType in the example).
So it seems that putting the collection it through the BindingSource solves the problem anyway and that there is then no need for BindingSource.GetListName(null).
Also I found no need to ToList() the query as the BindingSource will also do this for you.
Many thanks to Jason Down for putting me on the right track.
I was facing same problem for setting column width.
After lot of R & D, i changed code as below and its working fine.
Code:
DataGridTableStyle tableStyle = new DataGridTableStyle();
tableStyle.MappingName = dgCustom.DataSource.GetType().Name;
where dgCustom is DataGrid ID in dgCustom.DataSource.GetType().Name which is working perfectly.

Categories

Resources