Imported existing sources

This commit is contained in:
Daniel Brunner
2017-02-04 13:47:58 +01:00
parent 14a5be61cc
commit 7c97b9e539
86 changed files with 49665 additions and 0 deletions

190
Utilities/ColumnSelectionForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,190 @@
namespace BrightIdeasSoftware
{
partial class ColumnSelectionForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.buttonMoveUp = new System.Windows.Forms.Button();
this.buttonMoveDown = new System.Windows.Forms.Button();
this.buttonShow = new System.Windows.Forms.Button();
this.buttonHide = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.buttonOK = new System.Windows.Forms.Button();
this.buttonCancel = new System.Windows.Forms.Button();
this.objectListView1 = new BrightIdeasSoftware.ObjectListView();
this.olvColumn1 = new BrightIdeasSoftware.OLVColumn();
((System.ComponentModel.ISupportInitialize)(this.objectListView1)).BeginInit();
this.SuspendLayout();
//
// buttonMoveUp
//
this.buttonMoveUp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonMoveUp.Location = new System.Drawing.Point(295, 31);
this.buttonMoveUp.Name = "buttonMoveUp";
this.buttonMoveUp.Size = new System.Drawing.Size(87, 23);
this.buttonMoveUp.TabIndex = 1;
this.buttonMoveUp.Text = "Move &Up";
this.buttonMoveUp.UseVisualStyleBackColor = true;
this.buttonMoveUp.Click += new System.EventHandler(this.buttonMoveUp_Click);
//
// buttonMoveDown
//
this.buttonMoveDown.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonMoveDown.Location = new System.Drawing.Point(295, 60);
this.buttonMoveDown.Name = "buttonMoveDown";
this.buttonMoveDown.Size = new System.Drawing.Size(87, 23);
this.buttonMoveDown.TabIndex = 2;
this.buttonMoveDown.Text = "Move &Down";
this.buttonMoveDown.UseVisualStyleBackColor = true;
this.buttonMoveDown.Click += new System.EventHandler(this.buttonMoveDown_Click);
//
// buttonShow
//
this.buttonShow.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonShow.Location = new System.Drawing.Point(295, 89);
this.buttonShow.Name = "buttonShow";
this.buttonShow.Size = new System.Drawing.Size(87, 23);
this.buttonShow.TabIndex = 3;
this.buttonShow.Text = "&Show";
this.buttonShow.UseVisualStyleBackColor = true;
this.buttonShow.Click += new System.EventHandler(this.buttonShow_Click);
//
// buttonHide
//
this.buttonHide.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.buttonHide.Location = new System.Drawing.Point(295, 118);
this.buttonHide.Name = "buttonHide";
this.buttonHide.Size = new System.Drawing.Size(87, 23);
this.buttonHide.TabIndex = 4;
this.buttonHide.Text = "&Hide";
this.buttonHide.UseVisualStyleBackColor = true;
this.buttonHide.Click += new System.EventHandler(this.buttonHide_Click);
//
// label1
//
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.label1.BackColor = System.Drawing.SystemColors.Control;
this.label1.Location = new System.Drawing.Point(13, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(366, 19);
this.label1.TabIndex = 5;
this.label1.Text = "Choose the columns you want to see in this list. ";
//
// buttonOK
//
this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonOK.Location = new System.Drawing.Point(198, 304);
this.buttonOK.Name = "buttonOK";
this.buttonOK.Size = new System.Drawing.Size(87, 23);
this.buttonOK.TabIndex = 6;
this.buttonOK.Text = "&OK";
this.buttonOK.UseVisualStyleBackColor = true;
this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
//
// buttonCancel
//
this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.buttonCancel.Location = new System.Drawing.Point(295, 304);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(87, 23);
this.buttonCancel.TabIndex = 7;
this.buttonCancel.Text = "&Cancel";
this.buttonCancel.UseVisualStyleBackColor = true;
this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click);
//
// objectListView1
//
this.objectListView1.AllColumns.Add(this.olvColumn1);
this.objectListView1.AlternateRowBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(192)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
this.objectListView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.objectListView1.CellEditActivation = BrightIdeasSoftware.ObjectListView.CellEditActivateMode.SingleClick;
this.objectListView1.CheckBoxes = true;
this.objectListView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.olvColumn1});
this.objectListView1.FullRowSelect = true;
this.objectListView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.objectListView1.HideSelection = false;
this.objectListView1.Location = new System.Drawing.Point(12, 31);
this.objectListView1.MultiSelect = false;
this.objectListView1.Name = "objectListView1";
this.objectListView1.ShowGroups = false;
this.objectListView1.ShowSortIndicators = false;
this.objectListView1.Size = new System.Drawing.Size(273, 259);
this.objectListView1.TabIndex = 0;
this.objectListView1.UseCompatibleStateImageBehavior = false;
this.objectListView1.View = System.Windows.Forms.View.Details;
this.objectListView1.SelectionChanged += new System.EventHandler(this.objectListView1_SelectionChanged);
//
// olvColumn1
//
this.olvColumn1.AspectName = "Text";
this.olvColumn1.IsVisible = true;
this.olvColumn1.Text = "Column";
this.olvColumn1.Width = 267;
//
// ColumnSelectionForm
//
this.AcceptButton = this.buttonOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.buttonCancel;
this.ClientSize = new System.Drawing.Size(391, 339);
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.buttonOK);
this.Controls.Add(this.label1);
this.Controls.Add(this.buttonHide);
this.Controls.Add(this.buttonShow);
this.Controls.Add(this.buttonMoveDown);
this.Controls.Add(this.buttonMoveUp);
this.Controls.Add(this.objectListView1);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ColumnSelectionForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Column Selection";
((System.ComponentModel.ISupportInitialize)(this.objectListView1)).EndInit();
this.ResumeLayout(false);
}
#endregion
private BrightIdeasSoftware.ObjectListView objectListView1;
private System.Windows.Forms.Button buttonMoveUp;
private System.Windows.Forms.Button buttonMoveDown;
private System.Windows.Forms.Button buttonShow;
private System.Windows.Forms.Button buttonHide;
private BrightIdeasSoftware.OLVColumn olvColumn1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Button buttonOK;
private System.Windows.Forms.Button buttonCancel;
}
}

View File

