`
simen_net
  • 浏览: 307069 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

GXT 下拉树(Combo Tree)基本实现

 
阅读更多

由于项目的需要,一直想将GXT应用起来,可惜去年做了部分测试后由于身体原因停了下来。休息了大半年,虽然听力依然没 有恢复,但生活还要继续,工作还要努力[img]/images/smiles/icon_razz.gif" alt="[/img]


    这段时间相对清闲,为了做一些必要的技术储备,又重新跑一跑GXT。过去半年,重新阅读之前的代码又有了很大的收获。先进入正题:

===================废话的分割线=================

    下拉树(Combo Tree)是Ajax框架里非常有用的一个功能,至少在国内是。奇怪的是ExtJS到目前都没有提供一个官方的控件。大部分都使用render的方式将Tree放到Combo中,但是这个方法在GXT中就变得无效。http://www.iteye.com/problems/38852。去年我也是碰到这个问题后找到了这个提问,一直也没人解答。貌似作者还在EXT的官方论坛提问,也没有理。这次重新学习GXT,首先就是要解决这个问题。

    首先按照http://www.iteye.com/problems/38852的方法实现了下拉树,确实出现该文中提到的情况,审核元素发现,在树的上级是combo的下拉div,所有的事件在这层已经全部被拦截。要解决只能从这里入手。阅读了ComboBox.java,发现他和ExtJS类似,是使用一个List控件作为其下拉显示,尝试将其替换成TreePanel,初步实现了下拉树的功能。实现声明:该控件不完善,我只是进行了初步的精简和功能替换,还有很多工作需要完成,这里只做一个演示。存在的BUG:第一次展开下拉树不会出现,第二次点击才会出现。原因是TreePanel的加载是在展开之后,第一次没有加载到所有显示不出来。

扩展代码:

package com.strong.client.extend;

import java.util.ArrayList;
import java.util.List;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.XDOM;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.DomEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FieldEvent;
import com.extjs.gxt.ui.client.event.ListViewEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.PreviewEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.event.SelectionProvider;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.util.BaseEventPreview;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.util.Util;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.form.ListModelPropertyEditor;
import com.extjs.gxt.ui.client.widget.form.PropertyEditor;
import com.extjs.gxt.ui.client.widget.form.TriggerField;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanel;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
import com.strong.client.model.FileModel;

@SuppressWarnings("deprecation")
public class StrongComboBox<D extends ModelData> extends TriggerField<D> implements SelectionProvider<D> {

  /**
   * ComboBox error messages.
   */
  public class ComboBoxMessages extends TextFieldMessages {

    private String loadingText = GXT.MESSAGES.loadMask_msg();
    private String valueNoutFoundText;

    /**
     * Returns the loading text.
     * 
     * @return the loading text
     */
    public String getLoadingText() {
      return loadingText;
    }

    /**
     * Returns the value not found error text.
     * 
     * @return the error text
     */
    public String getValueNoutFoundText() {
      return valueNoutFoundText;
    }

    /**
     * Sets the loading text.
     * 
     * @param loadingText
     *          the loading text
     */
    public void setLoadingText(String loadingText) {
      this.loadingText = loadingText;
    }

    /**
     * When using a name/value combo, if the value passed to setValue is not
     * found in the store, valueNotFoundText will be displayed as the field text
     * if defined.
     * 
     * @param valueNoutFoundText
     */
    public void setValueNoutFoundText(String valueNoutFoundText) {
      this.valueNoutFoundText = valueNoutFoundText;
    }

  }

  /**
   * TriggerAction enum.
   */
  public enum TriggerAction {
    ALL, QUERY;
  }

  protected boolean autoComplete = false;
  protected boolean delayedCheck;

  protected TreeStore<D> store;
  private BaseEventPreview eventPreview;
  private boolean expanded;
  private El footer;
  private boolean forceSelection;
  private InputElement hiddenInput;
  private String lastSelectionText;
  private boolean lazyRender = true, initialized;
  private LayoutContainer list;
  private String listAlign = "tl-bl?";
  private String listStyle = "x-combo-list";
  private TreePanel<D> treePanel;
  private int maxHeight = 300;
  private int minChars = 4;
  private int minListWidth = 70;
  private D selectedItem;
  private TriggerAction triggerAction = TriggerAction.QUERY;
  private boolean typeAhead;

  private String valueField;

  /**
   * Creates a combo box.
   */
  @SuppressWarnings("unchecked")
  public StrongComboBox(TreeStore<D> treeStore) {
    this.setStore(treeStore);
    this.messages = new ComboBoxMessages();
    System.out.println("++++++++++++++++++");
    this.treePanel = new TreePanel<D>(store);
    setPropertyEditor(new ListModelPropertyEditor<D>());
    this.monitorWindowResize = true;
    this.windowResizeDelay = 0;
    initComponent();
  }

  /**
   * 设置TreePanel用于显示的属性
   * 
   * @param strDisplay
   */
  public void setDisplayProperty(String strDisplay) {
    this.treePanel.setDisplayProperty(FileModel.WEN_JIAN_MING);
  }

  public void addSelectionChangedListener(SelectionChangedListener<D> listener) {
    addListener(Events.SelectionChange, listener);
  }

  @Override
  public void clear() {
    getStore().clearFilters();
    boolean f = forceSelection;
    forceSelection = false;
    super.clear();
    forceSelection = f;
  }

  /**
   * Clears any text/value currently set in the field.
   */
  public void clearSelections() {
    setRawValue("");
    lastSelectionText = "";
    applyEmptyText();
    value = null;
  }

  /**
   * Hides the dropdown list if it is currently expanded. Fires the
   * <i>Collapse</i> event on completion.
   */
  public void collapse() {
    if (!expanded) {
      return;
    }
    eventPreview.remove();
    expanded = false;
    list.hide();

    RootPanel.get().remove(list);
    if (GXT.isAriaEnabled() && hasFocus) {
      // inspect 32 is keeping focus on hidden list item in dropdown
      input.blur();
      input.focus();
    }
    fireEvent(Events.Collapse, new FieldEvent(this));
  }

  /**
   * Execute a query to filter the dropdown list. Fires the BeforeQuery event
   * prior to performing the query allowing the query action to be canceled if
   * needed.
   * 
   * @param q
   *          the query
   * @param forceAll
   *          true to force the query to execute even if there are currently
   *          fewer characters in the field than the minimum specified by the
   *          minChars config option. It also clears any filter previously saved
   *          in the current store
   */
  public void doQuery(String q, boolean forceAll) {
    if (q == null) {
      q = "";
    }

    FieldEvent fe = new FieldEvent(this);
    fe.setValue(q);
    if (!fireEvent(Events.BeforeQuery, fe)) {
      return;
    }

    if (forceAll || q.length() >= minChars) {
      expand();
      selectedItem = null;
      onLoad(null);
    }
  }

  /**
   * Expands the dropdown list if it is currently hidden. Fires the
   * <i>expand</i> event on completion.
   */
  public void expand() {
    if (expanded || !hasFocus) {
      return;
    }

    if (!initialized) {
      createList(false);
    } else {
      RootPanel.get().add(list);
    }

    list.show();
    list.layout();
    list.el().updateZIndex(0);
    restrict();

    eventPreview.add();
    expanded = true;
    fireEvent(Events.Expand, new FieldEvent(this));
  }

  /**
   * Returns the combo's TreePanel view.
   * 
   * @return the view
   */
  public TreePanel<D> getTreePanel() {
    return treePanel;
  }

  /**
   * Returns the loading text.
   * 
   * @return the loading text
   */
  public String getLoadingText() {
    return getMessages().getLoadingText();
  }

  /**
   * Returns the dropdown list's max height.
   * 
   * @return the max height
   */
  public int getMaxHeight() {
    return maxHeight;
  }

  @SuppressWarnings("unchecked")
  @Override
  public ComboBoxMessages getMessages() {
    return (ComboBoxMessages) messages;
  }

  /**
   * Returns the min characters used for autocompete and typeahead.
   * 
   * @return the minimum number of characters
   */
  public int getMinChars() {
    return minChars;
  }

  /**
   * Returns the dropdown list's min width.
   * 
   * @return the min width
   */
  public int getMinListWidth() {
    return minListWidth;
  }

  @Override
  public ListModelPropertyEditor<D> getPropertyEditor() {
    return (ListModelPropertyEditor<D>) propertyEditor;
  }

  public List<D> getSelection() {
    List<D> sel = new ArrayList<D>();
    D v = getValue();
    if (v != null) {
      sel.add(v);
    }
    return sel;
  }

  /**
   * Returns the combo's store.
   * 
   * @return the store
   */
  public TreeStore<D> getStore() {
    return store;
  }

  /**
   * Returns the trigger action.
   * 
   * @return the trigger action
   */
  public TriggerAction getTriggerAction() {
    return triggerAction;
  }

  @Override
  public D getValue() {
    if (!initialized) {
      return value;
    }
    if (store != null) {
      getPropertyEditor().setList(store.getModels());
    }

    doForce();
    return super.getValue();
  }

  /**
   * Returns the value field name.
   * 
   * @return the value field name
   */
  public String getValueField() {
    return valueField;
  }

  /**
   * Returns the combo's list view.
   * 
   * @return the view
   */
  public TreePanel<D> getView() {
    return treePanel;
  }

  /**
   * Returns <code>true</code> if the panel is expanded.
   * 
   * @return the expand state
   */
  public boolean isExpanded() {
    return expanded;
  }

  /**
   * Returns true if lazy rendering is enabled.
   * 
   * @return true of lazy rendering
   */
  public boolean isLazyRender() {
    return lazyRender;
  }

  /**
   * Returns true if type ahead is enabled.
   * 
   * @return the type ahead state
   */
  public boolean isTypeAhead() {
    return typeAhead;
  }

  public void removeSelectionListener(SelectionChangedListener<D> listener) {
    removeListener(Events.SelectionChange, listener);
  }

  @Override
  public void reset() {
    getStore().clearFilters();
    boolean f = forceSelection;
    forceSelection = false;
    super.reset();
    forceSelection = f;
  }

  public void select(D sel) {
    // if (listView != null && sel != null) {
    // int index = store.indexOf(sel);
    // selectedItem = sel;
    // if (index < listView.getElements().size()) {
    // listView.getSelectionModel().select(sel, false);
    // fly(listView.getElement(index)).scrollIntoView(listView.getElement(),
    // false);
    // }
    // }
    System.out.println("===select D===" + sel);
  }

  /**
   * Select an item in the dropdown list by its numeric index in the list. This
   * function does NOT cause the select event to fire. The list must expanded
   * for this function to work, otherwise use #setValue.
   * 
   * @param index
   *          the index of the item to select
   */
  public void select(int index) {
    // select(store.getAt(index));
    System.out.println("===select int===" + index);
  }

  /**
   * The underlying data field name to bind to this ComboBox (defaults to
   * 'text').
   * 
   * @param displayField
   *          the display field
   */
  public void setDisplayField(String displayField) {
    getPropertyEditor().setDisplayProperty(displayField);
  }

  /**
   * Sets the panel's expand state.
   * 
   * @param expand
   *          <code>true<code> true to expand
   */
  public void setExpanded(boolean expand) {
    this.expanded = expand;
    if (isRendered()) {
      if (expand) {
        expand();
      } else {
        collapse();
      }
    }
  }

  /**
   * Sets whether the combo's value is restricted to one of the values in the
   * list, false to allow the user to set arbitrary text into the field
   * (defaults to false).
   * 
   * @param forceSelection
   *          true to force selection
   */
  public void setForceSelection(boolean forceSelection) {
    this.forceSelection = forceSelection;
  }

  /**
   * True to lazily render the combo's drop down list (default to true,
   * pre-render).
   * 
   * @param lazyRender
   *          true to lazy render the drop down list
   */
  public void setLazyRender(boolean lazyRender) {
    this.lazyRender = lazyRender;
  }

  /**
   * Sets a valid anchor position value. See {@link El#alignTo} for details on
   * supported anchor positions (defaults to 'tl-bl?').
   * 
   * @param listAlign
   *          the new list align value
   */
  public void setListAlign(String listAlign) {
    this.listAlign = listAlign;
  }

  /**
   * Sets the style for the drop down list (defaults to 'x-combo-list');
   * 
   * @param listStyle
   *          the list style
   */
  public void setListStyle(String listStyle) {
    this.listStyle = listStyle;
  }

  /**
   * Sets the loading text.
   * 
   * @param loadingText
   *          the loading text
   */
  public void setLoadingText(String loadingText) {
    getMessages().setLoadingText(loadingText);
  }

  /**
   * Sets the maximum height in pixels of the dropdown list before scrollbars
   * are shown (defaults to 300).
   * 
   * @param maxHeight
   *          the max hieght
   */
  public void setMaxHeight(int maxHeight) {
    this.maxHeight = maxHeight;
  }

  /**
   * Sets the minimum number of characters the user must type before
   * autocomplete and typeahead active (defaults to 4 if remote, or 0 if local).
   * 
   * @param minChars
   */
  public void setMinChars(int minChars) {
    this.minChars = minChars;
  }

  /**
   * Sets the minimum width of the dropdown list in pixels (defaults to 70, will
   * be ignored if listWidth has a higher value).
   * 
   * @param minListWidth
   *          the min width
   */
  public void setMinListWidth(int minListWidth) {
    this.minListWidth = minListWidth;
  }

  @Override
  public void setPropertyEditor(PropertyEditor<D> propertyEditor) {
    assert propertyEditor instanceof ListModelPropertyEditor<?> : "PropertyEditor must be a ListModelPropertyEditor instance";
    super.setPropertyEditor(propertyEditor);
  }

  @Override
  public void setRawValue(String text) {
    if (rendered) {
      if (text == null) {
        String msg = getMessages().getValueNoutFoundText();
        text = msg != null ? msg : "";
      }
      getInputEl().setValue(text);
    }
  }

  public void setSelection(List<D> selection) {
    if (selection.size() > 0) {
      setValue(selection.get(0));
    } else {
      setValue(null);
    }
  }

  /**
   * Sets the combo's store.
   * 
   * @param store
   *          the store
   */
  public void setStore(TreeStore<D> store) {
    this.store = store;
  }

  /**
   * The action to execute when the trigger field is activated. Use
   * {@link TriggerAction#ALL} to run the query specified by the allQuery config
   * option (defaults to {@link TriggerAction#QUERY}).
   * 
   * @param triggerAction
   *          the trigger action
   */
  public void setTriggerAction(TriggerAction triggerAction) {
    this.triggerAction = triggerAction;
  }

  @Override
  public void setValue(D value) {
    D oldValue = this.value;
    super.setValue(value);
    updateHiddenValue();
    this.lastSelectionText = getRawValue();
    if (!Util.equalWithNull(oldValue, value)) {
      SelectionChangedEvent<D> se = new SelectionChangedEvent<D>(this, getSelection());
      fireEvent(Events.SelectionChange, se);
    }
  }

  /**
   * Sets the model field used to retrieve the "value" from the model. If
   * specified, a hidden form field will contain the value. The hidden form
   * field name will be the combo's field name plus "-hidden".
   * 
   * @param valueField
   *          the value field name
   */
  public void setValueField(String valueField) {
    this.valueField = valueField;
  }

  protected void collapseIf(PreviewEvent pe) {
    if (!list.el().isOrHasChild(pe.getTarget()) && !el().isOrHasChild(pe.getTarget())) {
      collapse();
    }
  }

  protected void doForce() {
    if (forceSelection) {
      boolean f = forceSelection;
      forceSelection = false;
      String rv = getRawValue();
      if (getAllowBlank() && (rv == null || rv.equals(""))) {
        forceSelection = f;
        return;
      }

      if (getValue() == null) {
        if (lastSelectionText != null && !"".equals(lastSelectionText)) {
          setRawValue(lastSelectionText);
        } else {
          applyEmptyText();
        }
      }
      forceSelection = f;
    }
  }

  protected D findModel(String property, String value) {
    if (value == null)
      return null;
    for (D model : store.getModels()) {
      if (value.equals(getPropertyEditor().getStringValue(model))) {
        return model;
      }
    }
    return null;
  }

  protected void fireKey(FieldEvent fe) {
    if (fe.isNavKeyPress() && !isExpanded() && !delayedCheck) {
      fireEvent(Events.SpecialKey, fe);
    }
  }

  protected Element getAlignElement() {
    return getElement();
  }

  @Override
  protected El getFocusEl() {
    return input;
  }

  protected boolean hasFocus() {
    return hasFocus || expanded;
  }

  @SuppressWarnings("rawtypes")
  protected void initComponent() {
    eventPreview = new BaseEventPreview() {
      @Override
      protected boolean onPreview(PreviewEvent pe) {
        switch (pe.getType().getEventCode()) {
        case Event.ONSCROLL:
        case Event.ONMOUSEWHEEL:
        case Event.ONMOUSEDOWN:
          collapseIf(pe);
          break;
        case Event.ONKEYPRESS:
          if (expanded && pe.getKeyCode() == KeyCodes.KEY_ENTER) {
            pe.stopEvent();
            onViewClick(pe, false);
          }
          break;
        }
        return true;
      }
    };
    eventPreview.setAutoHide(false);

    new KeyNav(this) {

      @Override
      public void onDown(ComponentEvent ce) {
        if (!isReadOnly()) {
          ce.cancelBubble();
          if (!isExpanded()) {
            onTriggerClick(ce);
          } else {
            selectNext();
          }
        }
      }

      @Override
      public void onEnter(ComponentEvent ce) {
        if (expanded) {
          ce.cancelBubble();
          onViewClick(ce, false);
          delayedCheck = true;
          unsetDelayCheck();
        }
      }

      @Override
      public void onEsc(ComponentEvent ce) {
        if (expanded) {
          ce.cancelBubble();
          collapse();
        }
      }

      @Override
      public void onTab(ComponentEvent ce) {
        if (expanded) {
          onViewClick(ce, false);
        }
      }

      @Override
      public void onUp(ComponentEvent ce) {
        if (expanded) {
          ce.cancelBubble();
          selectPrev();
        }
      }

    };
  }

  protected void initTree() {
    if (treePanel == null) {
      treePanel = new TreePanel<D>(store);
    }
    String style = listStyle;
    treePanel.setStyleAttribute("overflowX", "hidden");
    treePanel.addStyleName(style + "-inner");
    treePanel.setStyleAttribute("padding", "0px");
    treePanel.setBorders(false);
    treePanel.getSelectionModel().addListener(Events.SelectionChange, new Listener<SelectionChangedEvent<D>>() {
      public void handleEvent(SelectionChangedEvent<D> se) {
        selectedItem = treePanel.getSelectionModel().getSelectedItem();
        if (GXT.isAriaEnabled()) {
          // Element e =
          // listView.getElement(listView.getStore().indexOf(selectedItem));
          // StrongComboBox.this.setAriaState("aria-activedescendant",
          // e.getId());
          System.out.println("===SelectionChangedEvent===" + se);
        }
      }
    });

    treePanel.addListener(Events.Select, new Listener<ListViewEvent<D>>() {
      public void handleEvent(ListViewEvent<D> le) {
        onViewClick(le, true);
      }
    });

    list = new LayoutContainer() {
      @Override
      protected void doAttachChildren() {
        super.doAttachChildren();
      }

      @Override
      protected void doDetachChildren() {
        super.doDetachChildren();
      }

      @Override
      protected void onRender(Element parent, int index) {
        super.onRender(parent, index);
        eventPreview.getIgnoreList().add(getElement());

        setAriaRole("presentation");
      }
    };
    list.setScrollMode(Scroll.NONE);
    list.setShim(true);
    list.setShadow(true);
    list.setBorders(true);
    list.setStyleName(style);
    list.hide();
    list.addStyleName("x-ignore");

    assert store != null : "ComboBox needs a store";

    list.add(treePanel);

    if (!lazyRender) {
      createList(true);
    }
  }

  protected void onBeforeLoad(StoreEvent<D> se) {
    if (!hasFocus()) {
      return;
    }
    if (expanded) {
      restrict();
    }
  }

  @Override
  protected void onDetach() {
    collapse();
    super.onDetach();
    if (eventPreview != null) {
      eventPreview.remove();
    }
  }

  protected void onEmptyResults() {
    collapse();
  }

  @Override
  protected void onKeyDown(FieldEvent fe) {
    if (fe.getKeyCode() == KeyCodes.KEY_TAB) {
      if (expanded) {
        onViewClick(fe, false);
      }
    }
    super.onKeyDown(fe);
  }

  protected void onLoad(StoreEvent<D> se) {
    if (!isAttached() || !hasFocus()) {
      return;
    }
    if (store.getAllItems().size() > 0) {
      if (expanded) {
        restrict();
      } else {
        expand();
      }

    } else {
      onEmptyResults();
    }
  }

  protected void onRender(Element parent, int index) {
    super.onRender(parent, index);
    initTree();

    if (!autoComplete) {
      getInputEl().dom.setAttribute("autocomplete", "off");
    }

    if (valueField != null) {
      hiddenInput = Document.get().createHiddenInputElement().cast();
      hiddenInput.setName(getName() + "-hidden");
      getElement().appendChild(hiddenInput);
    }

    eventPreview.getIgnoreList().add(getElement());

    setAriaState("aria-owns", treePanel.getId());
    setAriaRole("combobox");
  }

  protected void onSelect(D model, int index) {
    FieldEvent fe = new FieldEvent(this);
    if (fireEvent(Events.BeforeSelect, fe)) {
      setValue(model);
      collapse();
      fireEvent(Events.Select, fe);
    }
  }

  protected void onTriggerClick(ComponentEvent ce) {
    super.onTriggerClick(ce);
    if (expanded) {
      collapse();
    } else {
      onFocus(null);
      if (triggerAction != TriggerAction.ALL) {
        doQuery(getRawValue(), true);
      }
    }
    getInputEl().focus();
  }

  protected void onTypeAhead() {
    if (store.getAllItems().size() > 0) {
      D m = store.getChild(0);
      String newValue = propertyEditor.getStringValue(m);
      int len = newValue.length();
      int selStart = getRawValue().length();
      if (selStart != len) {
        setRawValue(newValue);
        select(selStart, newValue.length());
      }
    }
  }

  protected void onUpdate(StoreEvent<D> se) {
    // handle the case when the selected model's display property is updated
    if (!getRawValue().equals("") && getValue() == null && forceSelection) {
      setValue(null);
      store.clearFilters();
      setValue(se.getModel());
    }
  }

  protected void onViewClick(DomEvent de, boolean focus) {
    // int idx = -1;
    // // when testing in selenium the items will not be selected as the mouse
    // // is not moved during the test
    // Element elem = listView.findElement(de.getTarget());
    // if (elem != null) {
    // idx = listView.indexOf(elem);
    // } else {
    // D sel = listView.getSelectionModel().getSelectedItem();
    // if (sel != null) {
    // idx = store.indexOf(sel);
    // }
    // }
    // if (idx != -1) {
    // D sel = store.getAt(idx);
    // onSelect(sel, idx);
    // }
    //
    // if (focus) {
    // DeferredCommand.addCommand(new Command() {
    // public void execute() {
    // focus();
    // }
    // });
    // }
    System.out.println("===onViewClick===" + de + "==" + focus);
  }

  protected void onWindowResize(int width, int height) {
    collapse();
  }

  protected void restrict() {
    list.el().setVisibility(false);
    treePanel.setHeight("auto");
    list.setHeight("auto");
    int w = Math.max(getWidth(), minListWidth);

    int fh = footer != null ? footer.getHeight() : 0;
    int fw = list.el().getFrameWidth("tb") + fh;

    int h = treePanel.getHeight() + fw;

    int mH = Math.min(maxHeight, Window.getClientHeight() - 10);
    h = Math.min(h, mH);
    list.setSize(w, h);
    list.el().alignTo(getAlignElement(), listAlign, null);

    h -= fw;

    int width = w - list.el().getFrameWidth("lr");
    treePanel.syncSize();
    treePanel.setSize(width, h);

    int y = list.el().getY();
    int b = y + h + fw;
    int vh = XDOM.getViewportSize().height + XDOM.getBodyScrollTop();
    if (b > vh) {
      y = y - (b - vh) - 5;
      list.el().setTop(y);
    }
    list.el().setVisibility(true);
  }

  @Override
  protected void triggerBlur(ComponentEvent ce) {
    doForce();
    collapse();
    super.triggerBlur(ce);
  }

  protected void unsetDelayCheck() {
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        delayedCheck = false;
      }
    });
  }

  @Override
  protected boolean validateBlur(DomEvent e, Element target) {
    return list == null || (list != null && !list.isVisible() && !list.getElement().isOrHasChild(target));
  }

  @Override
  protected boolean validateValue(String value) {
    if (forceSelection) {
      boolean f = forceSelection;
      forceSelection = false;
      if (getValue() == null) {
        forceSelection = f;
        String rv = getRawValue();
        if (getAllowBlank() && (rv == null || rv.equals(""))) {
          return true;
        }
        markInvalid(getMessages().getBlankText());
        return false;
      }
      forceSelection = f;
    }
    return super.validateValue(value);
  }

  private void createList(boolean remove) {
    RootPanel.get().add(list);
    initialized = true;
    if (remove) {
      RootPanel.get().remove(list);
    }
  }

  private void selectNext() {
    int count = store.getAllItems().size();
    if (count > 0) {
      int selectedIndex = store.indexOf(selectedItem);
      if (selectedIndex == -1) {
        select(0);
      } else if (selectedIndex < count - 1) {
        select(selectedIndex + 1);
      }
    }
  }

  private void selectPrev() {
    int count = store.getAllItems().size();
    if (count > 0) {
      int selectedIndex = store.indexOf(selectedItem);
      if (selectedIndex == -1) {
        select(0);
      } else if (selectedIndex != 0) {
        select(selectedIndex - 1);
      }
    }
  }

  private void updateHiddenValue() {
    if (hiddenInput != null) {
      String v = "";
      D val = getValue();
      if (val != null && val.get(valueField) != null) {
        v = ((Object) val.get(valueField)).toString();
      }
      hiddenInput.setValue(v);
    }
  }

}
 

 

调用方法:

      StrongComboBox<FileModel> combotree = new StrongComboBox<FileModel>(FileStoreUtils.getFileTreeStore());
      combotree.setMaxHeight(200);
      combotree.setDisplayProperty(“name”);
 

 

 

 

分享到:
评论
1 楼 zhangyou1010 2012-03-12  
可以贴张效果图吗?

相关推荐

    GXT v2.2.1 API doc

    GXT提供了大量的组件,如表格(Grid)、树(Tree)、表单(Form)、菜单(Menu)等。API文档详细介绍了这些组件的创建、配置和使用方法,包括它们的属性、事件和方法。开发者可以通过查阅文档来了解如何实现特定...

    GXT 学习的好书

    - **GXT架构**:了解GXT的基本架构及其与GWT的关系。 - **开发环境搭建**:包括安装必要的工具如Eclipse IDE、SDK配置等。 - **第一个GXT项目**:从零开始创建一个简单的GXT应用程序,熟悉基本流程。 2. **UI...

    Gxt_BLOG(GXt项目)

    GXT不仅提供了大量的预定义组件,如表格、树形视图、图表等,还支持数据绑定、数据网格、分页和排序等功能,极大地简化了开发过程。 在"GXt项目"中,我们可以期待找到一系列关于如何使用GXT进行Web应用开发的知识点...

    GXT 软件包和API

    - `Widget`是所有GXT组件的基类,包括基本的布局容器和具体的功能组件。 - `Container`类代表容器组件,可以容纳其他组件,支持各种布局方式如绝对布局、流式布局、网格布局等。 - `Grid`类用于创建表格,提供了...

    gxt-api-2.2.5 doc

    1. **组件库**:GXT提供了大量的UI组件,如表格(Grid)、树形视图(Tree)、菜单(Menu)、对话框(Dialog)等,这些组件在设计上遵循了MVC模式,易于定制和扩展。 2. **数据绑定**:GXT支持双向数据绑定,使得...

    GXT组件使用教程

    在这个文档中,我们将了解GXT的基础概念,包括它的架构、主要组件类型以及如何创建一个基本的GXT应用。GXT提供了一系列预定义的UI组件,如表格、网格、表单、菜单、工具栏等,它们都具有丰富的功能和高度的可定制性...

    gwt gxt demo

    在GWT项目中,开发者可能会利用GXT的组件库来创建复杂的布局,如表格、树形视图、表单、分页控件等。此外,GXT还支持数据网格、图表和拖放等功能,增强了用户体验。同时,GWT的模块化系统使得开发者可以轻松组织和...

    gxt初学进阶教程

    ExtGWT与GWT协同工作主要是通过将GXT的组件库和资源加入到GWT项目中来实现。添加ExtGWT到GWT项目通常涉及以下步骤: 1. 从Sencha官网下载对应的GXT SDK。 2. 在GWT项目中配置和引用GXT相关类库。 3. 将GXT自带的资源...

    Gxt,包含resource

    GXT扩展了GWT的功能,提供了更高级的用户界面组件,如表格、树形视图、图表等。 "Ext"在标签中提及,这可能是指Ext JS,因为GXT最初是基于Ext JS设计的。Ext JS是一个JavaScript库,专注于构建桌面级的Web应用。GXT...

    gxt-1.2.3.jar.zip

    这些服务端组件帮助GXT应用在客户端和服务器之间进行数据同步,实现页面的异步更新和数据的动态加载。 GXT-1.2.3版本可能包含以下关键知识点: 1. **组件库**:GXT提供了一套完整的组件集合,包括数据视图、表格、...

    GXT Cascade ComboBox Samples

    通过分析和理解这些示例代码,开发者可以学习如何在GXT环境中实现级联下拉框,从而在自己的项目中复用或改进这一功能。同时,这也涉及到数据管理、事件处理和UI设计等多个方面,对提升GWT和GXT的编程技能大有裨益。

    gxt-2.2.5.zip

    GXT 2.2.5包含了大量的UI组件,如数据网格、表格、树形视图、图表、表单元素等,这些组件设计精美且功能强大,能够满足开发复杂企业级应用的需求。此外,它还支持自定义组件,开发者可以根据项目需求进行个性化定制...

    GXT的JAR包

    它基于Google的GWT(Google Web Toolkit),允许开发者使用Java语言编写客户端代码,然后通过GWT的编译器将其转换成高效的JavaScript代码,实现跨浏览器兼容性。 GWT(Google Web Toolkit)是一个开源工具集,它...

    一步一步教你新建GXT项目

    随着对GXT库的深入理解和实践,你可以构建更复杂的用户界面,利用其强大的组件和数据绑定功能,实现高效、美观的Web应用。在实际开发中,还需要学习GXT提供的各种组件、布局、事件处理、数据模型以及远程服务调用等...

    gxt初学进阶

    - **树(Tree)** - 可以用来展示层级结构的数据。 - **网格(Grid)** - 类似于表格,但更加强大,支持分页、行编辑等功能。 #### 五、ExtGWT的应用场景 - **企业级应用开发** - 构建复杂的业务逻辑界面。 - **数据...

    基于GXT的RPC与MVC实现登录和退出功能

    本文将深入探讨如何基于GXT的RPC与MVC实现登录和退出功能。 首先,让我们了解MVC模式。MVC是一种软件设计模式,将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。模型负责处理数据...

    GXT组件使用教程4——Aggregation Grid

    GXT组件包括表格、表单、菜单、工具栏、树形视图等,它们都具有高度可定制性和响应式设计。Aggregation Grid是GXT中的一种高级表格组件,它不仅可以展示大量数据,还能提供数据汇总功能。 2. **Aggregation Grid的...

    gxt、gwt与spring结合使用

    GXT是Ext JS的一个Java版本,提供了大量的桌面级UI组件,如表格、图表、树形视图等,同时还支持数据绑定和分页功能。GXT的优势在于其丰富的界面组件和对响应式设计的支持,使得开发者能够构建出类似桌面应用的用户...

Global site tag (gtag.js) - Google Analytics