I have a collection that populates a treeview, and I want to add functionality that allows a node to be added with custom properties. Would the best way to go about it to clone the selected node and then edit the properties from there, or to add a completely new blank node?
The following method is how I'm currently trying to add nodes.
public void add()
{
hClass clone = new hClass();
clone = SelectedNode;
Topics.Add(clone);
}
And then I call the method via a command.
It really depends on what you do with these nodes and which pieces of code an access them. Eric Lippert wrote a blog post focused on immutable collections which might shed more light on your problem.
The collection is a set of references (pointers) to the nodes. If you assign an existing instance of hClass to the collection, a change to the instance will be visible in every place that can access it. If you're using it in a WPF GUI, read about Responding to data source changes
Related
It seems like someXml.CreateElement("abc"); does only one thing: create the element. It does not add it as a child as I expected, nor does it seem to do anything else.
But that doesn't make much sense. Why create an element with an instance method instead of with a static method? That would indicate that it does have some relationship to the instance. But I couldn't find anything and hence my question.
The remarks in Microsoft's Documentation mentions that default attributes are created on the returned object. Namespaces come to mind as they may be automatically applied to the new element based on the XmlDocument's schema/defaults.
It also states that it must be manually added to the desired parent node.
From https://msdn.microsoft.com/en-us/library/fw1ys7w6(v=vs.110).aspx
Note that the instance returned implements the XmlElement interface, so default attributes would be created directly on the returned object.
Although this method creates the new object in the context of the document, it does not automatically add the new object to the document tree. To add the new object, you must explicitly call one of the node insert methods.
I think the reason that the method doesn't automatically add the element as a child like you were expecting is because there would be no way to know where the element should be added. The document could have many children and there is nothing to specify which element the created element should be added to. It can't just add it by default to the root element, because there's a good chance that isn't always going to be the desired location.
As previously mentioned, the perks of having it be an instance method rather than a static method would be to automatically create the default attributes (such as namespace) on the newly created element. That way once it is created it should just need to be added to the proper location in the document.
My initial thought was to create an Umbraco Relation and associate the Umbraco Member to the Nested Content node. Sadly, I found this form post asking a similar question and as you can see in Matt Bailsford's first response:
Unfortunately nested content can't have an ID value as they don't truely exist
I did find the issue/feature that was discussed in the forum post; however, it just adds parent information to the DetachedPublishedContent object and doesn't solve my issue. After reading the form post and the conversations of Hendy Racher, Matt Bailsford and Lee Kelleher in the github pull request, I still don't understand why Nested Content doesn't create a node in Umbraco.
So basically I need the Nested Content nodes to be created as Umbraco nodes and then stored as a JSON string in the property field. There are a few ways that I see this could be accomplished:
Create a Custom Property Editor for Umbraco Backoffice - I would start with a copy of Nested Content and add code to create the node and attach it before saving the node as a JSON string.
Use the Umbraco Multinode Treepicker control - This control was suggested by Hendy and Jeavon in this forum post as a way to allow a user to select multiple content nodes. Unfortunately going this route would require the user to create the "nested content" nodes first. Then they could associate those "nested content" nodes with the original node. We really like the user experience of the Nested Content control where it allows you to create nodes dynamically in the property editor.
Find a way to associate the Member to the "Nested Content" node - This option would require that I store an association between the top node and it's respective "nested content" node to a Member in Umbraco. There are two issues that come to mind when trying to go this route:
How should I associate the "nested content" node to the Member in a standard Umbraco way? - I immediately think of creating a link table in the database but, in my understanding, that is not the standard Umbraco way. I am still fuzzy about the best way to do this inside Umbraco.
Is there a way to uniquely identify the "nested content" node? - I realize there is a sort order value being set according to the pull request I found above but if the user reorders the nested content items will it change the "nested content" node to member association?
At this point, I am leaning towards going with option 1, but I wonder if option 3 is a better direction. In reality, I don't believe this is a new problem that someone hasn't already solved, and I hate to create another custom property editor if there is one just like it out there already.
So if you know of a better way to solve this problem please let me know.
The problem is - as you mention it - that Nested Content nodes aren't really real nodes. I don't think the right way to solve your problem is to try hacking Nested Content into doing something it really wasn't created to do.
The problem about creating nodes and also having references to them on the Nested Content node is that essentially every node in Umbraco needs to "live" somewhere.
You could choose to say that a node lives under the parent it is nested into, but how would you then differentiate between nested nodes and actual child nodes - this would require another hack as it is really working around how nodes are meant to be structured and handled in the Umbraco core.
Even if you did manage to get this working, I suspect you would have a lot to deal with to actually make it work as good as Nested Content currently does:
You would have somehow wrap every single node in the Nested Content editor into a object to be able to store meta data like the node ID it is connected to and the sortOrder when reordering all of the nested content nodes you have on there.
(edit: I think it actually already stores some sort of wrapper object here, but you would have to change the logic in here to actually handle a reference to another node instead of just deserializing json stored here, as a node)
You would also have to manually hook into events making sure the actual edits you do while on the parent node actually ends up being persisted to the nested nodes.
Deleting a nested content node, or a parent that has any nested nodes - you would have to handle deleting any orphaned nodes.
There's most likely a lot of stuff I've missed but my point is - you will have a lot of trouble trying to do this.
I think you should consider another approach if you really need to do this:
It would be possible to create a picker similar to a normal node picker, that simply allows you to browse through nodes as a normal picker would do. When you pick a node, instead of just selecting it, it should then fetch the nested content nodes and show those in the UI.
There's however the little quirk that you could essentially be having multiple properties storing each their set of nested content nodes on one single content item - so you would need some nice way of handling this in the UI.
When you select one or multiple of the nested nodes, what your picker would store would be something similar to [guid-of-the-real-node]_[propertyAlias]_[guid-of-nested-content-item].
I am not certain if Nested Content ever got the GUID unique ID/key feature implemented - Matt and I discussed it some time last year and we tried adding it in, in a custom build I needed for a project. If it isn't there I would suggest you ask Matt if he can get that in. It was essentially just giving each nested content item a "fake" unique ID (GUID) that you could use to identify it from other nested content items stored in the property. (You would have to ask Matt about the status of this)
Doing this would allow you to (on your member) have a reference that you lets you find the actual content node, then the property where the content is stored, and lastly the actual nested content node you have picked.
You should however note that this is very prone to breaking and needs a lot of null handling:
If you change the property alias of the property you are storing nested content in on the parent, it will lose the reference.
If you delete the content item storing the nested items, the picked items no longer exist and you have a missing reference in the picker on your member (needs null handling)
If you delete a nested content item - same as above. You have a missing reference in your picker.
Apart from the solution above, I don't really see another way of doing this currently with the requirements you have.
I have a class contains a lot of properties. One of them needs a special UI to be edited.
The user may press edit to this property in the UI then he make changes and either press OK or Cancel
e.g.
class A{
private List<Employee> employees;
public void EditMyEmployees(){
EmployeeEditorForm editor = new EmployeeEditor(employees);
if(editor.ShowDialog() == DialogResult.OK){
employees = editor.GetEditedEmployeesList();
}
}
}
The problem in the previous code that the editor has a copy of the reference to the employees List
and when the editor makes any edits in the List it will be reflected in the original object.
So, pressing OK or Cancel will have the same effect (the object is already updated) and no need for the step
employees = editor.GetEditedEmployeesList();
I know that making deep copy for the employees array before sending it to the editor will solve the problem but still I think this is not the efficient way to do it
I am looking for a design pattern that can make this in a better way.
You don't necessarily need to deep copy the whole collection. You just need to keep track of the elements that changed. Within your EmployeeEditor, use three lists (List<Employee> to keep track of:
Added employees
Removed employees
Changed employees
Upon Cancel, you would need to remove the "added" items, add back the "removed" items, and replace the changed items with their original state.
Note that the changed employees list would need to keep a copy of the original state of the object. If the Employee class has some sort of unique id, you can match cased on that id. Otherwise, the "changed" list would need to be a List<Tuple<Employee, Employee>> so that you can store the matching items.
Also note that, when changes happen in the employees list, you also need to make necessary changes in those three lists. For example, if an new employee is added and then removed, you also need to remove that record from the "added" list. Or it is possible an employee is changed and then removed, in which case you also need to remove from "changed" list.
Having said all this, if I were you, I would make a decision based on the expected use cases and real performance problems (not anticipated performance issues). It is very likely that simply deep copying your collection is the simplest and least error prone way.
There are two kinds of changes involved here: (1) changes to the list (Add / Remove) and (2) changes to individual elements of the list (employees in this case).
Now, part of the problem comes from the semantics of OK/Cancel. If you restrict the scope of these two buttons to changes of the second kind (i.e., changes to the elements of the list), you will be able to handle Remove with a confirmation dialog on this particular action ("Remove such and such"?). For the Addition you don't need anything special, just add a new element to the list. If the user changes their mind they will still have the Remove action available.
For changes on a particular element (second kind) you can use the command pattern as mentioned in the comment. More simply, you could initialize temporary variables for all the fields your editor displays from the element under edition. As the user modifies some values your editor will update the corresponding temporaries. If the user press Cancel you will simply forget those changes (or reinitialize them from the element). If the user press Apply (yes, you should include the Apply button also,) you will now write each of the temporary values onto the corresponding element's attribute. If the user hits OK you would Apply and Close.
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:
After this: Acess to a DataGrid through C# code and manipulate data inside the DataGrid I decided that i should deserialize my XML data and use it that way because i need basic CRUD manipulation in my application.
I already have my xml data class (using XSD tool, you can find the class here -> http://pastebin.com/6GWFAem6) and have my data deserialized, problem is:
I need a TabControl with as many tabs as Semestre in my xml, each tab will have GPASemestre.Nome header.
Inside each tab i need a DataGrid with Cadeiras of that specific Semestre.
I need to be able to CRUD data in the DataGrid and the tabs.
Questions:
To solve all of this what do you think is best? Creating everything (tabs + datagrid) and make the necessary binds (which i don't really know what they will be) / populate the DataGrid somehow, in C# only? Or there is a way to simplify code in C# using XAML?
Cadeiras are stored in arrays so, each time i add a new one, i need to create a new array (or create a new array with more spaces and manage it), i already saw some questions here where ppl used List's but where having troubles with it, is it possible to use a list or not? If so, what do i have to change in the XSD auto generated class?
Thanks in advance!
I would suggest the use of data-binding and data-templating (read those if you have not yet done so) for as much as possible, for that to work well the auto-generated classes need to be adjusted to support it.
The first step is implementing INotifyPropertyChanged in all the properties which are not collections so that if the property is changed the UI will update automatically. Only use arrays for deserialisation at best, after that copy the elements to a property which is of type ObservableCollection<T>, or any other collection which implements INotifyCollectionChanged so that the grids update when a new item is added to the collection and never touch the arrays again.
You could also make the Array property "virtual" (has no backing field, just manipulates on get & set), e.g.:
//The property & field used for binding and editing
private readonly ObservableCollection<GPASemestre> _ObservableSemestre = new ObservableCollection<GPASemestre>();
public ObservableCollection<GPASemestre> ObservableSemestre { get { return _ObservableSemestre; } }
//The property used for serialisation/deserialisation
public GPASemestre[] Semestre
{
get
{
return ObservableSemestre.ToArray();
}
set
{
ObservableSemestre.Clear();
foreach (var item in value)
{
ObservableSemestre.Add(item);
}
}
}