@@ -0,0 +1,263 @@
/*
* ColumnSelectionForm - A utility form that allows columns to be rearranged and/or hidden
*
* Author: Phillip Piper
* Date: 1/04/2011 11:15 AM
*
* Change log:
* 2013-04-21 JPP - Fixed obscure bug in column re-ordered. Thanks to Edwin Chen.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace BrightIdeasSoftware
{
/// <summary>
/// This form is an example of how an application could allows the user to select which columns
/// an ObjectListView will display, as well as select which order the columns are displayed in.
/// </summary>
/// <remarks>
/// <para>In Tile view, ColumnHeader.DisplayIndex does nothing. To reorder the columns you have
/// to change the order of objects in the Columns property.</para>
/// <para>Remember that the first column is special!
/// It has to remain the first column.</para>
/// </remarks>
public partial class ColumnSelectionForm : Form
{
/// <summary>
/// Make a new ColumnSelectionForm
/// </summary>
public ColumnSelectionForm()
{
InitializeComponent();
}
/// <summary>
/// Open this form so it will edit the columns that are available in the listview's current view
/// </summary>
/// <param name="olv">The ObjectListView whose columns are to be altered</param>
public void OpenOn(ObjectListView olv)
{
this.OpenOn(olv, olv.View);
}
/// <summary>
/// Open this form so it will edit the columns that are available in the given listview
/// when the listview is showing the given type of view.
/// </summary>
/// <param name="olv">The ObjectListView whose columns are to be altered</param>
/// <param name="view">The view that is to be altered. Must be View.Details or View.Tile</param>
public void OpenOn(ObjectListView olv, View view)
{
if (view != View.Details && view != View.Tile)
return;
this.InitializeForm(olv, view);
if (this.ShowDialog() == DialogResult.OK)
this.Apply(olv, view);
}
/// <summary>
/// Initialize the form to show the columns of the given view
/// </summary>
/// <param name="olv"></param>
/// <param name="view"></param>
protected void InitializeForm(ObjectListView olv, View view)
{
this.AllColumns = olv.AllColumns;
this.RearrangableColumns = new List<OLVColumn>(this.AllColumns);
foreach (OLVColumn col in this.RearrangableColumns) {
if (view == View.Details)
this.MapColumnToVisible[col] = col.IsVisible;
else
this.MapColumnToVisible[col] = col.IsTileViewColumn;
}
this.RearrangableColumns.Sort(new SortByDisplayOrder(this));
this.objectListView1.BooleanCheckStateGetter = delegate(Object rowObject) {
return this.MapColumnToVisible[(OLVColumn)rowObject];
};
this.objectListView1.BooleanCheckStatePutter = delegate(Object rowObject, bool newValue) {
// Some columns should always be shown, so ignore attempts to hide them
OLVColumn column = (OLVColumn)rowObject;
if (!column.CanBeHidden)
return true;
this.MapColumnToVisible[column] = newValue;
EnableControls();
return newValue;
};
this.objectListView1.SetObjects(this.RearrangableColumns);
this.EnableControls();
}
private List<OLVColumn> AllColumns = null;
private List<OLVColumn> RearrangableColumns = new List<OLVColumn>();
private Dictionary<OLVColumn, bool> MapColumnToVisible = new Dictionary<OLVColumn, bool>();
/// <summary>
/// The user has pressed OK. Do what's requied.
/// </summary>
/// <param name="olv"></param>
/// <param name="view"></param>
protected void Apply(ObjectListView olv, View view)
{
olv.Freeze();
// Update the column definitions to reflect whether they have been hidden
if (view == View.Details) {
foreach (OLVColumn col in olv.AllColumns)
col.IsVisible = this.MapColumnToVisible[col];
} else {
foreach (OLVColumn col in olv.AllColumns)
col.IsTileViewColumn = this.MapColumnToVisible[col];
}
// Collect the columns are still visible
List<OLVColumn> visibleColumns = this.RearrangableColumns.FindAll(
delegate(OLVColumn x) { return this.MapColumnToVisible[x]; });
// Detail view and Tile view have to be handled in different ways.
if (view == View.Details) {
// Of the still visible columns, change DisplayIndex to reflect their position in the rearranged list
olv.ChangeToFilteredColumns(view);
foreach (OLVColumn col in visibleColumns) {
col.DisplayIndex = visibleColumns.IndexOf((OLVColumn)col);
col.LastDisplayIndex = col.DisplayIndex;
}
} else {
// In Tile view, DisplayOrder does nothing. So to change the display order, we have to change the
// order of the columns in the Columns property.
// Remember, the primary column is special and has to remain first!
OLVColumn primaryColumn = this.AllColumns[0];
visibleColumns.Remove(primaryColumn);
olv.Columns.Clear();
olv.Columns.Add(primaryColumn);
olv.Columns.AddRange(visibleColumns.ToArray());
olv.CalculateReasonableTileSize();
}
olv.Unfreeze();
}
#region Event handlers
private void buttonMoveUp_Click(object sender, EventArgs e)
{
int selectedIndex = this.objectListView1.SelectedIndices[0];
OLVColumn col = this.RearrangableColumns[selectedIndex];
this.RearrangableColumns.RemoveAt(selectedIndex);
this.RearrangableColumns.Insert(selectedIndex-1, col);
this.objectListView1.BuildList();
EnableControls();
}
private void buttonMoveDown_Click(object sender, EventArgs e)
{
int selectedIndex = this.objectListView1.SelectedIndices[0];
OLVColumn col = this.RearrangableColumns[selectedIndex];
this.RearrangableColumns.RemoveAt(selectedIndex);
this.RearrangableColumns.Insert(selectedIndex + 1, col);
this.objectListView1.BuildList();
EnableControls();
}
private void buttonShow_Click(object sender, EventArgs e)
{
this.objectListView1.SelectedItem.Checked = true;
}
private void buttonHide_Click(object sender, EventArgs e)
{
this.objectListView1.SelectedItem.Checked = false;
}
private void buttonOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}
private void buttonCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void objectListView1_SelectionChanged(object sender, EventArgs e)
{
EnableControls();
}
#endregion
#region Control enabling
/// <summary>
/// Enable the controls on the dialog to match the current state
/// </summary>
protected void EnableControls()
{
if (this.objectListView1.SelectedIndices.Count == 0) {
this.buttonMoveUp.Enabled = false;
this.buttonMoveDown.Enabled = false;
this.buttonShow.Enabled = false;
this.buttonHide.Enabled = false;
} else {
// Can't move the first row up or the last row down
this.buttonMoveUp.Enabled = (this.objectListView1.SelectedIndices[0] != 0);
this.buttonMoveDown.Enabled = (this.objectListView1.SelectedIndices[0] < (this.objectListView1.GetItemCount() - 1));
OLVColumn selectedColumn = (OLVColumn)this.objectListView1.SelectedObject;
// Some columns cannot be hidden (and hence cannot be Shown)
this.buttonShow.Enabled = !this.MapColumnToVisible[selectedColumn] && selectedColumn.CanBeHidden;
this.buttonHide.Enabled = this.MapColumnToVisible[selectedColumn] && selectedColumn.CanBeHidden;
}
}
#endregion
/// <summary>
/// A Comparer that will sort a list of columns so that visible ones come before hidden ones,
/// and that are ordered by their display order.
/// </summary>
private class SortByDisplayOrder : IComparer<OLVColumn>
{
public SortByDisplayOrder(ColumnSelectionForm form)
{
this.Form = form;
}
private ColumnSelectionForm Form;
#region IComparer<OLVColumn> Members
int IComparer<OLVColumn>.Compare(OLVColumn x, OLVColumn y)
{
if (this.Form.MapColumnToVisible[x] && !this.Form.MapColumnToVisible[y])
return -1;
if (!this.Form.MapColumnToVisible[x] && this.Form.MapColumnToVisible[y])
return 1;
if (x.DisplayIndex == y.DisplayIndex)
return x.Text.CompareTo(y.Text);
else
return x.DisplayIndex - y.DisplayIndex;
}
#endregion
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

561
Utilities/Generator.cs Normal file
View File

@@ -0,0 +1,561 @@
/*
* Generator - Utility methods that generate columns or methods
*
* Author: Phillip Piper
* Date: 15/08/2009 22:37
*
* Change log:
* 2012-08-16 JPP - Generator now considers [OLVChildren] and [OLVIgnore] attributes.
* 2012-06-14 JPP - Allow columns to be generated even if they are not marked with [OLVColumn]
* - Converted class from static to instance to allow it to be subclassed.
* Also, added IGenerator to allow it to be completely reimplemented.
* v2.5.1
* 2010-11-01 JPP - DisplayIndex is now set correctly for columns that lack that attribute
* v2.4.1
* 2010-08-25 JPP - Generator now also resets sort columns
* v2.4
* 2010-04-14 JPP - Allow Name property to be set
* - Don't double set the Text property
* v2.3
* 2009-08-15 JPP - Initial version
*
* To do:
*
* Copyright (C) 2009-2014 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace BrightIdeasSoftware
{
/// <summary>
/// An object that implements the IGenerator interface provides the ability
/// to dynamically create columns
/// for an ObjectListView based on the characteristics of a given collection
/// of model objects.
/// </summary>
public interface IGenerator {
/// <summary>
/// Generate columns into the given ObjectListView that come from the given
/// model object type.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="type">The model type whose attributes will be considered.</param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
void GenerateAndReplaceColumns(ObjectListView olv, Type type, bool allProperties);
/// <summary>
/// Generate a list of OLVColumns based on the attributes of the given type
/// If allProperties to true, all public properties will have a matching column generated.
/// If allProperties is false, only properties that have a OLVColumn attribute will have a column generated.
/// </summary>
/// <param name="type"></param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
/// <returns>A collection of OLVColumns matching the attributes of Type that have OLVColumnAttributes.</returns>
IList<OLVColumn> GenerateColumns(Type type, bool allProperties);
}
/// <summary>
/// The Generator class provides methods to dynamically create columns
/// for an ObjectListView based on the characteristics of a given collection
/// of model objects.
/// </summary>
/// <remarks>
/// <para>For a given type, a Generator can create columns to match the public properties
/// of that type. The generator can consider all public properties or only those public properties marked with
/// [OLVColumn] attribute.</para>
/// </remarks>
public class Generator : IGenerator {
#region Static convenience methods
/// <summary>
/// Gets or sets the actual generator used by the static convinence methods.
/// </summary>
/// <remarks>If you subclass the standard generator or implement IGenerator yourself,
/// you should install an instance of your subclass/implementation here.</remarks>
public static IGenerator Instance {
get { return Generator.instance ?? (Generator.instance = new Generator()); }
set { Generator.instance = value; }
}
private static IGenerator instance;
/// <summary>
/// Replace all columns of the given ObjectListView with columns generated
/// from the first member of the given enumerable. If the enumerable is
/// empty or null, the ObjectListView will be cleared.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="enumerable">The collection whose first element will be used to generate columns.</param>
static public void GenerateColumns(ObjectListView olv, IEnumerable enumerable) {
Generator.GenerateColumns(olv, enumerable, false);
}
/// <summary>
/// Replace all columns of the given ObjectListView with columns generated
/// from the first member of the given enumerable. If the enumerable is
/// empty or null, the ObjectListView will be cleared.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="enumerable">The collection whose first element will be used to generate columns.</param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
static public void GenerateColumns(ObjectListView olv, IEnumerable enumerable, bool allProperties) {
// Generate columns based on the type of the first model in the collection and then quit
if (enumerable != null) {
foreach (object model in enumerable) {
Generator.Instance.GenerateAndReplaceColumns(olv, model.GetType(), allProperties);
return;
}
}
// If we reach here, the collection was empty, so we clear the list
Generator.Instance.GenerateAndReplaceColumns(olv, null, allProperties);
}
/// <summary>
/// Generate columns into the given ObjectListView that come from the public properties of the given
/// model object type.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="type">The model type whose attributes will be considered.</param>
static public void GenerateColumns(ObjectListView olv, Type type) {
Generator.Instance.GenerateAndReplaceColumns(olv, type, false);
}
/// <summary>
/// Generate columns into the given ObjectListView that come from the public properties of the given
/// model object type.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="type">The model type whose attributes will be considered.</param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
static public void GenerateColumns(ObjectListView olv, Type type, bool allProperties) {
Generator.Instance.GenerateAndReplaceColumns(olv, type, allProperties);
}
/// <summary>
/// Generate a list of OLVColumns based on the public properties of the given type
/// that have a OLVColumn attribute.
/// </summary>
/// <param name="type"></param>
/// <returns>A collection of OLVColumns matching the attributes of Type that have OLVColumnAttributes.</returns>
static public IList<OLVColumn> GenerateColumns(Type type) {
return Generator.Instance.GenerateColumns(type, false);
}
#endregion
#region Public interface
/// <summary>
/// Generate columns into the given ObjectListView that come from the given
/// model object type.
/// </summary>
/// <param name="olv">The ObjectListView to modify</param>
/// <param name="type">The model type whose attributes will be considered.</param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
public virtual void GenerateAndReplaceColumns(ObjectListView olv, Type type, bool allProperties) {
IList<OLVColumn> columns = this.GenerateColumns(type, allProperties);
TreeListView tlv = olv as TreeListView;
if (tlv != null)
this.TryGenerateChildrenDelegates(tlv, type);
this.ReplaceColumns(olv, columns);
}
/// <summary>
/// Generate a list of OLVColumns based on the attributes of the given type
/// If allProperties to true, all public properties will have a matching column generated.
/// If allProperties is false, only properties that have a OLVColumn attribute will have a column generated.
/// </summary>
/// <param name="type"></param>
/// <param name="allProperties">Will columns be generated for properties that are not marked with [OLVColumn].</param>
/// <returns>A collection of OLVColumns matching the attributes of Type that have OLVColumnAttributes.</returns>
public virtual IList<OLVColumn> GenerateColumns(Type type, bool allProperties) {
List<OLVColumn> columns = new List<OLVColumn>();
// Sanity
if (type == null)
return columns;
// Iterate all public properties in the class and build columns from those that have
// an OLVColumn attribute and that are not ignored.
foreach (PropertyInfo pinfo in type.GetProperties()) {
if (Attribute.GetCustomAttribute(pinfo, typeof(OLVIgnoreAttribute)) != null)
continue;
OLVColumnAttribute attr = Attribute.GetCustomAttribute(pinfo, typeof(OLVColumnAttribute)) as OLVColumnAttribute;
if (attr == null) {
if (allProperties)
columns.Add(this.MakeColumnFromPropertyInfo(pinfo));
} else {
columns.Add(this.MakeColumnFromAttribute(pinfo, attr));
}
}
// How many columns have DisplayIndex specifically set?
int countPositiveDisplayIndex = 0;
foreach (OLVColumn col in columns) {
if (col.DisplayIndex >= 0)
countPositiveDisplayIndex += 1;
}
// Give columns that don't have a DisplayIndex an incremental index
int columnIndex = countPositiveDisplayIndex;
foreach (OLVColumn col in columns)
if (col.DisplayIndex < 0)
col.DisplayIndex = (columnIndex++);
columns.Sort(delegate(OLVColumn x, OLVColumn y) {
return x.DisplayIndex.CompareTo(y.DisplayIndex);
});
return columns;
}
#endregion
#region Implementation
/// <summary>
/// Replace all the columns in the given listview with the given list of columns.
/// </summary>
/// <param name="olv"></param>
/// <param name="columns"></param>
protected virtual void ReplaceColumns(ObjectListView olv, IList<OLVColumn> columns) {
olv.Reset();
// Are there new columns to add?
if (columns == null || columns.Count == 0)
return;
// Setup the columns
olv.AllColumns.AddRange(columns);
this.PostCreateColumns(olv);
}
/// <summary>
/// Post process columns after creating them and adding them to the AllColumns collection.
/// </summary>
/// <param name="olv"></param>
public virtual void PostCreateColumns(ObjectListView olv) {
if (olv.AllColumns.Exists(delegate(OLVColumn x) { return x.CheckBoxes; }))
olv.UseSubItemCheckBoxes = true;
if (olv.AllColumns.Exists(delegate(OLVColumn x) { return x.Index > 0 && (x.ImageGetter != null || !String.IsNullOrEmpty(x.ImageAspectName)); }))
olv.ShowImagesOnSubItems = true;
olv.RebuildColumns();
olv.AutoSizeColumns();
}
/// <summary>
/// Create a column from the given PropertyInfo and OLVColumn attribute
/// </summary>
/// <param name="pinfo"></param>
/// <param name="attr"></param>
/// <returns></returns>
protected virtual OLVColumn MakeColumnFromAttribute(PropertyInfo pinfo, OLVColumnAttribute attr) {
return MakeColumn(pinfo.Name, DisplayNameToColumnTitle(pinfo.Name), pinfo.CanWrite, pinfo.PropertyType, attr);
}
/// <summary>
/// Make a column from the given PropertyInfo
/// </summary>
/// <param name="pinfo"></param>
/// <returns></returns>
protected virtual OLVColumn MakeColumnFromPropertyInfo(PropertyInfo pinfo) {
return MakeColumn(pinfo.Name, DisplayNameToColumnTitle(pinfo.Name), pinfo.CanWrite, pinfo.PropertyType, null);
}
/// <summary>
/// Make a column from the given PropertyDescriptor
/// </summary>
/// <param name="pd"></param>
/// <returns></returns>
public virtual OLVColumn MakeColumnFromPropertyDescriptor(PropertyDescriptor pd) {
OLVColumnAttribute attr = pd.Attributes[typeof(OLVColumnAttribute)] as OLVColumnAttribute;
return MakeColumn(pd.Name, DisplayNameToColumnTitle(pd.DisplayName), !pd.IsReadOnly, pd.PropertyType, attr);
}
/// <summary>
/// Create a column with all the given information
/// </summary>
/// <param name="aspectName"></param>
/// <param name="title"></param>
/// <param name="editable"></param>
/// <param name="propertyType"></param>
/// <param name="attr"></param>
/// <returns></returns>
protected virtual OLVColumn MakeColumn(string aspectName, string title, bool editable, Type propertyType, OLVColumnAttribute attr) {
OLVColumn column = this.MakeColumn(aspectName, title, attr);
column.Name = (attr == null || String.IsNullOrEmpty(attr.Name)) ? aspectName : attr.Name;
this.ConfigurePossibleBooleanColumn(column, propertyType);
if (attr == null) {
column.IsEditable = editable;
return column;
}
column.AspectToStringFormat = attr.AspectToStringFormat;
if (attr.IsCheckBoxesSet)
column.CheckBoxes = attr.CheckBoxes;
column.DisplayIndex = attr.DisplayIndex;
column.FillsFreeSpace = attr.FillsFreeSpace;
if (attr.IsFreeSpaceProportionSet)
column.FreeSpaceProportion = attr.FreeSpaceProportion;
column.GroupWithItemCountFormat = attr.GroupWithItemCountFormat;
column.GroupWithItemCountSingularFormat = attr.GroupWithItemCountSingularFormat;
column.Hyperlink = attr.Hyperlink;
column.ImageAspectName = attr.ImageAspectName;
column.IsEditable = attr.IsEditableSet ? attr.IsEditable : editable;
column.IsTileViewColumn = attr.IsTileViewColumn;
column.IsVisible = attr.IsVisible;
column.MaximumWidth = attr.MaximumWidth;
column.MinimumWidth = attr.MinimumWidth;
column.Tag = attr.Tag;
if (attr.IsTextAlignSet)
column.TextAlign = attr.TextAlign;
column.ToolTipText = attr.ToolTipText;
if (attr.IsTriStateCheckBoxesSet)
column.TriStateCheckBoxes = attr.TriStateCheckBoxes;
column.UseInitialLetterForGroup = attr.UseInitialLetterForGroup;
column.Width = attr.Width;
if (attr.GroupCutoffs != null && attr.GroupDescriptions != null)
column.MakeGroupies(attr.GroupCutoffs, attr.GroupDescriptions);
return column;
}
/// <summary>
/// Create a column.
/// </summary>
/// <param name="aspectName"></param>
/// <param name="title"></param>
/// <param name="attr"></param>
/// <returns></returns>
protected virtual OLVColumn MakeColumn(string aspectName, string title, OLVColumnAttribute attr) {
string columnTitle = (attr == null || String.IsNullOrEmpty(attr.Title)) ? title : attr.Title;
return new OLVColumn(columnTitle, aspectName);
}
/// <summary>
/// Convert a property name to a displayable title.
/// </summary>
/// <param name="displayName"></param>
/// <returns></returns>
protected virtual string DisplayNameToColumnTitle(string displayName) {
string title = displayName.Replace("_", " ");
// Put a space between a lower-case letter that is followed immediately by an upper case letter
title = Regex.Replace(title, @"(\p{Ll})(\p{Lu})", @"$1 $2");
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(title);
}
/// <summary>
/// Configure the given column to show a checkbox if appropriate
/// </summary>
/// <param name="column"></param>
/// <param name="propertyType"></param>
protected virtual void ConfigurePossibleBooleanColumn(OLVColumn column, Type propertyType) {
if (propertyType != typeof(bool) && propertyType != typeof(bool?) && propertyType != typeof(CheckState))
return;
column.CheckBoxes = true;
column.TextAlign = HorizontalAlignment.Center;
column.Width = 32;
column.TriStateCheckBoxes = (propertyType == typeof(bool?) || propertyType == typeof(CheckState));
}
/// <summary>
/// If this given type has an property marked with [OLVChildren], make delegates that will
/// traverse that property as the children of an instance of the model
/// </summary>
/// <param name="tlv"></param>
/// <param name="type"></param>
protected virtual void TryGenerateChildrenDelegates(TreeListView tlv, Type type) {
foreach (PropertyInfo pinfo in type.GetProperties()) {
OLVChildrenAttribute attr = Attribute.GetCustomAttribute(pinfo, typeof(OLVChildrenAttribute)) as OLVChildrenAttribute;
if (attr != null) {
this.GenerateChildrenDelegates(tlv, pinfo);
return;
}
}
}
/// <summary>
/// Generate CanExpand and ChildrenGetter delegates from the given property.
/// </summary>
/// <param name="tlv"></param>
/// <param name="pinfo"></param>
protected virtual void GenerateChildrenDelegates(TreeListView tlv, PropertyInfo pinfo) {
Munger childrenGetter = new Munger(pinfo.Name);
tlv.CanExpandGetter = delegate(object x) {
try {
IEnumerable result = childrenGetter.GetValueEx(x) as IEnumerable;
return !ObjectListView.IsEnumerableEmpty(result);
}
catch (MungerException ex) {
System.Diagnostics.Debug.WriteLine(ex);
return false;
}
};
tlv.ChildrenGetter = delegate(object x) {
try {
return childrenGetter.GetValueEx(x) as IEnumerable;
}
catch (MungerException ex) {
System.Diagnostics.Debug.WriteLine(ex);
return null;
}
};
}
#endregion
/*
#region Dynamic methods
/// <summary>
/// Generate methods so that reflection is not needed.
/// </summary>
/// <param name="olv"></param>
/// <param name="type"></param>
public static void GenerateMethods(ObjectListView olv, Type type) {
foreach (OLVColumn column in olv.Columns) {
GenerateColumnMethods(column, type);
}
}
public static void GenerateColumnMethods(OLVColumn column, Type type) {
if (column.AspectGetter == null && !String.IsNullOrEmpty(column.AspectName))
column.AspectGetter = Generator.GenerateAspectGetter(type, column.AspectName);
}
/// <summary>
/// Generates an aspect getter method dynamically. The method will execute
/// the given dotted chain of selectors against a model object given at runtime.
/// </summary>
/// <param name="type">The type of model object to be passed to the generated method</param>
/// <param name="path">A dotted chain of selectors. Each selector can be the name of a
/// field, property or parameter-less method.</param>
/// <returns>A typed delegate</returns>
/// <remarks>
/// <para>
/// If you have an AspectName of "Owner.Address.Postcode", this will generate
/// the equivilent of: <code>this.AspectGetter = delegate (object x) {
/// return x.Owner.Address.Postcode;
/// }
/// </code>
/// </para>
/// </remarks>
private static AspectGetterDelegate GenerateAspectGetter(Type type, string path) {
DynamicMethod getter = new DynamicMethod(String.Empty, typeof(Object), new Type[] { type }, type, true);
Generator.GenerateIL(type, path, getter.GetILGenerator());
return (AspectGetterDelegate)getter.CreateDelegate(typeof(AspectGetterDelegate));
}
/// <summary>
/// This method generates the actual IL for the method.
/// </summary>
/// <param name="type"></param>
/// <param name="path"></param>
/// <param name="il"></param>
private static void GenerateIL(Type modelType, string path, ILGenerator il) {
// Push our model object onto the stack
il.Emit(OpCodes.Ldarg_0);
OpCodes.Castclass
// Generate the IL to access each part of the dotted chain
Type type = modelType;
string[] parts = path.Split('.');
for (int i = 0; i < parts.Length; i++) {
type = Generator.GeneratePart(il, type, parts[i], (i == parts.Length - 1));
if (type == null)
break;
}
// If the object to be returned is a value type (e.g. int, bool), it
// must be boxed, since the delegate returns an Object
if (type != null && type.IsValueType && !modelType.IsValueType)
il.Emit(OpCodes.Box, type);
il.Emit(OpCodes.Ret);
}
private static Type GeneratePart(ILGenerator il, Type type, string pathPart, bool isLastPart) {
// TODO: Generate check for null
// Find the first member with the given nam that is a field, property, or parameter-less method
List<MemberInfo> infos = new List<MemberInfo>(type.GetMember(pathPart));
MemberInfo info = infos.Find(delegate(MemberInfo x) {
if (x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property)
return true;
if (x.MemberType == MemberTypes.Method)
return ((MethodInfo)x).GetParameters().Length == 0;
else
return false;
});
// If we couldn't find anything with that name, pop the current result and return an error
if (info == null) {
il.Emit(OpCodes.Pop);
il.Emit(OpCodes.Ldstr, String.Format("'{0}' is not a parameter-less method, property or field of type '{1}'", pathPart, type.FullName));
return null;
}
// Generate the correct IL to access the member. We remember the type of object that is going to be returned
// so that we can do a method lookup on it at the next iteration
Type resultType = null;
switch (info.MemberType) {
case MemberTypes.Method:
MethodInfo mi = (MethodInfo)info;
if (mi.IsVirtual)
il.Emit(OpCodes.Callvirt, mi);
else
il.Emit(OpCodes.Call, mi);
resultType = mi.ReturnType;
break;
case MemberTypes.Property:
PropertyInfo pi = (PropertyInfo)info;
il.Emit(OpCodes.Call, pi.GetGetMethod());
resultType = pi.PropertyType;
break;
case MemberTypes.Field:
FieldInfo fi = (FieldInfo)info;
il.Emit(OpCodes.Ldfld, fi);
resultType = fi.FieldType;
break;
}
// If the method returned a value type, and something is going to call a method on that value,
// we need to load its address onto the stack, rather than the object itself.
if (resultType.IsValueType && !isLastPart) {
LocalBuilder lb = il.DeclareLocal(resultType);
il.Emit(OpCodes.Stloc, lb);
il.Emit(OpCodes.Ldloca, lb);
}
return resultType;
}
#endregion
*/
}
}

