How do I create a bar chart showing percentages bound to a list of objects? - vb.net

Using the DataVisualization.Charting.Chart control, I need to create a bar chart (maybe stacked bar chart) that shows per person the number of hours booked for that person and a those hours' percentage of total hours. So far I am a bit overwhelmed by the number of collections and properties to set on this beast, so I'd appreciate some help on first getting my chart up, then I'll explore more on my own.
I need to bind the chart to a list of the following object:
Public Class DOHoursChartItem
Public Property Name As String
Public Property Hours As Double
Public Property Percent As Double
End Class
I'm not sure I need the percentage property here, in favour of somehow letting the chart control handle this and just give it the Hours value per point and a total hours value, but that's why I'm asking: how do I set up the chart I describe above?

I'm not very good in VB, so I will start posting an example in C# (then I can try to translate it if you really need).
Here are three examples of methods that you can use to bind your items to a mschart and get columns charts:
Example 1: single area and side-by-side columns
private void FillChartSingleArea()
{
// this set the datasource
this.chart1.DataSource = GetItems();
// clear all the (possible) existing series
this.chart1.Series.Clear();
// add the hours series
var hoursSeries = this.chart1.Series.Add("Hours");
hoursSeries.XValueMember = "Name";
hoursSeries.YValueMembers = "Hours";
hoursSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
// add the percentages series
var percSeries = this.chart1.Series.Add("Percentages");
percSeries.XValueMember = "Name";
percSeries.YValueMembers = "Percent";
percSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
}
Example 2: two charts one upon the other
private void FillChartDoubleArea()
{
// this set the datasource
this.chart1.DataSource = GetItems();
// clear all the (possible) existing series
this.chart1.Series.Clear();
// clear all the existing areas and add 2 new areas
this.chart1.ChartAreas.Clear();
this.chart1.ChartAreas.Add("Area1");
this.chart1.ChartAreas.Add("Area2");
// add the hours series
var hoursSeries = this.chart1.Series.Add("Hours");
hoursSeries.ChartArea = "Area1";
hoursSeries.XValueMember = "Name";
hoursSeries.YValueMembers = "Hours";
hoursSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
// add the percentages series
var percSeries = this.chart1.Series.Add("Percentages");
hoursSeries.ChartArea = "Area2";
percSeries.XValueMember = "Name";
percSeries.YValueMembers = "Percent";
percSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
}
Example 3: single area and stacked columns
private void FillStackedChartSingleArea()
{
// this set the datasource
this.chart1.DataSource = GetItems();
// clear all the (possible) existing series
this.chart1.Series.Clear();
// add the hours series
var hoursSeries = this.chart1.Series.Add("Hours");
hoursSeries.XValueMember = "Name";
hoursSeries.YValueMembers = "Hours";
hoursSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
// add the percentages series
var percSeries = this.chart1.Series.Add("Percentages");
percSeries.XValueMember = "Name";
percSeries.YValueMembers = "Percent";
percSeries.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn;
}
Where GetItems method is defined as follows (for all the examples):
private List<DOHoursChartItem> GetItems()
{
var items = new List<DOHoursChartItem>()
{
new DOHoursChartItem("John", 120),
new DOHoursChartItem("Amanda", 40),
new DOHoursChartItem("David", 70),
new DOHoursChartItem("Rachel", 10),
};
// compute the percentages
var totalHours = items.Sum(x => x.Hours);
foreach (var item in items)
item.Percent = (item.Hours * 100.0) / totalHours;
return items;
}
and DOHoursChartItem as :
class DOHoursChartItem
{
public String Name { get; set; }
public double Hours { get; set; }
public double Percent { get; set; }
public DOHoursChartItem(string name, double hours)
{
this.Name = name;
this.Hours = hours;
}
}
N.B.
these are actually Column charts; by setting the ChartType to Bar (or StackedBar), you will get the same result but the bars will have an horizontal orientation.

Related

FastObjectListView UpdateObject() randomly reorders rows within primary sort

