IHierarchyData and IHierarchicalEnumerable in Winforms - c#

Currently,I know how to do a lazy implementation of the loading procedure of the nodes in a treeview control, and read the related questions in stackoverflow, but I'm also reading about IHierarchyData and IHierarchicalEnumerable interfaces in asp.net (I didn't know to code asp.net) that allow to bind a collection to a treeview in order to display the items automatically.
It would like to know if I can do the same in winforms and C#. I think that the interfaces previous mentioned are not available in winforms.
Thanks.

The Windows Forms TreeView does not know how to bind to an IHierarchyData instance, which isn't surprising given that the IHierarchyData and related interfaces are intended for consumption by web controls (especially site maps).
However, it's really not too hard to build your own data binding class. This seemed like an interesting problem so I threw one together just for fun. I'll walk you through the inner workings.
First, create a basic Component class. Visual Studio will start you off with code like this:
public partial class TreeViewHierarchyBinding : Component
{
public TreeViewHierarchyBinding()
{
InitializeComponent();
}
public TreeViewHierarchyBinding(IContainer container)
{
container.Add(this);
InitializeComponent();
}
}
One obvious piece of "state" this component needs to have is a mapping from each TreeNode to its IHierarchyData. Now we can hack around this by throwing it in the TreeNode's Tag property, but let's aim to make this component as non-invasive as possible and keep track of its own state. Hence, we'll use a dictionary. Add this field to the class:
private Dictionary<TreeNode, IHierarchyData> nodeDictionary = new
Dictionary<TreeNode, IHierarchyData>();
Now, at a minimum, this component needs to know how to populate a specific parent TreeNode of a TreeView class from its correspondingly bound IHierarchyData, so let's write that code next:
private void PopulateChildNodes(TreeNodeCollection parentCollection,
IHierarchicalEnumerable children)
{
parentCollection.Clear();
foreach (object child in children)
{
IHierarchyData childData = children.GetHierarchyData(child);
TreeNode childNode = new TreeNode(childData.ToString());
if (childData.HasChildren)
{
childNode.Nodes.Add("Dummy"); // Make expandable
}
nodeDictionary.Add(childNode, childData);
parentCollection.Add(childNode);
}
}
private void UpdateRootNodes(TreeView tv, IHierarchyData hierarchyData)
{
if (tv == null)
{
return;
}
tv.Nodes.Clear();
if (hierarchyData != null)
{
IHierarchicalEnumerable roots = hierarchyData.GetChildren();
PopulateChildNodes(tv.Nodes, roots);
}
}
This part should be pretty straightforward. The first method just populates a TreeNodeCollection (i.e. the Nodes property of a TreeNode) with the hierarchy obtained from an IHierarchyData instance, using the IHierarchyEnumerable interface. The only really interesting things this method does are:
Adding a dummy node when the IHierarchyData instance has children; this makes the "+" visible in the tree view, otherwise we wouldn't be able to expand any deeper; and
Adding the newly-added node to the dictionary with the IHierarchyData instance it matches with.
The second method is even simpler, it does the initial "binding work", replacing whatever is in the root of the tree with our top-level IHierarchyData instance.
The next thing our component needs to be able to do is hook the loading events from the TreeView to perform lazy-loading. Here's the code to do that:
private void RegisterEvents(TreeView tv)
{
tv.BeforeExpand += TreeViewBeforeExpand;
}
private void UnregisterEvents(TreeView tv)
{
tv.BeforeExpand -= TreeViewBeforeExpand;
}
private void TreeViewBeforeExpand(object sender, TreeViewCancelEventArgs e)
{
if (e.Node.Checked)
{
return;
}
IHierarchyData hierarchyData;
if (nodeDictionary.TryGetValue(e.Node, out hierarchyData))
{
PopulateChildNodes(e.Node.Nodes, hierarchyData.GetChildren());
e.Node.Checked = true;
}
}
First two methods should be self-explanatory, and the third method is the actual lazy-loading code. We're cheating a little here, using the TreeNode.Checked property to delineate whether or not the child nodes have already been loaded so we don't do any unnecessary reloads. I always do this when I implement lazy-loaded trees because, in my experience, I almost never use the TreeNode.Checked property. However, if you do need to use this property for something else, you can either use a different property (like Tag), create another dictionary to hold the expanded states, or modify the existing dictionary to hold a composite class (containing the IHierarchyData as well as an Expanded property). I'm keeping it simple for now.
The rest should already make sense to you if you've implemented lazy-loading in a tree before, so let's skip ahead. Really the only thing left to do at this point is implement some designer/user properties that will actually wire up the tree and data:
private IHierarchyData dataSource;
private TreeView treeView;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IHierarchyData DataSource
{
get { return dataSource; }
set
{
if (value != dataSource)
{
dataSource = value;
nodeDictionary.Clear();
UpdateRootNodes(treeView, value);
}
}
}
[Category("Behavior")]
[DefaultValue(null)]
[Description("Specifies the TreeView that the hierarchy should be bound to.")]
public TreeView TreeView
{
get { return treeView; }
set
{
if (value != treeView)
{
if (treeView != null)
{
UnregisterEvents(treeView);
}
treeView = value;
nodeDictionary.Clear();
RegisterEvents(value);
UpdateRootNodes(treeView, dataSource);
}
}
}
Easy peasy. We've got a DataSource property that accepts the root IHierarchyData, and a TreeView property which you'll be able to access from the designer. Again, simple stuff here, when the DataSource property is updated, we just reset the lookup and repopulate the root. When the TreeView property is updated we have to do a little more work, registering the events, making sure to unregister events from the old tree view, and doing all the same stuff we do when the data source changes.
That's really all there is to it! Open up the Windows Forms designer, drop a TreeView, then drop a TreeViewHierarchyBinding and set its TreeView property to the tree view you just dropped. Finally, in your code somewhere (i.e. in the Form_Load event), give it a data source:
private void Form1_Load(object sender, EventArgs e)
{
DirectoryInfo dir = new DirectoryInfo("C:\\");
treeViewHierarchyBinding1.DataSource = new FileSystemHierarchyData(dir);
}
(Note - this uses the example FileSystemHierarchyData that's on the MSDN page for IHierarchyData. The example isn't very robust, it doesn't check for UnauthorizedAccessException or anything, but it's good enough to demonstrate this).
And that's it. Run your app and watch it bind. You can now reuse the TreeViewHierarchyBinding component anywhere - just drop it on a form, assign it a TreeView, and give it an IHierarchyData instance as a data source.
I've put the complete code on PasteBin if you want a copy-and-paste version.
Have fun!

