Custom property descriptor and flattening hierarchies - c#

I have a custom property descriptor that I use to support flattening object hierarchies.
To accomplish this I subclassed PropertyDescriptor and I store a linked list to the "next" (child) property that I want to retrieve the value for.
This enables me to bind subproperties to a grid(export to excel, whatever) in a "flat" manner.
eg.
Grid(bound Property, Caption)
Col1:Customer.Name(Customer)
Col2:Customer.Address(Address)
Col3:Customer.OutstandingOrders.Count(Outstanding Orders)
The problem is that once I add in a column with a duplicate name, regardless of the fact it's got a unique caption it will retrieve the property for the 1st one but still put the correct header in:
Col4:Customer.Company.Name(Company)
Any ideas?

The problem is that once I add in a column with a duplicate name, regardless of the fact it's got a unique caption it will retrieve the property for the 1st one but still put the correct header in.
Can you clarify that line? I've done this before, but I used the navigation path in the imaginary name - i.e. I might have the PropertyDescriptor.Name report Customer_Company_Name rather than Name, and use the .DisplayName to report something more readable.

Related

Storing the state of a VSTO Outlook plugin with a draft message

I'm working on a VSTO plugin for Outlook (C#) and find nothing about saving or embedding the state/options of my plugin with a draft message.
Is there any mean to do that ?
Ex: if my plugin makes the message to be displayed in red, I want to have my draft re-opened written in red.
Any idea ?
Use the UserProperties.Add method which creates a new user property in the UserProperties collection. Use the UserProperties property to return the UserProperties object for an Outlook item. This applies to all Outlook items except for the NoteItem.
Use the Add method to create a new UserProperty for an item and add it to the UserProperties object. The Add method allows you to specify a name and type for the new property. When you create a new property, it can also be added as a custom field to the folder that contains the item (using the same name as the property) by setting the AddToFolderFields parameter to true when calling the Add method. That field can then be used as a column in folder views.
To set for the first time a property created by the UserProperties.Add method, use the UserProperty.Value property instead of the SetProperties and SetProperty methods of the PropertyAccessor object.
If you need to keep the data for the folder or Outlook account in general (not per item) you may consider the StorageItem instead. That is a message object in MAPI that is always saved as a hidden item in the parent folder and stores private data for Outlook solutions.
The Outlook object model does not provide any collection object for StorageItem objects. However, you can use Folder.GetTable to obtain a Table with all the hidden items in a Folder, when you specify the TableContents parameter as olHiddenItems. If keeping your data private is of a high concern, you should encrypt the data before storing it.
As Eugene advised, you can use MailItem.UserProperties collection to set/read your custom properties.
A couple points to consider - since named properties are a finite resource, you can have at most 32k of them per mailbox. Once you go over, the mailbox is pretty much dead. So use as few unique properties as possible, and definitely do not use anything dynamic, such as the message subject in the property name.
If you set a user property on an outgoing message, Outlook might force it to go out in the TNEF format. To prevent that from happening, set the property value using MailItem.PropertyAccessor.SetProperty. You can use the same DASL property name that your user property uses, but the point is to avoid using the UserPropeties collection. You can see the DASL property name in OutlookSpy (I am its author) - select a message with your user property set, click IMessage button, select your property, see the DASL edit box.

Unique identifier for Interop.ListObject from VSTO - C#