277
Utilities/OLVExporter.cs Normal file
View File

@@ -0,0 +1,277 @@
/*
* OLVExporter - Export the contents of an ObjectListView into various text-based formats
*
* Author: Phillip Piper
* Date: 7 August 2012, 10:35pm
*
* Change log:
* 2012-08-07 JPP Initial code
*
* Copyright (C) 2012 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace BrightIdeasSoftware {
/// <summary>
/// An OLVExporter converts a collection of rows from an ObjectListView
/// into a variety of textual formats.
/// </summary>
public class OLVExporter {
/// <summary>
/// What format will be used for exporting
/// </summary>
public enum ExportFormat {
/// <summary>
/// Tab separated values, according to http://www.iana.org/assignments/media-types/text/tab-separated-values
/// </summary>
TabSeparated = 1,
/// <summary>
/// Alias for TabSeparated
/// </summary>
TSV = 1,
/// <summary>
/// Comma separated values, according to http://www.ietf.org/rfc/rfc4180.txt
/// </summary>
CSV,
/// <summary>
/// HTML table, according to me
/// </summary>
HTML
}
#region Life and death
/// <summary>
/// Create an empty exporter
/// </summary>
public OLVExporter() {}
/// <summary>
/// Create an exporter that will export all the rows of the given ObjectListView
/// </summary>
/// <param name="olv"></param>
public OLVExporter(ObjectListView olv) : this(olv, olv.Objects) {}
/// <summary>
/// Create an exporter that will export all the given rows from the given ObjectListView
/// </summary>
/// <param name="olv"></param>
/// <param name="objectsToExport"></param>
public OLVExporter(ObjectListView olv, IEnumerable objectsToExport) {
if (olv == null) throw new ArgumentNullException("olv");
if (objectsToExport == null) throw new ArgumentNullException("objectsToExport");
this.ListView = olv;
this.ModelObjects = ObjectListView.EnumerableToArray(objectsToExport, true);
}
#endregion
#region Properties
/// <summary>
/// Gets or sets whether hidden columns will also be included in the textual
/// representation. If this is false (the default), only visible columns will
/// be included.
/// </summary>
public bool IncludeHiddenColumns {
get { return includeHiddenColumns; }
set { includeHiddenColumns = value; }
}
private bool includeHiddenColumns;
/// <summary>
/// Gets or sets whether column headers will also be included in the text
/// and HTML representation. Default is true.
/// </summary>
public bool IncludeColumnHeaders {
get { return includeColumnHeaders; }
set { includeColumnHeaders = value; }
}
private bool includeColumnHeaders = true;
/// <summary>
/// Gets the ObjectListView that is being used as the source of the data
/// to be exported
/// </summary>
public ObjectListView ListView {
get { return objectListView; }
set { objectListView = value; }
}
private ObjectListView objectListView;
/// <summary>
/// Gets the model objects that are to be placed in the data object
/// </summary>
public IList ModelObjects {
get { return modelObjects; }
set { modelObjects = value; }
}
private IList modelObjects = new ArrayList();
#endregion
#region Commands
/// <summary>
/// Export the nominated rows from the nominated ObjectListView.
/// Returns the result in the expected format.
/// </summary>
/// <param name="format"></param>
/// <returns></returns>
/// <remarks>This will perform only one conversion, even if called multiple times with different formats.</remarks>
public string ExportTo(ExportFormat format) {
if (results == null)
this.Convert();
return results[format];
}
/// <summary>
/// Convert
/// </summary>
public void Convert() {
IList<OLVColumn> columns = this.IncludeHiddenColumns ? this.ListView.AllColumns : this.ListView.ColumnsInDisplayOrder;
StringBuilder sbText = new StringBuilder();
StringBuilder sbCsv = new StringBuilder();
StringBuilder sbHtml = new StringBuilder("<table>");
// Include column headers
if (this.IncludeColumnHeaders) {
List<string> strings = new List<string>();
foreach (OLVColumn col in columns)
strings.Add(col.Text);
WriteOneRow(sbText, strings, "", "\t", "", null);
WriteOneRow(sbHtml, strings, "<tr><td>", "</td><td>", "</td></tr>", HtmlEncode);
WriteOneRow(sbCsv, strings, "", ",", "", CsvEncode);
}
foreach (object modelObject in this.ModelObjects) {
List<string> strings = new List<string>();
foreach (OLVColumn col in columns)
strings.Add(col.GetStringValue(modelObject));
WriteOneRow(sbText, strings, "", "\t", "", null);
WriteOneRow(sbHtml, strings, "<tr><td>", "</td><td>", "</td></tr>", HtmlEncode);
WriteOneRow(sbCsv, strings, "", ",", "", CsvEncode);
}
sbHtml.AppendLine("</table>");
results = new Dictionary<ExportFormat, string>();
results[ExportFormat.TabSeparated] = sbText.ToString();
results[ExportFormat.CSV] = sbCsv.ToString();
results[ExportFormat.HTML] = sbHtml.ToString();
}
private delegate string StringToString(string str);
private void WriteOneRow(StringBuilder sb, IEnumerable<string> strings, string startRow, string betweenCells, string endRow, StringToString encoder) {
sb.Append(startRow);
bool first = true;
foreach (string s in strings) {
if (!first)
sb.Append(betweenCells);
sb.Append(encoder == null ? s : encoder(s));
first = false;
}
sb.AppendLine(endRow);
}
private Dictionary<ExportFormat, string> results;
#endregion
#region Encoding
/// <summary>
/// Encode a string such that it can be used as a value in a CSV file.
/// This basically means replacing any quote mark with two quote marks,
/// and enclosing the whole string in quotes.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private static string CsvEncode(string text) {
if (text == null)
return null;
const string DOUBLEQUOTE = @""""; // one double quote
const string TWODOUBEQUOTES = @""""""; // two double quotes
StringBuilder sb = new StringBuilder(DOUBLEQUOTE);
sb.Append(text.Replace(DOUBLEQUOTE, TWODOUBEQUOTES));
sb.Append(DOUBLEQUOTE);
return sb.ToString();
}
/// <summary>
/// HTML-encodes a string and returns the encoded string.
/// </summary>
/// <param name="text">The text string to encode. </param>
/// <returns>The HTML-encoded text.</returns>
/// <remarks>Taken from http://www.west-wind.com/weblog/posts/2009/Feb/05/Html-and-Uri-String-Encoding-without-SystemWeb</remarks>
private static string HtmlEncode(string text) {
if (text == null)
return null;
StringBuilder sb = new StringBuilder(text.Length);
int len = text.Length;
for (int i = 0; i < len; i++) {
switch (text[i]) {
case '<':
sb.Append("&lt;");
break;
case '>':
sb.Append("&gt;");
break;
case '"':
sb.Append("&quot;");
break;
case '&':
sb.Append("&amp;");
break;
default:
if (text[i] > 159) {
// decimal numeric entity
sb.Append("&#");
sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture));
sb.Append(";");
} else
sb.Append(text[i]);
break;
}
}
return sb.ToString();
}
#endregion
}
}

View File

@@ -0,0 +1,561 @@
/*
* TypedObjectListView - A wrapper around an ObjectListView that provides type-safe delegates.
*
* Author: Phillip Piper
* Date: 27/09/2008 9:15 AM
*
* Change log:
* v2.6
* 2012-10-26 JPP - Handle rare case where a null model object was passed into aspect getters.
* v2.3
* 2009-03-31 JPP - Added Objects property
* 2008-11-26 JPP - Added tool tip getting methods
* 2008-11-05 JPP - Added CheckState handling methods
* 2008-10-24 JPP - Generate dynamic methods MkII. This one handles value types
* 2008-10-21 JPP - Generate dynamic methods
* 2008-09-27 JPP - Separated from ObjectListView.cs
*
* Copyright (C) 2006-2014 Phillip Piper
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;
namespace BrightIdeasSoftware
{
/// <summary>
/// A TypedObjectListView is a type-safe wrapper around an ObjectListView.
/// </summary>
/// <remarks>
/// <para>VCS does not support generics on controls. It can be faked to some degree, but it
/// cannot be completely overcome. In our case in particular, there is no way to create
/// the custom OLVColumn's that we need to truly be generic. So this wrapper is an
/// experiment in providing some type-safe access in a way that is useful and available today.</para>
/// <para>A TypedObjectListView is not more efficient than a normal ObjectListView.
/// Underneath, the same name of casts are performed. But it is easier to use since you
/// do not have to write the casts yourself.
/// </para>
/// </remarks>
/// <typeparam name="T">The class of model object that the list will manage</typeparam>
/// <example>
/// To use a TypedObjectListView, you write code like this:
/// <code>
/// TypedObjectListView&lt;Person> tlist = new TypedObjectListView&lt;Person>(this.listView1);
/// tlist.CheckStateGetter = delegate(Person x) { return x.IsActive; };
/// tlist.GetColumn(0).AspectGetter = delegate(Person x) { return x.Name; };
/// ...
/// </code>
/// To iterate over the selected objects, you can write something elegant like this:
/// <code>
/// foreach (Person x in tlist.SelectedObjects) {
/// x.GrantSalaryIncrease();
/// }
/// </code>
/// </example>
public class TypedObjectListView<T> where T : class
{
/// <summary>
/// Create a typed wrapper around the given list.
/// </summary>
/// <param name="olv">The listview to be wrapped</param>
public TypedObjectListView(ObjectListView olv) {
this.olv = olv;
}
//--------------------------------------------------------------------------------------
// Properties
/// <summary>
/// Return the model object that is checked, if only one row is checked.
/// If zero rows are checked, or more than one row, null is returned.
/// </summary>
public virtual T CheckedObject {
get { return (T)this.olv.CheckedObject; }
}
/// <summary>
/// Return the list of all the checked model objects
/// </summary>
public virtual IList<T> CheckedObjects {
get {
IList checkedObjects = this.olv.CheckedObjects;
List<T> objects = new List<T>(checkedObjects.Count);
foreach (object x in checkedObjects)
objects.Add((T)x);
return objects;
}
set { this.olv.CheckedObjects = (IList)value; }
}
/// <summary>
/// The ObjectListView that is being wrapped
/// </summary>
public virtual ObjectListView ListView {
get { return olv; }
set { olv = value; }
}
private ObjectListView olv;
/// <summary>
/// Get or set the list of all model objects
/// </summary>
public virtual IList<T> Objects {
get {
List<T> objects = new List<T>(this.olv.GetItemCount());
for (int i = 0; i < this.olv.GetItemCount(); i++)
objects.Add(this.GetModelObject(i));
return objects;
}
set { this.olv.SetObjects(value); }
}
/// <summary>
/// Return the model object that is selected, if only one row is selected.
/// If zero rows are selected, or more than one row, null is returned.
/// </summary>
public virtual T SelectedObject {
get { return (T)this.olv.SelectedObject; }
set { this.olv.SelectedObject = value; }
}
/// <summary>
/// The list of model objects that are selected.
/// </summary>
public virtual IList<T> SelectedObjects {
get {
List<T> objects = new List<T>(this.olv.SelectedIndices.Count);
foreach (int index in this.olv.SelectedIndices)
objects.Add((T)this.olv.GetModelObject(index));
return objects;
}
set { this.olv.SelectedObjects = (IList)value; }
}
//--------------------------------------------------------------------------------------
// Accessors
/// <summary>
/// Return a typed wrapper around the column at the given index
/// </summary>
/// <param name="i">The index of the column</param>
/// <returns>A typed column or null</returns>
public virtual TypedColumn<T> GetColumn(int i) {
return new TypedColumn<T>(this.olv.GetColumn(i));
}
/// <summary>
/// Return a typed wrapper around the column with the given name
/// </summary>
/// <param name="name">The name of the column</param>
/// <returns>A typed column or null</returns>
public virtual TypedColumn<T> GetColumn(string name) {
return new TypedColumn<T>(this.olv.GetColumn(name));
}
/// <summary>
/// Return the model object at the given index
/// </summary>
/// <param name="index">The index of the model object</param>
/// <returns>The model object or null</returns>
public virtual T GetModelObject(int index) {
return (T)this.olv.GetModelObject(index);
}
//--------------------------------------------------------------------------------------
// Delegates
/// <summary>
/// CheckStateGetter
/// </summary>
/// <param name="rowObject"></param>
/// <returns></returns>
public delegate CheckState TypedCheckStateGetterDelegate(T rowObject);
/// <summary>
/// Gets or sets the check state getter
/// </summary>
public virtual TypedCheckStateGetterDelegate CheckStateGetter {
get { return checkStateGetter; }
set {
this.checkStateGetter = value;
if (value == null)
this.olv.CheckStateGetter = null;
else
this.olv.CheckStateGetter = delegate(object x) {
return this.checkStateGetter((T)x);
};
}
}
private TypedCheckStateGetterDelegate checkStateGetter;
/// <summary>
/// BooleanCheckStateGetter
/// </summary>
/// <param name="rowObject"></param>
/// <returns></returns>
public delegate bool TypedBooleanCheckStateGetterDelegate(T rowObject);
/// <summary>
/// Gets or sets the boolean check state getter
/// </summary>
public virtual TypedBooleanCheckStateGetterDelegate BooleanCheckStateGetter {
set {
if (value == null)
this.olv.BooleanCheckStateGetter = null;
else
this.olv.BooleanCheckStateGetter = delegate(object x) {
return value((T)x);
};
}
}
/// <summary>
/// CheckStatePutter
/// </summary>
/// <param name="rowObject"></param>
/// <param name="newValue"></param>
/// <returns></returns>
public delegate CheckState TypedCheckStatePutterDelegate(T rowObject, CheckState newValue);
/// <summary>
/// Gets or sets the check state putter delegate
/// </summary>
public virtual TypedCheckStatePutterDelegate CheckStatePutter {
get { return checkStatePutter; }
set {
this.checkStatePutter = value;
if (value == null)
this.olv.CheckStatePutter = null;
else
this.olv.CheckStatePutter = delegate(object x, CheckState newValue) {
return this.checkStatePutter((T)x, newValue);
};
}
}
private TypedCheckStatePutterDelegate checkStatePutter;
/// <summary>
/// BooleanCheckStatePutter
/// </summary>
/// <param name="rowObject"></param>
/// <param name="newValue"></param>
/// <returns></returns>
public delegate bool TypedBooleanCheckStatePutterDelegate(T rowObject, bool newValue);
/// <summary>
/// Gets or sets the boolean check state putter
/// </summary>
public virtual TypedBooleanCheckStatePutterDelegate BooleanCheckStatePutter {
set {
if (value == null)
this.olv.BooleanCheckStatePutter = null;
else
this.olv.BooleanCheckStatePutter = delegate(object x, bool newValue) {
return value((T)x, newValue);
};
}
}
/// <summary>
/// ToolTipGetter
/// </summary>
/// <param name="column"></param>
/// <param name="modelObject"></param>
/// <returns></returns>
public delegate String TypedCellToolTipGetterDelegate(OLVColumn column, T modelObject);
/// <summary>
/// Gets or sets the cell tooltip getter
/// </summary>
public virtual TypedCellToolTipGetterDelegate CellToolTipGetter {
set {
if (value == null)
this.olv.CellToolTipGetter = null;
else
this.olv.CellToolTipGetter = delegate(OLVColumn col, Object x) {
return value(col, (T)x);
};
}
}
/// <summary>
/// Gets or sets the header tool tip getter
/// </summary>
public virtual HeaderToolTipGetterDelegate HeaderToolTipGetter {
get { return this.olv.HeaderToolTipGetter; }
set { this.olv.HeaderToolTipGetter = value; }
}
//--------------------------------------------------------------------------------------
// Commands
/// <summary>
/// This method will generate AspectGetters for any column that has an AspectName.
/// </summary>
public virtual void GenerateAspectGetters() {
for (int i = 0; i < this.ListView.Columns.Count; i++)
this.GetColumn(i).GenerateAspectGetter();
}
}
/// <summary>
/// A type-safe wrapper around an OLVColumn
/// </summary>
/// <typeparam name="T"></typeparam>
public class TypedColumn<T> where T : class
{
/// <summary>
/// Creates a TypedColumn
/// </summary>
/// <param name="column"></param>
public TypedColumn(OLVColumn column) {
this.column = column;
}
private OLVColumn column;
/// <summary>
///
/// </summary>
/// <param name="rowObject"></param>
/// <returns></returns>
public delegate Object TypedAspectGetterDelegate(T rowObject);
/// <summary>
///
/// </summary>
/// <param name="rowObject"></param>
/// <param name="newValue"></param>
public delegate void TypedAspectPutterDelegate(T rowObject, Object newValue);
/// <summary>
///
/// </summary>
/// <param name="rowObject"></param>
/// <returns></returns>
public delegate Object TypedGroupKeyGetterDelegate(T rowObject);
/// <summary>
///
/// </summary>
/// <param name="rowObject"></param>
/// <returns></returns>
public delegate Object TypedImageGetterDelegate(T rowObject);
/// <summary>
///
/// </summary>
public TypedAspectGetterDelegate AspectGetter {
get { return this.aspectGetter; }
set {
this.aspectGetter = value;
if (value == null)
this.column.AspectGetter = null;
else
this.column.AspectGetter = delegate(object x) {
return x == null ? null : this.aspectGetter((T)x);
};
}
}
private TypedAspectGetterDelegate aspectGetter;
/// <summary>
///
/// </summary>
public TypedAspectPutterDelegate AspectPutter {
get { return aspectPutter; }
set {
this.aspectPutter = value;
if (value == null)
this.column.AspectPutter = null;
else
this.column.AspectPutter = delegate(object x, object newValue) {
this.aspectPutter((T)x, newValue);
};
}
}
private TypedAspectPutterDelegate aspectPutter;
/// <summary>
///
/// </summary>
public TypedImageGetterDelegate ImageGetter {
get { return imageGetter; }
set {
this.imageGetter = value;
if (value == null)
this.column.ImageGetter = null;
else
this.column.ImageGetter = delegate(object x) {
return this.imageGetter((T)x);
};
}
}
private TypedImageGetterDelegate imageGetter;
/// <summary>
///
/// </summary>
public TypedGroupKeyGetterDelegate GroupKeyGetter {
get { return groupKeyGetter; }
set {
this.groupKeyGetter = value;
if (value == null)
this.column.GroupKeyGetter = null;
else
this.column.GroupKeyGetter = delegate(object x) {
return this.groupKeyGetter((T)x);
};
}
}
private TypedGroupKeyGetterDelegate groupKeyGetter;
#region Dynamic methods
/// <summary>
/// Generate an aspect getter that does the same thing as the AspectName,
/// except without using reflection.
/// </summary>
/// <remarks>
/// <para>
/// If you have an AspectName of "Owner.Address.Postcode", this will generate
/// the equivilent of: <code>this.AspectGetter = delegate (object x) {
/// return x.Owner.Address.Postcode;
/// }
/// </code>
/// </para>
/// <para>
/// If AspectName is empty, this method will do nothing, otherwise
/// this will replace any existing AspectGetter.
/// </para>
/// </remarks>
public void GenerateAspectGetter() {
if (!String.IsNullOrEmpty(this.column.AspectName))
this.AspectGetter = this.GenerateAspectGetter(typeof(T), this.column.AspectName);
}
/// <summary>
/// Generates an aspect getter method dynamically. The method will execute
/// the given dotted chain of selectors against a model object given at runtime.
/// </summary>
/// <param name="type">The type of model object to be passed to the generated method</param>
/// <param name="path">A dotted chain of selectors. Each selector can be the name of a
/// field, property or parameter-less method.</param>
/// <returns>A typed delegate</returns>
private TypedAspectGetterDelegate GenerateAspectGetter(Type type, string path) {
DynamicMethod getter = new DynamicMethod(String.Empty,
typeof(Object), new Type[] { type }, type, true);
this.GenerateIL(type, path, getter.GetILGenerator());
return (TypedAspectGetterDelegate)getter.CreateDelegate(typeof(TypedAspectGetterDelegate));
}
/// <summary>
/// This method generates the actual IL for the method.
/// </summary>
/// <param name="type"></param>
/// <param name="path"></param>
/// <param name="il"></param>
private void GenerateIL(Type type, string path, ILGenerator il) {
// Push our model object onto the stack
il.Emit(OpCodes.Ldarg_0);
// Generate the IL to access each part of the dotted chain
string[] parts = path.Split('.');
for (int i = 0; i < parts.Length; i++) {
type = this.GeneratePart(il, type, parts[i], (i == parts.Length - 1));
if (type == null)
break;
}
// If the object to be returned is a value type (e.g. int, bool), it
// must be boxed, since the delegate returns an Object
if (type != null && type.IsValueType && !typeof(T).IsValueType)
il.Emit(OpCodes.Box, type);
il.Emit(OpCodes.Ret);
}
private Type GeneratePart(ILGenerator il, Type type, string pathPart, bool isLastPart) {
// TODO: Generate check for null
// Find the first member with the given nam that is a field, property, or parameter-less method
List<MemberInfo> infos = new List<MemberInfo>(type.GetMember(pathPart));
MemberInfo info = infos.Find(delegate(MemberInfo x) {
if (x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property)
return true;
if (x.MemberType == MemberTypes.Method)
return ((MethodInfo)x).GetParameters().Length == 0;
else
return false;
});
// If we couldn't find anything with that name, pop the current result and return an error
if (info == null) {
il.Emit(OpCodes.Pop);
if (Munger.IgnoreMissingAspects)
il.Emit(OpCodes.Ldnull);
else
il.Emit(OpCodes.Ldstr, String.Format("'{0}' is not a parameter-less method, property or field of type '{1}'", pathPart, type.FullName));
return null;
}
// Generate the correct IL to access the member. We remember the type of object that is going to be returned
// so that we can do a method lookup on it at the next iteration
Type resultType = null;
switch (info.MemberType) {
case MemberTypes.Method:
MethodInfo mi = (MethodInfo)info;
if (mi.IsVirtual)
il.Emit(OpCodes.Callvirt, mi);
else
il.Emit(OpCodes.Call, mi);
resultType = mi.ReturnType;
break;
case MemberTypes.Property:
PropertyInfo pi = (PropertyInfo)info;
il.Emit(OpCodes.Call, pi.GetGetMethod());
resultType = pi.PropertyType;
break;
case MemberTypes.Field:
FieldInfo fi = (FieldInfo)info;
il.Emit(OpCodes.Ldfld, fi);
resultType = fi.FieldType;
break;
}
// If the method returned a value type, and something is going to call a method on that value,
// we need to load its address onto the stack, rather than the object itself.
if (resultType.IsValueType && !isLastPart) {
LocalBuilder lb = il.DeclareLocal(resultType);
il.Emit(OpCodes.Stloc, lb);
il.Emit(OpCodes.Ldloca, lb);
}
return resultType;
}
#endregion
}
}