The interfaces are available, but will require you to add a reference to System.Web.UI. (It might also require you to use the full .NET Framework redistributable rather than the Client Profile, although I'm not certain about that.)
The larger question is: Does the WinForms TreeView control automatically understand how to work with these interfaces? I believe the answer to that question is "No", but you would need to test/verify that.

There's an interesting article here that shows you how to build extension methods to achieve what I think you're looking for. There is no native availability within the System.Windows.Forms.TreeView to bind to a collection from what I can find.
You CAN include System.Web.UI in your project to make the IHierarchyData and IHierarchicalEnumerable interfaces available, but the TreeView will not be able to attach to them without the extension methods.
The sample source code from the web site will let you bind any IDictionary collection to the TreeView.

Related

Hierarchical change notifications in an object hierarchy

I have a recursive hierarchy of three kinds of object in a C# library. Let's call them Boxes, Nuts and Bolts. Boxes can contain other Boxes, or Nuts and Bolts. Nuts and Bolts obviously can't contain anything.
Let's assume each Box has ObservableCollections of Box, Nut, and Bolt. Each Nut and Bolt implements INotifyPropertyChanged.
Is there an accepted best practice for propagating notifications of changes to the observable collections, or property changes on any Nut or Bolt to an object which holds a reference to the topmost Box? Or any particular design patterns you would recommend?
EDIT: to give some background to this issue, I lead the Chemistry for Word project. You can see the component that display structures in real time on the left.
Now, believe it or not, this currently draws everything through data binding. Each of those molecule displays on the LHS is an ItemsControl. (And yes, I am using WPF with MVVM!) This has proven to have too many overheads and lack of flexibility for a long-term solution. So I have gone back to generating DrawingVisuals directly. This approach allows much more fine control.
The Boxes, Nuts and Bolts in my original example are Molecules, Atoms and Bonds. If any of these are added, removed or changed then the display has to know about it so it can update. As I have already implemented the interfaces and objects for data binding then I want to exploit the code I already have.
I've had a similar model with fast-path access to upstream Node instances in directed acyclic graphs. A Node has a weak reference to its immediate parent. Node has a property to get the Root...that tries to return its parent's Root. If there's no parent, then that node is a root. Rootness is based solely on containment. Note that the parent isn't a collection...because sometimes the child node isn't even in a collection. Something more-or-less like...
public abstract class Node
{
WeakReference<Node> parent;
public Node Root
{
get { return Parent?.Root ?? this; }
}
public Node Parent
{
get
{
if ( parent != null )
{
if ( parent.TryGetTarget( out Node parentNode ) )
{
return parentNode;
}
}
return this;
}
internal set { /*...*/ } //--> if you're brave...
}
}
Edit:
Regarding WeakReferences...one of the things our graphs can have is references to nodes in other graphs. We have a node resolver service that will fetch those other nodes. These out-looking references are represented by an identity value (a GUID or a Long) and an associated weak reference. This way, we can load the specified node as needed, but not keep it any longer than necessary. The resolver maintains an LRU cache of nodes resolved this way.
If such a resolved reference needs to resolve its own parent, a similar mechanism exists to allow the dependent node to resolve its parent. Even a node's collected child nodes may be lazy loaded via the resolver service (although there are annotations that inform our framework when to lazy-load and when not).
So, the weak references help with all these incidentally-resolved scenarios. Er...more accurately, they help us not screw up the garbage collection in such scenarios.
In some analytical scenarios, we'll have hundreds of thousands of nodes popping in and out. I could imagine similar dynamics in chemistry modeling.
Why don't you call the parent in the change notification. Something like the following pseudo code:
Bolt()
{
NotifyPropertyChanged(property)
{
PropertyChanged(this, property);
}
ChildNutPropertyChanged(Nut child, property)
{
PropertyChanged(this, child + property);
}
}
Nut(Bolt parentBolt)
{
parent = parentBolt;
NotifyPropertyChanged(property)
{
PropertyChanged(this, property);
parent.NotifyPropertyChanged(this, property);
}
}
If you encapsulate your ObservableCollection of Nuts and Bolts and only make a ReadOnlyObservableCollection public, you could make an Add(Nut nut) method (and another one for bolts) that registers to the added Nut's NotifyPropertyChanged event.
This way, you'll know in the Box when a property of a child has changed and take action.

How to serialize & deserialize user inputs gathered from inside a panel?

I'm creating an winforms application, that has the user make inputs in different panels. I already wrote a method to traverse through the panel and get the inputs from the different Controls. Now I need to find a way to serialize these inputs and deserialize them later on, so that all inputs are again in the right Controls (e.g. "Jack" is again in the TextBox "tbName").
I thought of multiple solutions, e.g. creating a list for each panel, which serializes to a .txt with a structure similiar to "tbName=Jack" and so on. But I don't really know how I would deserialize that, without traversing both my panel controls and the list again. Or can I possibly serialize the whole Panel object together with the Child-Controls?
//This is the method I use to gather the inputs from the panels.
public IEnumerable<Control> GetControls(Control parentControl)
{
foreach (Control child in parentControl.Controls)
{
yield return child;
foreach (Control controlChild in GetControls(child))
{
yield return controlChild;
}
}
}
It's not advised to serialize the whole form, as it has a lot of information you don't need (and t hat may affect performance). Instead, create a separate class, make it [Serializable()], make all the variables you need to store your information, and serialize that class.
EDIT:
Say you have the following form:
namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// here, you create the serializing and deserializing methods
public void SerializingInfo()
{
// done however you see fit
}
public StorageClass DeserializingInfo()
{
// also done however you see fit
}
}
}
Then, add another class to your project, which in my example, is named StorageClass.
This will look like:
namespace Test
{
[Serializable()]
public class StorageClass
{
// has all your properties
}
}
Then, whatever you need to storage, you can do so by setting/getting the properties in the Form1. When you serialize it, all the properties are serialized together, and you can retrieve it by accessing their getter method in DeserializeInfo().
For a LIMITED number of controls, you could simply create Settings in your Project --> Properties for each one:
Then, in the ApplicationSettings property for your control, click the three dots to the right of PropertyBinding...
...and select the setting for the Text entry:
You'll now have this:
Finally, in the FormClosing() event of the form, save the settings:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.Save();
}
Thanks for the answers, both are correct and working, but in the end I figured out my own solution to the problem:
I made a seperate class with all the attributes I needed, just like #krobelusmeetsyndra suggested, and made a generic List of the class object i just made. Then I traversed through the controls (with the method from my question), put the data in the List and serialized that data with the XmlSerializer.
Same with deserializing: I made a List of my own object type, then loaded the data from the XML in that list, and then assigned it to the right controls.
Hope that helps everyone with the same question!

