I have a WPF treeview that displays multiple nested classes based on hierarchical datatemplates. It uses the classes here: https://complexdatatemplates.codeplex.com/. This question applies to DataGrids also, though or any control who's selected item is an anonymous class.
What I want to do seems simple. I want to select an item, then press a button and operate directly on the selected item--not the SelectedItem property of the tree view, but the object behind it. But, since the compiler doesn't know the class of the selected item until runtime, it understandably won't give me access to any of the methods or properties in it.
The class in this case is 'Roms', the treeview is 'DB_tree'. I can create a new object from the selected item, and cast it as Roms, then do whatever I want. Like this
roms = (Roms)DB_tree.SelectedItem;
But I can't figure out how to operate on the actual object that would be returned by DB_tree.SelectedItem.
Well, it turns out that by assigning (Roms)DB_tree.SelectedItem to another object
DummyObject = (Roms)DB_tree.SelectedItem
Then everything I do to DummyObject is done to the original object. The equals operator for Objects acts more like assigning an alias than an equals operator.
Strange, that, though clearly common sense to OO programmers everywhere. If the equals operator worked the same way for doubles or ints it would be impossible to do math, now that I understand it, it is cleaning up a lot of my code.
Related
I'm trying to build a check list for a ToolStripMenuItem that automatically handles the checking and unchecking of an item and then I provide an event to the programmer allowing them to handle what happens next. If something like this already exists, I would LOVE to know where it is. I've created the collection editor for my custom ToolStripMenuItem and I can add check lists to this collection of checklists. My problem is you create the collection editor like this:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(typeof(ToolStripItemExtCollectionEditor), typeof(UITypeEditor))]
I need to be able to pass this ToolStripMenuItem's DropDownitems to this collection editor so when you add a new checklist and click on the items property of the checklist you can add/remove any one of the known ToolStripMenuItems to/from the checklist. Passing a reference won't work since all of this is happening inside an attribute and I wouldn't know where to begin if the answer is reflection.
This answer applies to VB.NET. I plan on turning this into C# for a DLL, but for now it's in vb.net because that's where I started this idea from and the language the project is in.
Here's what I have so far:
ToolStripMenuItemExt
Purpose: My custom ToolStripMenuItem.
ToolStripMenuItemExt has a CheckListSheet which contains a reference to ToolStripMenuItemExt's DropDownItems (I passed in dropdownitems byref and not byval). It has one property that returns the CheckLists object in CheckListSheet.
CheckListSheet
Purpose: Maintains a reference to the collection I'm observing through an observable collection type and an object of the collection I return in ToolStripMenuItemExt.
CheckListSheet has the CheckLists object. The dropdownitems I pass in byref are stored in an ObservableToolStripItemCollection which hopefully when I get to testing it allows me to update the collection of checklists easier since it inherits ObservableCollection(of ToolStripItemCollection). This class also has a shared function that returns the observable collection which has a scope identifier of private shared.
CheckLists
Purpose: The CollectionBase type that stores CheckList objects.
CheckList
Purpose: Stores the ToolStripItemCollection whose objects act as a single item checked checklist (only one item is checked at a time).
This has some properties for the designer and the collection for the check list. Eventually I'll add in the logic to check and automatically uncheck and raise an event for it.
MenuItemCheckListCollectionEditor
Purpose: Allows a collection of known and instantiated ToolStripItem objects to be displayed and added to a CheckList.
Right now it demands I give it a Type or array of Types so it can establish itself what type of CollectionEditor it is. I haven't be able to show a drop down of types or a drop down of ToolStripItem objects. Any class having ToolStrip in their name inherits ToolStripItem which is why I use this type of object.
If ANYONE has any advice on my current answer or can forecast any foreseeable pitfalls please share. I don't care if you talk in c# or vb.net. Maybe I just need to stop and turn this into c# code. Maybe this is impossible. I am making progress though. What would be extremely helpful is figuring out how MenuStrip's collection editor is able to populate a dropdown of ToolStripItems
[Update]
A collection Editor requires you to provide a type for it to display. This type has to inherit CollectionBase which means at design time there's no way for it to reference the dropdownitems. :sigh:
In my program I have TreeView nodes that I need to be able to shift up and down, basically change the order. My TreeView is an ObservableCollection of a specific Data Model. Each node has a property called "Rank", this is the value that I would like to sort the collection by. With that being said I referred to this question. From that question I discovered this blog page. I am attempting the second method involving the sort function of a List.
This is the example that I am looking at:
List<Person> list = new List<Person>(people);
list.Sort();
Please note that the "Rank" value of each node is in working order and changing correctly. I just need to find a way to re-order the collection based off of that property and reflect it in the View.
My Problem: When trying to implement the above solution I get an InvalidOperationException. I feel like I do not understand how to tell the List to sort based off of rank.
What my code looks like:
List<TreeModel> sortedTree = new List<TreeModel>(TreeCollection);
sortedTree.Sort();
What am I missing here? How do I sort the collection based off of the rank property and reflect those changes in the view?
Thank you.
*I believe I may have posted about this before, so if for some reason this question is too similar my older one, I'll just delete the old one.
Sort throws InvalidOperationException its component type does not have a default comparison:
The default comparer Comparer.Default cannot find an implementation of the IComparable generic interface or the IComparable interface for type T.
You can however supply the comparison as the first parameter to Sort:
sortedTree.Sort((x, y) => x.Rank.CompareTo(y.Rank));
To pass the sorted items back to the original collection, you could either clear/repopulate CurrentCollection, or simply assign it a new instance (don't forget to RaisePropertyChanged if you do the latter):
CurrentCollection = new ObservableCollection<TreeModel>(sortedTree);
You need to pass property name on which you want to sort your list like this -
sortedTree = sortedTree.OrderBy(m => m.Rank).ToList();
I thought I had type editors and converters nailed until I tried to persist a Readonly Reference type property after editing it in a UITypeEditor.
In my UITypeEditor, because I'm working with a read only property, I'm careful to pass back the original value (after updating the relevant sub property).
This change is reflected immediately on the designer but will not be persisted unless I do something like resize the control that the property is attached to.
To fix this I, blindly, include a call to context.OnComponentChanged() before returning the value.
I can see why this is needed. It's a reference type, I've altered it (not replaced it), and the property grid doesn't know this. I have a couple of questions for clarification:
Do I need a call to context.OnComponentChanging as well? A simple call to OnComponentChanged works in the tests I've done so far, but I don't want biting on the arse at some point in the future.
Also, is there any danger that, with my call to OnComponentChanging, I'll be persisting other components, in DesignerTransactions, that I shouldn't be persisting?
(update) ICustomTypeDescriptor works for my Windows Forms app, but not for Silverlight; Not supported. I will keep investigating this idea though and see where i get to.
(/update)
I have, say a few switch panels (for those that like analogies).
Each of these switch panels has switches that have a Name(string) can be in state(bool) of On or Off.
The switchpanel and switches are objects that have INotify interface on them.
Using the switches Names, I create a list of all possible switch names over the collection and create a dynamic class that has all these Names as properties.
SwitchPanel1 (Switches( Switch1 ("Main",On) , Switch2("Slave",Off)))
SwitchPanel2 (Switches( Switch1 ("Bilge",On) , Switch2("Main",Off)))
Produces a collection of
(Main,Bilge,Slave)
And a dynamic class is produced that has the properties:
SwitchPanel : (SwitchPanel)
Main : (Switch)
Bilge : (Switch)
Slave: (Switch)
The idea is that if the switch panel has a switch with the Name of the property, it is placed on that property. So using a bit of linq
propeties["Main"].SetValue(newSwitchType,SwitchPanel.Switches.FirstOrDefault(sw => sw.Name == "Main"));
I want to cast this new dynamic class to INotfyPropertyChanged AND catch the actual changes on these new properties, so if a switch changes state the dynamic object will report it.
Why? It needs to be displayed in a list view and the list view I'm using has its binding by supplying the Property name, and not the binding path.
It also attempts to catch INotify events by casting the object against INotifyPropertyChanged. This means it will sort and/or group when things change.
If you know of a better way to do this let me know. Please.
You probably don't need a dynamic class. You can implement runtime binding properties via ICustomTypeDescriptor / GetProperties(), creating your own PropertyDescriptor implementation that returns the named switch. It isn't clear what knows first about the change, but you could either use INotifyPropertyChanged, or the older property-specific change event, again tied to each property (so each PropertyDescriptor attaches to, for example, the event in the named switch.
Not trivial, but not impossible either.
I'm currently having a problem with a ShoppingCart for my customer.
He wants to be able to add Text between the CartItems so I was wondering if there is some way to still only have one List.
My solution would be to have two lists, one of type IList that gets iterated over when calculating Weight and overall Price of the Cart while having another IList that only exposes the necessary fields for displaying it in the ListView and that is a SuperType of CartItem. (But how do I then access additional fields for the listView, defaulting weight and price to 0 in the Description-Text-Class would break LSP).
But having two lists somehow feels a bit odd (and still gives me problems), so I was wondering if I could do some sort of a TypedList where I specify the Type of each item.
Any suggestions are welcome, I'm not really happy with both options.
Use an interface:
ICartListItem
And make your list be:
List<ICartListItem>
Now, create several types, have all of them implement this interface, and you can store them all safely in your list.
Alternatively, if you want there to be some default logic in a CartItem, use a base class instead of an interface.
You can make a class and, inside of that, define the properties of the required list type and then make a list of same class.
For example, if I wanted to make a list of strings and bools, I would make two properties in one class and then make a list of that class.
The Interface sounds like overkill. I'd just add a property to your current CartItem named something like "TextAfterItem".
Also: make sure your customer understands the cost of this feature in terms of security overhead. It sounds like they think this should be a simple update, but you're allowing users to enter text that will be displayed directly back to the page, and that's a dangerous proposition.