2008年10月4日星期六

还是JFC,还是实时曲线图,就是加入了一个封装。




/*
* Copyrights (C) 2008 Bearice (Bearice@Gmail.com)
* Release under GNU/GPL Version 2.
*/

package cn.bearice.ipcontroller.manager.graph;

/**
*
* @author Bearice
*/
public interface RealTimeGraphDataSouce {
public Number getDate();
}


/*
* RealTimeGraphPanel.java
*
* Created on 2008年10月1日, 下午2:04
*/
package cn.bearice.ipcontroller.manager.graph;

import cn.bearice.java.util.StopableThread;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

/**
*
* @author Bearice
*/
public class RealTimeGraphPanel extends javax.swing.JPanel {

private static final long serialVersionUID = 8250077948583513551L;
private TimeSeriesCollection timeSeriesCollection = new TimeSeriesCollection();
private JFreeChart jFreeChart = ChartFactory.createTimeSeriesChart(null, null, null, timeSeriesCollection, true, true, false);
private ChartPanel chartPanel = new ChartPanel(jFreeChart);
private XYPlot xyplot = jFreeChart.getXYPlot();
private LinkedHashMap tss = new LinkedHashMap();
private Class timeClass = Millisecond.class;
private long updateRate = 1000;
private long fixedDisplayRange = 50000;
private int maxSimpleCount = 10000;
private long maxSimpleAge = Long.MAX_VALUE;

public int getMaxSimpleCount() {
return maxSimpleCount;
}

public synchronized void setMaxSimpleCount(int maxSimpleCount) {
int old = this.maxSimpleCount;
this.fixedDisplayRange = maxSimpleCount;
Collection t = new ArrayList(tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.setMaximumItemCount(maxSimpleCount);
}
firePropertyChange("maxSimpleCount", old, maxSimpleCount);
}

public long getMaxSimpleAge() {
return maxSimpleAge;
}

public synchronized void setMaxSimpleAge(long maxSimpleAge) {
long old = this.maxSimpleAge;
this.maxSimpleAge = maxSimpleAge;
Collection t = new ArrayList(tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.setMaximumItemAge(maxSimpleAge);
}
firePropertyChange("maxSimpleAge", old, maxSimpleAge);
}

public long getFixedDisplayRange() {
return fixedDisplayRange;
}

public synchronized void setFixedDisplayRange(long fixedDisplayRange) {
long old = this.fixedDisplayRange;
this.fixedDisplayRange = fixedDisplayRange;
if (jrdoFixed.isSelected()) {
jrdoFixedActionPerformed(null);
}
firePropertyChange("fixedDisplayRange", old, fixedDisplayRange);
}

public long getUpdateRate() {
return updateRate;
}

public synchronized void setUpdateRate(long updateRate) {
long old = this.updateRate;
this.updateRate = updateRate;
if (t1 != null) {
t1.interrupt();
}
firePropertyChange("updateRate", old, updateRate);
}

public void setTitle(String text) {
jFreeChart.setTitle(text);
}

public TextTitle getTitle() {
return jFreeChart.getTitle();
}

public void setTitle(TextTitle title) {
jFreeChart.setTitle(title);
}

public Class getTimeClass() {
return timeClass;
}

protected void setTimeClass(Class timeClass) {
this.timeClass = timeClass;
}

public String getXLable() {
return xyplot.getDomainAxis().getLabel();
}

public void setXLable(String xLable) {
xyplot.getDomainAxis().setLabel(xLable);
}

public String getYLable() {
return xyplot.getRangeAxis().getLabel();
}

public void setYLable(String yLable) {
xyplot.getRangeAxis().setLabel(yLable);
}

public RealTimeGraphPanel() {
}

public RealTimeGraphPanel(Class timeClass) {
setTimeClass(timeClass);
}

public RealTimeGraphPanel(String title, String xLable, String yLable) {
setTitle(title);
setXLable(xLable);
setYLable(yLable);
init();
}

/** Creates new form RealTimeGraphPanel */
public RealTimeGraphPanel(Class timeClass, String title, String xLable, String yLable) {
this(timeClass);
setTitle(title);
setXLable(xLable);
setYLable(yLable);
init();
}

public synchronized void init() {
ValueAxis valueaxis = xyplot.getDomainAxis();
valueaxis.setAutoRange(true);
valueaxis = xyplot.getRangeAxis();
valueaxis.setAutoRange(true);
initComponents();
}

public synchronized void addTimeSeries(RealTimeGraphDataSouce ds, String name) {
TimeSeries timeSeries = new TimeSeries(name, timeClass);
timeSeries.setMaximumItemCount(maxSimpleCount);
timeSeries.setMaximumItemAge(maxSimpleAge);
timeSeriesCollection.addSeries(timeSeries);
tss.put(ds, timeSeries);
}

public static void main(String[] args) {
JFrame frame = new JFrame("Test Chart");
final RealTimeGraphPanel rtgp = new RealTimeGraphPanel(Second.class, "Triangle Functions", "Time", "Value");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
return Math.sin(i++ * 0.01);
}
}, "Sin(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
return Math.cos(i++ * 0.01);
}
}, "Cos(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
double d = Math.cos(i * 0.01) + Math.sin(i * 0.01);
i++;
return d;
}
}, "Cos(t)+Sin(t)");
rtgp.addTimeSeries(new RealTimeGraphDataSouce() {

int i;

public Number getDate() {
double d = Math.cosh(i * 0.01);
i++;
return d;
}
}, "cosh(t)");
rtgp.setUpdateRate(20);
rtgp.setTimeClass(Millisecond.class);
frame.getContentPane().add(rtgp, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {

@Override
public void windowClosing(WindowEvent windowevent) {
System.exit(0);
}
});
}

