`
javawhf
  • 浏览: 7200 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

创建基于gwtext的数据绑定应用(1)

阅读更多
参考Delphi的数据绑定设计,此设计的一大特点就是以数据源控件为中心外围大量的扩展各种数据控件,并且能够支持和响应数据源的事件,这里我也要做这样一件事情,数据源称为Dataset

创建Dataset
毫无疑问,Dataset是整个数据绑定应用的核心,这个Dataset应该包括如下这些内容的支持:
1、数据存储(使用Store)
2、数据交互(与服务器端的交互);
3、事件触发;
3、动作处理;

public class Dataset implements DatasetInterceptor, JSONable {
	private final DatasetView view;
	private final List<Record> removedRecords = new ArrayList<Record>();
	private final List<Record> insertedRecords = new ArrayList<Record>();
	private final Set<Record> selectedRecords = new HashSet<Record>();
	private final List<DatasetInterceptor> interceptors = new ArrayList<DatasetInterceptor>();	//包含所有客户端的拦截器以及所有的slave数据集
	private final Map<String, DatasetAction> actions = new HashMap<String, DatasetAction>();	//数据处理动作

	protected int cursor = -1;		// 当前游标位置 (用户正在操作的位置)
	protected int start;			// 本次取数据的开始位置
	protected int pageSize;			// 分页大小
	protected int rowCount;			// 本次提取数据的行数
	protected int totalRowCount;	// 记录集的总行数
	protected Store store;
	protected Dataset masterDataset;
	protected boolean append;
	
	protected final ConditionSet staticConditions = new ConditionSet(Constants.CONDITION_AND);	//静态条件,服务器定义的条件,或者主从表定义的条件
	protected final ConditionSet dynamicConditions = new ConditionSet(Constants.CONDITION_AND);	//动态条件,客户端的查询等,每次会被清空,与staticConditions是and关系的
	
	public JSONValue toJSON() {
		JSONProxy json = new JSONProxy();
		json.put("cursor", this.cursor);
		json.put("start", this.start);
		json.put("pageSize", this.pageSize);
		json.put("rowCount", this.rowCount);
		json.put("totalRowCount", this.totalRowCount);
		json.put("view", this.view);
		json.put("entity", this.view.getEntity());
		json.put("viewCode", this.view.getCode());
		//
		if(this.staticConditions != null) {
			json.put("staticConditions", this.staticConditions);
		}
		if(this.dynamicConditions != null) {
			json.put("dynamicConditions", this.dynamicConditions);
		}
		//
		List<JSONValue> removedRecords = new ArrayList<JSONValue>();
		for(Record rec: this.removedRecords) { 
			removedRecords.add(this.view.toJSON(rec));
		}
		json.putArray("removedRecords", removedRecords);
		//
		List<JSONValue> modifiedRecords = new ArrayList<JSONValue>();
		Record[] records = this.getStore().getModifiedRecords();
		for(Record rec: records) {
			modifiedRecords.add(this.view.toJSON(rec));
		}
		json.putArray("modifiedRecords", modifiedRecords);
		//子数据集
		for(DatasetInterceptor in: this.interceptors) {
			if(in instanceof Dataset) {
				Dataset child = (Dataset)in;
				json.putNew2Array("child", new String[]{child.view.getCode()});
				json.put(child.view.getCode(), child.toJSON());
			}
		}
		return json.toJSON();
	}
	
	public Dataset(DatasetView view) {
		this.view = view;
		this.pageSize = view.getPageSize();
		this.getStore();
		if(this.view.isAutoLoad()) {
			this.query();
		}
		if(view.getActions() != null) {
			for(DatasetActionDescriptor desc: this.view.getActions().getActions()) {
				DatasetAction da = DatasetActionFactory.createDatasetAction(desc.getAction(), desc.getDesicion());
				if(da != null)
					this.actions.put(desc.getAction(), da);
			}
			for(DatasetActionDescriptors group: this.view.getActions().getGroups()) {
				for(DatasetActionDescriptor desc: group.getActions()) {
					DatasetAction da = DatasetActionFactory.createDatasetAction(desc.getAction(), desc.getDesicion());
					if(da != null)
						this.actions.put(desc.getAction(), da);
				}
			}
		}
	}
	
	public Dataset(Dataset masterDataset, DatasetView view) {
		this(view);
		this.masterDataset = masterDataset;
		this.masterDataset.addInterceptor(this);
	}
	
	// properties
	
	public ConditionSet getStaticConditions() {
		return staticConditions;
	}

	public ConditionSet getDynamicConditions() {
		return dynamicConditions;
	}
	
	public List<Record> getRemovedRecords() {
		return removedRecords;
	}

	public List<Record> getInsertedRecords() {
		return insertedRecords;
	}

	public int getStart() {
		return start;
	}

	public int getPageSize() {
		return this.pageSize;
	}
	
	public void setPageSize(int pageSize) {
		if(pageSize > 0) {
			this.pageSize = pageSize;
		}
	}
	
	public int getPageCount() {
		return this.totalRowCount / this.pageSize + (this.totalRowCount % this.pageSize == 0?0:1);
	}
	
	public int getRowCount() {
		return rowCount;
	}

	public int getTotalRowCount() {
		return totalRowCount;
	}

	public int getCursor() {
		return this.cursor;
	}

	public Record getRecordAtCursor() {
		return this.getRecordAt(this.cursor);
	}
	
	public Record getRecordAt(int rowIndex) {
		Store store = this.getStore();
		if(rowIndex >= 0 && rowIndex < store.getCount())
			return store.getAt(rowIndex);
		else
			return null;
	}
	
	public void set(String field, Object value) {
		Record rec = this.getRecordAtCursor();
		if(rec == null) return;
		Object oldValue = null;
		if(value instanceof String) {
			oldValue = rec.getAsString(field);
		} else if(value instanceof Integer) {
			oldValue = rec.getAsInteger(field);
		} else if(value instanceof Double) {
			oldValue = rec.getAsDouble(field);
		} else if(value instanceof Float) {
			oldValue = rec.getAsFloat(field);
		} else if(value instanceof Boolean) {
			oldValue = rec.getAsBoolean(field);
		} else if(value instanceof Date) {
			oldValue = rec.getAsDate(field);
		} else {
			oldValue = rec.getAsObject(field);
		}
		if(!this.fireBeforeIntercept(Constants.ACTION_DATACHANGED, new Object[]{field, oldValue, value})) {
			return;
		}
		this.fireOnIntercept(Constants.ACTION_DATACHANGED, new Object[]{field, oldValue, value});
		rec.set(field, value);
		this.fireAfterIntercept(Constants.ACTION_DATACHANGED, new Object[]{field, oldValue, value}, 
				Constants.DATASET_ACTION_RESULT_OK);
	}
	
	public Store getStore() {
		if(this.store == null) {
            JsonReader reader = new JsonReader(this.view.asRecordDef());
            reader.setRoot("rows");
            reader.setTotalProperty("totalRowCount");
            if(StringUtils.isEmpty(this.view.getGroupBy())) {
            	this.store = new Store(reader);
            } else {
            	this.store = new GroupingStore(reader);
                ((GroupingStore)store).setGroupField(this.view.getGroupBy());
            }
            store.setSortInfo(new SortState(this.view.getFields()[0].getName(), SortDir.ASC));
		}
		return this.store;
	}
	
	public DatasetView getView() {
		return this.view;
	}
	
	public Dataset getMasterDataset() {
		return this.masterDataset;
	}
	
	public boolean isAppend() {
		return append;
	}

	public void setAppend(boolean append) {
		this.append = append;
	}

	public void loadJsonData(String jsonString) {
		Store store = this.getStore();
		store.loadJsonData(jsonString, append);
		this.totalRowCount = store.getTotalCount();
		this.rowCount = store.getCount();
		this.cursor = -1;
	}
	
	// selected rows
	public void selectRows(Record[] rows) {
		Set<Record> set = new HashSet<Record>();
		for(Record row: rows) {
			set.add(row);
		}
		this.resetSelectionRows(set, null);
	}
	
	public void deselectRows(Record[] rows) {
		Set<Record> set = new HashSet<Record>();
		for(Record row: rows) {
			set.add(row);
		}
		this.resetSelectionRows(null, set);
	}
	
	public void resetSelectionRows(Record[] rows) {
		if(rows == null) return;
		Set<Record> newRows = new HashSet<Record>(rows.length);
		for(Record row: rows) {
			newRows.add(row);
		}
		Set<Record> selectionRows = new HashSet<Record>();
		Set<Record> deselectionRows = new HashSet<Record>();
		for(Record row: this.selectedRecords) {
			if(!newRows.contains(row)) {
				deselectionRows.add(row);
			}
		}
		for(Record row: newRows) {
			if(!this.selectedRecords.contains(row)) {
				selectionRows.add(row);
			}
		}
		this.resetSelectionRows(selectionRows, deselectionRows);
	}
	
	public void resetSelectionRows(Set<Record> selectionRows, Set<Record> deselectionRows) {
		if(!this.fireBeforeIntercept(Constants.ACTION_SELECTIONCHANGED, new Object[]{this.selectedRecords, selectionRows, deselectionRows})) {
			return;
		}
		this.fireOnIntercept(Constants.ACTION_SELECTIONCHANGED, new Object[]{this.selectedRecords, selectionRows, deselectionRows});
		if(selectionRows != null)
			this.selectedRecords.addAll(selectionRows);
		if(deselectionRows != null)
			this.selectedRecords.removeAll(deselectionRows);
		this.fireAfterIntercept(Constants.ACTION_SELECTIONCHANGED, new Object[]{this.selectedRecords, selectionRows, deselectionRows}, 
				Constants.DATASET_ACTION_RESULT_OK);
	}
	
	public final Set<Record> getSelectedRecords() {
		return selectedRecords;
	}
	
	/**
	 * 数据集复位,恢复初始状态
	 */
	public void reset() {
		this.totalRowCount = 0;
		this.cursor = -1;
		this.insertedRecords.clear();
		this.removedRecords.clear();
	}
	
	public void resetTotalRowCount() {
		this.totalRowCount = 0;
	}

	// events
	public void addInterceptor(DatasetInterceptor event) {
		if(this.containsInterceptor(event)) return;
		this.interceptors.add(event);
	}
	
	public void removeInterceptor(DatasetInterceptor event) {
		if(event == null) return;
		this.interceptors.remove(event);
	}
	
	public boolean containsInterceptor(DatasetInterceptor event) {
		return this.interceptors.contains(event);
	}
	
	public boolean fireBeforeIntercept(String operation, Object[] params) {
		boolean res = true;
		for(DatasetInterceptor e: this.interceptors) {
			res = e.doBeforeIntercept(this, operation, params) && res;
		}
		return res;
	}
	
	public void fireOnIntercept(String operation, Object[] params) {
		for(DatasetInterceptor e: this.interceptors) {
			e.onIntercept(this, operation, params);
		}
	}
	
	public void fireAfterIntercept(String operation, Object[] params, int resultCode) {
		for(DatasetInterceptor e: this.interceptors) {
			e.doAfterIntercept(this, operation, params, resultCode);
		}
	}

	// actions
	public void setAction(String actionName, DatasetAction action) {
		this.actions.put(actionName, action);
	}
	
	public void removeAction(String actionName) {
		this.actions.remove(actionName);
	}
	
	@Override
	public void doAfterIntercept(Dataset dataset, String operation, Object[] params, int resultCode) {
		
	}

	@Override
	public boolean doBeforeIntercept(Dataset dataset, String operation, Object[] params) {
		return true;
	}

	@Override
	public void onIntercept(Dataset dataset, String operation, Object[] params) {
		
	}
	
	// operations
	public boolean isDirty() {
		return this.store.getModifiedRecords().length >= 0 || !this.removedRecords.isEmpty();
	}
	
	public void move(int p) {
		if(this.rowCount == 0) return;
		int c = this.cursor + p;
		if(c == this.cursor) return;
		if(c < 0) c = 0;
		if(c >= this.rowCount) c = this.rowCount - 1;
		this.moveTo(c);
	}
	
	public void moveTo(int p) {
		if(this.cursor == p) return;
		if(!this.fireBeforeIntercept(Constants.ACTION_MOVE, new Object[]{this.cursor, p})) {
			return;
		}
		this.fireOnIntercept(Constants.ACTION_MOVE, new Object[]{this.cursor, p});
		if(this.rowCount > 0)
			this.cursor = p;
		this.fireAfterIntercept(Constants.ACTION_MOVE, new Object[]{this.cursor, p}, 
				this.rowCount >0?0:this.rowCount);
	}
	
	public boolean first() {
		if(this.rowCount == 0) return false;
		this.moveTo(0);
		return true;
	}
	
	public boolean previous() {
		if(this.rowCount == 0 || this.cursor == 0) return false;
		this.move(-1);
		return true;
	}
	
	public boolean next() {
		if(this.rowCount == 0 || this.cursor >= this.rowCount - 1) return false;
		this.move(1);
		return true;
	}
	
	public boolean last() {
		if(this.rowCount == 0 || this.cursor >= this.rowCount - 1) return false;
		this.moveTo(this.rowCount - 1);
		return true;
	}
	
	public boolean firstPage() {
		if(this.start == 0) return false;
		this.start = 0;
		this.query();
		return true;
	}
	
	public boolean previousPage() {
		if(this.start == 0 || this.totalRowCount < this.pageSize) return false;
		this.start -= this.pageSize;
		this.query();
		return true;
	}
	
	public boolean nextPage() {
		if(this.totalRowCount == 0 || this.start >= this.totalRowCount - this.pageSize - 1) return false;
		this.start += this.pageSize;
		this.query();
		return true;
	}
	
	public boolean lastPage() {
		if(this.totalRowCount == 0 || this.start >= this.totalRowCount - this.pageSize - 1) return false;
		int lastPageSize = this.totalRowCount % this.pageSize;
		if(lastPageSize == 0) lastPageSize = this.pageSize;
		this.start = this.totalRowCount - lastPageSize;
		this.query();
		return true;
	}
	
	public void query() {
		this.submit(Constants.ACTION_QUERY, null, DatasetActionInterceptorFactory.getActionInterceptor(Constants.ACTION_QUERY));
	}
	
	public void insert(int row) {
		final String action = Constants.ACTION_INSERT;
		final DatasetInterceptor actionInterceptor = DatasetActionInterceptorFactory.getActionInterceptor(action);
		if(!fireBeforeIntercept(action, new Object[]{this})) {
			return;
		}
		if(actionInterceptor != null && !actionInterceptor.doBeforeIntercept(this, action, new Object[]{this})) {
			return;
		}
		//
		Record rec = this.view.asRecordDef().createRecord(new Object[]{});
		fireOnIntercept(action, new Object[]{rec});
		if(actionInterceptor != null) {
			actionInterceptor.onIntercept(this, action, new Object[]{rec, row});
		}
		//
		if(row  == -1) {
			row = this.cursor;
		}
		this.getStore().insert(row, rec);
		this.insertedRecords.add(rec);
		fireAfterIntercept(action, new Object[]{rec, row}, Constants.DATASET_ACTION_RESULT_OK);
		if(actionInterceptor != null) {
			actionInterceptor.doAfterIntercept(Dataset.this, action, new Object[]{rec, row}, Constants.DATASET_ACTION_RESULT_OK);
		}
	}
	
	public void remove(int row) {
		final String action = Constants.ACTION_REMOVE;
		final DatasetInterceptor actionInterceptor = DatasetActionInterceptorFactory.getActionInterceptor(action);
		if(!fireBeforeIntercept(action, new Object[]{this})) {
			return;
		}
		if(actionInterceptor != null && !actionInterceptor.doBeforeIntercept(this, action, new Object[]{this})) {
			return;
		}
		//
		if(row  == -1) {
			row = this.cursor;
		}
		Record removedRecord = null;
		if(row >= 0 && row < this.store.getCount()) {
			removedRecord = this.store.getAt(row);
			this.getStore().remove(removedRecord);
			fireAfterIntercept(action, new Object[]{removedRecord, row}, Constants.DATASET_ACTION_RESULT_OK);
			if(actionInterceptor != null) {
				actionInterceptor.doAfterIntercept(Dataset.this, action, new Object[]{removedRecord, row}, Constants.DATASET_ACTION_RESULT_OK);
			}
			if(this.selectedRecords.contains(removedRecord)) {
				this.deselectRows(new Record[]{removedRecord});
			}
		} else {
			fireAfterIntercept(action, new Object[]{null, row}, Constants.DATASET_ACTION_RESULT_NOK);
			if(actionInterceptor != null) {
				actionInterceptor.doAfterIntercept(Dataset.this, action, new Object[]{removedRecord, row}, Constants.DATASET_ACTION_RESULT_NOK);
			}
		}
	}
	
	public void submit(final String action, final Map<String, String> params, final DatasetInterceptor actionInterceptor) {
		//
		if(actionInterceptor != null && !actionInterceptor.doBeforeIntercept(this, action, new Object[]{this})) {
			return;
		}
		if(!fireBeforeIntercept(action, new Object[]{this})) {
			return;
		}
		//
		JSONProxy json = new JSONProxy(this);
		json.put("serviceID", this.view.getServiceID());
		json.put("action", action);
		//
		if(actionInterceptor != null) {
			actionInterceptor.onIntercept(this, action, new Object[]{json, this.view.getServiceID(), action});
		}
		fireOnIntercept(action, new Object[]{json, this.view.getServiceID(), action});
		
		JsonRemoteService.Util.getInstance().invoke(this.view.getServiceID(), action, json.toJSON().toString(), new AsyncCallback<String[]>(){
			@Override
			public void onFailure(Throwable caught) {
				Logger.error(caught);
				//
				if(actionInterceptor != null) {
					actionInterceptor.doAfterIntercept(Dataset.this, action, new Object[]{caught}, -1);
				}
				fireAfterIntercept(action, new Object[]{caught}, -1);
			}

			@Override
			public void onSuccess(String[] result) {
				String jsonString 	= result[0];
				String serviceID 	= result[1];
				String action 		= result[2];
				int resultCode 		= Integer.parseInt(result[3]);
				Logger.debug("SERVICEID:" + serviceID + "; ACTION:" + action + "RESULTCODE:"+resultCode+"; RESULT: " +jsonString);
				//
				if(actionInterceptor != null) {
					actionInterceptor.doAfterIntercept(Dataset.this, action, new Object[]{result}, resultCode);
				}
				fireAfterIntercept(action, new Object[]{result}, resultCode);
			}
			
		});
	}
	
	public void rejectChanges() {
		this.removedRecords.clear();
		this.insertedRecords.clear();
		this.getStore().rejectChanges();
	}
	
	public void commitChanges() {
		this.removedRecords.clear();
		this.insertedRecords.clear();
		this.getStore().commitChanges();
	}
	
	public void handle(String actionName) {
		DatasetAction action = this.actions.get(actionName);
		if(action != null) {
			action.handle(this);
		} else {
			Logger.error("Action " + actionName + " not found!");
		}
	}
	
	public boolean canHandle(String actionName) {
		DatasetAction action = this.actions.get(actionName);
		if(action != null) {
			return action.canHandle(this);
		} else {
			Logger.error("Action " + actionName + " not found!");
		}
		return false;
	}
}
分享到:
评论

相关推荐

    gwtext学习三部曲

    了解如何在项目中引入gwtext库,并创建第一个gwtext应用。此外,还会讲解gwtext的组件体系,包括按钮、表单、面板、树形视图等基本组件的使用方法。 第二部分:GWT Ext深入 这部分将带你深入理解GWT Ext的功能和...

    gwtext编写的小系统

    GWText是一个基于Google Web Toolkit (GWT) 和EXT GWT (GXT) 框架的开源项目,用于构建富互联网应用程序(Rich Internet Applications, RIA)。这个“gwtext编写的小系统”是一个示例项目,旨在帮助初学者理解如何...

    GWTAPI文档 + GWTEXT文档 + GWTEXTUX文档

    此外,文档还会涵盖组件的事件处理、样式设置、数据绑定等方面,使得开发者能够构建出功能强大且交互性良好的Web应用。 GWTEXTUX则进一步扩展了GWTEXT的功能,专注于提供更高级的用户界面组件和用户体验。例如,它...

    gwt-ext开发文档

    Ext GWT则是基于GWT的用户界面库,它提供了丰富的组件和布局,使得构建复杂的、交互式的Web应用变得更加容易。 在"开发文档"中,我们可以期待找到一系列关于如何使用GWT Ext进行开发的详细信息。这些文档通常会包含...

    gwt-ext培训教程

    GWT-Ext是一个基于Google Web Toolkit (GWT)的JavaScript库,它提供了丰富的用户界面组件和强大的数据绑定机制,使得开发者能够构建出交互性强、视觉效果优秀的Web应用程序。本教程将深入探讨GWT-Ext的核心概念、...

    gwt-ext 实例

    8. **自定义组件**:如果博客中包含高级内容,可能会讲解如何基于gwt-ext现有的组件创建自定义组件,以满足特定项目需求。 9. **性能优化**:如何通过合理使用gwt-ext组件和优化策略来提高GWT应用的性能,比如减少...

    Gwt-Ext基础-中级-进阶

    7. **数据绑定**:Gwt-Ext的数据绑定机制,包括模型(Model)、视图(View)和控制器(Controller)的概念,以及如何实现数据的双向绑定。 在"中级篇"中,你将深入到以下内容: 1. **高级组件**:探索Gwt-Ext的...

    GWT-Ext超级Widget功能类库

    2. **数据绑定**:GWT-Ext支持双向数据绑定,使得UI组件的状态与后台数据模型可以自动保持同步,减少了手动处理数据更新的工作。 3. **布局管理**:库中的组件可以使用各种布局管理器(如绝对布局、流式布局、网格...

    GWT-Ext_体验之旅.doc

    GWT-Ext 是一个基于 Google Web Toolkit (GWT) 和 ExtJS 的开源控件库,专为构建富互联网应用程序(RIA)提供强大的功能。它允许开发者使用纯 Java 语言进行界面开发,极大地提高了开发效率。GWT-Ext 的核心特点在于它...

    非常好的gwt-ext培训教程

    在实际开发中,GWT-Ext 还涉及到组件的布局管理、事件处理、数据绑定、异步通信(RPC)等方面的知识。学习 GWT-Ext 需要对 GWT 和 ExtJs 有一定的了解,同时掌握 Java 语言和基本的 Web 开发概念。通过实践和不断...

    gwt-ext-tree

    2. **数据绑定(Data Binding)**:GWT-Ext-Tree 支持与后台数据源进行双向绑定,可以通过 JSON 或其他数据格式动态加载和更新树结构。这使得数据的实时更新和异步加载成为可能,提高了用户体验。 3. **事件处理...

    Ext + Gwt + Struts2.0开发3

    1. **Ext**:这是一个基于JavaScript的富客户端UI框架,提供了大量的组件和强大的数据绑定功能,用于构建桌面级的Web应用程序。它的核心库包括Ext JS,而GWT-Ext则是将Ext JS与Google Web Toolkit(GWT)结合的库,...

    搭建简单的EXT-GWT(GXT)的开发环境

    EXT-GWT(GXT)是一种基于Google Web Toolkit (GWT)的开源JavaScript库,用于构建富客户端的Web应用程序。GXT提供了丰富的用户界面组件和样式,使得开发者能够创建出类似于桌面应用的用户体验。本文将详细介绍如何...

    Gwt-Ext学习笔记之基础篇- www.hxiaseo.cn技术网站

    压缩包文件"gwtext-2.0.3"可能包含的是Gwt-Ext的一个特定版本,2.0.3,这个版本可能包含了库文件、示例代码、文档等资源,供学习者参考和使用。在这个版本中,开发者可以找到各种UI组件的示例,如表格、面板、按钮、...

    Ext GWT 2.0 Beginner’s Guide

    随着读者对Ext GWT基础知识的掌握,第二部分将深入探讨更高级的主题,如数据绑定、事件处理和主题定制。这部分将教授如何使用Ext GWT的数据存储和模型系统来管理应用程序中的数据流,以及如何通过监听器和处理器响应...

    GWT入门 GWT中文教程

    首先,EXT-GWT2.0.1是EXT-GWT(GWT-Ext)的一个版本,它是基于GWT的组件库,提供了丰富的用户界面组件,如表格、树形视图、菜单等,使得开发者可以构建出具有桌面级用户体验的Web应用。EXT-GWT的API和DOC文档对于...

    Ext + Gwt + Struts2.0开发1

    Ext的核心特性包括数据绑定、布局管理、可自定义的组件以及强大的API,使其成为开发复杂Web应用的理想选择。 **GWT (Google Web Toolkit)** 是Google推出的一个开源框架,它允许开发者使用Java语言编写客户端代码,...

    EXT-GWT(GXT) 2.2.4 JAVA API DOC chm 版

    EXT-GWT,全称为Ext GWT,是Sencha公司推出的一款基于Java的开源富互联网应用程序(Rich Internet Application,RIA)开发框架。GXT是EXT-GWT的简称,它提供了丰富的用户界面组件,允许开发者用Java代码来构建复杂的...

    Ext GWT 2.0, Beginner's Guide

    Ext GWT,全称EXT Java Widget Toolkit,是Sencha公司开发的一个用于构建富客户端Web应用程序的开源库,它基于Google的GWT(Google Web Toolkit)技术。这本书将引导读者逐步学习如何利用Ext GWT创建功能丰富的、...

    gwt入门项目代码

    GXT(Ext GWT)是Sencha公司开发的一个基于GWT的UI组件库,提供了丰富的用户界面组件和强大的数据绑定功能,用于构建复杂的Web应用。 本"gwt入门项目代码"是一个很好的起点,适合初学者了解和学习GWT和GXT框架。这...

Global site tag (gtag.js) - Google Analytics