Creating custom TreeView/TreeNode - c#

I need to extend the TreeNode class such that I can add custom properties to each node (seeing as WebForms TreeNode doesn't include the Tag property). So this is my CustomTreeNode:
public class CustomTreeNode : TreeNode
{
public CustomTreeNode()
{
}
public CustomTreeNode(int nodeId, string nodeType)
{
NodeId = nodeId;
NodeType = nodeType;
}
public string NodeType { get; set; }
public int NodeId { get; set; }
}
If I create a CustomTreeNode and add it to a TreeView:
CustomTreeNode node = new CustomTreeNode(1, "CustomType");
treeView.Nodes.Add(node);
I would then get a casting exception doing the following:
CustomTreeNode selectedNode = (CustomTreeNode)TreeView.SelectedNode;
because TreeView returns a TreeNode, not a CustomTreeNode.
I've done some reading, and it looks like I need to extend the TreeView class, and override the CreateNode() method to return CustomTreeNode instead of TreeNode. So I created this:
public class CustomTreeView : TreeView
{
protected override TreeNode CreateNode()
{
return new CustomTreeNode();
}
}
The problem is however, CreateNode() doesn't take any arguments, so you have to have call the empty constructor for the CustomTreeNode class. So when I created my CustomTreeNode above, when I get it back from the CustomTreeView, the nodeId and nodeType values have been lost because the empty constructor returns a node without any values.
Any help much appreciated.

This is what I came up with (experts, any advice welcomed). Instantiate the CustomTreeNodes in your code behind and set the properties via setters. Modify your CustomTreeNode class to persist the values in ViewState. The node returned by your custom tree view's CreateNode will load the ViewState information.
TreeNode class:
[DefaultProperty("Text")]
[ToolboxData("<{0}:CustomTreeNode runat=server></{0}:CustomTreeNode>")]
public class CustomTreeNode : TreeNode
{
private const int NODE_TYPE = 1;
private const int NODE_ID = 2;
public string NodeType { get; set; }
public int NodeId { get; set; }
protected override void LoadViewState(Object savedState)
{
if (savedState != null)
{
object[] myState = (object[])savedState;
if (myState[0] != null)
base.LoadViewState(myState[0]);
if (myState[NODE_TYPE] != null)
this.NodeType = (string)myState[NODE_TYPE];
if (myState[NODE_ID] != null)
this.NodeId = (int)myState[NODE_ID];
}
}
protected override Object SaveViewState()
{
object baseState = base.SaveViewState();
object[] allStates = new object[3];
allStates[0] = baseState;
allStates[NODE_TYPE] = this.NodeType;
allStates[NODE_ID] = this.NodeId;
return allStates;
}
}
TreeView class:
[DefaultProperty("Text")]
[ToolboxData("<{0}:CustomTreeView runat=server></{0}:CustomTreeView>")]
public class CustomTreeView : TreeView
{
protected override TreeNode CreateNode()
{
// Tree node will get its members populated with the data from VIEWSTATE
return new CustomTreeNode();
}
}
Simple .aspx file (Assuming that your custom control is defined in an assembly "Foo" and a namespace "Bar":
<%# Register TagPrefix="customControl" Assembly="Foo" Namespace="Bar" %>
<customControl:CustomTreeView ID="sampleTree"
runat="server" onselectednodechanged="sampleTree_SelectedNodeChanged"></customControl:CustomTreeView>
<asp:Label ID="lblSelectedNode" runat="server" ></asp:Label>
CodeBehind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
PopulateTree();
}
}
private void PopulateTree()
{
sampleTree.Nodes.Clear();
CustomTreeNode root = new CustomTreeNode();
root.Value = "root node";
sampleTree.Nodes.Add(root);
// Creating some fake nodes (you would of course be using real data)
for (int i = 0; i < 10; i++)
{
CustomTreeNode child = new CustomTreeNode();
child.NodeId = i; // Saved in ViewState
child.NodeType = "Type " + i; // Saved in ViewState
child.Value = child.NodeType;
root.ChildNodes.Add(child);
}
}
protected void sampleTree_SelectedNodeChanged(object sender, EventArgs e)
{
CustomTreeView cTreeView = (CustomTreeView) sender;
lblSelectedNode.Text = ((CustomTreeNode)cTreeView.SelectedNode).NodeType;
}