Extending the TreeView control for incremental filtering/searching

I'm trying to extend the winforms TreeView control to allow incremental filtering and searching similar to the Solution Explorer in VS2012/VS2013.
Ideally, I would like it to be capable of replacing the existing TreeView with minimal code change - as far as the consumer is concerned, the only difference would be a method void Filter(string). Because of this, I think it would make sense for the Nodes property to return the TreeNodeCollection with ALL nodes, even ones not showing because of an applied filter.
I have the code written to handle the filtering, and it actually works quite well except when I access base.Nodes, it returns my filtered nodes and not the full list.
The problem I have is, I'm unable to clone or create a new instance of TreeNodeCollection, because the constructor is marked as internal. So my ideal code would look something like this:
public class TreeViewEx : TreeView
{
// results in a compiler error:
private TreeNodeCollection _allNodes = new TreeNodeCollection();
public new TreeNodeCollection Nodes { get { return _allNodes; } }
public TreeNodeCollection FilteredNodes { get { return base.Nodes; } }
public void Filter(string searchString)
{
base.BeginUpdate();
base.Nodes.Clear();
foreach (TreeNode node in FilterInternal(_allNodes, searchString))
{
base.Nodes.Add(node);
}
base.EndUpdate();
}
}
So as you can see, I'm trying to decouple the nodes that are shown in the UI from the nodes that the consumer would access. Of course with TreeNodeCollection having an internal constructor only, I'm unable to create a new instance or clone it.
I considered these two options, but neither sound like good solutions:
Use reflection to instantiate the TreeNodeCollection object (due to the internal constructor) for the second list. This option seems like it would be more efficient than #2, but of course I'm creating an instance of an object I'm not supposed to.
Instantiate a second TreeView in memory and use the Nodes property from that to maintain my second list. This seems like it might be a lot of overhead.
I want the end result to still be a TreeNodeCollection so the TreeView can be used to replace our existing controls with minimal code and we do have several places using the Find method, which doesn't exist in List<TreeNode>.
Does anyone have any recommendations on how to handle this? What about performance/resource-wise with my two considerations?
Thank you
Update 1:
Per Pat's recommendation, I decided to take a step back and avoid messing with Nodes altogether. So now I've added a List<TreeNode> AllNodes property and have the Nodes just display the nodes that appear in the TreeView (the filtered list), so now it's a bit simpler.
My problem now is, how do I know when AllNodes has an item added to it so I can keep Nodes in sync? I've considered using a BindingList so I have the ListChanged event, but then I would need to have my TreeNode and node's children/grand-children/etc (AllNodes[0].Nodes) use a custom class that inherits from TreeNode and change the Nodes property, and TreeNode.Nodes isn't overridable. Is there another way? I could make a new property called NodeExs or something, but that seems very unintuitive and I could see another dev coming along later and pulling his hair out because the Nodes property is there but doesn't work.
With regard to your proposed solutions, #2 is out because a TreeNode cannot belong more than one control. And while it might be possible to create an instance of TreeNodeCollection via reflection, it won't be very useful because its designed to be coupled to a TreeView or another TreeNode. You won't be able to add/remove nodes from the collection.
Because of this, I think it would make sense for the Nodes property to
return the TreeNodeCollection with ALL nodes, even ones not showing
because of an applied filter.
I disagree, the TreeNodeCollection returned by the Nodes property is used by the framework and OS to render the control. You really don't want to hide this property or alter its functionality.
If a consumer needs to have access to _allNodes, create a List<TreeNode> AllNodes property or use a custom collection.
I've found out that the TreeNodeCollection should only be used to read the listed nodes. Instead, I've used List<TreeNode> to list nodes. In my project, I created a List<TreeNode> for each level on the TreeView. I filled the lists at the same time when I filled the TreeView, at the startup. In the end, I used AddRange() to make and combine a list of the all nodes. This way I had all the nodes listed and categorized.
It's easy and fast to create this kinds of lists. I also created a List<string> version of the all nodes list, which I set up as an AutoCompleteCustomSource for my TextBox. This way I was able to use TextBox with AutoComplete for searching the nodes.
I'd make different lists for the consumers and other categories. Then I'd only add the items to the TreeView which meet the given criteria. You can also use treeView.Nodes.Remove() to remove any nodes. You'd still have the actual node stored on the lists, and could add it back again later.
These are just some ideas.