Data is a generic List of domain objects.
I click the "Deploy Status" column header to sort on that column.
I have a button that does nothing more than folv.UpdateObject(someObject) .
Every time I press that button, the Deploy Status column maintains its sort, but all rows within the sorted blocks are randomly reordered, as per screenshot.
I have commented out everything in the form's code beyond loading the data, the test button, and the FastObjectListView's column.Add() and .SetObjects(). There are no event handlers wired up for the FastObjectListView. I am not setting PrimarySort or SecondarySort in code; only by clicking with the mouse.
You should be able to fix this problem by either calling Sort after your button's call to UpdateObject or changing your usage of UpdateObject to RefreshObject
Reproducing the problem (C# Repro for the issue in the API)
This seems to reproduce the problem you are having. Run the code, sort the Other column ascending. Click the update button.
public class MainForm : Form
{
public MainForm()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
//
// MainForm
//
this.ClientSize = new System.Drawing.Size(300, 300);
this.Name = "MainForm";
this.ResumeLayout(false);
this.PerformLayout();
var OLVa = new FastObjectListView();
OLVa.Width = 250;
OLVa.Height = 250;
OLVa.Columns.Add(new OLVColumn("ID", "ID"));
OLVa.Columns.Add(new OLVColumn("Other", "Other"));
var l1 = new lolz(1, 3);
OLVa.AddObject(l1);
OLVa.AddObject(new lolz(2,3));
this.Controls.Add(OLVa);
var btn = new Button()
{
Text = "Update",
Top = OLVa.Bottom
};
btn.Click += (s,e)=>OLVa.UpdateObject(l1);
this.Controls.Add(btn);
}
private class lolz
{
public int ID;
public int Other;
public lolz(int id, int other)
{
ID = id;
Other = other;
}
}
}
Fixing the problem
The following would fix it for the above example:
btn.Click += (s,e)=>
{
OLVa.BeginUpdate();
try
{
OLVa.UpdateObject(l1);
OLVa.Sort();
}
finally
{
OLVa.EndUpdate();
}
};

How to set label the for the x-axis?

How do I set the label for the xAxis? On the left side it currently has the label "amount". How do I add it programmatically?
Depending on your preferences, you can use the data property of an Entry to store the label and then return it in your IAxisValueFormatter implementation:
public class LabelValueFormatter implements IAxisValueFormatter {
private final DataSet mData;
public LabelValueFormatter(DataSet data) {
mData = data;
}
#Override
public String getFormattedValue(float value, AxisBase axis) {
// return the entry's data which represents the label
return (String) mData.getEntryForXPos(value, DataSet.Rounding.CLOSEST).getData();
}
}

TableViewer with EMF databinding and cell editing - close but not quite

I am going through Tom Shindl's instructions on how to add EMF databinding on to tables, here is my code for the data binding:
protected DataBindingContext initDataBindings() {
//going to use this person instead
Person p = ProjectFactory.eINSTANCE.createPerson();
p.setFirstName("tony");
Committership c = ProjectFactory.eINSTANCE.createCommittership();
c.setName("HELP");
Committership anotherC = ProjectFactory.eINSTANCE.createCommittership();
anotherC.setName("PELASE");
Committership yetAnotherC = ProjectFactory.eINSTANCE.createCommittership();
yetAnotherC.setName("EMERGENCY");
p.getCommittership().add(c);
p.getCommittership().add(anotherC);
p.getCommittership().add(yetAnotherC);
CommandStack cs = new BasicCommandStack();
AdapterFactory af = new ProjectItemProviderAdapterFactory();
EditingDomain editingDomain = new AdapterFactoryEditingDomain(af, cs);
//data binding context
DataBindingContext bindingContext = new DataBindingContext();
//
ObservableListContentProvider listContentProvider = new ObservableListContentProvider();
IObservableMap[] attributeMap = new IObservableMap[1];
attributeMap[0] = EMFEditProperties.value(
editingDomain,
FeaturePath.fromList(ProjectPackage.Literals.COMMITTERSHIP__NAME)
).observeDetail(listContentProvider.getKnownElements());
TableViewerColumn column = new TableViewerColumn(tableViewer, SWT.NONE);
column.getColumn().setText("First Name");
column.getColumn().setWidth(200);
column.setLabelProvider(new GenericMapCellLabelProvider("{0}", attributeMap));
//tableViewer.setLabelProvider(new ObservableMapLabelProvider(attributeMap)); -- no need for this anymore
tableViewer.setContentProvider(listContentProvider);
//instead of giving it this list and doing it the non-EMF way
IObservableList selfList = Properties.selfList(Person.class).observe(p.getCommittership());
//property that you are looking for
IListProperty prop = EMFEditProperties.list(editingDomain, ProjectPackage.Literals.PERSON__COMMITTERSHIP);
IObservableValue master = EMFEditProperties.value(editingDomain, ProjectPackage.Literals.COMMITTERSHIP__NAME)
.observe(p);
/**this should not be returning null, instead it should be a
* list of the values from the person committership EList
*/
IObservableList someList = prop.observeDetail(master);
//set input requires and IObservableList!!
tableViewer.setInput(someList);
//
return bindingContext;
}
ok, now just to talk through what is happening and where I am stuck.
this line here would work for JFace data binding:
IObservableList selfList = Properties.selfList(Person.class).observe(p.getCommittership());
it populates the table happily, it is a list containing the three people I added, nice.
now making it work with EMF databinding, I am trying this:
//property that you are looking for
IListProperty prop = EMFEditProperties.list(editingDomain, ProjectPackage.Literals.PERSON__COMMITTERSHIP);
IObservableValue master = EMFEditProperties.value(editingDomain, ProjectPackage.Literals.COMMITTERSHIP__NAME)
.observe(p);
/**this should not be returning null, instead it should be a
* list of the values from the person committership EList
*/
IObservableList someList = prop.observeDetail(master);
the problem is that someList is empty and hence he table won't populate, could someone explain why?
It is definitely those three line that have some logic problem in there.
What I really want is an IObservableList of observed EMF objects...
help would be really appreciated, since Shindl's tutorial doesn't explain where he go the master from...I thought I would create a master:
IObservableValue master = EMFEditProperties.value(editingDomain, ProjectPackage.Literals.COMMITTERSHIP__NAME)
.observe(p);
and do prop.observeDetail(master)
but it is returning an empty list as I mentioned above...if only I could at least get it do display the data, the closest I have come is having three cells but not data in them.
IObservableList listObservable = prop.observe(p);
tableViewer.setInput(listObservable);
That fixed it for me in terms of viewing the data.
As for editing the cells, I did this in the end:
public class CommittershipNameEditingSupport extends EditingSupport {
private static TableViewer tableViewer;
private final CellEditor editor;
public CommittershipNameEditingSupport(TableViewer tableViewer) {
super(tableViewer);
this.tableViewer = tableViewer;
this.editor = new TextCellEditor(tableViewer.getTable());
}
#Override
protected CellEditor getCellEditor(Object element) {
return editor;
}
#Override
protected boolean canEdit(Object element) {
return true;
}
#Override
protected Object getValue(Object element) {
return ((Committership) element).getName();
}
#Override
protected void setValue(Object element, Object value) {
((Committership) element).setName(String.valueOf(value));
tableViewer.update(element, null);
}
}
and in the main view where I craeted the column I just added the method for cell editing support...it works nicely now :)

