How to create a tree representing namespaces from their string representations - c#

How can I create a tree-like data structure for the namespaces.
For example, for these namespaces:
Enums.NEWENUMS.NEW1
Enums.NEWENUMS.NEW2
Enums.NEWENUMS.NEW3
Enums.OLDENUMS
Enums.TEST.SUB
Enums.TEST.SUB.OK
And then load it into a treeview like shown below:
I tried to split the namespaces, but for the life of me I can't think of a logic to generate it correctly.
Also tried to generate it the way you generate a directory structure, but can't get my head around it since namespaces need splitting.

1. Parsing namespace
Here is the class that represents namespace. It represents namespace as a dictionary of directly nested namespaces. To generate Namespaces from strings it provides static methods that use recursive calls and LINQ:
public class Namespace : IDictionary<String, Namespace>
{
#region Static
public static IEnumerable<Namespace> FromStrings(IEnumerable<String> namespaceStrings)
{
// Split all strings
var splitSubNamespaces = namespaceStrings
.Select(fullNamespace =>
fullNamespace.Split('.'));
return FromSplitStrings(null, splitSubNamespaces);
}
public static IEnumerable<Namespace> FromSplitStrings(Namespace root, IEnumerable<IEnumerable<String>> splitSubNamespaces)
{
if (splitSubNamespaces == null)
throw new ArgumentNullException("splitSubNamespaces");
return splitSubNamespaces
// Remove those split sequences that have no elements
.Where(splitSubNamespace =>
splitSubNamespace.Any())
// Group by the outermost namespace
.GroupBy(splitNamespace =>
splitNamespace.First())
// Create Namespace for each group and prepare sequences that represent nested namespaces
.Select(group =>
new
{
Root = new Namespace(group.Key, root),
SplitSubnamespaces = group
.Select(splitNamespace =>
splitNamespace.Skip(1))
})
// Select nested namespaces with recursive split call
.Select(obj =>
new
{
Root = obj.Root,
SubNamespaces = FromSplitStrings(obj.Root, obj.SplitSubnamespaces)
})
// Select only uppermost level namespaces to return
.Select(obj =>
obj.Root)
// To avoid deferred execution problems when recursive function may not be able to create nested namespaces
.ToArray();
}
#endregion
#region Fields
private IDictionary<String, Namespace> subNamespaces;
#endregion
#region Constructors
private Namespace(String nameOnLevel, Namespace parent)
{
if (String.IsNullOrWhiteSpace(nameOnLevel))
throw new ArgumentException("nameOfLevel");
this.Parent = parent;
this.NameOnLevel = nameOnLevel;
this.subNamespaces = new Dictionary<String, Namespace>();
if (this.Parent != null)
{
this.Parent.Add(this.NameOnLevel, this);
}
}
private Namespace(String nameOfLevel)
: this(nameOfLevel, null)
{
}
#endregion
#region Properties
public String NameOnLevel
{
get;
private set;
}
public String FullName
{
get
{
if (this.Parent == null)
return this.NameOnLevel;
return String.Format("{0}.{1}",
this.Parent.FullName,
this.NameOnLevel);
}
}
private Namespace _Parent;
public Namespace Parent
{
get
{
return this._Parent;
}
private set
{
if (this.Parent != null)
this.Parent.Remove(this.NameOnLevel);
this._Parent = value;
}
}
#endregion
#region IDictionary implementation
public void Add(string key, Namespace value)
{
if (this.ContainsKey(key))
throw new InvalidOperationException("Namespace already contains namespace with such name on level");
this.subNamespaces.Add(key, value);
}
public bool ContainsKey(string key)
{
return this.subNamespaces.ContainsKey(key);
}
public ICollection<string> Keys
{
get { return this.subNamespaces.Keys; }
}
public bool Remove(string key)
{
if (!this.ContainsKey(key))
throw new KeyNotFoundException();
this[key]._Parent = null;
return this.subNamespaces.Remove(key);
}
public bool TryGetValue(string key, out Namespace value)
{
return this.subNamespaces.TryGetValue(key, out value);
}
public ICollection<Namespace> Values
{
get { return this.subNamespaces.Values; }
}
public ICollection<Namespace> Subnamespaces
{
get { return this.subNamespaces.Values; }
}
public Namespace this[string nameOnLevel]
{
get
{
return this.subNamespaces[nameOnLevel];
}
set
{
if (value == null)
throw new ArgumentException("value");
Namespace toReplace;
if (this.TryGetValue(nameOnLevel, out toReplace))
{
toReplace.Parent = null;
}
value.Parent = this;
}
}
public void Add(KeyValuePair<string, Namespace> item)
{
this.Add(item.Key, item.Value);
}
public void Clear()
{
foreach (var subNamespace in this.subNamespaces.Select(kv => kv.Value))
{
subNamespace._Parent = null;
}
this.subNamespaces.Clear();
}
public bool Contains(KeyValuePair<string, Namespace> item)
{
return this.subNamespaces.Contains(item);
}
public void CopyTo(KeyValuePair<string, Namespace>[] array, int arrayIndex)
{
this.subNamespaces.CopyTo(array, arrayIndex);
}
public int Count
{
get { return this.subNamespaces.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(KeyValuePair<string, Namespace> item)
{
return this.subNamespaces.Remove(item);
}
public IEnumerator<KeyValuePair<string, Namespace>> GetEnumerator()
{
return this.subNamespaces.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region Overrides
public override string ToString()
{
return this.FullName;
}
#endregion
}
P.S: This class may have few incorrectly implemented methods.
P.S.1: Parsing methods can be rewritten without LINQ. Actually this LINQ solution is not very idiomatic or a really good example of how and when to use LINQ. But it is short and mostly simple.
2. Adding namespaces to the TreeView
You haven't mentioned the UI framework you use, so I have defaulted to Windows Forms. Assuming that you have added TreeView named treeView_Namespaces to the form:
public Form1()
{
InitializeComponent();
var namespaceStrings = new String[]
{
"Enums.NEWENUMS.NEW1",
"Enums.NEWENUMS.NEW2",
"Enums.NEWENUMS.NEW3",
"Enums.OLDENUMS",
"Enums.TEST.SUB",
"Enums.TEST.SUB.OK"
};
var namespaces = Namespace.FromStrings(namespaceStrings);
AddNamespaces(this.treeView_Namespaces.Nodes, namespaces);
}
void AddNamespaces(TreeNodeCollection nodeCollection, IEnumerable<Namespace> namespaces)
{
foreach (var aNamespace in namespaces)
{
TreeNode node = new TreeNode(aNamespace.NameOnLevel);
nodeCollection.Add(node);
AddNamespaces(node.Nodes, aNamespace.Subnamespaces);
node.Expand();
}
}
3. If you need to generate such tree from real namespaces
To do that you will have to walk through types in the Assembly and get all their namespaces:
For example, this code get all types in the currently executing Assembly:
var namespaceStrings = Assembly
.GetExecutingAssembly()
.GetTypes()
.Select(type =>
type.Namespace)
.Where(#namespace =>
#namespace != null);

Related

How to cast object to type specified by Type variable

I am looking for a way to cast object variable into type with generic type argument specified by other variable of type Type.
I am limited to .NET 3.5, so no dynamic can be used :(
Main idea here is that I have access to a dictionary:
Dictionary<Type, object> data;
Data to that dictionary is added only in form of:
data.Add(T, new DataSub<T>(someValueOfTypeT));
The problem is, that when I'm trying to reverse the process:
foreach(var dataType in data.Keys) {
var dataValue = data[dataType];
ProcessDataValue(dataType, dataValue);
}
Now the question is how do I manage to cast object to DataSub?
Simplified DataSub.cs:
public class DataSub<T>
{
private T _cache;
public T Value {
get { return _cache; }
set { _cache = value; }
}
}
How it could work in ProcessDataValue:
public void ProcessDataValue(Type dataType, object dataValue)
{
var data = dataValue as DataSub<dataType>;
if (data == null) return;
AddProcessedDataValue(dataType, data.Value.ToString());
}
if you can do minimal changes to the classes you posted and if - as is showed in your example - what you would do with DataSub.Value is invoking ToString, may be you can obtain the result you need with
public interface IDataSub {
bool MatchesType(Type t);
object GetValue();
}
public class DataSub<T> : IDataSub {
private T _cache;
public T Value {
get { return _cache; }
set { _cache = value; }
}
public bool MatchesType(Type t) {
return typeof(T) == t; // or something similar, in order to handle inheritance
}
public object GetValue() {
return Value;
}
}
public class Client {
Dictionary<Type, IDataSub> data = new Dictionary<Type, IDataSub>() ;
public void AddData<T>(T someValueOfTypeT) {
data.Add(typeof(T), new DataSub<T> { Value = someValueOfTypeT });
}
public void UseData() {
foreach(var dataType in data.Keys) {
var dataValue = data[dataType];
ProcessDataValue(dataType, dataValue);
}
}
public void ProcessDataValue(Type dataType, IDataSub dataValue)
{
if(dataValue.MatchesType(dataType))
AddProcessedDataValue(dataType, dataValue.GetValue().ToString());
}
}
If the usage of DataSub.Value.ToString is only an example, and in the real world you need to access DataSub.Value using its type T, you should apply a broader reworking of you code.
What do you think about the following approach? This is an application of the pattern I like call set of responsibility (I wrote the linked post about this topic), a variation of GoF's chain of responsibility:
public interface IDataSub {
object GetValue();
}
public class DataSub<T> : IDataSub {
private T _cache;
public T Value {
get { return _cache; }
set { _cache = value; }
}
public object GetValue() {
return Value;
}
}
public interface IDataHandler {
bool CanHandle(Type type);
void Handle(object data);
}
public class Client {
private readonly Dictionary<Type, IDataSub> data = new Dictionary<Type, IDataSub>();
private readonly IList<IDataHandler> handlers = new List<IDataHandler>();
public void AddData<T>(T someValueOfTypeT) {
data.Add(typeof(T), new DataSub<T> { Value = someValueOfTypeT });
}
public void RegisterHandler(IDataHandler handler) {
handlers.Add(handler);
}
public void UseData() {
foreach(var dataType in data.Keys) {
handlers.FirstOrDefault(h => h.CanHandle(dataType))?.Handle(data[dataType].GetValue());
}
}
// Lambda-free version
// public void UseData() {
// foreach(var dataType in data.Keys) {
// for (int i = 0; i < handlers.Count; i++) {
// if (handlers[i].CanHandle(dataType)) {
// handlers[i].Handle(data[dataType].GetValue());
// break; // I don't like breaks very much...
// }
// }
// }
// }
}
class StringDataHandler : IDataHandler {
public bool CanHandle(Type type) {
// Your logic to check if this handler implements logic applyable to instances of type
return typeof(string) == type;
}
public void Handle(object data) {
string value = (string) data;
// Do something with string
}
}
class IntDataHandler : IDataHandler {
public bool CanHandle(Type type) {
// Your logic to check if this handler implements logic applyable to instances of type
return typeof(int) == type;
}
public void Handle(object data) {
int value = (int) data;
// Do something with int
}
}
This approach allow you to decouple data storage and data iteration logic from data-handling logic specific of different data-types: IDataHandler's implementations known what type of data they can handle and cast generic object reference to desired type. If you prefer, you can merge CanHandle method into Handle method, remving the former method and changing UseData to
public void UseData() {
foreach(var dataType in data.Keys) {
foreach(var handler in handlers) {
handler.Handle(dataType, data[dataType].GetValue())
}
}
}
and handler implementations to
class IntDataHandler : IDataHandler {
public void Handle(Type dataType, object data) {
if(typeof(int) == type) {
int value = (int) data;
// Do something with int
}
}
}
This variant is slightly more type-safe, because in the first variant was already possibile to call Handle method without a previus call to CanHandle.
If you liked this approach, you can bring it forward, simplifying your data structure and converting data from IDictionary to IList:
public interface IDataSub {
object GetValue();
}
public class DataSub<T> : IDataSub {
private T _cache;
public T Value {
get { return _cache; }
set { _cache = value; }
}
public object GetValue() {
return Value;
}
}
public interface IDataHandler {
bool CanHandle(object data);
void Handle(object data);
}
public class Client {
private readonly IList<IDataSub> data = new List<IDataSub>();
private readonly IList<IDataHandler> handlers = new List<IDataHandler>();
public void AddData<T>(T someValueOfTypeT) {
data.Add(new DataSub<T> { Value = someValueOfTypeT });
}
public void RegisterHandler(IDataHandler handler) {
handlers.Add(handler);
}
public void UseData() {
foreach(var dataItem in data) {
var value = dataItem.GetValue();
handlers.FirstOrDefault(h => h.CanHandle(value))?.Handle(value);
}
}
// Lambda-free version as above...
class StringDataHandler : IDataHandler {
public bool CanHandle(object data) {
// Your logic to check if this handler implements logic applyable to instances of String
return data is string;
}
public void Handle(object data) {
string value = (string) data;
// Do something with string
}
}
class IntDataHandler : IDataHandler {
public bool CanHandle(Type type) {
// Your logic to check if this handler implements logic applyable to instances of int
return type is int;
}
public void Handle(object data) {
int value = (int) data;
// Do something with int
}
}
The CanHandle-free variant can simplify IDataHandler interface and its implementation in this case, too...
I hope my answer can help you resolving you design scenario; I build it upon an approach I like very much, because it allows to apply subtype-specific logic to instances of different classe, given they share a common superclass (as object in my code samples).

Constrain a list of System.Type to types that inherit from a base type

If I have the code:
List<Type> Requires = new List<Type>();
How would I constrain the types within this list so they have a common parent?
For example:
List<Type : Component> Requires = new List<Type>()
Edit: A little bit more background so maybe people can understand why I need this. I have a class Entity that contains a list of Components. Each component needs to have a list of Component types that acts as a list of dependencies. So at runtime when you try to add a Component to a Entity it will do a quick check to see if that Entity has the required components already attached to it.
Example:
//Entity.cs
//...
_components = new List<Component>();
//...
public T AddComponent<T>() where T : Component, new()
{
var temp = new T();
if (_components.Exists((x) => x is T)) return null;
foreach (var t in temp.Requires)
{
if (_components.Exists(x => x.GetType() == t)) return null;
}
_components.Add(new T());
temp.gameObject = this;
return temp;
}
//...
//Component.cs
//...
protected internal Entity gameObject;
protected internal List<Type> Requires { get; }
//...
After much work I found a solution to my own problem.
//Component.cs
public abstract class Component {
//...
protected internal Entity gameObject;
private RequiresList _requires;
//...
protected internal RequiresList Requires
{
get => _requires;
private set => _requires = (RequiresList)value.FindAll(x => x.IsSubclassOf(typeof(Component)));
}
//...
public class RequiresList : List<Type>
{
public RequiresList() { }
public RequiresList(IEnumerable<Type> types) : base(types) { }
public RequiresList(int capacity) : base(capacity) { }
public new Type this[int index]
{
get => base[index];
set
{
if (isComp(value))
base[index] = value;
}
}
public new void Add(Type type)
{
if (isComp(type))
base.Add(type);
}
private static bool isComp(Type type)
{
return type.IsSubclassOf(typeof(Component));
}
}
//...
}
//Entity.cs
public abstract class Entity {
//...
_components = new List<Component>();
//...
public T AddComponent<T>() where T : Component, new()
{
var temp = new T();
if (_components.Exists((x) => x is T)) return null;
foreach (var t in temp.Requires)
{
if (_components.Exists(x => x.GetType() == t)) return null;
}
_components.Add(new T());
temp.gameObject = this;
return temp;
}
//...
}
I created a new storage type call RequiresList which checks all System.Types that are inserted into it to see if they are a subclass of Component. I also made sure that if someone tried to replace the list with an entirely new one it will remove any indexes of the new list that aren't Components

Polymorphic object creation without IF condition

I have an abstract class like this:
public abstract class Records
{
public string Type;
public string Source;
public int Value;
protected Records(string type, string source, int value)
{
Type = type;
Source = source;
Value = value;
}
}
I would like to create many classes inheriting this class, and filling their Type field with a value coming from a static class like this:
public static class ContentTypesString
{
public static string DocumentNew { get { return "Document - New this Month"; }}
public static string HeadlinesNew { get { return "Headlines - New this Month"; }}
etc...
}
I would like to be able to create those child classes without having a test "if foo == "document" then type = ContentTypesString.DocumentNew" or an equivalent switch case (I really have a lot of cases)
Is there a design pattern that suits my needs?
EDIT : As several people pointed out, i should show how i create my instances.
private delegate SPListItemCollection Query(SPWeb web, DateTime startDate, DateTime endDate);
private readonly Query _queries;
#region Constructors
public QueryHandler(SPWeb web, DateTime startTimeSelectedDate, DateTime endTimeSelectedDate)
{
if (web == null) throw new ArgumentNullException("web");
_web = web;
_startTimeSelectedDate = startTimeSelectedDate;
_endTimeSelectedDate = endTimeSelectedDate;
RecordsList = new List<Records>();
// Query Invocation List
_queries = NumberPagePerMonthQuery.PreparedQuery;
_queries += NumberDocumentsPerMonthQuery.PreparedQuery;
_queries += NumberHeadlinesPerMonthQuery.PreparedQuery;
_queries += NumberLeaderboxPerMonthQuery.PreparedQuery;
_queries += NumberNewsPerMonthQuery.PreparedQuery;
_queries += NumberPagesModifiedPerMonthQuery.PreparedQuery;
_queries += NumberPicturesPerMonthQuery.PreparedQuery;
_queries += NumberTeasingPerMonthQuery.PreparedQuery;
}
#endregion Constructors
#region Public Methods
// what about NullReferenceException ? C#6 : item?.Foreach(item => {}); ?
/*** NO C#6 compiler in VS2012... ***/
public void Queries()
{
foreach (var del in _queries.GetInvocationList())
{
var queryresult =
(SPListItemCollection) del.DynamicInvoke(_web, _startTimeSelectedDate, _endTimeSelectedDate);
RecordsList.Add(new Records(del.Method.Name, _web.Title, queryresult.Count));
}
}
EDIT² :
The solution i chose
public List<IQuery> QueryList { get; } // no delegate anymore, and static classes became implementations of IQuery interface.
#region Constructors
public QueryHandler(SPWeb web, DateTime startTimeSelectedDate, DateTime endTimeSelectedDate)
{
if (web == null) throw new ArgumentNullException("web");
_web = web;
_startTimeSelectedDate = startTimeSelectedDate;
_endTimeSelectedDate = endTimeSelectedDate;
RecordsList = new List<Records>();
QueryList = new List<IQuery>
{
new NumberDocumentsPerMonthQuery(),
new NumberHeadlinesPerMonthQuery(),
new NumberLeaderboxPerMonthQuery(),
new NumberNewsPerMonthQuery(),
new NumberPagePerMonthQuery(),
new NumberPagesModifiedPerMonthQuery(),
new NumberPicturesPerMonthQuery(),
new NumberTeasingPerMonthQuery()
};
}
#endregion Constructors
#region Public Methods
// what about NullReferenceException ? C#6 : item?.Foreach(item => {}); ?
/*** NO C#6 compiler in VS2012... ***/
public void Queries()
{
foreach (var query in QueryList)
{
var queryresult = query.PreparedQuery(_web, _startTimeSelectedDate, _endTimeSelectedDate);
RecordsList.Add(query.CreateRecord(_web.Title, queryresult.Count));
}
}
Record class follow the implementation suggested by #dbraillon
Implementation of IQuery interface were added the method :
public Records CreateRecord(string source, int value)
{
return new ModifiedPagesPerMonthRecord(source, value); //or another child of Record class.
}
And voilĂ . Thank you all for the help.
You want to make collection of records, by string code of object type, and parameters.
One of many way to do it - use builder.
Firstly we need to configurate builder:
var builder = new RecordBuilder()
.RegisterBuilder("document", (source, value) => new Document(source, value))
.RegisterBuilder("headlines", (source, value) => new Headlines(source, value));
here we specify how to build record with code "document" and "headlines".
To build a record call:
builder.Build("document", "source", 1);
Builder code can by something like this
(here we look if we know how to build record of the passed type and make it):
public class RecordBuilder
{
public Records Build(string code, string source, int value)
{
Func<string, int, Records> buildAction;
if (recordBuilders.TryGetValue(code, out buildAction))
{
return buildAction(source, value);
}
return null;
}
public RecordBuilder RegisterBuilder(string code, Func<string, int, Records> buildAction)
{
recordBuilders.Add(code, buildAction);
return this;
}
private Dictionary<string, Func<string, int, Records>> recordBuilders = new Dictionary<string, Func<string, int, Records>> ();
}
public class Document : Records
{
public Document(string source, int value) : base(ContentTypesString.DocumentNew, source, value)
{
}
}
public class Headlines : Records
{
public Headlines(string source, int value) : base(ContentTypesString.HeadlinesNew, source, value)
{
}
}
Is that what you need ?
public abstract class Records
{
public string Type;
public string Source;
public int Value;
protected Records(string type, string source, int value)
{
Type = type;
Source = source;
Value = value;
}
}
public class DocumentRecords : Records
{
public DocumentRecords(string source, int value)
: base(ContentTypesString.DocumentNew, source, value) // use here
{
}
}
public class HeadlinesRecords : Records
{
public HeadlinesRecords(string source, int value)
: base(ContentTypesString.HeadlinesNew, source, value) // use here
{
}
}
public static class ContentTypesString
{
public static string DocumentNew { get { return "Document - New this Month"; } }
public static string HeadlinesNew { get { return "Headlines - New this Month"; } }
}

Bind complex object to cell of DataTable in C#

I have a DataTable with complex objects.
For example,
class ComplexDataWrapper
{
public string Name{ get; set; }
public ComplexData Data{ get; set; }
public ComplexDataWrapper(ComplexData data)
{
this.Data = data;
this.Name = "Something";
}
public override string ToString()
{
return Name;
}
}
And now I want to bind cells from DataTable to objects of ComplexDataWrapper
So, I try something like this :
...
var column = new DataColumn() { ColumnName = columnName, DataType = typeof(ComplexDataWrapper)};
row[column] = new ComplexDataWrapper(data);
But, I want to bind for only one property, for example, Name.
And in the gridview (DataTable is a data source for this view) I want to edit this property(Name).
var complexDataWrapper = row[column] as ComplexDataWrapper;
complexDataWrapper always equals to NULL.
I know that I miss something.
So my questions : How I can bind my cell of DataTable to complex object? Plus in grid view I want to edit exactly one property of complex object.
Thanks. Hopefully, everything is clear.
So my questions : How I can bind my cell of DataTable to complex object? Plus in grid view I want to edit exactly one property of complex object.
What you need here is the ability to bind to a so called property path (e.g. obj.Prop1.Prop2). Unfortunately WinForms has limited support for that - it's supported for simple data binding (like control.DataBindings.Add(...)) but not for list data binding which is used by DataGridView control and similar.
Fortunately it's still doable with some (most of the time trivial) coding, because the data binding is build around an abstraction called PropertyDescriptor. By default it is implemented via reflection, but nothing prevents you to create your own implementation and do whatever you like inside it. That allows you to do many things that are not possible with reflection, in particular to simulate "properties" that actually do not exist.
Here we will utilize that possibility to create a "property" that actually gets/sets its value from a child property of the original property, while from outside it still looks like a single property, thus allowing to data bind to it:
public class ChildPropertyDescriptor : PropertyDescriptor
{
public static PropertyDescriptor Create(PropertyDescriptor sourceProperty, string childPropertyPath, string displayName = null)
{
var propertyNames = childPropertyPath.Split('.');
var propertyPath = new PropertyDescriptor[1 + propertyNames.Length];
propertyPath[0] = sourceProperty;
for (int i = 0; i < propertyNames.Length; i++)
propertyPath[i + 1] = propertyPath[i].GetChildProperties()[propertyNames[i]];
return new ChildPropertyDescriptor(propertyPath, displayName);
}
private ChildPropertyDescriptor(PropertyDescriptor[] propertyPath, string displayName)
: base(propertyPath[0].Name, null)
{
this.propertyPath = propertyPath;
this.displayName = displayName;
}
private PropertyDescriptor[] propertyPath;
private string displayName;
private PropertyDescriptor RootProperty { get { return propertyPath[0]; } }
private PropertyDescriptor ValueProperty { get { return propertyPath[propertyPath.Length - 1]; } }
public override Type ComponentType { get { return RootProperty.ComponentType; } }
public override bool IsReadOnly { get { return ValueProperty.IsReadOnly; } }
public override Type PropertyType { get { return ValueProperty.PropertyType; } }
public override bool CanResetValue(object component) { var target = GetTarget(component); return target != null && ValueProperty.CanResetValue(target); }
public override object GetValue(object component) { var target = GetTarget(component); return target != null ? ValueProperty.GetValue(target) : null; }
public override void ResetValue(object component) { ValueProperty.ResetValue(GetTarget(component)); }
public override void SetValue(object component, object value) { ValueProperty.SetValue(GetTarget(component), value); }
public override bool ShouldSerializeValue(object component) { var target = GetTarget(component); return target != null && ValueProperty.ShouldSerializeValue(target); }
public override AttributeCollection Attributes { get { return ValueProperty.Attributes; } }
public override string Category { get { return ValueProperty.Category; } }
public override TypeConverter Converter { get { return ValueProperty.Converter; } }
public override string Description { get { return ValueProperty.Description; } }
public override bool IsBrowsable { get { return ValueProperty.IsBrowsable; } }
public override bool IsLocalizable { get { return ValueProperty.IsLocalizable; } }
public override string DisplayName { get { return displayName ?? RootProperty.DisplayName; } }
public override object GetEditor(Type editorBaseType) { return ValueProperty.GetEditor(editorBaseType); }
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) { return ValueProperty.GetChildProperties(GetTarget(instance), filter); }
public override bool SupportsChangeEvents { get { return ValueProperty.SupportsChangeEvents; } }
public override void AddValueChanged(object component, EventHandler handler)
{
var target = GetTarget(component);
if (target != null)
ValueProperty.AddValueChanged(target, handler);
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
var target = GetTarget(component);
if (target != null)
ValueProperty.RemoveValueChanged(target, handler);
}
private object GetTarget(object source)
{
var target = source;
for (int i = 0; target != null && target != DBNull.Value && i < propertyPath.Length - 1; i++)
target = propertyPath[i].GetValue(target);
return target != DBNull.Value ? target : null;
}
}
The code is not so small, but all it does is basically delegating the calls to the corresponding methods of the property descriptor chain representing the path from the original property to the child property. Also please note that many methods of the PropertyDescriptor are used only during the design time, so creating a custom concrete runtime property descriptor usually needs only to implement ComponentType, PropertyType, GetValue and SetValue (if supported).
So far so good. This is just the first part of the puzzle. We can create a "property", now we need a way to let data binding use it.
In order to do that, we'll utilize another data binding related interface called ITypedList:
Provides functionality to discover the schema for a bindable list, where the properties available for binding differ from the public properties of the object to bind to.
In other words, it allows us to provide "properties" for the list elements. But how? If we were implementing the data source list, it would be easy. But here we want to do that for a list that we don't know in advance (I'm trying the keep the solution generic).
The solutions is to wrap the original list in onother one that will implement IList (the minimum requirement for list data binding) by delegating all the calls to the underlying list, but by implementing ITypedList will control the properties used for binding:
public static class ListDataView
{
public static IList Create(object dataSource, string dataMember, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper)
{
var source = (IList)ListBindingHelper.GetList(dataSource, dataMember);
if (source == null) return null;
if (source is IBindingListView) return new BindingListView((IBindingListView)source, propertyMapper);
if (source is IBindingList) return new BindingList((IBindingList)source, propertyMapper);
return new List(source, propertyMapper);
}
private class List : IList, ITypedList
{
private readonly IList source;
private readonly Func<PropertyDescriptor, PropertyDescriptor> propertyMapper;
public List(IList source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) { this.source = source; this.propertyMapper = propertyMapper; }
// IList
public object this[int index] { get { return source[index]; } set { source[index] = value; } }
public int Count { get { return source.Count; } }
public bool IsFixedSize { get { return source.IsFixedSize; } }
public bool IsReadOnly { get { return source.IsReadOnly; } }
public bool IsSynchronized { get { return source.IsSynchronized; } }
public object SyncRoot { get { return source.SyncRoot; } }
public int Add(object value) { return source.Add(value); }
public void Clear() { source.Clear(); }
public bool Contains(object value) { return source.Contains(value); }
public void CopyTo(Array array, int index) { source.CopyTo(array, index); }
public IEnumerator GetEnumerator() { return source.GetEnumerator(); }
public int IndexOf(object value) { return source.IndexOf(value); }
public void Insert(int index, object value) { source.Insert(index, value); }
public void Remove(object value) { source.Remove(value); }
public void RemoveAt(int index) { source.RemoveAt(index); }
// ITypedList
public string GetListName(PropertyDescriptor[] listAccessors) { return ListBindingHelper.GetListName(source, listAccessors); }
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var properties = ListBindingHelper.GetListItemProperties(source, listAccessors);
if (propertyMapper != null)
properties = new PropertyDescriptorCollection(properties.Cast<PropertyDescriptor>()
.Select(propertyMapper).Where(p => p != null).ToArray());
return properties;
}
}
private class BindingList : List, IBindingList
{
private IBindingList source;
public BindingList(IBindingList source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) : base(source, propertyMapper) { this.source = source; }
private ListChangedEventHandler listChanged;
public event ListChangedEventHandler ListChanged
{
add
{
var oldHandler = listChanged;
if ((listChanged = oldHandler + value) != null && oldHandler == null)
source.ListChanged += OnListChanged;
}
remove
{
var oldHandler = listChanged;
if ((listChanged = oldHandler - value) == null && oldHandler != null)
source.ListChanged -= OnListChanged;
}
}
private void OnListChanged(object sender, ListChangedEventArgs e)
{
var handler = listChanged;
if (handler != null)
handler(this, e);
}
public bool AllowNew { get { return source.AllowNew; } }
public bool AllowEdit { get { return source.AllowEdit; } }
public bool AllowRemove { get { return source.AllowRemove; } }
public bool SupportsChangeNotification { get { return source.SupportsChangeNotification; } }
public bool SupportsSearching { get { return source.SupportsSearching; } }
public bool SupportsSorting { get { return source.SupportsSorting; } }
public bool IsSorted { get { return source.IsSorted; } }
public PropertyDescriptor SortProperty { get { return source.SortProperty; } }
public ListSortDirection SortDirection { get { return source.SortDirection; } }
public object AddNew() { return source.AddNew(); }
public void AddIndex(PropertyDescriptor property) { source.AddIndex(property); }
public void ApplySort(PropertyDescriptor property, ListSortDirection direction) { source.ApplySort(property, direction); }
public int Find(PropertyDescriptor property, object key) { return source.Find(property, key); }
public void RemoveIndex(PropertyDescriptor property) { source.RemoveIndex(property); }
public void RemoveSort() { source.RemoveSort(); }
}
private class BindingListView : BindingList, IBindingListView
{
private IBindingListView source;
public BindingListView(IBindingListView source, Func<PropertyDescriptor, PropertyDescriptor> propertyMapper) : base(source, propertyMapper) { this.source = source; }
public string Filter { get { return source.Filter; } set { source.Filter = value; } }
public ListSortDescriptionCollection SortDescriptions { get { return source.SortDescriptions; } }
public bool SupportsAdvancedSorting { get { return source.SupportsAdvancedSorting; } }
public bool SupportsFiltering { get { return source.SupportsFiltering; } }
public void ApplySort(ListSortDescriptionCollection sorts) { source.ApplySort(sorts); }
public void RemoveFilter() { source.RemoveFilter(); }
}
}
Actually as you can see, I've added wrappers for other data source interfaces like IBindingList and IBindingListView. Again, the code is not so small, but it's just delegating the calls to the underlying objects (when creating one for your concrete data, you usually would inherit from List<T> or BiundingList<T> and implement only the two ITypedList members). The essential part is the GetItemProperties method implementation which along with the propertyMapper lambda allows you to replace one property with another.
With all that in place, solving the specific problem from the post is simple a matter of wrapping the DataTable and mapping the Complex property to the Complex.Name property:
class ComplexData
{
public int Value { get; set; }
}
class ComplexDataWrapper
{
public string Name { get; set; }
public ComplexData Data { get; set; } = new ComplexData();
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form();
var gridView = new DataGridView { Dock = DockStyle.Fill, Parent = form };
gridView.DataSource = ListDataView.Create(GetData(), null, p =>
{
if (p.PropertyType == typeof(ComplexDataWrapper))
return ChildPropertyDescriptor.Create(p, "Name", "Complex Name");
return p;
});
Application.Run(form);
}
static DataTable GetData()
{
var dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Complex", typeof(ComplexDataWrapper));
for (int i = 1; i <= 10; i++)
dt.Rows.Add(i, new ComplexDataWrapper { Name = "Name#" + i, Data = new ComplexData { Value = i } });
return dt;
}
}
To recap, custom PropertyDescriptor and ITypedList allow you to create unlimited types of views of your data, which then can be used by any data bound aware control.
I believe your architecture is flawed for what you're trying to achieve.
If you are using the gridView to edit a single property on your complex type, then there is no need to bind the entire type into the datatable the is the datasource of your grid.
Instead you should bind only the property you wish to edit, and when the the data comes back, simply assign it to the complex type in the right place.

Namespace dict?

I'm devising a template language. In it, there are 3 kinds of tokens: tags, directives, and variables. Each of these tokens have a name, and there's getting to be quite a few of them. They're extensible too.
To allow name reuse I want to add namespaces.
Right now all the variables are just stored in a dict. The key is the variable name, and the value is the variable value. That way I can quickly retrieve the value of a variable. However, supposing I want to allow dot-notation, namespace.variable, how can I store these variables, such that the namespace is optional? If the namespace is included the dict should only scan that namespace, if not, I guess it scans all namespaces.
Is there a container that will do this?
You should structure your symbol data internally as a dictionary of dictionary of string. The top level dictionary is for namespaces, and each dictionary below each namespace name is the container for all symbols in that namespace.
Looking up an unqualified symbol is simply a matter of looking for the symbol in each namespace in a particular order. In C# or Delphi, the order is determined by the order in which the namespaces are declared at the top of the source file, in reverse order of declaration (most recent is the first to be searched).
You can create your own implementation of IDictionary<string, object> instead of using the framework's Dictionary<TKey, TValue>.
Externally, there would be no change to the way you are consuming it.
Internally, it would consist of a Dictionary<string, Dictionary<string, object>>.
So, if your dictionary is asked for the value matching key "namespace.variable", internally it would split that string, get the Dictionary<string, Dictionary<string, object>> with key "namespace" and then return the value in that Dictionary<string, object> for key "variable."
To make the namespace optional, you have one entry where the key is string.Empty. Whether adding or getting items, any time a key is provided that does not contain ., you'll use the entry with key string.Empty.
My solution:
Class
public class NamespaceDictionary<T> : IDictionary<string, T>
{
private SortedDictionary<string, Dictionary<string, T>> _dict;
private const char _separator = '.';
public NamespaceDictionary()
{
_dict = new SortedDictionary<string, Dictionary<string, T>>();
}
public NamespaceDictionary(IEnumerable<KeyValuePair<string, T>> collection)
: this()
{
foreach (var item in collection)
Add(item);
}
#region Implementation of IEnumerable
public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
{
return _dict.SelectMany(x => x.Value).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
private static Tuple<string, string> Split(string name)
{
int pos = name.LastIndexOf(_separator);
string ns = pos == -1 ? "" : name.Substring(0, pos);
string var = name.Substring(pos + 1);
return new Tuple<string, string>(ns, var);
}
#region Implementation of ICollection<KeyValuePair<string,TValue>>
public void Add(KeyValuePair<string, T> item)
{
Add(item.Key, item.Value);
}
public void Clear()
{
_dict.Clear();
}
public bool Contains(KeyValuePair<string, T> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, T> item)
{
return Remove(item.Key);
}
public int Count
{
get { return _dict.Sum(p => p.Value.Count); }
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
#region Implementation of IDictionary<string,TValue>
public bool ContainsKey(string name)
{
var tuple = Split(name);
return ContainsKey(tuple.Item1, tuple.Item2);
}
public bool ContainsKey(string ns, string key)
{
if (ns == "")
return _dict.Any(pair => pair.Value.ContainsKey(key));
return _dict.ContainsKey(ns) && _dict[ns].ContainsKey(key);
}
public void Add(string name, T value)
{
var tuple = Split(name);
Add(tuple.Item1, tuple.Item2, value);
}
public void Add(string ns, string key, T value)
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns].Add(key, value);
}
public bool Remove(string ns, string key)
{
if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
if (_dict[ns].Count == 1) _dict.Remove(ns);
else _dict[ns].Remove(key);
return true;
}
return false;
}
public bool Remove(string key)
{
var tuple = Split(key);
return Remove(tuple.Item1, tuple.Item2);
}
public bool TryGetValue(string name, out T value)
{
var tuple = Split(name);
return TryGetValue(tuple.Item1, tuple.Item2, out value);
}
public bool TryGetValue(string ns, string key, out T value)
{
if (ns == "")
{
foreach (var pair in _dict)
{
if (pair.Value.ContainsKey(key))
{
value = pair.Value[key];
return true;
}
}
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
{
value = _dict[ns][key];
return true;
}
value = default(T);
return false;
}
public T this[string ns, string key]
{
get
{
if (ns == "")
{
foreach (var pair in _dict)
if (pair.Value.ContainsKey(key))
return pair.Value[key];
}
else if (_dict.ContainsKey(ns) && _dict[ns].ContainsKey(key))
return _dict[ns][key];
throw new KeyNotFoundException();
}
set
{
if (!_dict.ContainsKey(ns))
_dict[ns] = new Dictionary<string, T>();
_dict[ns][key] = value;
}
}
public T this[string name]
{
get
{
var tuple = Split(name);
return this[tuple.Item1, tuple.Item2];
}
set
{
var tuple = Split(name);
this[tuple.Item1, tuple.Item2] = value;
}
}
public ICollection<string> Keys
{
get { return _dict.SelectMany(p => p.Value.Keys).ToArray(); }
}
public ICollection<T> Values
{
get { return _dict.SelectMany(p => p.Value.Values).ToArray(); }
}
#endregion
}
Test
var dict = new NamespaceDictionary<int>();
dict.Add("ns1.var1", 1);
dict.Add("ns2.var1", 2);
dict.Add("var2", 3);
dict.Add("ns2.var2", 4);
dict.Add("ns3", "var1", 5);
dict["ns4.var1"] = 6;
Console.WriteLine(dict["var1"]);
Console.WriteLine(dict["ns2.var1"]);
Console.WriteLine(dict["var2"]);
Console.WriteLine(dict["ns2.var2"]);
Console.WriteLine(dict["ns2", "var2"]);
Console.WriteLine(dict["ns3.var1"]);
Console.WriteLine(dict["ns4", "var1"]);
Output
1
2
3
4
4
5
6
Help
I used a SortedDictionary thinking it would retain the order that the namespaces were added, but it's actually sorting the namespaces alphabetically. Is there an dict class that will retain the order the items were added, but not sort them?

Categories

Resources