WPF: property similar to WinForms Modified

In WinForms controls like a TextBox have property Modified that gets value "true" after changing the control's content and may be set to "false" manually. Their WPF analogues seem not to have such property (neither IsModified in new naming style). So do I have to handle their modifying events myself or there's some more convenient way?
For example I have few textboxes and a function, which combines their contents into one document for preview. Opening the preview I want to keep an old content for the document, if none of the textboxes was changed or to call the function to produce new document's content if at least one textbox was edited.
In WPF it's easier to control everything through ViewModel/Model... This might be too much/not what you're looking for. But through experience, I feel that the pattern below pays off in easy usage.
Wrap your simple data class (with all the properties that it is using now/in your question now) in a class/Model that implements IEditableObject, INotifyPropertyChanged and possibly IEquitable. Lets call your class Data.
In a wrapper class create fields:
Data _current;
Data _proposed;
Data _previous;
IEditableObject requires you to implement BeginEdit(), EndEdit() and CancelEdit().
in them you need to control the state _current, proposed, and previous. For example,
public void CancelEdit()
{
_current = _previous;
_proposed = null;
}
public void EndEdit()
{
_previous = _proposed;
}
public void BeginEdit()
{
_proposed = _current;
}
You might need more logic in methods above, so this is just an example. The key of knowing if your object has changes is implementing a flag, lot's of people call it IsDirty:
pubic bool IsDirty { get { return _current != _previous; } }
Now the user of this class can easily check the state. Oh, and on more thing each property would have the following mechanism:
public string Example
{
get { return _current.Example;}}
set
{
if(_current.Example == value) return;
BeginEdit();
_current.Example = value;
RaisePropertyChanged (() -> Example);
}
}
What's nice about implementing IEditableObject, all controls respond to it, DataGrid is a good example and also you can easily return to the original state by cancelling edit.
Anyway, there are lots of samples that you should browse for. I just hope to can get you started onto that path...
P.S. this pattern was used before WPF came out, its super common in WinForms as well
WPF doesn't have that because UI is not Data and therefore your UI is not the right place to store information about whether your data has changed or not.
Crappy dinosaur winforms doesn't allow a clean and true separation between UI and application logic/data and therefore has all sorts of horrible hacks in order to mash together these completely separate concepts.
You must learn to develop correctly, using the MVVM pattern. Then you will realize there's no sense in placing state data on any UI elements.

