In a recent project, I came across a peculiar requirement.
Modified version of the requirement:
1. Define a static variable in base class (say Code)
2. Define a property in base class that accesses Code (say Message)
3. Modify the value of Code in derived class
4. BaseInstance.Message should access the value of Code defined in base class whereas DerivedInstance.Message should access the one defined in derived class.
So I tried the following
namespace StaticTest
{
class Base
{
protected static int Code = 1;
public String Message
{
get
{
switch (Code)
{
case 1:
return "One";
case 2:
return "Two";
}
return "Default";
}
}
}
class Derived : Base
{
protected static new int Code = 2;
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Base.Message: " + new Base().Message);
Console.WriteLine("Derived.Message: " + new Derived().Message);
Console.Read();
}
}
}
This did not work and the output was
Base.Message: One
Derived.Message: One
Turns out that Message needs to be redefined in Derived to be able to access the new value of Code. With a lot of derived classes, this was impractical and Message’s getter was a bit more involved than what I have written above.
I found a neat solution by Dave Booker posted here.
Define a custom attribute class
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public class CodeAttribute : Attribute
{
public int Code;
public CodeAttribute(int code)
{
Code = code;
}
}
Then define attribute values for the 2 classes:
[CodeAttribute(1)]
class Base
{ ... }
[CodeAttribute(2)]
class Derived
{ ... }
Now delete the Code property from the classes and modify Message to retrieve the code like this:
public String Message
{
get
{
int Code = ((CodeAttribute) Attribute.GetCustomAttribute(this.GetType(), typeof(CodeAttribute))).Code;
switch (Code)
{
case 1:
return "One";
case 2:
return "Two";
}
return "Default";
}
}
This returned the values as expected. If the derived class is not supplied with an attribute value, it just inherits the value from its parent. So not exactly a static variable but it behaves like one that can be inherited and overriden if necessary. Quite elegant!
The original requirement was to eliminate any instances of both classes. This meant Message had to be a static property. I haven’t been able to figure out how to do that … specifically how to replace “this.GetType()” with something static that returns which class the property was invoked with rather than the class which it was defined in (which is what the reflection based methods do).
Any ideas? Other than getting into IL code to see if and how this can be done?
SmartGWT widgets can render themselves sensibly using the data source definition. So for a grid, we don’t need to define the columns explicitly. However, we can override the rendering of some or all columns as needed.
Assume that our data source and data looks like this:
class CompanyDataSource extends DataSource {
public CompanyDataSource(String id) {
setID(id);
DataSourceTextField companyName = new DataSourceTextField("company", "Company");
DataSourceFloatField price = new DataSourceFloatField("price", "Price");
DataSourceDateField lastChanged = new DataSourceDateField("lastChanged", "Last Changed");
setFields(companyName, price, lastChanged);
}
}
"3m Co", "71.72", "9/1 01:25 AM"
And the grid that renders this data
DataSource dataSource = new CompanyDataSource(“companyList1”);
dataSource.setClientOnly(true);
ListGrid companyGrid = new ListGrid();
companyGrid.setHeight(300);
companyGrid.setWidth(500);
companyGrid.setTitle("SmartGWT grid");
companyGrid.setDataSource(dataSource);
companyGrid.setAutoFetchData(true);
To override the display format of lastChanged column, one approach would be
ListGridField lastChangedField = new ListGridField("lastChanged", "Last Changed (new title)", 180);
CellFormatter dateFormatter = new CellFormatter(){
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
DateTimeFormat inputFormat = DateTimeFormat.getFormat("d/M hh:mm a");
DateTimeFormat outputFormat = DateTimeFormat.getFormat("MMM dd HH 'hours' mm 'minutes'");
Date inputDate = inputFormat.parse(value.toString());
String output = outputFormat.format(inputDate);
return output;
}
};
lastChangedField.setCellFormatter(dateFormatter);
companyGrid.setFields(lastChangedField);
We define a ListGridField with the same name “lastChanged” as in the data source. SmartGWT will use this name to do the merge. Our custom cell formatter matches the input date format and returns it in a different format.
But now the grid will only display this field. To cause it to display other data source fields as they are, we need to call
companyGrid.setUseAllDataSourceFields(true);
We can also override the date format in the data source itself so that all widgets sourcing this data will reflect it. As I was thinking about this, I came across this post on the SmartGWT forums. The API will support this functionality soon and as that thread also mentions, for formatting dates, you can use ListGrid.setDateFormatter() and DateItem.setDisplayFormat().
Thats it for this time. Ill keep posting tidbits as I learn along the way.
GWT-Ext Store instances are all over my code. And I am populating them by various means. While migrating from GWT-Ext to SmartGWT, converting Stores to DataSources will probably take up a lot of time. I was wondering if I could migrate and test only the grids first and deal with the stores later. Code below could help do just that. It creates a DataSource from a Store. You can use this data source to populate SmartGWT grids and when the grids are working fine, replace the stores completely. Isn’t exactly a life saver but nevertheless. Have tested it only on a trivial store.
It doesn’t matter but assume that the RecordDef used to create the Store looks like this:
RecordDef recordDef = new RecordDef(new FieldDef[] {
new StringFieldDef("company"),
new FloatFieldDef("price"),
new FloatFieldDef("change"),
new FloatFieldDef("pctChange"),
new DateFieldDef("lastChanged", "n/j h:ia"),
new StringFieldDef("symbol"),
new StringFieldDef("industry")
});
Create a DataSource with the same fields:
private DataSource createDataSource(Store store){
DataSource dataSource = new DataSource();
dataSource.setClientOnly(true);
String[] fields = store.getFields();
for(String field:fields) {
DataSourceField smartgwtField = new DataSourceField();
smartgwtField.setName(field);
dataSource.addField(smartgwtField);
}
return dataSource;
}
Add a method that copies records from the Store to the data source created above and call it in the Store’s onLoad handler
public void copyStore(Store store, DataSource dataSource) {
Record[] records = store.getRecords();
String[] fields = store.getFields();
for(Record gwtextRecord:records) {
ListGridRecord smartgwtRecord = new ListGridRecord();
for(String field:fields) {
smartgwtRecord.setAttribute(field, gwtextRecord.getAsString(field));
}
dataSource.addData(smartgwtRecord);
}
}
For heavy stores, this code will probably punish the browser and column data types are ignored as well but it is meant only for the dev environment until all components have been replaced eventually.
PS: If you need it, here is a small sample with GWT-Ext and SmartGWT grids being populated from the same RPC method. You will need to add gwtext.jar and smartgwt.jar and use Cypal studio plugin or add compile/run scripts.
I needed to install Trac on my laptop. Trac is an enhanced wiki and issue tracking system for software development projects. I had used it about 2 years back and had liked it then. However, I had never installed it myself. I assumed it will be like any other installation. But just after starting out, I realized that I was grossly mistaken. Installing Trac and getting it running with the webadmin plugin and users configured for your projects is a little more than a post-work headache.
I have the following setup:
- OS: Vista business 32-bit
- Python: Not already installed on my system
After installation, we will be running Trac 0.10.4 with webadmin plugin and users configured for a project. This installation will be running on Apache and we won’t be using Trac’s standalone webserver (tracd). I am not using Trac’s subversion feature since I am already running VisualSVN server (and I love its ease of use). Ill try to switch sometime later if thats not another royal pain.
It turns out that the steps are quite simple … if you happen to know them.
You should have the Trac installation running now. Backup is as simple as creating a copy of the “projects” folder. If you can’t see the admin link on the upper right corner after all this, be prepared to spend the night getting it to show up. Start here!
Finally, if you have any Trac tips up your sleeve, I would love to read them. Things such as setting up multiple projects, having different sets of users for projects …
SmartGWT 1.0, a web application library based on SmartClient and GWT, was released this Monday. From the release announcement at Sanjiv’s blog and the showcase demos, it is evident that this library packs a lot of power and is definitely worth checking out. I have come across SmartClient only recently. And now that I know of it, Ill try and get acquainted with it when I can.
Links:
SmartClient: http://www.smartclient.com/
SmartGWT: http://code.google.com/p/smartgwt/
Visual Builder videos: http://www.smartclient.com/technology/visualbuilder.jsp
While working with a GWT-Ext application, I found that most of the Combo boxes used are just meant to be readonly drop down lists. Like the style 2 combo boxes we had in VB. A 2D array with display and value fields is all that is needed. Yet, every combo needs 6-7 lines to get going.
This utility method may be handy in such situations:
public static ComboBox getDropDownCombo(Object[][] data, String fieldLabel, String emptyText, int width, int listWidth)
{
Store store = new SimpleStore(new String[]{"display", "value"}, data);
ComboBox combo = new ComboBox();
combo.setEditable(false);
combo.setStore(store);
combo.setDisplayField("display");
combo.setValueField("value");
combo.setMode(ComboBox.LOCAL);
combo.setTriggerAction(ComboBox.ALL);
if(fieldLabel != null)
combo.setFieldLabel(fieldLabel);
else
combo.setHideLabel(true);
if(width != -1)
combo.setWidth(width);
if(listWidth != -1)
combo.setListWidth(listWidth);
if(emptyText != null)
combo.setEmptyText(emptyText);
else
// if there is no empty text, select the first value by default
combo.setValue(data[0][1].toString());
return combo;
}
Sample usage could be:
String[][] data = {{ "Mark as read", "1"}, {"Mark as unread", "2"}, {"Add star", "3"}, {"Remove star", "4"}};
ComboBox cmbMin = UIHelper.getDropDownCombo(data, "Actions", "[Select]", 275, -1);
Yesterday, while dabbling with jQuery and getting amazed by the wealth of plugins and effects available for this library, I came across http://digitalbush.com/2008/07/31/masked-input-plugin-114/. It allows you to fit text fields with an input mask to allow only fixed-width input in a certain format. I had seen it first in MS Access a long long time ago.
To use it with GWT-Ext, include jquery.js and jquery.maskedinput.js in your host HTML file. Use <script></script> and not <script/> to avoid wasting time later.
<script language = "javascript" type="text/javascript" src="js/jquery.js" ></script>
<script language = "javascript" type="text/javascript" src="js/jquery.maskedinput.js" ></script>
Now write some native code to call the plugin’s methods:
private native void addPlaceholder(String placeholder, String maskString) /*-{
$wnd.$.mask.addPlaceholder(placeholder, maskString);
}-*/;
private native void mask(String id, String maskString, String placeholderString) /*-{
if(placeholderString != null)
$wnd.$('#' + id).mask(maskString, {placeholder:placeholderString});
else
$wnd.$('#' + id).mask(maskString);
}-*/;
Add the mask to a text field:
addPlaceholder("~", "[+-]");
textField.doOnRender(new Function(){
public void execute() {
mask(textField.getId(), "Rs. ~9999.99/-", " ");
}
});
This will only allow values of the type “Rs. +2345.50/-” with “Rs. ” and “/-” already filled in for the user.
Update: See this: http://gwt-ext.com/forum/viewtopic.php?f=9&t=2984. Thanks mdeg and vanderbill!
Quite sometime back, Khadaa commented that Dan’s HTMLEditor would be a good plugin to have as part of the GWT-Ext-UX project. I have just committed the GWT-Ext wrapper code. The wrapper code is quite small but I was stuck with some nasty bugs which took some time to squish. And I learnt a couple of things in the process.
Logs say that quite a number of readers arrived here searching for GWT-Ext HtmlEditor. Check out from SVN to start using the widget right away. Only the styles plugin is in. Undo/redo and images plugins are pending. Will try to work on them soon. If you want to add them to the project yourself, go right ahead.
Related links:
Gwt-Ext forum threads have quite a lot of solutions contributed by users. But the forum search is a real pain and I have to struggle a lot for finding what I am looking for.
Use this *very helpful* Google custom search for searching the forums and most likely you will find your question already answered: http://www.google.com/coop/cse?cx=014252995673596707595%3Ao37mulitp-e.
I wonder why I didn’t think of this before seeing Sanjiv’s post!
Gwt-Ext 2.0.4 was released today. Mario announced the release at http://gwt-ext.com/forum/viewtopic.php?f=12&t=1622. I quote from the release notes.
Features
- Support for GWT 1.5 RC1
- FF3 Support
- PageBus
Messaging support which allows loosely coupled components to communicate with each other via traditional pubhish / subscribe paradigm. See demo in Showcase under ‘Miscellaneous -> Publish Subscribe’
- Eclipse Project Files
You can now use GWTExt directly from SVN as an Eclipse project. You just need to add the GWT_HOME variable to point to the GWT Directory in Eclipse
Windows|Preferences|Java|Build Path|Classpath Variables
Changes
- Various Bug Fixes
- Fixed Memory leak when add and remove Panels (Mix of GWT and GWT-Ext)
GwtExtUx Changes
- FileTreePanel
- SwfUploadPanel
- CustomReader
- Removed individual UX from GwtExtUx.gwt.xml. This means whenever a UX is needed, They have to be added individually in the users gwt.xml file.
- Latest patch in Multiselect.js including memory leak fixes, drag back, etc…
Download the latest version from http://gwt-ext.com/download/. Gwt-Ext-UX is being subjected to some last minute tests and a new version should be out in a couple of days.
If you have just started, the forums are always buzzing with activity and you are most welcome. These beginner screencasts may also be helpful.