I need a way to insert (or use an already implemented property that could serve as) a unique identifier into a Microsoft.Office.Interop.Excel.ListObject instance.
The problem is that when I'm creating a new ListObject as:
var excelTable = worksheet.ListObjects.Add(ExcelInterop.XlListObjectSourceType.xlSrcExternal, DUMMY_CONNECTIONSTRING, false, true, cellRange);
I cannot rely on the Name property of excelTable to browse for it in the collection since the user could change the value of that property anytime afterwards.
After browsing trough the object properties I found nothing I could use out of the box (like a Tag property for example, which exists in a Microsoft.Office.Tools.Excel.ListObjecttype of object I cannot use at this point due to dependencies) ...and other weird stuff like a DisplayName that appears not only unable to be set directly but also to reflect the exact same value that the Name property has at all times (Why would you want to have 2 properties that reflect the same value at any time?).
I've thought on either creating my own implementation of this class or probably use the Comment property to store a GUID (which I don't know why kinda feels wrong):
excelTable.Comment = Guid.NewGuid().ToString();
Can you suggest of another way to accomplish this task?
Thanks a lot!
It is quite frustrating that there is no "Tag" (or similar) property that you could set on Excel objects. I'm facing the same issues as you. Here are two options that you can use:
alternative text property (for the table it is only visible by right clicking the table, selecting table and alternative text). This is probably a bit cleaner than Comment since the UI for comment is always visible.
you could also generate a wrapper object that contains a direct reference to the ListObject. So one wrapper object for each created ListObject. This works fine until you save / open the workbook again. If you need to be able to identify the table again after reopening the workbook you would still need to write some id to Comment or Alternative text. You could probably do a clean implementation by using events like BeforeSave and AfterSave (add alternative text before save so it saves to disk, then remove it again after save so that the user doesn't see it. When the workbook opens you load up your wrapper objects and remove the alternative text).

MEF update exported part metadata (the metadata view is invalid because property has a property set method)

I have an application and I'm using MEF to compose it. I want to know if it is possible to update the Metadata information of the parts after they were imported.
The reason to do this is the following: I display the imported parts' name and an typeof(int) property in a ListBox, and they are not loaded until the corresponding ListBoxItem is selected (pretty standard). Now I want to update the Metadata info of one part when some event raises, so the displayed info in the ListBox is somethind like "[Part name] ([new number])".
I'm importing the metadata as an Interface that defines it's info, but when I set the int property to be editable (with a set accesor) I receive the following execption at composition time:
"The MetadataView 'myMetadataInterface' is invalid
because property 'myInt' has a property set method."
Is there ANY way to achieve this? Or is the metadata ALWAYS read only once the part is created?
I know this question looks weird, but it doesn't make it any less difficult and therefore interesting ;-)
EDIT (based on Lee's answer, in order to keep people to the core of the question)
I just want to know if it is possible to update a Metadata property after the part is composed, but before it is actually loaded (HasValue == false). Don't worry about filtering or finding the part.
I added a property to the export inteface, which is meant only to be represented in the UI and to be updated, this property has no other function and the parts are not filtered by it.
Thanks
Metadata filtering and DefaultValueAttribute
When you specifiy a metadata view, an implicit filtering will occur to
match only those exports which contain the metadata properties defined
in the view. You can specify on the metadata view that a property is
not required, by using the
System.ComponentModel.DefaultValueAttribute. Below you can see where
we have specified a default value of false on IsSecure. This means if
a part exports IMessageSender, but does not supply IsSecure metadata,
then it will still be matched.
citation
Short Version (EDITED in after question edit).
You shouldn't ever need to update metadata at runtime. If you have some data that should be updated and belongs to a mef part, you need to choose to either have it be updated by recompiling, or store that data in a flexible storage outside of the dll. There's no way to store the change you made in the dll without recompiling, so this is a flawed design.
Previous post.
Altering values on the view would by lying about the components loaded. Sure the metadata is just an interface to an object that returns initialized values; sure you can technically update those values, but that's not the purpose of metadata.
You wouldn't be changing the Name field of an instance of Type. Why not? Because it's metadata. Updating metadata at runtime would imply that the nature of the instance of real data is somehow modified.
This line of code, if possible, wouldn't introduce the Triple type.
typeof(Double).Name = "Triple";
var IGotATriple = new Triple();
If you want to alter values, you need to just make another object with that information and bind to that. Metadata is compiled in. If you change it after a part is loaded, it doesn't change anything in the part's source, so you'd be lying. (unless you're going to have access to the source-code and you change it there and recompile).
Let's look at an example:
[Export(typeof(IPart))]
[ExportMetadata("Part Name","Gearbox")]
[ExportMetadata("Part Number","123")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class GearBoxPart : Part { public double GearRatio ... }
Now, let's assume that you had a UI that showed available parts and their numbers. Now, the manufacturer changes the part number for whatever reason and you want to update it. If this is possible, you might want to consider storing part number in a manifest or database instead. Alternatively you'd have to recompile every time a part number changes.
Recompile is possible. You have a controller UI that does the above, but instead of updating the metadata, you submit a request to rebuild the part's codefile. The request would be handled by parsing the codefile, replacing the part number, then sending off for a batch recompile and redistribute the new dll. That's a lot of work for nothing IMO.
So, you setup a database. Then you change the object metadata to this.
[ExportMetadata("OurCompanyNamePartNumber","123")]
Then you have a database/manifest/xml that maps your unique permanent static part number that your company devises to the current part number. Modifications in your control UI update the database/manifest/xml.
<PartMap>
<PartMapEntry OurCompanyNamePartNumber="123" ManufacturerPartNumber="456"/>
...
</PartMap>
Then the end-user UI does lookups for the part by manufacturer part number, and the mef code looks in the PartMap to get the mef part number.

Dynamically binding data contained within an object to labels in a windows form

I have the following data and objects in my program.
A DynamicObjectContainer that contains the following objects.
MeasurementParameters : DataContainer (DataContainer is the base class)
This MeasurementParameters object has many public properties, whose names I know only during runtime. I have also set up internal wiring in the DataContainer base class such that, I can access the values of the properties contained in the MeasurementParameters class using an easy to use interface.
Ex : Say I have a property in MeasurementParameters named as "pumpspeed" (type string). I can access the value of that property using this function.
MeasurementParameters.GetStringValue("pumpspeed");
I have achieved this by creating lists of delegates internally in the DataContainer object using reflection during construction of the object. (This is a one time thing.)
So far so good.
Now I am stuck at the point where I want to display these values within MeasurementParameters in a windows form.
Since I only know the property names at runtime, I have to provide the user with some method to map the property names (defined only by him in a script file) to the fixed labels within the form. So the user saves the mapping data to the table in the following format.
Entry : "pumpspeed" "label22"
I want a fast and efficient method to fetch this mapping from the database, fetch required data from the MeasuremetParameters object and display it in the windows form.
NOTE : If this is a one time operation, I have many solutions. The problem is two fold.
There are a huge number of properties in the MeasurementParameters (at around 200)
The MeasurementParameters object contains functions that update it's properties continuously. SO My windows form has to call those functions to update the MeasurementParameters object data, fetch the data and display it in the correct labels.
ALSO, this should happen in cycles of around 2 -3 times a second. (ideally)
Can anyone help me in architecting a solution for this?? A general object structure and relationship advice will also be helpful to me.
I can post the code I am using if required.
Not seeing a huge problem here
So you have Table ObjectID, PropertyName, ControlName
On opening the form / selecting the object, query them out
Build a Dictionary Keyed by PropertyName with a Value of the Label (looked up by the name of teh control from the query MyForm.Controls.FindByName(Somename). Add an OnPropertyChangedEvent to your class that throws up the name of the Property in event args then add a handler on the form
Mappings[e.PropertyName].Text = Object[e.PropertyName].GetStringValue;
Might have to twidlle with it to deal with say display controls that aren't Labels, or Panels on the Form, but it should just batter away.

CollectionViewSource Sorting Dynamically

I have a Car class with various properties.
I have a static Cars collection class in which, for simplicity, I have defined a bunch of car items which I can make available in XAML via an object data provider.
I have a collection view source defined in XAML which binds successfully to my ObjectDataProvider as it should.
I have a listbox which shows the collection.
I added the sort to the CVS as recommended in all the standard tutorials and all works fine.
My Question:
Suppose I want to sort on a different field. Surely there is a way to change this without having to give the code to the customer. So I implemented a combo box.
I use the following code to load a list of properties from the Car class into the combo box but I don’t just get a list of properties. I also get their data types. I don’t want this.
Car xyz=new Car(); //Make a temp Car Object so we can get a list of properties.
//Assign this to the combobox for listing.
cbxSortPrimary.ItemsSource = xyz.GetType().GetProperties();
Result (what displays in the combo box):
System.String Model
Double Price
Int32 NoOfPrevOwners
DataType PropertyName
ect... ect...
ect... ect...
ect... ect...
My goal is to load in the property names to the combo box. Then use the selected property name to build a line of code like:
myListBox.Items.SortDescriptions.Add(new SortDescription(ComboBox.SelectedItem.ToString(), ListSortDirection.Descending));
Where the ComboBox.SelectedItem.ToString() will contain the name of the property to be sorted on.
So how do I get rid of the data type in front of the property name. I know I can do all sorts of messy loading into another list, then a bunch of string handling looking for the first space from the right and chopping everything before that. But surely there must be an easier way.
Effectively what I am looking to do is to let the user sort on a different property of the Car class (So I need to load the properties somewhere and make them available for the user to select, hence the combobox). What I am asking is that there must be an easy way to get the list of properties without all the string manipulation code, and hopefully without much reflection (unless I am already using it without knowing), as it seems like a very basic requirement.
Thanks in advance for any help!
It's not messy at all:
var properties = new List<string>();
foreach (var info in typeof(Car).GetProperties())
{
properties.Add(info.Name);
}
cbxSortPrimary.ItemsSource = properties;

Categories