Telerik Mail Merge - Friendly Names (using RadRichTextBox)

I think i'm missing something obvious...
I'm using the Telerik Rad controls for WPF but i assume that the Rich text box uses some similar implementation for the mail merge functionality.
I want to have some friendly names on my mail merge fields. (namely spaces in the field names)
So i have a class for instance
Public Class someclass
{
<DisplayName("This is the complex description of the field")>
Public property thisfieldnamehasacomplexdescription as string
Public property anothercomplexfield as string
}
This is the only way i know to get "Friendly" names in the dropdown that is the mail merge.
So the two fields turn up okay as :
"This is the complex description of the field"
"anothercomplexfield"
but only anothercomplexfield actually populates with data when you do the merge.
Am i going to have to template the raddropdownbutton that holds the mail merge fields?
Is there an example of this somewhere?
Also a sub question. How do i add a scroll bar on these things?
(also i know this board is not a TELERIK specific board (duh!) but this might be useful to someone in the future. So i'll copy the answer i get from Telerik into here!
http://www.telerik.com/community/forums/wpf/richtextbox/558428-radrichtextbox-mailmerge---using-displayname-to-create-a-friendly-name-with-spaces.aspx )
This is what telerik gave me:
With the default MergeFields, it is not possible to change the display name fragment of the field in order to achieve a more friendly look. This should be possible if you implement a custom MergeField by deriving from the MergeField class. Here is a sample implementation that shows how this can be done:
public class CustomMergeField : MergeField
{
private const string CustomFieldName = "CustomField";
static CustomMergeField()
{
CodeBasedFieldFactory.RegisterFieldType(CustomMergeField.CustomFieldName, () => { return new CustomMergeField(); });
}
public override string FieldTypeName
{
get
{
return CustomMergeField.CustomFieldName;
}
}
public override Field CreateInstance()
{
return new CustomMergeField();
}
protected override DocumentFragment GetDisplayNameFragment()
{
return base.CreateFragmentFromText(string.Format(Field.DisplayNameFragmentFormat, this.GetFriendlyFieldName(this.PropertyPath)));
}
private string GetFriendlyFieldName(string fieldName)
{
int lettersInEnglishAlphabet = 26;
List<char> separators = new List<char>(lettersInEnglishAlphabet);
for (int i = 0; i < lettersInEnglishAlphabet; i++)
{
separators.Add((char)('A' + i));
}
StringBuilder newFieldName = new StringBuilder();
int previousIndex = 0;
for (int i = 1; i < fieldName.Length; i++)
{
if (separators.Contains(fieldName[i]))
{
if (previousIndex > 0)
{
newFieldName.Append(" ");
}
newFieldName.Append(fieldName.Substring(previousIndex, i - previousIndex));
previousIndex = i;
}
}
newFieldName.Append(" " + fieldName.Substring(previousIndex));
return newFieldName.ToString();
}
}
Note that the fragment that is shown when the DisplayMode is Code cannot be changed.
As for your other question, you can change the content of the dropdown button to show the friendly name of the fields and to include a scrollbar in the following way:
1. First, remove the binding of the button to the InsertMergeFieldEmptyCommand from XAML and give it a name (e.g. insertMergeField).
2. Next, add the following code in code-behind:
AddMergeFieldsInDropDownContent(this.insertMergeFieldButton);
private void AddMergeFieldsInDropDownContent(RadRibbonDropDownButton radRibbonDropDownButton)
{
Grid grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(100, GridUnitType.Pixel) });
ScrollViewer scrollViewer = new ScrollViewer();
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
StackPanel stackPanel = new StackPanel();
foreach (string fieldName in this.editor.Document.MailMergeDataSource.GetColumnNames())
{
RadRibbonButton fieldButton = new RadRibbonButton()
{
Text = this.GetFriendlyFieldName(fieldName),
Size = ButtonSize.Medium,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left
};
fieldButton.Command = this.editor.Commands.InsertFieldCommand;
fieldButton.CommandParameter = new MergeField() { PropertyPath = fieldName };
//or
//fieldButton.CommandParameter = new CustomMergeField() { PropertyPath = fieldName };
stackPanel.Children.Add(fieldButton);
}
stackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
scrollViewer.Content = stackPanel;
grid.Children.Add(scrollViewer);
radRibbonDropDownButton.DropDownContent = grid;
}
You can, of course optimize the code of the GetFriendlyName method and add it in a way that will be available by both classes.