Related

Cannot access value from property in C#

I have created a class
public class GlobalVariable
{
private Guid _gebruikerID=Guid.Empty;
public Guid GebruikerID
{
get
{
return _gebruikerID;
}
set
{
_gebruikerID = value;
}
}
}
now I create an instance of that class
GlobalVariable gv = new GlobalVariable();
now I assign a value to that instance
foreach (var item in Gebruiker)
{
lblGebruikerID.Text = "GebruikerID: " + Convert.ToString(item.GebruikerId);
gv.GebruikerID = item.GebruikerId;
}
now the last step is that I want to use gv.GebuikerID' value in another event
protected void btnShowIDs_Click(object sender, EventArgs e)
{
lblAdressIDToBeDeleted.Text = gv.GebruikerID.ToString();
}
but unfortunately it shows me 00000000-0000-0000-0000-000000000000 but when I put breakpoint in the foreach loop it does show me the GUID.

How can I pair objects to radio buttons?

I'm working on a small form app, and I have "paired" my radio buttons with lists in a common class. The purpose of this was to turn on/off the corresponding list
public class myType
{
public RadioButton button { get; set; }
public ListBox list { get; set; }
}
I proceed to create these pairs through a for loop inside an array
for (int i = 0; i < broj_botuna; i++)
{
theArray[i] = new myType();
}
I use a common event handler for all the radio buttons:
private void test_CheckedChanged(object sender, EventArgs e)
{
var xx = sender as RadioButton;
//do stuff
positionInArray = Array.IndexOf(theArray, xx);
}
except that the last line of code "xx" should be of type "myType" and not "radioButton" that I managed to retrieve.
So could anyone tell me how do I get the reference from "radioButton" to "myType"? Or is there a better alternative?
You can use Array.FindIndex like:
var positionInArray = Array.FindIndex(theArray, b => b.button == xx);
You could create some constructs that allow you to easily associate your properties to the parent object if you wanted to.
This approach would allow you to always reference your parent type provided that you added a bit more code in your get's and set's.
static void Main()
{
myType item = new myType();
var button = new Button();
myType.button = button;
var list = new ListBox();
myType.list = list;
item = list.GetParent();
bool isSameButton = button == item.button;
bool isSameList = list == item.list;
Assert.IsTrue(isSameButton);
Assert.IsTrue(isSameList);
}
public class myType
{
private RadioButton _button;
public RadioButton button
{
get { return _button; }
set {
value.AssociateParent(this);
_button = value;
}
}
private ListBox _list;
public ListBox list
{
get { return _list; }
set {
value.AssociateParent(this);
_list= value;
}
}
}
public static class Extensions
{
private static Dictionary<object, object> Items { get; set; }
static Extensions()
{
Items = new Dictionary<object, object>();
}
public static void AssociateParent(this object child, object parent)
{
Items[child] = parent;
}
public static object GetParent(this object child)
{
if (Items.ContainsKey(child)) return Items[child];
return null;
}
}

Is there a way to register events in bulk?

I have number of mother classes, each of these classes have different number of children objects of the same type. And the mother class need to register an event to all of these children objects.
Wondering if there's any good way to do the registration automatically?
Something like: go through all the properties inside the mother class, find properties of the children type, and register event.
You can use reflections to subscribe to the events.
Define Child Class:
class Child {
public Child() { }
// Define event
public event EventHandler DidSomethingNaughty;
// Proeprty used to trigger event
public bool IsNaughty {
get { return this.isNaughty; }
set {
this.isNaughty = value;
if (this.IsNaughty) {
if (this.DidSomethingNaughty != null) {
this.DidSomethingNaughty(this, new EventArgs());
}
}
}
}
// Private data member for property
private bool isNaughty = false;
}
Define Mother Class:
class Mother {
public Mother() {
this.subscribeToChildEvent();
}
// Properties
public Child Child1 {
get { return this.child1; }
set { this.child1 = value; }
}
public Child Child2 {
get { return this.child1; }
set { this.child2 = value; }
}
public Child Child3 {
get { return this.child3; }
set { this.child3 = value; }
}
public Child Child4 {
get { return this.child4; }
set { this.child4 = value; }
}
// Private data members for the properties
private Child child1 = new Child();
private Child child2 = new Child();
private Child child3 = new Child();
private Child child4 = new Child();
// This uses reflection to get the properties find those of type child
// and subscribe to the DidSomethingNaughty event for each
private void subscribeToChildEvent() {
System.Reflection.PropertyInfo[] properties =
typeof(Mother).GetProperties();
foreach (System.Reflection.PropertyInfo pi in properties) {
if (pi.ToString().StartsWith("Child")) {
Child child = pi.GetValue(this, null) as Child;
child.DidSomethingNaughty +=
new EventHandler(child_DidSomethingNaughty);
}
}
}
private void child_DidSomethingNaughty(object sender, EventArgs e){
Child child = (Child)sender;
if (child.IsNaughty) {
this.discipline(child);
}
}
private void discipline(Child child) {
MessageBox.Show("Someone was naughty");
// reset the flag so the next time the childe is
//naughty the event will raise
child.IsNaughty = false;
}
}
Then initialize a Mother object and it will subscript to the events:
Mother m = new Mother();
Then set the IsNaughty property of Child1 to true:
m.Child1.IsNaughty = true;
You should get a message box:
Resources:
https://stackoverflow.com/a/737156/1967692
https://stackoverflow.com/a/3179869/1967692

Is there any to set the visibility of xtra tree list nodes dynamically

I am using xtratreelist in my application with only first level and I want to make some of the nodes visible but not all. Here is the code, but after that all the not are showing in the list
TreeList tr = new Treelist();
for (int x = 0; x < tr.Nodes.Count; x++)
{
tr.Nodes[x].Visible = false;
}
I suggest you use the NodesIterator, here is an example, and it works for me :
The data class :
public class Service
{
public string Name { get; set; }
public bool Visible { get; set; }
}
And in my form :
private void TreeForm_Load(object sender, EventArgs e)
{
treeList1.DataSource = Service.GetServices();
treeList1.NodesIterator.DoLocalOperation(setNodeVisibility, treeList1.Nodes);
}
private void setNodeVisibility(DevExpress.XtraTreeList.Nodes.TreeListNode node)
{
var service = treeList1.GetDataRecordByNode(node) as Service;
if (service == null)
return;
node.Visible = service.Visible;
}
Go through the following links
FindNodeByID
FindNodeByFieldValue
FindNodeByKeyID

(is there an) Easy way to check if property in (complex) pocomodel has changed?

I was wondering if there is an 'easy'/good way to check if a property has changed. Like in the hierarchy below when Child.Name has changed (isDirty) I would like to know.
GrantParent
- Parent
-- Child
In my current situation I need to navigate through the model to see if anything has changed.
ps: I'm using IChangeTracking.
Been thinking about caching a hash of the serialized object. (too slow?)
Or creating changedevent which call's the parent until it reaches the grantparent. (chatty?)
public class Parent: BaseEntity
{
private Child _child;
public Child Child
{
get { return _child; }
set { _child = value; OnPropertyChanged("Child"); }
}
}
public class Child : BaseEntity
{
private int _id;
public int Id {
get { return _id; }
set { _id = value; OnPropertyChanged("Id"); }
}
}
[DataContract]
[Serializable]
public abstract class BaseEntity : INotifyPropertyChanged
{
protected BaseEntity()
{
PropertyChanged += PropertyChangedEventHandler;
}
private void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e)
{
if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal))
{
this.IsChanged = true;
}
}
protected void OnPropertyChanged<T>(Expression<Func<T>> property)
{
MemberExpression me = property.Body as MemberExpression;
if (me == null || me.Expression != property.Parameters[0]
|| me.Member.MemberType != MemberTypes.Property)
{
throw new InvalidOperationException(
"Now tell me about the property");
}
var handler = PropertyChanged;
if (handler != null) handler(this,
new PropertyChangedEventArgs(me.Member.Name));
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsChanged
{
get
{
lock (_notifyingObjectIsChangedSyncRoot)
{
return _notifyingObjectIsChanged;
}
}
protected set
{
lock (_notifyingObjectIsChangedSyncRoot)
{
if (!Boolean.Equals(_notifyingObjectIsChanged, value))
{
_notifyingObjectIsChanged = value;
if (IsDirtyChanged != null)
IsDirtyChanged();
this.OnPropertyChanged("IsChanged");
}
}
}
}
private bool _notifyingObjectIsChanged;
private readonly object _notifyingObjectIsChangedSyncRoot = new Object();
public void AcceptChanges()
{
this.IsChanged = false;
}
}
In the end I used a compare on the XML model from the XML serializer I already used. I did't 'need' instant change detection once a second (or so) would be enough. Now I check the XML model with the one I had since the last save.
You'll need to have each of the properties keep track of it themselves, and either store some information indicating what properties have changed, or possibly firing off an event when an item is changed.
essentially each property will have logic similar to this:
public class MyClass : INotifyPropertyChanged
{
private int _value;
public int Value
{
get
{
return _value;
}
set
{
_value = value;
PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
This will allow you to add an event handler to the PropertyChanged event so that code will be fired when a property is changed.
I recently worked on a project where we had all of the nodes/leaves implement a node.Modified property and used INotifyPropertyChanged to raise state change of node.Modified. Then all of the parents subscribed to their children's property change and if node.Modified was ever set true, then they'd set their own node.Modified to true.
Like you say, it's a little chatty, but hasn't come close to becoming a performance bottleneck for us since we're not seeing thousands of changes every second and our hierarchy is only 3 levels deep.
Here's a quick sample:
class Node : INotifyPropertyChanged
{
public Node()
{
Children = new List<Node>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var temp = PropertyChanged;
if (temp != null)
temp(this, new PropertyChangedEventArgs(name));
}
public IList<Node> Children { get; private set; }
public void AddChild(Node node)
{
node.PropertyChanged += ChildPropertyChanged;
Children.Add(node);
}
void ChildPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Modified")
Modified |= ((Node)sender).Modified;
}
bool _modified = false;
public bool Modified
{
get { return _modified; }
set
{
if (_modified != value)
{
_modified = value;
OnPropertyChanged("Modified");
}
}
}
EDIT: There is another way using a sort of message bus. It may not be perfect, but it is another approach to the problem, so I will share that as well. I quickly hacked up a trivial Msg bus...
static class Bus<T>
{
public static Dictionary<object, Action<object, T>> Subscriptions = new Dictionary<object, Action<object, T>>();
public static void Raise(object sender, T message)
{
foreach (Action<object, T> action in Subscriptions.Values)
{
action(sender, message);
}
}
public static void Subscribe(object subscriber, Action<object, T> action)
{
Subscriptions[subscriber] = action;
}
public static void Unsubscribe(object subscriber)
{
if (Subscriptions.ContainsKey(subscriber))
Subscriptions.Remove(subscriber);
}
}
public class WasModified { }
And the modified Node
class Node
{
public Node()
{
Children = new List<Node>();
}
public IList<Node> Children { get; private set; }
bool _modified = false;
public bool Modified
{
get { return _modified; }
set
{
if (_modified != value)
{
_modified = value;
if (_modified == true)
Bus<WasModified>.Raise(this, new WasModified());
}
}
}
}
Finally, it's use.
static void Main(string[] args)
{
Node parent = new Node();
Bus<WasModified>.Subscribe(parent, (s,a)=> parent.Modified = true);
Node child = new Node();
Node gchild = new Node();
parent.Children.Add(child);
parent.Children.Add(gchild);
gchild.Modified = true;
Console.WriteLine(parent.Modified);
Console.ReadLine();
}
The message bus doesn't need to bubble up to parent objects and you don't need to recurse into them each time you want to see if Modified was changed, so perhaps it's what you're looking for.

Categories