How to deal with *many* context menus

I'm re-writing in C# (with Winforms) an old VB6 app that uses a single context menu with multiple Items that change their Caption, Visible, and Enabled traits based on a monolithic function called "InitControls"
The function is 500 lines long and consists primarily of a switch statement that decides what controls to enable based on the selected item's tag (there's a tree view and list view; it selects the selected item from the active one and gets its tag). It then enables, disables, and modifies the text of the visible items, and clears any useless Separators. The original uses ActiveBar (a custom control) which allows it to change the text in one place and display the item in menus, context menus, and toolbars all at once.
I'm currently just re-implementing the logic line for line in C#, but I hate it because I'm not really fixing anything, just putting the problem into a new language (and possibly screwing it up in the process). I created a class that allowed me to change the text, enabled and visible properties of any "subscribed" Menu Items in one place and even add/remove event handlers for all subscriBed menu items. It works, and even seems apparently correct, but I'm pretty sure there's got to be a better way. My MainForm is ENORMOUS.
What is the standard .NET way of handling complex Context Menu and Toolbar logic?
From what I understand, you basically want to refactor a large switch-case method. Googling for "switch case refactoring" should give you several examples you can check out to find something that suits you best.
Usually, when you are refactoring a switch case, this means that you want to extract logic from each case block into a new class, possibly an implementation of an interface common to all cases. The right implentation of your class will depend on the condition of an individual case statement: this is called a Strategy pattern, because each condition demands a different strategy.
In your case, you need to slightly extend the pattern: you have a number of candidates for the context menu, each of them being able to handle a certain node type. In that case, your right-click handler needs to let them decide if they can provide functionality for a certain node.
[Edit]
To clarify a bit, I will provide a simple example.
I mentioned that individual implementations should be extracted into classes which implement the same interface, which should be responsible for changing menu items' appearance and state, based on the current condition.
interface IMenuStateManager
{
// this method updates state of one or
// more menu elements, according to the
// specified selected node info
void UpdateState(ISelectedNodeInfo info);
}
Our first, basic implementation of the IMenuStateManager interface will do nothing more that simply call other managers' implementations. This is called a Composite object pattern, because it allows us to treat a group of objects as a single object:
// composite class for a list of menu managers
class CompositeMenuStateManager : IMenuStateManager
{
private readonly IMenuStateManager[] _childManagers;
// params keyword will allow as to pass a comma separated list
// of managers, which is neat
public CompositeMenuStateManager(params IMenuStateManager[] managers)
{
_childManagers = managers;
}
// this is where the job gets done, but composite
// class doesn't do much work by itself
public void UpdateState(ISelectedNodeInfo info)
{
// allow each state manager to change its state
foreach (IMenuStateManager mgr in _childManagers)
{
mgr.UpdateState(info);
}
}
}
Now, you still have an enormous list of possible menu candidates, but now their logic is separated into different classes, and then wrapped in a single composite object.
IMenuStateManager _menuManager = new CompositeMenuStateManager
(
// note: each menu "manager" can manage one or more
// items, if you find it useful.
// For example, ClipboardMenuStateManager can be
// a composite manager itself (cut/copy/paste).
new ClipboardMenuStateManager(some params),
new SomeOtherMenuItemManager(various params),
new YetAnotherMenuItemManager(various params),
...
);
I guess that menu states get updated when a node is selected, but this is something you should easily adapt to your app. That particular event handler delegates the whole responsibility to our composite menu manager:
void Node_Selected(sender object, EventArgs args)
{
// find out which node was clicked
Node node = object as Node;
// get the data (model) node for this tree node
INodeData data = node.Tag as INodeData;
// create some info which will be passed to the manager.
// you can pass information that might be useful,
// or just simply pass the node data itself
ISelectedNodeInfo info = new SelectedNodeInfo(data, some other stuff);
// let the manager do the rest of the job
_menuManager.UpdateState(info);
}
Since you will probably have three menu items doing the same job at the same time (main menu, context menu, toolbar), you will probably want to make each IMenuStateManager implementation update all three of them at the same time. The simplest way should be to to pass an array of ToolStripItem objects, which is the base abstract class for several different menu elements:
class PrintMenuManager : IMenuStateManager
{
private readonly ToolStripItem[] _items;
// this constructor can accept several menu elements
public PrintMenuManager(params ToolStripItem[] items)
{
_items = items;
}
public void UpdateState(ISelectedNodeInfo node)
{
foreach (ToolStripItem item in _items)
{
// if node is printable, enable
// all "print" menu items and buttons
item.Enabled = (node.IsPrintable);
}
}
}
When creating the PrintMenuManager instance, you can pass all buttons and menu items which are related:
// (this should be one of the child managers in
// the composite menu manager, but you get it)
IMenuStateManager printMnuManaegr = new PrintMenuManager
(
this.printMenuItem,
this.printContextMenuItem,
this.printToolbarButton,
);
Whew, this turned out to be a lengthy one at the end. :)
Ok, that's about it for a start.

Categories