I have a class:
public class A
{
private IB link;
public IB Link
{
get { return link; }
set
{
link = value;
b.Link = this;
}
}
...
}
and an interface:
public interface IB
{
A Link { get; set; }
}
I will use it like this:
public class B1 : IB, Button
{
public A Link { get; set; }
...
}
public class B2 : IB, TextBox
{
public A Link { get; set; }
...
}
b1 = new B1();
b2 = new B2();
A a = new A();
a.Link = b1;
...
a.Link = b2;
But I have to encapsulate the IB.Link property, it should changed only in the A class (along with the A.Link property). Is this possible?
Update:
Sorry for ambiguity of this example. My real code is too large and not finished: I have a structure of nodes. Each node has a link to Control. So, visual structure of controls can be constructed. We can manage the controls from nodes, but not get access to node from control, for example, from OnMouseClick method. We need have back reference - the IMyControl.OwnerNode property. IMyControl is interface that contains only this property. So, we can create "MyControl : IMyControl, Control" class and implement into it mouse click logic. When we assign control to node, both references must be created, direct and back, but it take place in code of node class, not in MyControl and IMyControl code. Property field in IMyControl interface must be accessible for write from NodeClass and unaccessible for write from derived classes. That i am trying to accomplish here.
If I understand you correctly, you can use this draft:
class Node
{
public ControlWrapper Link { get; set; }
}
abstract class ControlWrapper
{
private readonly Node _node;
private readonly Control _control;
public Node Node
{
get { return _node; }
}
public Control Control
{
get { return _control; }
}
public ControlWrapper(Node node, Control control)
{
if (node == null)
throw new ArgumentNullException("node");
if (control == null)
throw new ArgumentNullException("control");
_node = node;
_control = control;
}
}
class ControlWrapper<TControl> : ControlWrapper
where TControl : System.Windows.Forms.Control
{
public TControl Control
{
get { return (TControl)base.Control; }
}
public ControlWrapper(Node node, TControl control)
: base (node, control)
{
}
}
class Program
{
static void Main(string[] args)
{
Node n1 = new Node();
n1.Link = new ControlWrapper<TextBox>(n1, new TextBox());
Node n2 = new Node();
n2.Link = new ControlWrapper<Button>(n2, new Button());
}
}
Abstract class ControlWrapper provides you with back-link to node (you can't encapsulate logic in interface, so abstract class goes here), typed derived generic class provides constructor for creating actual implementations of control-wrappers.
If you want this relation automatically force its consistency, you should write code like this:
class Program
{
static void Main(string[] args)
{
Node n1 = new Node();
n1.SetControl(new TextBox());
Node n2 = new Node();
n2.SetControl(new Button());
}
}
class Node
{
private ControlWrapper _link;
public ControlWrapper Link
{
get { return _link; }
}
public void SetControl<TControl>(TControl control)
where TControl : System.Windows.Forms.Control
{
ControlWrapper prevLink = Link;
if (prevLink != null)
prevLink.Dispose();
_link = new ControlWrapper<TControl>(this, control);
}
}
// microsoft basic dispose pattern
// http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx#basic_pattern
abstract class ControlWrapper : IDisposable
{
private readonly Node _node;
private readonly Control _control;
public Node Node
{
get { return _node; }
}
public Control Control
{
get { return _control; }
}
public ControlWrapper(Node node, Control control)
{
if (node == null)
throw new ArgumentNullException("node");
if (control == null)
throw new ArgumentNullException("control");
_node = node;
_control = control;
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_control != null)
_control.Dispose();
}
}
}
class ControlWrapper<TControl> : ControlWrapper
where TControl : System.Windows.Forms.Control
{
public TControl Control
{
get { return (TControl)base.Control; }
}
public ControlWrapper(Node node, TControl control)
: base (node, control)
{
}
}
Related
I have a very simple controls library for Windows Forms and I am getting problems to implement accessibility.
I have a very simple Form with a member that contains a list of controls of my library, and I have overriden the CreateAccessibilityInstance:
public partial class Form1 : Form
{
protected override AccessibleObject CreateAccessibilityInstance()
{
return new AccessibleForm(this);
}
public MyContainer MyContainer;
public Form1()
{
InitializeComponent();
MyContainer = new MyContainer();
MyContainer.Controls.Add(new MyButton());
}
}
The AccessibleForm class looks like:
public class AccessibleForm: Control.ControlAccessibleObject
{
private Form1 form1;
public AccessibleForm(Form1 owner):base(owner)
{
this.form1 = owner;
}
public override AccessibleObject GetChild(int index)
{
return this.form1.MyContainer.Controls[index].AccessibilityObject;
}
public override int GetChildCount()
{
return this.form1.MyContainer.Controls.Count() ;
}
}
MyContanier and MyButton classes inherits from BaseControl, they are very easy:
public class BaseControl : Control
{
protected override AccessibleObject CreateAccessibilityInstance()
{
return new AccessibleObject();
}
}
public class MyContainer:BaseControl
{
public List<BaseControl> Controls { get; set; }
public MyContainer()
{
this.Controls = new List<BaseControl>();
}
}
public class MyButton:BaseControl
{
}
The point is that when I run the UIVerify tool to see if my controls are generating the correct structure I can not see them:
Another point is, that if I modify the GetChild method from AccessibleForm class in this way:
public override AccessibleObject GetChild(int index)
{
return new AccessibleObject();
////return this.form1.MyContainer.Controls[index].AccessibilityObject;
}
I can see a node on the UIVerify:
But modifying the GetChild method to return a custom accessible object it shows me nothing.
Why are not my controls on the tree?
I do not know what I am missing.
Override Name,value,Role in AccessibleForm class
The problem is as follows:
In my User Control i have Label List (List ) which fills adding Labels which UC through a "Smart Tag" (using a DesignerActionMethodItem).
The problem is that it works correctly when we are in design time, for example, I add 3 items at design time, but when I test the application these items disappear as if they had never added.
P.S.:
I have:
MyControl class,
[Designer(typeof(MenuItemPanelDesigner ))]
public partial class MenuItemPanel : UserControl
{
private List<Label> _listaItems;
public MenuItemPanel()
{
InitializeComponent();
}
public List<Label> ListaItems
{
get
{
if (this._listaItems == null)
{
this._listaItems = new List<Label>();
}
return this._listaItems;
}
}
public void AgregarItem()
{
Label nuevoItem = new Label();
nuevoItem.Text = "Item " + this._listaItems.Count;
nuevoItem.AutoSize = false;
nuevoItem.TextAlign = ContentAlignment.MiddleCenter;
nuevoItem.Cursor = Cursors.Hand;
this.ListaItems.Add(nuevoItem);
this.Controls.Add(nuevoItem);
nuevoItem.Dock = DockStyle.Top;
nuevoItem.Height = 50;
}
}
MyControlDesigner class
class MenuItemPanelDesigner : System.Windows.Forms.Design.ControlDesigner
{
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists
{
get
{
if (null == actionLists)
{
actionLists = new DesignerActionListCollection();
actionLists.Add(new MenuItemPanelDesignerActionList(this.Component));
}
return actionLists;
}
}
}
and MyControlDesignerActionList
class MenuItemPanelDesignerActionList : DesignerActionList
{
private MenuItemPanel colUserControl;
private DesignerActionUIService designerActionUISvc = null;
//The constructor associates the control with the smart tag list.
public MenuItemPanelDesignerActionList(IComponent component) : base(component)
{
this.colUserControl = (MenuItemPanel)component;
this.designerActionUISvc = (DesignerActionUIService)GetService(typeof(DesignerActionUIService));
}
// Implementation of this abstract method creates smart tag items,
// associates their targets, and collects into list.
public override DesignerActionItemCollection GetSortedActionItems()
{
DesignerActionItemCollection items = new DesignerActionItemCollection();
//Define static section header entries.
items.Add(new DesignerActionHeaderItem("Items"));
items.Add(new DesignerActionMethodItem(this,"AgregarItem","Agregar Item"));
return items;
}
// Metodos
public void AgregarItem()
{
this.colUserControl.AgregarItem();
}
}
I'm currently struggling with the implementation of a set of file system classes. I guess this calls for the composite pattern if I'm not mistaken. So I set up the following classes:
An abstract class Node which has a reference to its parent folder and two classes Folder and File that implement Node. A folder contains a collection of all its children and methods to add and remove children.
The thing is, I can't figure out how to implement all the methods properly. In all the examples I have seen there is no reference to the parent in the children. How can the AddChild method ensure that the child's parent reference is set correctly? I solved that by checking whether child.Parent has already been set to the folder or it throws an ArgumentException. The matter is further complicated by the fact that AddChild might also throw an exception like DuplicateNameException or something. So my methods look like this now:
File.AddTo(Folder folder) {
this.Parent = folder;
try {
folder.AddChild(this);
} catch {
this.Parent = null;
throw;
}
}
Folder.AddChild(Node child)
{
if(child.Parent != this)
throw new ArgumentException(...);
...
}
Now I have this ugly AddTo method and cannot do something like someFolder.AddChild(new File(...)). I wonder how it was implemented with ListViewItem for instance. There I can just do someListView.Items.Add(new ListViewItem(...)).
My solution works, but I'm not convinced that it's the right way to do this. Maybe someone has a better solution or can point me to a good example. Thanks in advance.
EDIT: Minimal full class definitions below.
abstract class Node
{
public Folder Parent { get; protected set; }
public string Name { get; private set; }
public Node(string name) {
Parent = null;
Name = name;
}
}
class Folder : Node {
private Dictionary<string, Node> _children;
public Folder(string name) : base(name) {
// Other initializations here...
}
public void AddChild(Node child) {
if(child is Folder)
((Folder)child).Parent = this; // Damn, doesn't work for files!!!
else if(child.Parent != this)
throw new ArgumentException();
if(_children.ContainsKey(child.Name))
throw new DuplicateNameException();
_children[child.Name] = child;
}
}
class File : Node {
public File(string name) : base(name) {
// Other initializations here...
}
public void AddTo(Folder folder) {
Parent = folder;
try {
folder.AddChild(this);
} catch {
Parent = null;
}
}
}
How about doing it the other way around:
Folder.AddChild(Node child)
{
child.Parent = this;
this._children.Add(child); // or what ever your doing to store the children
...
}
If you're adding a child to the parent, that should be done through a method on parent. Parent can then confirm/validate its own state and that its preconditions are satisfied. It's not up to a node to figure out whether its parent is valid -- let the parent do that.
So, by way of code, you have something like:
public class Node
{
public string Name { get; set; }
public abstract void Add(Node child);
protected abstract void CreateOnDisk();
}
public class File
{
public override void Add(Node child)
{
//No op, since you can't add a child to a file
}
protected override void CreateOnDisk()
{
File.Create(this.Name);
}
}
public class Directory
{
public override void Add(Node child)
{
child.Name = Path.Combine(this.Name, child.Name);
child.CreateOnDisk();
}
protected override CreateOnDisk()
{
Directory.Create(this.Name);
}
}
I just freelanced a bit off the top of my head, but that's to give an idea. I really think there's no need to keep track of your parent, and I think that's going to turn out in the end to be a fairly cumbersome solution.
When I implement bidirectional associations, I usually move all association maintenance to one of the sides. In this case, I'd chose Folder.
public abstract class Node
{
public Folder Parent { get; set; }
public string Name { get; set; }
public abstract long Size { get; }
}
public class File : Node
{
private long _size;
public override long Size
{
get { return _size; }
}
public void AddTo(Folder folder)
{
folder.Add(this);
}
public void RemoveFrom(Folder folder)
{
folder.Remove(this);
}
}
public class Folder : Node
{
private List<Node> _children = new List<Node>();
public void Add(Node node)
{
if (node.Parent == this)
return; // already a child of this folder
_children.Add(node);
node.Parent = this;
}
public void Remove(Node node)
{
if (node.Parent != this)
return; // not a child of this folder
_children.Remove(node);
node.Parent = null;
}
public override long Size
{
get { return _children.Sum(node => node.Size); }
}
}
PS try to eliminate bidirectional association, it adds lot of headache.
UPDATE
With unidirectional association you have simple code, without ugly Folder field in Node class (I hate when base class depends on its child). Also no headache with adding/removing files.
public abstract class Node
{
public string Name { get; set; }
public abstract long Size { get; }
}
public class File : Node
{
private long _size;
public override long Size
{
get { return _size; }
}
}
public class Folder : Node
{
private List<Node> _children = new List<Node>();
public void Add(Node node)
{
if (_children.Contains(node))
return;
_children.Add(node);
}
public void Remove(Node node)
{
if (!_children.Contains(node))
return;
_children.Remove(node);
}
public override long Size
{
get { return _children.Sum(node => node.Size); }
}
}
AddChild() is a method on the parent.
Thinking about the purpose of the method, and your desire to maintain a reference to the parent in the child, you need to expose a property on the child that can be set by the parent, presumably in the AddChild method.
public abstract class Node
{
private Node parent;
internal void SetParent(Node parent)
{
this.parent = parent;
}
}
public class Folder : Node
{
void AddChild(Node child)
{
this.children.Add(child);
child.SetParent(this); // or, you could use a C# Property
}
}
public class File : Node
{
}
The child knows how to establish its parent; the parent knows how to adopt a child.
This question already has answers here:
Is there a way to reach a `protected` member of another object from a derived type?
(7 answers)
Closed 9 years ago.
I want to organise a scene graph.
I have general class SceneNode:
public class SceneNode
{
protected SceneNode prev, next;
protected SceneNodeContainer parent;
public SceneNode Parent { get { return parent; } }
public SceneNode PreviousNode { get { return prev; } }
public SceneNode NextNode { get { return next; } }
}
I also have class SceneNodeContainer, which is like this:
public class SceneNodeContainer : SceneNode
{
SceneNode firstChild, lastChild;
public SceneNode FirstChild { get { return firstChild; } }
public SceneNode LastChild { get { return lastChild; } }
public void Add(SceneNode node)
{
Debug.Assert(node != null);
Debug.Assert(node.parent == null);
node.parent = this;
node.prev = lastChild;
node.next = null;
if (lastChild == null)
{
lastChild = node;
firstChild = lastChild;
}
else
{
lastChild.next = node;
lastChild = node;
}
}
public void Remove(SceneNode node)
{
Debug.Assert(node != null);
Debug.Assert(node.parent == this);
//unlink node
if (node.next != null)
node.next.prev = node.prev;
if (node.prev != null)
node.prev.next = node.next;
if (node == firstChild)
firstChild = node.next;
if (node == lastChild)
lastChild = node.prev;
node.parent = null;
node.next = null;
node.prev = null;
}
}
IntelliSense says that node.parent and other protected fields cannot be accessed from SceneNodeContainer. How can I overcome this?
You can't, because of the way protected works - it only allows access to protected fields of objects which are known to be of the child type (or a subtype). So if node were a SceneNodeContainer variable, you'd have access to the fields - but otherwise, you don't.
From section 3.5.3 of the C# 4 spec:
When a protected instance member is accessed outside the program text of the class in which it is declared, and when a protected internal instance member is accessed outside the program text of the program in which it is declared, the access must take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place through an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class.
(As an aside, I'd personally avoid protected fields anyway. I make non-constant fields private in almost all cases.)
use protected internal instead of protected then you can access from sub classes of same assembly.
public class SceneNode
{
protected internal SceneNode prev, next;
protected internal SceneNodeContainer parent;
public SceneNode Parent { get { return parent; } }
public SceneNode PreviousNode { get { return prev; } }
public SceneNode NextNode { get { return next; } }
}
public interface IMyControl<in T> where T : ICoreEntity
{
void SetEntity(T dataObject);
}
public class MyControl : UserControl, IMyControl<DataObject> // DataObject implements ICoreEntity
{
void SetEntity(T dataObject);
}
All fine so far, but why does this create null?
var control = LoadControl("~/Controls/MyControl.ascx"); // assume this line works
IMyControl<ICoreEntity> myControl = control;
myControl is now null...
You cannot have dataObject as parameter for this to work. Methods could only return it.
public interface ICoreEntity { }
public class DataObject: ICoreEntity { }
public interface IMyControl<out T> where T : ICoreEntity
{
T GetEntity();
}
public class MyControl : IMyControl<DataObject> // DataObject implements ICoreEntity
{
public DataObject GetEntity()
{
throw new NotImplementedException();
}
}
Now you can:
MyControl control = new MyControl();
IMyControl<ICoreEntity> myControl = control;