/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
//
private void initComponents() {
bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

buttonGroup1 = new javax.swing.ButtonGroup();
jPanel1 = (chartPanel);
jScrollPane1 = new javax.swing.JScrollPane();
jPanel2 = new javax.swing.JPanel();
jLabel3 = new javax.swing.JLabel();
jSpinner2 = new javax.swing.JSpinner();
jLabel4 = new javax.swing.JLabel();
jLabel1 = new javax.swing.JLabel();
jSpinner1 = new javax.swing.JSpinner();
jLabel2 = new javax.swing.JLabel();
jbtnStart = new javax.swing.JButton();
jbtnStop = new javax.swing.JButton();
jbtnClear = new javax.swing.JButton();
jrdoScaled = new javax.swing.JRadioButton();
jrdoFixed = new javax.swing.JRadioButton();
jLabel6 = new javax.swing.JLabel();
jLabel5 = new javax.swing.JLabel();
jSpinner4 = new javax.swing.JSpinner();
jSpinner3 = new javax.swing.JSpinner();

setName("Form"); // NOI18N

jPanel1.setName("jPanel1"); // NOI18N

javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 460, Short.MAX_VALUE)
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 193, Short.MAX_VALUE)
);

jScrollPane1.setBorder(javax.swing.BorderFactory.createTitledBorder("Options"));
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
jScrollPane1.setName("jScrollPane1"); // NOI18N

jPanel2.setName("jPanel2"); // NOI18N

jLabel3.setText("Update rate:");
jLabel3.setName("jLabel3"); // NOI18N

jSpinner2.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), Long.valueOf(0L), null, Long.valueOf(50L)));
jSpinner2.setName("jSpinner2"); // NOI18N

org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${updateRate}"), jSpinner2, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

jLabel4.setText("ms");
jLabel4.setName("jLabel4"); // NOI18N

jLabel1.setText("Range:");
jLabel1.setName("jLabel1"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel1, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jSpinner1.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), Long.valueOf(0L), null, Long.valueOf(500L)));
jSpinner1.setName("jSpinner1"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${fixedDisplayRange}"), jSpinner1, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);
binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jSpinner1, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jLabel2.setText("ms");
jLabel2.setName("jLabel2"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jrdoFixed, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel2, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jbtnStart.setText("Start");
jbtnStart.setName("jbtnStart"); // NOI18N
jbtnStart.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStartActionPerformed(evt);
}
});

jbtnStop.setText("Stop");
jbtnStop.setEnabled(false);
jbtnStop.setName("jbtnStop"); // NOI18N
jbtnStop.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnStopActionPerformed(evt);
}
});

jbtnClear.setText("Clear");
jbtnClear.setName("jbtnClear"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, jbtnStop, org.jdesktop.beansbinding.ELProperty.create("${enabled}"), jbtnClear, org.jdesktop.beansbinding.BeanProperty.create("enabled"));
bindingGroup.addBinding(binding);

jbtnClear.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jbtnClearActionPerformed(evt);
}
});

buttonGroup1.add(jrdoScaled);
jrdoScaled.setSelected(true);
jrdoScaled.setText("Scaled");
jrdoScaled.setName("jrdoScaled"); // NOI18N
jrdoScaled.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jrdoScaledActionPerformed(evt);
}
});

