207 lines
5.6 KiB
C#
207 lines
5.6 KiB
C#
|
using System;
|
||
|
using System.Reflection;
|
||
|
using System.Collections;
|
||
|
using System.Windows.Forms;
|
||
|
|
||
|
namespace Browser {
|
||
|
/// <summary>
|
||
|
/// Summary description for ObjectTreeView.
|
||
|
/// </summary>
|
||
|
public class ObjectTreeView {
|
||
|
private abstract class Thunk {
|
||
|
public abstract string Text { get; }
|
||
|
public abstract object Object { get; }
|
||
|
public abstract TreeNode ThisNode { get; }
|
||
|
}
|
||
|
|
||
|
private class SimpleThunk : Thunk {
|
||
|
private TreeNode thisNode;
|
||
|
private object ovalue;
|
||
|
private string text;
|
||
|
public SimpleThunk(TreeNode thisNode, object ovalue, string text) {
|
||
|
this.thisNode = thisNode;
|
||
|
this.ovalue = ovalue;
|
||
|
this.text = text;
|
||
|
}
|
||
|
public override TreeNode ThisNode {
|
||
|
get { return thisNode; }
|
||
|
}
|
||
|
public override object Object {
|
||
|
get { return ovalue; }
|
||
|
}
|
||
|
public override string Text {
|
||
|
get { return text; }
|
||
|
}
|
||
|
}
|
||
|
private class PropertyThunk : Thunk {
|
||
|
private TreeNode thisNode;
|
||
|
private object ovalue;
|
||
|
private string text;
|
||
|
private PropertyInfo property;
|
||
|
private object self;
|
||
|
private bool computed = false;
|
||
|
public PropertyThunk(TreeNode thisNode, string text, PropertyInfo property, object self) {
|
||
|
this.thisNode = thisNode;
|
||
|
this.text = text;
|
||
|
this.property = property;
|
||
|
this.self = self;
|
||
|
}
|
||
|
public override TreeNode ThisNode {
|
||
|
get {
|
||
|
return thisNode;
|
||
|
}
|
||
|
}
|
||
|
public override object Object {
|
||
|
get {
|
||
|
if (!computed) {
|
||
|
computed = true;
|
||
|
try {
|
||
|
ovalue = property.GetValue(self, null);
|
||
|
thisNode.Text = property.Name + ": " + Identity(ovalue);
|
||
|
} catch {
|
||
|
ovalue = "<<exception thrown by property>>";
|
||
|
}
|
||
|
}
|
||
|
return ovalue;
|
||
|
}
|
||
|
}
|
||
|
public override string Text {
|
||
|
get { return text; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private TreeNode previousNode;
|
||
|
private TreeView tv;
|
||
|
private Model model;
|
||
|
private Hashtable o2thunk = new Hashtable();
|
||
|
private Hashtable n2thunk = new Hashtable();
|
||
|
|
||
|
public ObjectTreeView(Model model, TreeView tv) {
|
||
|
this.model = model;
|
||
|
this.tv = tv;
|
||
|
tv.HideSelection = false;
|
||
|
model.ObjectChangedHandler += new ObjectChangedEventHandler(this.ObjectChanged);
|
||
|
tv.MouseUp += new MouseEventHandler(this.UserAfterSelect);
|
||
|
this.ObjectChanged();
|
||
|
}
|
||
|
|
||
|
public static Control NewBrowser(Model model) {
|
||
|
TreeView tv = new TreeView();
|
||
|
new ObjectTreeView(model, tv);
|
||
|
return tv;
|
||
|
}
|
||
|
|
||
|
public void UserAfterSelect(object o, MouseEventArgs argv) {
|
||
|
UserAfterSelect();
|
||
|
}
|
||
|
|
||
|
public void UserAfterSelect() {
|
||
|
if (tv.SelectedNode != previousNode && tv.SelectedNode != null) {
|
||
|
Thunk thunk = (Thunk)n2thunk[tv.SelectedNode];
|
||
|
model.ChangeObject(thunk.Object);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void ObjectChanged() {
|
||
|
object o = model.CurrentObject;
|
||
|
if (o != null && (tv.SelectedNode == null || !IsLeafType(o) || o != ((Thunk)n2thunk[tv.SelectedNode]).Object)) {
|
||
|
Thunk thunk = AddRoot(o, tv);
|
||
|
tv.SelectedNode = thunk.ThisNode;
|
||
|
}
|
||
|
previousNode = tv.SelectedNode;
|
||
|
}
|
||
|
|
||
|
private Thunk AddRoot(object o, TreeView tv) {
|
||
|
if (o != null && o2thunk.Contains(o)) {
|
||
|
return (Thunk)o2thunk[o];
|
||
|
}
|
||
|
TreeNode n = new TreeNode();
|
||
|
if (o == null) {
|
||
|
n.Text = "null";
|
||
|
tv.Nodes.Add(n);
|
||
|
Thunk t = new SimpleThunk(n, o, "null");
|
||
|
n2thunk[n] = t;
|
||
|
return t;
|
||
|
}
|
||
|
n.Text = "(" + o.GetType().Name + ") " + Identity(o);
|
||
|
Thunk thunk = new SimpleThunk(n, o, n.Text);
|
||
|
o2thunk[o] = thunk;
|
||
|
n2thunk[n] = thunk;
|
||
|
tv.Nodes.Add(n);
|
||
|
AddMembers(o, n.Nodes);
|
||
|
return thunk;
|
||
|
}
|
||
|
|
||
|
private Thunk AddKid(object o, TreeNodeCollection tnc, string name) {
|
||
|
TreeNode n = new TreeNode();
|
||
|
n.Text = name + ": " + Identity(o);
|
||
|
tnc.Add(n);
|
||
|
Thunk thunk = new SimpleThunk(n, o, n.Text);
|
||
|
n2thunk[n] = thunk;
|
||
|
if (o != null) {
|
||
|
System.Type t = o.GetType();
|
||
|
if (t.IsValueType && !IsLeafType(o)) {
|
||
|
o2thunk[o] = thunk;
|
||
|
AddMembers(o, n.Nodes);
|
||
|
}
|
||
|
}
|
||
|
return thunk;
|
||
|
}
|
||
|
private Thunk AddPropertyKid(TreeNodeCollection tnc, PropertyInfo pi, object self) {
|
||
|
TreeNode n = new TreeNode();
|
||
|
n.Text = pi.Name + ": <property>";
|
||
|
tnc.Add(n);
|
||
|
Thunk thunk = new PropertyThunk(n, n.Text, pi, self);
|
||
|
n2thunk[n] = thunk;
|
||
|
return thunk;
|
||
|
}
|
||
|
|
||
|
|
||
|
private void AddMembers(object o, TreeNodeCollection tnc) {
|
||
|
System.Type t = o.GetType();
|
||
|
foreach (FieldInfo f in t.GetFields(bindingFlags)) {
|
||
|
object i = f.GetValue(o);
|
||
|
AddKid(i, tnc, f.Name);
|
||
|
}
|
||
|
foreach (PropertyInfo pi in t.GetProperties(bindingFlags)) {
|
||
|
if (pi.GetIndexParameters().Length == 0) {
|
||
|
AddPropertyKid(tnc, pi, o);
|
||
|
}
|
||
|
}
|
||
|
if (o is IList) {
|
||
|
IList a = (IList) o;
|
||
|
for (int i = 0; i < a.Count; i++) {
|
||
|
object x = a[i];
|
||
|
AddKid(x, tnc, "["+i.ToString()+"]");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool IsLeafType(object o) {
|
||
|
if (o == null)
|
||
|
return true;
|
||
|
System.Type t = o.GetType();
|
||
|
return o is string
|
||
|
|| o is IImage
|
||
|
//|| t.IsValueType
|
||
|
|| t.IsPrimitive
|
||
|
// || o is ICollection && ((ICollection)o).Count == 0
|
||
|
;
|
||
|
}
|
||
|
private static string Identity(object o) {
|
||
|
if (o == null)
|
||
|
return "null";
|
||
|
if (o is IImage)
|
||
|
return ((IImage)o).Image();
|
||
|
else
|
||
|
if (o is ICollection && ((ICollection)o).Count == 0)
|
||
|
return String.Format("{0}#{1} (empty)", o.GetType().Name, o.GetHashCode());
|
||
|
else if (IsLeafType(o))
|
||
|
return o.ToString();
|
||
|
else
|
||
|
return String.Format("{0}#{1}", o.GetType().Name, o.GetHashCode());
|
||
|
}
|
||
|
private const BindingFlags bindingFlags = BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic;
|
||
|
}
|
||
|
}
|