How to get the selected value of a dynamically created selectlist component

I am not sure whether this is even possible, but I am trying to get the values of dynamically created selectlists in a VisualForce Apex controller class.
I am dynamically creating a selectlist for each field in a particular object (e.g. Contact) using the code below, but now I do not know how to get the selected value back. I have tried setting the value of each in the constructor and on a separate line (not in code sample below), but this does not seem to work.
VisualForce page:
<apex:dynamicComponent componentValue="{!contactPageBlockSection}" />
Apex controller:
public Component.Apex.PageBlockSection GetContactPageBlockSection(string objectName)
{
Map<string, Schema.SObjectField> FieldMap;
FieldMap = Schema.SObjectType.Contact.fields.getMap();
Set<string> FieldSet = FieldMap.keySet();
List<string> FieldList = new List<string>();
FieldList.addAll(FieldSet);
FieldList.sort();
Component.Apex.PageBlockSection pbs = new Component.Apex.PageBlockSection(columns = 2);
for (string fieldName : FieldList)
{
Component.Apex.PageBlockSectionItem pbsi = new Component.Apex.PageBlockSectionItem();
Schema.DescribeFieldResult field = (FieldMap.get(fieldName)).getDescribe();
if (field.isUpdateable() && field.IsAccessible())
{
Schema.DisplayType dt = field.getType();
Component.Apex.OutputLabel lblText = new Component.Apex.OutputLabel(escape = false);
lblText.value = field.getLabel();
pbsi.childComponents.add(lblText);
Component.Apex.SelectList selList = new Component.Apex.SelectList(id = field.getName(), multiselect = false, size = 1, style = 'width:200px;');
if (dt == Schema.DisplayType.Integer || dt == Schema.DisplayType.Double || dt == Schema.DisplayType.Currency || dt == Schema.DisplayType.Percent)
{
AddSelectOption(selList, 'Keep highest value');
AddSelectOption(selList, 'Keep lowest value');
AddSelectOption(selList, 'Keep master value');
pbsi.childComponents.add(selList);
pbs.childComponents.add(pbsi);
}
}
}
return pbs;
}
private void AddSelectOption(Component.Apex.SelectList selList, string option)
{
Component.Apex.SelectOption selOption = new Component.Apex.SelectOption();
selOption.itemLabel = option;
selOption.itemValue = option;
selList.childComponents.add(selOption);
}
Many thanks in advance
just bind the value attribute of the dynamically created select list to a string property using expression syntax like:
String selectedValue {get; set;}
...
setList.expressions.value = '{!selectedValue}';
the property should then store the selected value just as it would in a static declarative definition of the select list.
You could try and use dynamic visualforce binding. I.E.
map<String,String> FieldToString {get; set;}
...
selList.expressions.value = '{!FieldToString[\'' + fieldname + '\']}';
However, I have never used dynamic visualforce bindings within dynamic visualforce so caveat emptor.