buttonGroup1.add(jrdoFixed);
jrdoFixed.setText("Fixed");
jrdoFixed.setName("jrdoFixed"); // NOI18N
jrdoFixed.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jrdoFixedActionPerformed(evt);
}
});

jLabel6.setText("MSA");
jLabel6.setToolTipText("Maxium Simple Time (ms)");
jLabel6.setName("jLabel6"); // NOI18N

jLabel5.setText("MSC");
jLabel5.setToolTipText("Maxium Simple Count");
jLabel5.setName("jLabel5"); // NOI18N

jSpinner4.setModel(new javax.swing.SpinnerNumberModel(Long.valueOf(0L), null, null, Long.valueOf(1L)));
jSpinner4.setName("jSpinner4"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${maxSimpleAge}"), jSpinner4, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

jSpinner3.setModel(new javax.swing.SpinnerNumberModel());
jSpinner3.setName("jSpinner3"); // NOI18N

binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, this, org.jdesktop.beansbinding.ELProperty.create("${maxSimpleCount}"), jSpinner3, org.jdesktop.beansbinding.BeanProperty.create("value"));
bindingGroup.addBinding(binding);

javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel4)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 80, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel2))
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel2Layout.createSequentialGroup()
.addComponent(jbtnStart)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnStop)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jbtnClear)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jrdoScaled)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jrdoFixed)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jLabel6, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel5, javax.swing.GroupLayout.Alignment.LEADING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(jSpinner4)
.addComponent(jSpinner3, javax.swing.GroupLayout.DEFAULT_SIZE, 73, Short.MAX_VALUE))
.addContainerGap(13, Short.MAX_VALUE))
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jbtnStart)
.addComponent(jbtnStop)
.addComponent(jbtnClear)
.addComponent(jrdoFixed)
.addComponent(jrdoScaled))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel3)
.addComponent(jSpinner2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel4)
.addComponent(jLabel1)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel2)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup()
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jLabel5)
.addComponent(jSpinner3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel6)
.addComponent(jSpinner4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap())
);

jScrollPane1.setViewportView(jPanel2);

javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
);

bindingGroup.bind();
}//


class RefThread extends StopableThread {

@Override
protected void loop() throws InterruptedException {
LinkedHashMap lhm = new LinkedHashMap(tss);
for (RealTimeGraphDataSouce ds : tss.keySet()) {
try {
Number val = ds.getDate();
TimeSeries ts = lhm.get(ds);
ts.addOrUpdate((RegularTimePeriod) timeClass.newInstance(), val);
} catch (Exception ex) {
Logger.getLogger(RealTimeGraphPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
Thread.sleep(updateRate);
}
}
RefThread t1;

public synchronized void start() {
t1 = new RefThread();
t1.start();
jbtnStart.setEnabled(false);
jbtnStop.setEnabled(true);
}

public synchronized void stop() {
t1.exit();
t1 = null;
jbtnStart.setEnabled(true);
jbtnStop.setEnabled(false);
}

public synchronized void clear() {
Collection t = new ArrayList(this.tss.values());
for (TimeSeries timeSeries : t) {
timeSeries.clear();
}
}
private void jbtnStartActionPerformed(java.awt.event.ActionEvent evt) {
start();
}

private void jbtnClearActionPerformed(java.awt.event.ActionEvent evt) {
clear();
}

private void jrdoFixedActionPerformed(java.awt.event.ActionEvent evt) {
xyplot.getDomainAxis().setFixedAutoRange(fixedDisplayRange);
}

private void jrdoScaledActionPerformed(java.awt.event.ActionEvent evt) {
xyplot.getDomainAxis().setFixedAutoRange(0);
}

private void jbtnStopActionPerformed(java.awt.event.ActionEvent evt) {
stop();
}
// Variables declaration - do not modify
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JLabel jLabel4;
private javax.swing.JLabel jLabel5;
private javax.swing.JLabel jLabel6;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSpinner jSpinner1;
private javax.swing.JSpinner jSpinner2;
private javax.swing.JSpinner jSpinner3;
private javax.swing.JSpinner jSpinner4;
private javax.swing.JButton jbtnClear;
private javax.swing.JButton jbtnStart;
private javax.swing.JButton jbtnStop;
private javax.swing.JRadioButton jrdoFixed;
private javax.swing.JRadioButton jrdoScaled;
private org.jdesktop.beansbinding.BindingGroup bindingGroup;
// End of variables declaration
}

1 条评论: