Imported existing sources
This commit is contained in:
190
Utilities/ColumnSelectionForm.Designer.cs
generated
Normal file
190
Utilities/ColumnSelectionForm.Designer.cs
generated
Normal 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;
|
||||
}
|
||||
}
|
263
Utilities/ColumnSelectionForm.cs
Normal file
263
Utilities/ColumnSelectionForm.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
120
Utilities/ColumnSelectionForm.resx
Normal file
120
Utilities/ColumnSelectionForm.resx
Normal 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
561
Utilities/Generator.cs
Normal 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
277
Utilities/OLVExporter.cs
Normal 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("<");
|
||||
break;
|
||||
case '>':
|
||||
sb.Append(">");
|
||||
break;
|
||||
case '"':
|
||||
sb.Append(""");
|
||||
break;
|
||||
case '&':
|
||||
sb.Append("&");
|
||||
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
|
||||
}
|
||||
}
|
561
Utilities/TypedObjectListView.cs
Normal file
561
Utilities/TypedObjectListView.cs
Normal 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<Person> tlist = new TypedObjectListView<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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user