S
Sky Sigal
(PS: Cross post from microsoft.pulic.dotnet.framework.aspnet.webcontrols)
I've been looking lately for a way to keep the Properties panel for Controls
'clean'...
My goal is to keep similar public properties of a custom Control neatly tied
together -- rather than all over the IDE.
One such set of values that will rarely be changed, so should have little
priority in the IDE Properties panel, and therefore a good candidate for
keeping
in an expandableobject like way, is maybe Captions that are used
through the control.
So assuming that I made the following class
class MyControl : WebControl {
....
public class cCaptions {
public string _Btn_Submit = "Login";
public string _Btn_NewUser = "Register";
public string Btn_Submit {get{return _Btn_Submit;}set{_Btn_Submit=value;}}
public string Btn_NewUser {get{return
_Btn_NewUser;}set{_Btn_NewUser=value;}}
}//Subclass:End
....
private cCaptions _Captions = new cCaptions;
....
[TypeConverter(typeof(TestConverter)),
PersistenceMode(PersistenceMode.Attribute )]
public cCaptions {get {return _Captions;}set {_Captions = value;}
....
}//Control:End
As you can see the public property now requires a custom TypeConverter so
that it can be seen in the IDE -- and deal with persistence as an attribute.
I originally thought/tried using the standard ExpandableObject one -- but I
needed a way to persist it in one string -- just like how style="" attribute
is done, with : and ; as sep chars between key and value...
Well.
The TypeConverter I wrote works atleast for converting between object and
string, and now in the IDE's Property panel I get the expandableObject look
(+/- tree) as well as a means of reading it as one string which looks like
<MyControl CAPTIONS="Btn_Submit:Login;Btn_NewUser:Register"></MyControl>
My problem with this is that it all LOOKS right -- but its not acting
right...
Problems/Observations:
a) When I edit the string directly in the Properties panel -- eg change it
to "Btn_Submit:HELLO;Btn_NewUser:Come on in!" it ---well...doesn't do it.
The moment I hit Save -- it reverts back to to the default values. :-(
b) When I click + to expand the Expandable options, and edit the Btn_Submit
value directly -- it updates the summary string line/title to the new
values -- AND updates the interface's actual label (I have to hit save to
get this to trigger)-- but when I check the Html of the control -- its not
been notified of the changes and still shows default values...
c) If I edit the Html of the Control to another value. Nothing happens.
In other words -- total disconnection between the values of the property --
and what is persisted.
Anybody have an idea?
I attach the TypeConverter that I wrote in case it is the cause -- or if
not, that it may be the starting point for someone else...
using System;
using System.Web.UI.Design;
using System.Web;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Collections;
namespace XAct.Web.Controls {
public class TestConverter : ExpandableObjectConverter {
System.Type _Type = typeof(XAct.Web.Controls.LoginPanel.cCaptions);
public override bool CanConvertFrom(ITypeDescriptorContext context, Type t)
{
if (t == typeof(string)) {return true;}
return base.CanConvertFrom(context, t);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo info,object value) {
if (value is string) {
string[] tParts;
tParts = SplitPlus((string) value, ";");
object o = _Type.Assembly.CreateInstance(_Type.ToString());
string tKey = string.Empty;
string tValue = string.Empty;
int tPos = 0;
foreach (string tPart in tParts){
if (tPart == string.Empty){continue;}
tPos = tPart.IndexOf(':');
if (tPos == -1){
tPos = tPart.Length;
tKey = tPart;
tValue = string.Empty;
}else{
tKey = tPart.Substring(0,tPos);
tValue = tPart.Substring(tPos+1);
System.Reflection.PropertyInfo oPI =
_Type.GetProperty(tKey,System.Reflection.BindingFlags.SetProperty |
System.Reflection.BindingFlags.Public
|System.Reflection.BindingFlags.IgnoreCase);
if (oPI != null){
oPI.SetValue(o,tValue,null);
}
}
}
return o;
}
return base.ConvertFrom(context, info, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo
culture, object value, Type destType) {
object o=null;
try {
o = System.Convert.ChangeType(value,_Type);
}catch{}
if ((o !=null) && (destType == typeof(string))) {
string tResult = string.Empty;
string tDivChar = string.Empty;
PropertyInfo[] oPIs = _Type.GetProperties();
foreach(PropertyInfo oPI in oPIs){
string tKey = oPI.Name;
object oVal = oPI.GetValue(o,null);
string tValue = string.Empty;
if (oVal != null){
tValue = oVal.ToString();
if (tValue != string.Empty){tResult += tDivChar + tKey + ":" + tValue;}
if (tDivChar == string.Empty){tDivChar = ";";}
}
}
return tResult;
}
return base.ConvertTo(context, culture, value, destType);
}
/// <summary>
/// Split function that only splits if not within brackets or quotes.
/// </summary>
/// <param name="qString"></param>
/// <param name="qDivChar"></param>
/// <returns></returns>
public static string[] SplitPlus(string qString, string qDivChar) {
if (qDivChar== String.Empty){qDivChar = ",";}
ArrayList tResults = new ArrayList();
string tChar="";
string tWord = "";
bool tEscaped=false;
string tLastChar = "";
System.Collections.Stack tQuotes=new System.Collections.Stack();
for (int i=0;i<qString.Length;i++) {
tChar = qString.ToString();
if (tQuotes.Count == 0) {
//We are outside of quotes, so look for quote beginnings...
if ((tChar == "(") ||
(tChar == "{") ||
(tChar == "[") ||
(tChar == "'") ||
(tChar == "\"")) {
tQuotes.Push(tChar);
tLastChar=tChar;
}
if ((tChar == qDivChar)) {
tResults.Add(tWord);
tWord="";
tChar = "";
}
}
else {
//We are within quotes...need to look for close chars:
if (tEscaped ==false) {
if (tChar == "\\") {tEscaped=true;}
else {
tLastChar =(string)tQuotes.Peek();
if ((tChar == "\"") && (tChar == tLastChar)) {
tQuotes.Pop();
}
else if ((tChar == "\'") && (tChar == tLastChar)) {
tQuotes.Pop();
}
else if ((tChar == "]") && (tLastChar == "[")) {
tQuotes.Pop();
}
else if ((tChar == "}") && (tLastChar == "{")) {
tQuotes.Pop();
}
if ((tChar == ")") && (tLastChar == "(")) {
tQuotes.Pop();
}
}
}
else {
tEscaped = false;
}
}
tWord = tWord+tChar;
}
if (tWord!= String.Empty) {tResults.Add(tWord);}
return (string[])tResults.ToArray(typeof(string));
}
}//Class:End
}
I've been looking lately for a way to keep the Properties panel for Controls
'clean'...
My goal is to keep similar public properties of a custom Control neatly tied
together -- rather than all over the IDE.
One such set of values that will rarely be changed, so should have little
priority in the IDE Properties panel, and therefore a good candidate for
keeping
in an expandableobject like way, is maybe Captions that are used
through the control.
So assuming that I made the following class
class MyControl : WebControl {
....
public class cCaptions {
public string _Btn_Submit = "Login";
public string _Btn_NewUser = "Register";
public string Btn_Submit {get{return _Btn_Submit;}set{_Btn_Submit=value;}}
public string Btn_NewUser {get{return
_Btn_NewUser;}set{_Btn_NewUser=value;}}
}//Subclass:End
....
private cCaptions _Captions = new cCaptions;
....
[TypeConverter(typeof(TestConverter)),
PersistenceMode(PersistenceMode.Attribute )]
public cCaptions {get {return _Captions;}set {_Captions = value;}
....
}//Control:End
As you can see the public property now requires a custom TypeConverter so
that it can be seen in the IDE -- and deal with persistence as an attribute.
I originally thought/tried using the standard ExpandableObject one -- but I
needed a way to persist it in one string -- just like how style="" attribute
is done, with : and ; as sep chars between key and value...
Well.
The TypeConverter I wrote works atleast for converting between object and
string, and now in the IDE's Property panel I get the expandableObject look
(+/- tree) as well as a means of reading it as one string which looks like
<MyControl CAPTIONS="Btn_Submit:Login;Btn_NewUser:Register"></MyControl>
My problem with this is that it all LOOKS right -- but its not acting
right...
Problems/Observations:
a) When I edit the string directly in the Properties panel -- eg change it
to "Btn_Submit:HELLO;Btn_NewUser:Come on in!" it ---well...doesn't do it.
The moment I hit Save -- it reverts back to to the default values. :-(
b) When I click + to expand the Expandable options, and edit the Btn_Submit
value directly -- it updates the summary string line/title to the new
values -- AND updates the interface's actual label (I have to hit save to
get this to trigger)-- but when I check the Html of the control -- its not
been notified of the changes and still shows default values...
c) If I edit the Html of the Control to another value. Nothing happens.
In other words -- total disconnection between the values of the property --
and what is persisted.
Anybody have an idea?
I attach the TypeConverter that I wrote in case it is the cause -- or if
not, that it may be the starting point for someone else...
using System;
using System.Web.UI.Design;
using System.Web;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Collections;
namespace XAct.Web.Controls {
public class TestConverter : ExpandableObjectConverter {
System.Type _Type = typeof(XAct.Web.Controls.LoginPanel.cCaptions);
public override bool CanConvertFrom(ITypeDescriptorContext context, Type t)
{
if (t == typeof(string)) {return true;}
return base.CanConvertFrom(context, t);
}
public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo info,object value) {
if (value is string) {
string[] tParts;
tParts = SplitPlus((string) value, ";");
object o = _Type.Assembly.CreateInstance(_Type.ToString());
string tKey = string.Empty;
string tValue = string.Empty;
int tPos = 0;
foreach (string tPart in tParts){
if (tPart == string.Empty){continue;}
tPos = tPart.IndexOf(':');
if (tPos == -1){
tPos = tPart.Length;
tKey = tPart;
tValue = string.Empty;
}else{
tKey = tPart.Substring(0,tPos);
tValue = tPart.Substring(tPos+1);
System.Reflection.PropertyInfo oPI =
_Type.GetProperty(tKey,System.Reflection.BindingFlags.SetProperty |
System.Reflection.BindingFlags.Public
|System.Reflection.BindingFlags.IgnoreCase);
if (oPI != null){
oPI.SetValue(o,tValue,null);
}
}
}
return o;
}
return base.ConvertFrom(context, info, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo
culture, object value, Type destType) {
object o=null;
try {
o = System.Convert.ChangeType(value,_Type);
}catch{}
if ((o !=null) && (destType == typeof(string))) {
string tResult = string.Empty;
string tDivChar = string.Empty;
PropertyInfo[] oPIs = _Type.GetProperties();
foreach(PropertyInfo oPI in oPIs){
string tKey = oPI.Name;
object oVal = oPI.GetValue(o,null);
string tValue = string.Empty;
if (oVal != null){
tValue = oVal.ToString();
if (tValue != string.Empty){tResult += tDivChar + tKey + ":" + tValue;}
if (tDivChar == string.Empty){tDivChar = ";";}
}
}
return tResult;
}
return base.ConvertTo(context, culture, value, destType);
}
/// <summary>
/// Split function that only splits if not within brackets or quotes.
/// </summary>
/// <param name="qString"></param>
/// <param name="qDivChar"></param>
/// <returns></returns>
public static string[] SplitPlus(string qString, string qDivChar) {
if (qDivChar== String.Empty){qDivChar = ",";}
ArrayList tResults = new ArrayList();
string tChar="";
string tWord = "";
bool tEscaped=false;
string tLastChar = "";
System.Collections.Stack tQuotes=new System.Collections.Stack();
for (int i=0;i<qString.Length;i++) {
tChar = qString.ToString();
if (tQuotes.Count == 0) {
//We are outside of quotes, so look for quote beginnings...
if ((tChar == "(") ||
(tChar == "{") ||
(tChar == "[") ||
(tChar == "'") ||
(tChar == "\"")) {
tQuotes.Push(tChar);
tLastChar=tChar;
}
if ((tChar == qDivChar)) {
tResults.Add(tWord);
tWord="";
tChar = "";
}
}
else {
//We are within quotes...need to look for close chars:
if (tEscaped ==false) {
if (tChar == "\\") {tEscaped=true;}
else {
tLastChar =(string)tQuotes.Peek();
if ((tChar == "\"") && (tChar == tLastChar)) {
tQuotes.Pop();
}
else if ((tChar == "\'") && (tChar == tLastChar)) {
tQuotes.Pop();
}
else if ((tChar == "]") && (tLastChar == "[")) {
tQuotes.Pop();
}
else if ((tChar == "}") && (tLastChar == "{")) {
tQuotes.Pop();
}
if ((tChar == ")") && (tLastChar == "(")) {
tQuotes.Pop();
}
}
}
else {
tEscaped = false;
}
}
tWord = tWord+tChar;
}
if (tWord!= String.Empty) {tResults.Add(tWord);}
return (string[])tResults.ToArray(typeof(string));
}
}//Class:End
}