├── .gitignore ├── FastWpfGrid.sln ├── FastWpfGrid.v11.suo ├── FastWpfGrid ├── ActiveSeries.cs ├── DataTableGridModel.cs ├── EventArgs.cs ├── ExplicitColumnDefinition.cs ├── FastGridCellAddress.cs ├── FastGridCellImpl.cs ├── FastGridControl.xaml ├── FastGridControl.xaml.cs ├── FastGridControl_Arrange.cs ├── FastGridControl_DependencyProps.cs ├── FastGridControl_Input.cs ├── FastGridControl_Invalidation.cs ├── FastGridControl_Render.cs ├── FastGridControl_Selection.cs ├── FastGridControl_StyleProps.cs ├── FastGridModelBase.cs ├── FastWpfGrid.csproj ├── IFastGridCell.cs ├── IFastGridCellBlock.cs ├── IFastGridModel.cs ├── IFastGridView.cs ├── ImageHolder.cs ├── Properties │ └── AssemblyInfo.cs ├── SelectionChangedEventArgs.cs ├── SelectionQuickCommand.cs └── SeriesSizes.cs ├── FastWpfGridTest ├── App.config ├── App.xaml ├── App.xaml.cs ├── FastWpfGridTest.csproj ├── GridModel1.cs ├── GridModel2.cs ├── GridModel3.cs ├── Images │ ├── flip_horizontal_small.png │ ├── flip_transform_small.png │ ├── flip_vertical_small.png │ ├── foreign_keysmall.png │ ├── grid1.png │ ├── grid2.png │ └── primary_keysmall.png ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── TextRenderPanel.cs ├── FastWpfGridUnitTest ├── FastWpfGridUnitTest.csproj ├── GridTest.cs └── Properties │ └── AssemblyInfo.cs ├── LICENSE ├── README.md └── WriteableBitmapEx ├── BitmapContext.cs ├── BitmapFactory.cs ├── ClearTypeLetterGlyph.cs ├── DpiDetector.cs ├── GrayScaleLetterGlyph.cs ├── IntGeometry.cs ├── LetterGlyphTool.cs ├── NativeMethods.cs ├── PortableFontDesc.cs ├── Properties ├── AssemblyInfo.cs └── WBX_key.snk ├── ScrollingTool.cs ├── WriteableBitmapAntialiasingExtensions.cs ├── WriteableBitmapBaseExtensions.cs ├── WriteableBitmapBlitExtensions.cs ├── WriteableBitmapContextExtensions.cs ├── WriteableBitmapConvertExtensions.cs ├── WriteableBitmapEx.Wpf.csproj ├── WriteableBitmapFillExtensions.cs ├── WriteableBitmapFilterExtensions.cs ├── WriteableBitmapLineExtensions.cs ├── WriteableBitmapShapeExtensions.cs ├── WriteableBitmapSplineExtensions.cs └── WriteableBitmapTransformationExtensions.cs /.gitignore: -------------------------------------------------------------------------------- 1 | /_ReSharper.FastWpfGrid 2 | bin 3 | obj 4 | -------------------------------------------------------------------------------- /FastWpfGrid.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastWpfGrid", "FastWpfGrid\FastWpfGrid.csproj", "{E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastWpfGridTest", "FastWpfGridTest\FastWpfGridTest.csproj", "{A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WriteableBitmapEx.Wpf", "WriteableBitmapEx\WriteableBitmapEx.Wpf.csproj", "{B0AA6A94-6784-4221-81F0-244A68C374C0}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastWpfGridUnitTest", "FastWpfGridUnitTest\FastWpfGridUnitTest.csproj", "{B94371DD-DDC9-4889-B49F-7D76E3E52B01}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {B0AA6A94-6784-4221-81F0-244A68C374C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {B0AA6A94-6784-4221-81F0-244A68C374C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {B0AA6A94-6784-4221-81F0-244A68C374C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {B0AA6A94-6784-4221-81F0-244A68C374C0}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {B94371DD-DDC9-4889-B49F-7D76E3E52B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {B94371DD-DDC9-4889-B49F-7D76E3E52B01}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {B94371DD-DDC9-4889-B49F-7D76E3E52B01}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {B94371DD-DDC9-4889-B49F-7D76E3E52B01}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /FastWpfGrid.v11.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGrid.v11.suo -------------------------------------------------------------------------------- /FastWpfGrid/ActiveSeries.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class ActiveSeries 10 | { 11 | public HashSet ScrollVisible = new HashSet(); 12 | public HashSet Selected = new HashSet(); 13 | public HashSet Frozen = new HashSet(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /FastWpfGrid/DataTableGridModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace FastWpfGrid 9 | { 10 | public class DataTableGridModel : FastGridModelBase 11 | { 12 | private DataTable _dataSource; 13 | public List ExplicitColumns; 14 | 15 | public override int ColumnCount 16 | { 17 | get 18 | { 19 | if (ExplicitColumns != null) return ExplicitColumns.Count; 20 | if (_dataSource != null) return _dataSource.Columns.Count; 21 | return 0; 22 | } 23 | } 24 | 25 | public override int RowCount 26 | { 27 | get { return _dataSource != null ? _dataSource.Rows.Count : 0; } 28 | } 29 | 30 | public override string GetCellText(int row, int column) 31 | { 32 | if (_dataSource != null && row < _dataSource.Rows.Count) 33 | { 34 | if (ExplicitColumns != null) 35 | { 36 | if (column < ExplicitColumns.Count) 37 | { 38 | object value = _dataSource.Rows[row][ExplicitColumns[column].DataField]; 39 | if (value != null) return value.ToString(); 40 | } 41 | } 42 | else 43 | { 44 | object value = _dataSource.Rows[row][column]; 45 | if (value != null) return value.ToString(); 46 | } 47 | } 48 | return ""; 49 | } 50 | 51 | public override string GetColumnHeaderText(int column) 52 | { 53 | if (ExplicitColumns != null) 54 | { 55 | if (column < ExplicitColumns.Count) return ExplicitColumns[column].HeaderText; 56 | return ""; 57 | } 58 | if (_dataSource != null && column < _dataSource.Columns.Count) 59 | { 60 | return _dataSource.Columns[column].ColumnName; 61 | } 62 | return ""; 63 | } 64 | 65 | public DataTable DataSource 66 | { 67 | get { return _dataSource; } 68 | set 69 | { 70 | _dataSource = value; 71 | NotifyRefresh(); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /FastWpfGrid/EventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class RowClickEventArgs : EventArgs 10 | { 11 | public int Row; 12 | public FastGridControl Grid; 13 | public bool Handled; 14 | } 15 | 16 | public class ColumnClickEventArgs : EventArgs 17 | { 18 | public int Column; 19 | public FastGridControl Grid; 20 | public bool Handled; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FastWpfGrid/ExplicitColumnDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class ExplicitColumnDefinition 10 | { 11 | public string DataField; 12 | public string HeaderText; 13 | 14 | public ExplicitColumnDefinition(string dataField, string headerText) 15 | { 16 | DataField = dataField; 17 | HeaderText = headerText; 18 | } 19 | 20 | public ExplicitColumnDefinition(string name) 21 | { 22 | DataField = name; 23 | HeaderText = name; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridCellAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public struct FastGridCellAddress 10 | { 11 | public static readonly FastGridCellAddress Empty = new FastGridCellAddress(); 12 | public static readonly FastGridCellAddress GridHeader = new FastGridCellAddress(null, null, true); 13 | 14 | public bool Equals(FastGridCellAddress other) 15 | { 16 | return Row == other.Row && Column == other.Column; 17 | } 18 | 19 | public override bool Equals(object obj) 20 | { 21 | if (ReferenceEquals(null, obj)) return false; 22 | return obj is FastGridCellAddress && Equals((FastGridCellAddress) obj); 23 | } 24 | 25 | public override int GetHashCode() 26 | { 27 | unchecked 28 | { 29 | return (Row.GetHashCode()*397) ^ Column.GetHashCode(); 30 | } 31 | } 32 | 33 | public readonly int? Row; 34 | public readonly int? Column; 35 | public bool IsGridHeader; 36 | 37 | public FastGridCellAddress(int? row, int? col, bool isGridHeader = false) 38 | { 39 | Row = row; 40 | Column = col; 41 | IsGridHeader = isGridHeader; 42 | } 43 | 44 | public FastGridCellAddress ChangeRow(int? row) 45 | { 46 | return new FastGridCellAddress(row, Column, IsGridHeader); 47 | } 48 | 49 | public FastGridCellAddress ChangeColumn(int? col) 50 | { 51 | return new FastGridCellAddress(Row, col, IsGridHeader); 52 | } 53 | 54 | public bool IsCell 55 | { 56 | get { return Row.HasValue && Column.HasValue; } 57 | } 58 | 59 | public bool IsRowHeader 60 | { 61 | get { return Row.HasValue && !Column.HasValue; } 62 | } 63 | 64 | public bool IsColumnHeader 65 | { 66 | get { return Column.HasValue && !Row.HasValue; } 67 | } 68 | 69 | public bool IsEmpty 70 | { 71 | get { return Row == null && Column == null && !IsGridHeader; } 72 | } 73 | 74 | public bool TestCell(int row, int col) 75 | { 76 | return row == Row && col == Column; 77 | } 78 | 79 | public static bool operator ==(FastGridCellAddress a, FastGridCellAddress b) 80 | { 81 | return a.Row == b.Row && a.Column == b.Column && a.IsGridHeader == b.IsGridHeader; 82 | } 83 | 84 | public static bool operator !=(FastGridCellAddress a, FastGridCellAddress b) 85 | { 86 | return !(a == b); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridCellImpl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | 8 | namespace FastWpfGrid 9 | { 10 | public class FastGridBlockImpl : IFastGridCellBlock 11 | { 12 | public FastGridBlockType BlockType { get; set; } 13 | public Color? FontColor { get; set; } 14 | public bool IsItalic { get; set; } 15 | public bool IsBold { get; set; } 16 | public string TextData { get; set; } 17 | public string ImageSource { get; set; } 18 | public int ImageWidth { get; set; } 19 | public int ImageHeight { get; set; } 20 | public MouseHoverBehaviours MouseHoverBehaviour { get; set; } 21 | public object CommandParameter { get; set; } 22 | public string ToolTip { get; set; } 23 | 24 | public FastGridBlockImpl() 25 | { 26 | MouseHoverBehaviour = MouseHoverBehaviours.ShowAllWhenMouseOut; 27 | } 28 | } 29 | 30 | public class FastGridCellImpl : IFastGridCell 31 | { 32 | public Color? BackgroundColor { get; set; } 33 | public CellDecoration Decoration { get; set; } 34 | public Color? DecorationColor { get; set; } 35 | public string ToolTipText { get; set; } 36 | public TooltipVisibilityMode ToolTipVisibility { get; set; } 37 | 38 | public List Blocks = new List(); 39 | 40 | public int BlockCount 41 | { 42 | get { return Blocks.Count; } 43 | } 44 | 45 | public int RightAlignBlockCount { get; set; } 46 | 47 | public IFastGridCellBlock GetBlock(int blockIndex) 48 | { 49 | return Blocks[blockIndex]; 50 | } 51 | 52 | public string GetEditText() 53 | { 54 | return null; 55 | } 56 | 57 | public void SetEditText(string value) 58 | { 59 | } 60 | 61 | public IEnumerable SetBlocks 62 | { 63 | set 64 | { 65 | Blocks.Clear(); 66 | Blocks.AddRange(value); 67 | } 68 | } 69 | 70 | public FastGridBlockImpl AddImageBlock(string image, int width = 16, int height = 16) 71 | { 72 | var res = new FastGridBlockImpl 73 | { 74 | BlockType = FastGridBlockType.Image, 75 | ImageWidth = width, 76 | ImageHeight = height, 77 | ImageSource = image, 78 | }; 79 | Blocks.Add(res); 80 | return res; 81 | } 82 | 83 | public FastGridBlockImpl AddTextBlock(object text) 84 | { 85 | var res = new FastGridBlockImpl 86 | { 87 | BlockType = FastGridBlockType.Text, 88 | TextData = text == null ? null : text.ToString(), 89 | }; 90 | Blocks.Add(res); 91 | return res; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridControl.xaml: -------------------------------------------------------------------------------- 1 |  8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridControl_DependencyProps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace FastWpfGrid 9 | { 10 | partial class FastGridControl 11 | { 12 | #region property Model 13 | 14 | public IFastGridModel Model 15 | { 16 | get { return (IFastGridModel) this.GetValue(ModelProperty); } 17 | set { this.SetValue(ModelProperty, value); } 18 | } 19 | 20 | public static readonly DependencyProperty ModelProperty = DependencyProperty.Register( 21 | "Model", typeof (IFastGridModel), typeof (FastGridControl), new PropertyMetadata(null, OnModelPropertyChanged)); 22 | 23 | private static void OnModelPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 24 | { 25 | ((FastGridControl) dependencyObject).OnModelPropertyChanged(); 26 | } 27 | 28 | #endregion 29 | 30 | #region property IsTransposed 31 | 32 | public bool IsTransposed 33 | { 34 | get { return (bool)this.GetValue(IsTransposedProperty); } 35 | set { this.SetValue(IsTransposedProperty, value); } 36 | } 37 | 38 | public static readonly DependencyProperty IsTransposedProperty = DependencyProperty.Register( 39 | "IsTransposed", typeof(bool), typeof(FastGridControl), new PropertyMetadata(false, OnIsTransposedPropertyChanged)); 40 | 41 | private static void OnIsTransposedPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 42 | { 43 | ((FastGridControl)dependencyObject).OnIsTransposedPropertyChanged(); 44 | } 45 | 46 | #endregion 47 | 48 | #region property UseClearType 49 | 50 | public bool UseClearType 51 | { 52 | get { return (bool)this.GetValue(UseClearTypeProperty); } 53 | set { this.SetValue(UseClearTypeProperty, value); } 54 | } 55 | 56 | public static readonly DependencyProperty UseClearTypeProperty = DependencyProperty.Register( 57 | "UseClearType", typeof(bool), typeof(FastGridControl), new PropertyMetadata(true, OnUseClearTypePropertyChanged)); 58 | 59 | private static void OnUseClearTypePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 60 | { 61 | ((FastGridControl)dependencyObject).OnUseClearTypePropertyChanged(); 62 | } 63 | 64 | #endregion 65 | 66 | #region property AllowFlexibleRows 67 | 68 | public bool AllowFlexibleRows 69 | { 70 | get { return (bool)this.GetValue(AllowFlexibleRowsProperty); } 71 | set { this.SetValue(AllowFlexibleRowsProperty, value); } 72 | } 73 | 74 | public static readonly DependencyProperty AllowFlexibleRowsProperty = DependencyProperty.Register( 75 | "AllowFlexibleRows", typeof(bool), typeof(FastGridControl), new PropertyMetadata(false, OnAllowFlexibleRowsPropertyChanged)); 76 | 77 | private static void OnAllowFlexibleRowsPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 78 | { 79 | ((FastGridControl)dependencyObject).OnAllowFlexibleRowsPropertyChanged(); 80 | } 81 | 82 | #endregion 83 | 84 | } 85 | } -------------------------------------------------------------------------------- /FastWpfGrid/FastGridControl_Invalidation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Threading; 7 | 8 | namespace FastWpfGrid 9 | { 10 | partial class FastGridControl 11 | { 12 | private bool _isInvalidated; 13 | private bool _isInvalidatedAll; 14 | private bool _InvalidatedGridHeader; 15 | private List _invalidatedRows = new List(); 16 | private List _invalidatedColumns = new List(); 17 | private List> _invalidatedCells = new List>(); 18 | private List _invalidatedRowHeaders = new List(); 19 | private List _invalidatedColumnHeaders = new List(); 20 | 21 | private class InvalidationContext : IDisposable 22 | { 23 | private FastGridControl _grid; 24 | 25 | internal InvalidationContext(FastGridControl grid) 26 | { 27 | _grid = grid; 28 | _grid.EnterInvalidation(); 29 | } 30 | 31 | public void Dispose() 32 | { 33 | _grid.LeaveInvalidation(); 34 | } 35 | } 36 | 37 | private int _invalidationCount; 38 | 39 | private void LeaveInvalidation() 40 | { 41 | _invalidationCount--; 42 | if (_invalidationCount == 0) 43 | { 44 | if (_isInvalidated) 45 | { 46 | RenderGrid(); 47 | } 48 | } 49 | } 50 | 51 | private void EnterInvalidation() 52 | { 53 | _invalidationCount++; 54 | } 55 | 56 | private InvalidationContext CreateInvalidationContext() 57 | { 58 | return new InvalidationContext(this); 59 | } 60 | 61 | private void CheckInvalidation() 62 | { 63 | if (_isInvalidated) return; 64 | if (_invalidationCount > 0) return; 65 | Dispatcher.BeginInvoke(DispatcherPriority.Render, (Action) RenderInvoked); 66 | } 67 | 68 | private void RenderInvoked() 69 | { 70 | if (!_isInvalidated) return; 71 | RenderGrid(); 72 | } 73 | 74 | public void InvalidateAll() 75 | { 76 | CheckInvalidation(); 77 | _isInvalidatedAll = true; 78 | _isInvalidated = true; 79 | } 80 | 81 | public void InvalidateRowHeader(int row) 82 | { 83 | CheckInvalidation(); 84 | _isInvalidated = true; 85 | _invalidatedRowHeaders.Add(row); 86 | } 87 | 88 | public void InvalidateColumnHeader(int column) 89 | { 90 | CheckInvalidation(); 91 | _isInvalidated = true; 92 | _invalidatedColumnHeaders.Add(column); 93 | } 94 | 95 | public void InvalidateColumn(int column) 96 | { 97 | CheckInvalidation(); 98 | _isInvalidated = true; 99 | _invalidatedColumns.Add(column); 100 | _invalidatedColumnHeaders.Add(column); 101 | } 102 | 103 | public void InvalidateRow(int row) 104 | { 105 | CheckInvalidation(); 106 | _isInvalidated = true; 107 | _invalidatedRows.Add(row); 108 | _invalidatedRowHeaders.Add(row); 109 | } 110 | 111 | public void InvalidateCell(int row, int column) 112 | { 113 | CheckInvalidation(); 114 | _isInvalidated = true; 115 | _invalidatedCells.Add(Tuple.Create(row, column)); 116 | } 117 | 118 | public void InvalidateGridHeader() 119 | { 120 | CheckInvalidation(); 121 | _isInvalidated = true; 122 | _InvalidatedGridHeader = true; 123 | } 124 | 125 | public void InvalidateCell(FastGridCellAddress cell) 126 | { 127 | if (cell.IsEmpty) return; 128 | if (cell.IsGridHeader) 129 | { 130 | InvalidateGridHeader(); 131 | return; 132 | } 133 | if (cell.IsRowHeader) 134 | { 135 | InvalidateRowHeader(cell.Row.Value); 136 | return; 137 | } 138 | if (cell.IsColumnHeader) 139 | { 140 | InvalidateColumnHeader(cell.Column.Value); 141 | return; 142 | } 143 | InvalidateCell(cell.Row.Value, cell.Column.Value); 144 | } 145 | 146 | private void ClearInvalidation() 147 | { 148 | _invalidatedRows.Clear(); 149 | _invalidatedColumns.Clear(); 150 | _invalidatedCells.Clear(); 151 | _invalidatedColumnHeaders.Clear(); 152 | _invalidatedRowHeaders.Clear(); 153 | _isInvalidated = false; 154 | _isInvalidatedAll = false; 155 | _InvalidatedGridHeader = false; 156 | } 157 | 158 | private bool ShouldDrawCell(int row, int column) 159 | { 160 | if (!_isInvalidated || _isInvalidatedAll) return true; 161 | 162 | if (_invalidatedRows.Contains(row)) return true; 163 | if (_invalidatedColumns.Contains(column)) return true; 164 | if (_invalidatedCells.Contains(Tuple.Create(row, column))) return true; 165 | return false; 166 | } 167 | 168 | private bool ShouldDrawRowHeader(int row) 169 | { 170 | if (!_isInvalidated || _isInvalidatedAll) return true; 171 | 172 | if (_invalidatedRows.Contains(row)) return true; 173 | if (_invalidatedRowHeaders.Contains(row)) return true; 174 | return false; 175 | } 176 | 177 | private bool ShouldDrawColumnHeader(int column) 178 | { 179 | if (!_isInvalidated || _isInvalidatedAll) return true; 180 | 181 | if (_invalidatedColumns.Contains(column)) return true; 182 | if (_invalidatedColumnHeaders.Contains(column)) return true; 183 | return false; 184 | } 185 | 186 | private bool ShouldDrawGridHeader() 187 | { 188 | if (!_isInvalidated || _isInvalidatedAll) return true; 189 | return _InvalidatedGridHeader; 190 | } 191 | 192 | public void InvalidateModelCell(int row, int column) 193 | { 194 | InvalidateCell(ModelToReal(new FastGridCellAddress(row, column))); 195 | } 196 | 197 | public void InvalidateModelRowHeader(int row) 198 | { 199 | InvalidateCell(ModelToReal(new FastGridCellAddress(row, null))); 200 | } 201 | public void InvalidateModelRow(int row) 202 | { 203 | if (IsTransposed) 204 | { 205 | InvalidateColumn(_columnSizes.ModelToReal(row)); 206 | } 207 | else 208 | { 209 | InvalidateRow(_rowSizes.ModelToReal(row)); 210 | } 211 | } 212 | public void InvalidateModelColumnHeader(int column) 213 | { 214 | InvalidateCell(ModelToReal(new FastGridCellAddress(null, column))); 215 | } 216 | public void InvalidateModelColumn(int column) 217 | { 218 | if (IsTransposed) 219 | { 220 | InvalidateRow(_rowSizes.ModelToReal(column)); 221 | } 222 | else 223 | { 224 | InvalidateColumn(_columnSizes.ModelToReal(column)); 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridControl_Selection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace FastWpfGrid 10 | { 11 | partial class FastGridControl 12 | { 13 | public event EventHandler SelectedCellsChanged; 14 | 15 | private HashSet _selectedCells = new HashSet(); 16 | private Dictionary _selectedRows = new Dictionary(); 17 | private Dictionary _selectedColumns = new Dictionary(); 18 | 19 | int? _selectedRealRowCountLimit; 20 | bool _selectedRealRowCountLimitLoaded; 21 | public int? SelectedRealRowCountLimit 22 | { 23 | get 24 | { 25 | if (_selectedRealRowCountLimitLoaded) return _selectedRealRowCountLimit; 26 | _selectedRealRowCountLimitLoaded = true; 27 | if (Model == null) 28 | { 29 | _selectedRealRowCountLimit = null; 30 | } 31 | else 32 | { 33 | _selectedRealRowCountLimit = IsTransposed ? Model.SelectedColumnCountLimit : Model.SelectedRowCountLimit; 34 | } 35 | return _selectedRealRowCountLimit; 36 | } 37 | } 38 | 39 | int? _selectedRealColumnCountLimit; 40 | bool _selectedRealColumnCountLimitLoaded; 41 | public int? SelectedRealColumnCountLimit 42 | { 43 | get 44 | { 45 | if (_selectedRealColumnCountLimitLoaded) return _selectedRealColumnCountLimit; 46 | _selectedRealColumnCountLimitLoaded = true; 47 | if (Model == null) 48 | { 49 | _selectedRealColumnCountLimit = null; 50 | } 51 | else 52 | { 53 | _selectedRealColumnCountLimit = IsTransposed ? Model.SelectedRowCountLimit : Model.SelectedColumnCountLimit; 54 | } 55 | return _selectedRealColumnCountLimit; 56 | } 57 | } 58 | 59 | private bool _isLimitedSelection = false; 60 | 61 | private void CheckChangedLimitedSelection() 62 | { 63 | if (IsLimitedSelection != _isLimitedSelection) 64 | { 65 | InvalidateAll(); 66 | _isLimitedSelection = IsLimitedSelection; 67 | } 68 | } 69 | 70 | private void ClearSelectedCells() 71 | { 72 | _selectedCells.Clear(); 73 | _selectedRows.Clear(); 74 | _selectedColumns.Clear(); 75 | 76 | CheckChangedLimitedSelection(); 77 | } 78 | 79 | private void AddSelectedCell(FastGridCellAddress cell) 80 | { 81 | if (!cell.IsCell) return; 82 | 83 | if (SelectedRealRowCountLimit.HasValue && _selectedRows.Count >= SelectedRealRowCountLimit.Value && !_selectedRows.ContainsKey(cell.Row.Value)) return; 84 | if (SelectedRealColumnCountLimit.HasValue && _selectedColumns.Count >= SelectedRealColumnCountLimit.Value && !_selectedColumns.ContainsKey(cell.Column.Value)) return; 85 | 86 | if (_selectedCells.Contains(cell)) return; 87 | 88 | _selectedCells.Add(cell); 89 | 90 | if (!_selectedRows.ContainsKey(cell.Row.Value)) _selectedRows[cell.Row.Value] = 0; 91 | _selectedRows[cell.Row.Value]++; 92 | 93 | if (!_selectedColumns.ContainsKey(cell.Column.Value)) _selectedColumns[cell.Column.Value] = 0; 94 | _selectedColumns[cell.Column.Value]++; 95 | 96 | CheckChangedLimitedSelection(); 97 | } 98 | 99 | private void RemoveSelectedCell(FastGridCellAddress cell) 100 | { 101 | if (!cell.IsCell) return; 102 | 103 | if (!_selectedCells.Contains(cell)) return; 104 | 105 | _selectedCells.Remove(cell); 106 | 107 | if (_selectedRows.ContainsKey(cell.Row.Value)) 108 | { 109 | _selectedRows[cell.Row.Value]--; 110 | if (_selectedRows[cell.Row.Value] == 0) _selectedRows.Remove(cell.Row.Value); 111 | } 112 | 113 | if (_selectedColumns.ContainsKey(cell.Column.Value)) 114 | { 115 | _selectedColumns[cell.Column.Value]--; 116 | if (_selectedColumns[cell.Column.Value] == 0) _selectedColumns.Remove(cell.Column.Value); 117 | } 118 | 119 | CheckChangedLimitedSelection(); 120 | } 121 | 122 | public bool IsLimitedSelection 123 | { 124 | get 125 | { 126 | return (SelectedRealRowCountLimit.HasValue && _selectedRows.Count >= SelectedRealRowCountLimit.Value) 127 | || 128 | (SelectedRealColumnCountLimit.HasValue && _selectedColumns.Count >= SelectedRealColumnCountLimit.Value); 129 | } 130 | } 131 | 132 | private void SetSelectedRectangle(FastGridCellAddress origin, FastGridCellAddress cell) 133 | { 134 | var newSelected = GetCellRange(origin, cell); 135 | foreach (var added in newSelected) 136 | { 137 | if (_selectedCells.Contains(added)) continue; 138 | InvalidateCell(added); 139 | AddSelectedCell(added); 140 | } 141 | foreach (var removed in _selectedCells.ToList()) 142 | { 143 | if (newSelected.Contains(removed)) continue; 144 | InvalidateCell(removed); 145 | RemoveSelectedCell(removed); 146 | } 147 | SetCurrentCell(cell); 148 | OnChangeSelectedCells(true); 149 | } 150 | 151 | private void OnChangeSelectedCells(bool isInvokedByUser) 152 | { 153 | if (SelectedCellsChanged != null) SelectedCellsChanged(this, new SelectionChangedEventArgs { IsInvokedByUser = isInvokedByUser }); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridControl_StyleProps.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | 9 | namespace FastWpfGrid 10 | { 11 | partial class FastGridControl 12 | { 13 | private Color _cellFontColor = Colors.Black; 14 | private Color _headerBackground = Color.FromRgb(0xF6, 0xF7, 0xF9); 15 | private Color _headerCurrentBackground = Color.FromRgb(190, 207, 220); 16 | private Color _selectedColor = Color.FromRgb(51, 153, 255); 17 | private Color _selectedTextColor = Colors.White; 18 | private Color _limitedSelectedColor = Color.FromRgb(51, 220, 220); 19 | private Color _limitedSelectedTextColor = Colors.White; 20 | private Color _mouseOverRowColor = Color.FromRgb(235, 235, 255); // Colors.LemonChiffon; // Colors .Beige; 21 | private string _cellFontName = "Arial"; 22 | private int _cellFontSize; 23 | private Color _gridLineColor = Colors.LightGray; 24 | private int _cellPaddingHorizontal = 2; 25 | private int _cellPaddingVertical = 1; 26 | private int _blockPadding = 2; 27 | private int _columnResizeTheresold = 2; 28 | private int? _minColumnWidthOverride; 29 | 30 | private Color[] _alternatingColors = new Color[] 31 | { 32 | Colors.White, 33 | Colors.White, 34 | Color.FromRgb(235, 235, 235), 35 | Colors.White, 36 | Colors.White, 37 | Color.FromRgb(235, 245, 255) 38 | }; 39 | 40 | private Color _activeRegionFrameColor = Color.FromRgb(0xAA, 0xAA, 0xFF); 41 | private Color _activeRegionHoverFillColor = Color.FromRgb(0xAA, 0xFF, 0xFF); 42 | private int _wideColumnsLimit = 250; 43 | 44 | public int ColumnResizeTheresold 45 | { 46 | get { return _columnResizeTheresold; } 47 | set { _columnResizeTheresold = value; } 48 | } 49 | 50 | public string CellFontName 51 | { 52 | get { return _cellFontName; } 53 | set 54 | { 55 | _cellFontName = value; 56 | RecalculateDefaultCellSize(); 57 | RenderChanged(); 58 | } 59 | } 60 | 61 | public int? MinColumnWidthOverride 62 | { 63 | get { return _minColumnWidthOverride; } 64 | set 65 | { 66 | _minColumnWidthOverride = value; 67 | RecalculateDefaultCellSize(); 68 | InvalidateAll(); 69 | } 70 | } 71 | 72 | public int MinColumnWidth 73 | { 74 | get { return _minColumnWidthOverride ?? _columnSizes.DefaultSize; } 75 | } 76 | 77 | public int CellFontSize 78 | { 79 | get { return _cellFontSize; } 80 | set 81 | { 82 | _cellFontSize = value; 83 | RecalculateDefaultCellSize(); 84 | InvalidateAll(); 85 | } 86 | } 87 | 88 | public int RowHeightReserve 89 | { 90 | get { return _rowHeightReserve; } 91 | set 92 | { 93 | _rowHeightReserve = value; 94 | RecalculateDefaultCellSize(); 95 | RenderGrid(); 96 | } 97 | } 98 | 99 | public Color CellFontColor 100 | { 101 | get { return _cellFontColor; } 102 | set 103 | { 104 | _cellFontColor = value; 105 | RenderGrid(); 106 | } 107 | } 108 | 109 | public Color SelectedColor 110 | { 111 | get { return _selectedColor; } 112 | set 113 | { 114 | _selectedColor = value; 115 | RenderGrid(); 116 | } 117 | } 118 | 119 | public Color SelectedTextColor 120 | { 121 | get { return _selectedTextColor; } 122 | set 123 | { 124 | _selectedTextColor = value; 125 | RenderGrid(); 126 | } 127 | } 128 | 129 | public Color LimitedSelectedColor 130 | { 131 | get { return _limitedSelectedColor; } 132 | set 133 | { 134 | _limitedSelectedColor = value; 135 | RenderGrid(); 136 | } 137 | } 138 | 139 | public Color LimitedSelectedTextColor 140 | { 141 | get { return _limitedSelectedTextColor; } 142 | set 143 | { 144 | _limitedSelectedTextColor = value; 145 | RenderGrid(); 146 | } 147 | } 148 | 149 | public Color MouseOverRowColor 150 | { 151 | get { return _mouseOverRowColor; } 152 | set { _mouseOverRowColor = value; } 153 | } 154 | 155 | public Color GridLineColor 156 | { 157 | get { return _gridLineColor; } 158 | set 159 | { 160 | _gridLineColor = value; 161 | RenderChanged(); 162 | } 163 | } 164 | 165 | public Color[] AlternatingColors 166 | { 167 | get { return _alternatingColors; } 168 | set 169 | { 170 | if (value.Length < 1) throw new Exception("Invalid value"); 171 | _alternatingColors = value; 172 | RenderChanged(); 173 | } 174 | } 175 | 176 | public int CellPaddingHorizontal 177 | { 178 | get { return _cellPaddingHorizontal; } 179 | set 180 | { 181 | _cellPaddingHorizontal = value; 182 | RenderChanged(); 183 | } 184 | } 185 | 186 | public int CellPaddingVertical 187 | { 188 | get { return _cellPaddingVertical; } 189 | set 190 | { 191 | _cellPaddingVertical = value; 192 | RenderChanged(); 193 | } 194 | } 195 | 196 | public int BlockPadding 197 | { 198 | get { return _blockPadding; } 199 | set 200 | { 201 | _blockPadding = value; 202 | RenderChanged(); 203 | } 204 | } 205 | 206 | public Color HeaderBackground 207 | { 208 | get { return _headerBackground; } 209 | set 210 | { 211 | _headerBackground = value; 212 | RenderChanged(); 213 | } 214 | } 215 | 216 | public Color HeaderCurrentBackground 217 | { 218 | get { return _headerCurrentBackground; } 219 | set 220 | { 221 | _headerCurrentBackground = value; 222 | RenderChanged(); 223 | } 224 | } 225 | 226 | public Color ActiveRegionFrameColor 227 | { 228 | get { return _activeRegionFrameColor; } 229 | set 230 | { 231 | _activeRegionFrameColor = value; 232 | RenderChanged(); 233 | } 234 | } 235 | 236 | public Color ActiveRegionHoverFillColor 237 | { 238 | get { return _activeRegionHoverFillColor; } 239 | set { _activeRegionHoverFillColor = value; } 240 | } 241 | 242 | public int WideColumnsLimit 243 | { 244 | get { return _wideColumnsLimit; } 245 | set { _wideColumnsLimit = value; } 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /FastWpfGrid/FastGridModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | 9 | namespace FastWpfGrid 10 | { 11 | public abstract class FastGridModelBase : IFastGridModel, IFastGridCell, IFastGridCellBlock 12 | { 13 | private List _grids = new List(); 14 | private int? _requestedRow; 15 | private int? _requestedColumn; 16 | private HashSet _frozenRows = new HashSet(); 17 | private HashSet _hiddenRows = new HashSet(); 18 | private HashSet _frozenColumns = new HashSet(); 19 | private HashSet _hiddenColumns = new HashSet(); 20 | 21 | public abstract int ColumnCount { get; } 22 | 23 | public abstract int RowCount { get; } 24 | 25 | public virtual string GetCellText(int row, int column) 26 | { 27 | return String.Format("Row={0}, Column={1}", row + 1, column + 1); 28 | } 29 | 30 | public virtual void SetCellText(int row, int column, string value) 31 | { 32 | } 33 | 34 | public virtual IFastGridCell GetCell(IFastGridView view, int row, int column) 35 | { 36 | _requestedRow = row; 37 | _requestedColumn = column; 38 | return this; 39 | } 40 | 41 | public virtual string GetRowHeaderText(int row) 42 | { 43 | return (row + 1).ToString(); 44 | } 45 | 46 | public virtual IFastGridCell GetRowHeader(IFastGridView view, int row) 47 | { 48 | _requestedRow = row; 49 | _requestedColumn = null; 50 | return this; 51 | } 52 | 53 | public virtual IFastGridCell GetColumnHeader(IFastGridView view, int column) 54 | { 55 | _requestedColumn = column; 56 | _requestedRow = null; 57 | return this; 58 | } 59 | 60 | public virtual IFastGridCell GetGridHeader(IFastGridView view) 61 | { 62 | return new FastGridCellImpl(); 63 | } 64 | 65 | public virtual string GetColumnHeaderText(int column) 66 | { 67 | return "Column " + (column + 1).ToString(); 68 | } 69 | 70 | public virtual void AttachView(IFastGridView view) 71 | { 72 | _grids.Add(view); 73 | } 74 | 75 | public virtual void DetachView(IFastGridView view) 76 | { 77 | _grids.Remove(view); 78 | } 79 | 80 | public virtual void HandleCommand(IFastGridView view, FastGridCellAddress address, object commandParameter, ref bool handled) 81 | { 82 | } 83 | 84 | public virtual HashSet GetHiddenColumns(IFastGridView view) 85 | { 86 | return _hiddenColumns; 87 | } 88 | 89 | public virtual HashSet GetFrozenColumns(IFastGridView view) 90 | { 91 | return _frozenColumns; 92 | } 93 | 94 | public virtual HashSet GetHiddenRows(IFastGridView view) 95 | { 96 | return _hiddenRows; 97 | } 98 | 99 | public virtual HashSet GetFrozenRows(IFastGridView view) 100 | { 101 | return _frozenRows; 102 | } 103 | 104 | public void SetColumnArrange(HashSet hidden, HashSet frozen) 105 | { 106 | _hiddenColumns = hidden; 107 | _frozenColumns = frozen; 108 | NotifyColumnArrangeChanged(); 109 | } 110 | 111 | public void SetRowArrange(HashSet hidden, HashSet frozen) 112 | { 113 | _hiddenRows = hidden; 114 | _frozenRows = frozen; 115 | NotifyRowArrangeChanged(); 116 | } 117 | 118 | public void InvalidateAll() 119 | { 120 | _grids.ForEach(x => x.InvalidateAll()); 121 | } 122 | 123 | public void InvalidateCell(int row, int column) 124 | { 125 | _grids.ForEach(x => x.InvalidateModelCell(row, column)); 126 | } 127 | 128 | public void InvalidateRowHeader(int row) 129 | { 130 | _grids.ForEach(x => x.InvalidateModelRowHeader(row)); 131 | } 132 | 133 | public void InvalidateColumnHeader(int column) 134 | { 135 | _grids.ForEach(x => x.InvalidateModelColumnHeader(column)); 136 | } 137 | 138 | public void NotifyAddedRows() 139 | { 140 | _grids.ForEach(x => x.NotifyAddedRows()); 141 | } 142 | 143 | public void NotifyRefresh() 144 | { 145 | _grids.ForEach(x => x.NotifyRefresh()); 146 | } 147 | 148 | public void NotifyColumnArrangeChanged() 149 | { 150 | _grids.ForEach(x => x.NotifyColumnArrangeChanged()); 151 | } 152 | 153 | public void NotifyRowArrangeChanged() 154 | { 155 | _grids.ForEach(x => x.NotifyRowArrangeChanged()); 156 | } 157 | 158 | public virtual Color? BackgroundColor 159 | { 160 | get { return null; } 161 | } 162 | 163 | public virtual int BlockCount 164 | { 165 | get { return 1; } 166 | } 167 | 168 | public virtual int RightAlignBlockCount 169 | { 170 | get { return 0; } 171 | } 172 | 173 | public virtual IFastGridCellBlock GetBlock(int blockIndex) 174 | { 175 | return this; 176 | } 177 | 178 | public virtual string GetEditText() 179 | { 180 | return GetCellText(_requestedRow.Value, _requestedColumn.Value); 181 | } 182 | 183 | public virtual void SetEditText(string value) 184 | { 185 | SetCellText(_requestedRow.Value, _requestedColumn.Value, value); 186 | } 187 | 188 | public virtual void HandleSelectionCommand(IFastGridView view, string command) 189 | { 190 | } 191 | 192 | public virtual string ToolTipText 193 | { 194 | get { return null; } 195 | } 196 | 197 | public virtual TooltipVisibilityMode ToolTipVisibility 198 | { 199 | get { return TooltipVisibilityMode.Always; } 200 | } 201 | 202 | public virtual FastGridBlockType BlockType 203 | { 204 | get { return FastGridBlockType.Text; } 205 | } 206 | 207 | public virtual bool IsItalic 208 | { 209 | get { return false; } 210 | } 211 | 212 | public virtual bool IsBold 213 | { 214 | get { return false; } 215 | } 216 | 217 | public virtual Color? FontColor 218 | { 219 | get { return null; } 220 | } 221 | 222 | public virtual string TextData 223 | { 224 | get 225 | { 226 | if (_requestedColumn == null && _requestedRow == null) return null; 227 | if (_requestedColumn != null && _requestedRow != null) return GetCellText(_requestedRow.Value, _requestedColumn.Value); 228 | if (_requestedColumn != null) return GetColumnHeaderText(_requestedColumn.Value); 229 | if (_requestedRow != null) return GetRowHeaderText(_requestedRow.Value); 230 | return null; 231 | } 232 | } 233 | 234 | public virtual string ImageSource 235 | { 236 | get { return null; } 237 | } 238 | 239 | public virtual int ImageWidth 240 | { 241 | get { return 16; } 242 | } 243 | 244 | public virtual int ImageHeight 245 | { 246 | get { return 16; } 247 | } 248 | 249 | public virtual MouseHoverBehaviours MouseHoverBehaviour 250 | { 251 | get { return MouseHoverBehaviours.ShowAllWhenMouseOut; } 252 | } 253 | 254 | public virtual object CommandParameter 255 | { 256 | get { return null; } 257 | } 258 | 259 | public virtual string ToolTip 260 | { 261 | get { return null; } 262 | } 263 | 264 | public virtual CellDecoration Decoration 265 | { 266 | get { return CellDecoration.None; } 267 | } 268 | 269 | public virtual Color? DecorationColor 270 | { 271 | get { return null; } 272 | } 273 | 274 | public virtual int? SelectedRowCountLimit 275 | { 276 | get { return null; } 277 | } 278 | 279 | public virtual int? SelectedColumnCountLimit 280 | { 281 | get { return null; } 282 | } 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /FastWpfGrid/FastWpfGrid.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3} 8 | Library 9 | Properties 10 | FastWpfGrid 11 | FastWpfGrid 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | false 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | FastGridControl.xaml 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | Designer 77 | MSBuild:Compile 78 | 79 | 80 | 81 | 82 | {b0aa6a94-6784-4221-81f0-244a68c374c0} 83 | WriteableBitmapEx.Wpf 84 | 85 | 86 | 87 | 94 | -------------------------------------------------------------------------------- /FastWpfGrid/IFastGridCell.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | 8 | namespace FastWpfGrid 9 | { 10 | public enum CellDecoration 11 | { 12 | None, 13 | StrikeOutHorizontal, 14 | } 15 | 16 | public enum TooltipVisibilityMode 17 | { 18 | Always, 19 | OnlyWhenTrimmed, 20 | } 21 | 22 | public interface IFastGridCell 23 | { 24 | Color? BackgroundColor { get; } 25 | 26 | int BlockCount { get; } 27 | int RightAlignBlockCount { get; } 28 | IFastGridCellBlock GetBlock(int blockIndex); 29 | CellDecoration Decoration { get; } 30 | Color? DecorationColor { get; } 31 | 32 | /// 33 | /// return NULL disables inline editor 34 | /// 35 | /// 36 | string GetEditText(); 37 | void SetEditText(string value); 38 | 39 | string ToolTipText { get; } 40 | TooltipVisibilityMode ToolTipVisibility { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /FastWpfGrid/IFastGridCellBlock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | 9 | namespace FastWpfGrid 10 | { 11 | public enum FastGridBlockType 12 | { 13 | Text, 14 | Image, 15 | } 16 | 17 | public enum MouseHoverBehaviours 18 | { 19 | HideWhenMouseOut, 20 | HideButtonWhenMouseOut, 21 | ShowAllWhenMouseOut, 22 | } 23 | 24 | public interface IFastGridCellBlock 25 | { 26 | FastGridBlockType BlockType { get; } 27 | 28 | Color? FontColor { get; } 29 | bool IsItalic { get; } 30 | bool IsBold { get; } 31 | string TextData { get; } 32 | 33 | string ImageSource { get; } 34 | int ImageWidth { get; } 35 | int ImageHeight { get; } 36 | 37 | MouseHoverBehaviours MouseHoverBehaviour { get; } 38 | object CommandParameter { get; } 39 | string ToolTip { get; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FastWpfGrid/IFastGridModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public interface IFastGridModel 10 | { 11 | int ColumnCount { get; } 12 | int RowCount { get; } 13 | IFastGridCell GetCell(IFastGridView grid, int row, int column); 14 | IFastGridCell GetRowHeader(IFastGridView view, int row); 15 | IFastGridCell GetColumnHeader(IFastGridView view, int column); 16 | IFastGridCell GetGridHeader(IFastGridView view); 17 | void AttachView(IFastGridView view); 18 | void DetachView(IFastGridView view); 19 | void HandleCommand(IFastGridView view, FastGridCellAddress address, object commandParameter, ref bool handled); 20 | 21 | HashSet GetHiddenColumns(IFastGridView view); 22 | HashSet GetFrozenColumns(IFastGridView view); 23 | HashSet GetHiddenRows(IFastGridView view); 24 | HashSet GetFrozenRows(IFastGridView view); 25 | 26 | void HandleSelectionCommand(IFastGridView view, string command); 27 | 28 | int? SelectedRowCountLimit { get; } 29 | int? SelectedColumnCountLimit { get; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FastWpfGrid/IFastGridView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public interface IFastGridView 10 | { 11 | /// 12 | /// invalidates whole grid 13 | /// 14 | void InvalidateAll(); 15 | 16 | /// 17 | /// invalidates given cell 18 | /// 19 | /// 20 | /// 21 | void InvalidateModelCell(int row, int column); 22 | 23 | /// 24 | /// invalidates given row header 25 | /// 26 | /// 27 | void InvalidateModelRowHeader(int row); 28 | 29 | /// 30 | /// invalidates given row (all cells including header) 31 | /// 32 | /// 33 | void InvalidateModelRow(int row); 34 | 35 | /// 36 | /// invalidates given column header 37 | /// 38 | /// 39 | void InvalidateModelColumnHeader(int column); 40 | 41 | /// 42 | /// invalidates given column (all cells including header) 43 | /// 44 | /// 45 | void InvalidateModelColumn(int column); 46 | 47 | /// 48 | /// invalidates grid header (top-left header cell) 49 | /// 50 | void InvalidateGridHeader(); 51 | 52 | /// 53 | /// forces grid to refresh all data 54 | /// 55 | void NotifyRefresh(); 56 | 57 | /// 58 | /// notifies grid about new rows added to the end 59 | /// 60 | void NotifyAddedRows(); 61 | 62 | /// 63 | /// notifies grid, that result of GetHiddenColumns() or GetFrozenColumns() is changed 64 | /// 65 | void NotifyColumnArrangeChanged(); 66 | 67 | /// 68 | /// notifies grid, that result of GetHiddenRows() or GetFrozenRows() is changed 69 | /// 70 | void NotifyRowArrangeChanged(); 71 | 72 | /// 73 | /// set/get whether grid is transposed 74 | /// 75 | bool IsTransposed { get; set; } 76 | 77 | /// 78 | /// gets whether flexible rows (real rows) are curently used 79 | /// 80 | bool FlexibleRows { get; } 81 | 82 | /// 83 | /// gets or sets whereher flexible rows are allows 84 | /// 85 | bool AllowFlexibleRows { get; set; } 86 | 87 | /// 88 | /// gets selected model cells 89 | /// 90 | /// 91 | HashSet GetSelectedModelCells(); 92 | 93 | /// 94 | /// gets summary of active rows 95 | /// 96 | /// 97 | ActiveSeries GetActiveRows(); 98 | 99 | /// 100 | /// gets summary of active columns 101 | /// 102 | /// 103 | ActiveSeries GetActiveColumns(); 104 | 105 | /// 106 | /// shows quick acces menu to selection 107 | /// 108 | /// 109 | void ShowSelectionMenu(IEnumerable commands); 110 | 111 | /// 112 | /// hides inline editor 113 | /// 114 | /// 115 | void HideInlineEditor(bool saveCellValue = true); 116 | 117 | /// 118 | /// handles command 119 | /// 120 | /// 121 | /// 122 | void HandleCommand(FastGridCellAddress address, object command); 123 | 124 | ///// 125 | ///// selects all cells in grid (with given limits) 126 | ///// 127 | ///// 128 | ///// 129 | //void SelectAll(int? rowCountLimit, int? columnCountLimit); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /FastWpfGrid/ImageHolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | using System.Windows.Media.Imaging; 8 | 9 | namespace FastWpfGrid 10 | { 11 | public class ImageHolder 12 | { 13 | public WriteableBitmap Bitmap; 14 | public BitmapImage Image; 15 | public WriteableBitmapExtensions.BlendMode BlendMode; 16 | public Color KeyColor = Colors.White; 17 | 18 | public ImageHolder(WriteableBitmap bitmap, BitmapImage image) 19 | { 20 | Bitmap = bitmap; 21 | Image = image; 22 | 23 | using (var context = Bitmap.GetBitmapContext()) 24 | { 25 | int w = Bitmap.PixelWidth; 26 | int h = Bitmap.PixelHeight; 27 | 28 | for (int x = 0; x < w; x++) 29 | { 30 | for (int y = 0; y < h; y++) 31 | { 32 | var color = Bitmap.GetPixel(x, y); 33 | if (color.A != 0xFF) 34 | { 35 | BlendMode = WriteableBitmapExtensions.BlendMode.Alpha; 36 | return; 37 | } 38 | } 39 | } 40 | 41 | BlendMode = WriteableBitmapExtensions.BlendMode.ColorKeying; 42 | KeyColor = bitmap.GetPixel(0, 0); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /FastWpfGrid/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FastWpfGrid")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("FastWpfGrid")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3e2a4823-66d9-474e-8dd9-5fbdf6094989")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /FastWpfGrid/SelectionChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class SelectionChangedEventArgs : EventArgs 10 | { 11 | public bool IsInvokedByUser; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /FastWpfGrid/SelectionQuickCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class SelectionQuickCommand 10 | { 11 | public string Text { get; set; } 12 | public IFastGridModel Model { get; set; } 13 | 14 | public SelectionQuickCommand(IFastGridModel model, string text) 15 | { 16 | Text = text; 17 | Model = model; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FastWpfGrid/SeriesSizes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FastWpfGrid 8 | { 9 | public class SeriesSizeItem 10 | { 11 | public int ScrollIndex = -1; 12 | public int ModelIndex; 13 | public int FrozenIndex = -1; 14 | public int Size; 15 | public int Position; 16 | 17 | public int EndPosition 18 | { 19 | get { return Position + Size; } 20 | } 21 | } 22 | 23 | /// 24 | /// Manager to hold column/row sizes and indexes 25 | /// Terminology: 26 | /// ModelIndex - index in model 27 | /// ScrollIndex - index in scroll are (=RealIndex-FrozenCount) 28 | /// FrozenIndex - index in frozen area 29 | /// RealIndex - index in frozen and scroll area (first are frozen items, than scroll items) 30 | /// Grid uses mostly RealIndex 31 | /// 32 | public class SeriesSizes 33 | { 34 | private Dictionary _sizeOverridesByModelIndex = new Dictionary(); 35 | private int _count; 36 | public int DefaultSize; 37 | public int? MaxSize; 38 | private List _hiddenAndFrozenModelIndexes; 39 | private List _frozenModelIndexes; 40 | 41 | // these items are updated in BuildIndex() 42 | private List _scrollItems = new List(); 43 | //private Dictionary _itemByIndex = new Dictionary(); 44 | private List _positions = new List(); 45 | private List _scrollIndexes = new List(); 46 | private List _frozenItems = new List(); 47 | 48 | public int Count 49 | { 50 | get { return _count; } 51 | set { _count = value; } 52 | } 53 | 54 | public int ScrollCount 55 | { 56 | get { return _count - (_hiddenAndFrozenModelIndexes != null ? _hiddenAndFrozenModelIndexes.Count : 0); } 57 | } 58 | 59 | public int FrozenCount 60 | { 61 | get { return (_frozenModelIndexes != null ? _frozenModelIndexes.Count : 0); } 62 | } 63 | 64 | public int FrozenSize 65 | { 66 | get { return _frozenItems.Sum(x => x.Size); } 67 | } 68 | 69 | public int RealCount 70 | { 71 | get { return FrozenCount + ScrollCount; } 72 | } 73 | 74 | public void Clear() 75 | { 76 | _scrollItems.Clear(); 77 | //_itemByIndex.Clear(); 78 | _sizeOverridesByModelIndex.Clear(); 79 | _positions.Clear(); 80 | _scrollIndexes.Clear(); 81 | _frozenItems.Clear(); 82 | _hiddenAndFrozenModelIndexes = null; 83 | _frozenModelIndexes = null; 84 | } 85 | 86 | public void PutSizeOverride(int modelIndex, int size) 87 | { 88 | if (MaxSize.HasValue && size > MaxSize) size = MaxSize.Value; 89 | if (!_sizeOverridesByModelIndex.ContainsKey(modelIndex)) _sizeOverridesByModelIndex[modelIndex] = size; 90 | if (size > _sizeOverridesByModelIndex[modelIndex]) _sizeOverridesByModelIndex[modelIndex] = size; 91 | } 92 | 93 | public void BuildIndex() 94 | { 95 | _scrollItems.Clear(); 96 | //_itemByIndex.Clear(); 97 | 98 | _scrollIndexes = _sizeOverridesByModelIndex.Keys.Select(ModelToReal).Select(x => x - FrozenCount).Where(x => x >= 0).ToList(); 99 | _scrollIndexes.Sort(); 100 | 101 | int lastScrollIndex = -1; 102 | int lastEndPosition = 0; 103 | 104 | foreach (int scrollIndex in _scrollIndexes) 105 | { 106 | int modelIndex = RealToModel(scrollIndex + FrozenCount); 107 | int size = _sizeOverridesByModelIndex[modelIndex]; 108 | var item = new SeriesSizeItem 109 | { 110 | ScrollIndex = scrollIndex, 111 | ModelIndex = modelIndex, 112 | Size = size, 113 | Position = lastEndPosition + (scrollIndex - lastScrollIndex - 1)*DefaultSize, 114 | }; 115 | _scrollItems.Add(item); 116 | //_itemByIndex[index] = item; 117 | lastScrollIndex = scrollIndex; 118 | lastEndPosition = item.EndPosition; 119 | } 120 | 121 | _positions = _scrollItems.Select(x => x.Position).ToList(); 122 | 123 | 124 | _frozenItems.Clear(); 125 | int lastpos = 0; 126 | for (int i = 0; i < FrozenCount; i++) 127 | { 128 | int modelIndex = _frozenModelIndexes[i]; 129 | int size = GetSizeByModelIndex(modelIndex); 130 | var item = new SeriesSizeItem 131 | { 132 | FrozenIndex = i, 133 | ModelIndex = modelIndex, 134 | Size = size, 135 | Position = lastpos, 136 | }; 137 | _frozenItems.Add(item); 138 | lastpos += size; 139 | } 140 | } 141 | 142 | public int GetScrollIndexOnPosition(int position) 143 | { 144 | int itemOrder = _positions.BinarySearch(position); 145 | if (itemOrder >= 0) return itemOrder; 146 | itemOrder = ~itemOrder; // bitwise complement - index is next larger index 147 | if (itemOrder == 0) return position/DefaultSize; 148 | if (position <= _scrollItems[itemOrder - 1].EndPosition) return _scrollItems[itemOrder - 1].ScrollIndex; 149 | return (position - _scrollItems[itemOrder - 1].Position)/DefaultSize + _scrollItems[itemOrder - 1].ScrollIndex; 150 | } 151 | 152 | public int GetFrozenIndexOnPosition(int position) 153 | { 154 | foreach (var item in _frozenItems) 155 | { 156 | if (position >= item.Position && position <= item.EndPosition) return item.FrozenIndex; 157 | } 158 | return -1; 159 | } 160 | 161 | public int GetSizeSum(int startScrollIndex, int endScrollIndex) 162 | { 163 | int order1 = _scrollIndexes.BinarySearch(startScrollIndex); 164 | int order2 = _scrollIndexes.BinarySearch(endScrollIndex); 165 | 166 | int count = endScrollIndex - startScrollIndex; 167 | 168 | 169 | if (order1 < 0) order1 = ~order1; 170 | if (order2 < 0) order2 = ~order2; 171 | 172 | int result = 0; 173 | 174 | for (int i = order1; i <= order2; i++) 175 | { 176 | if (i < 0) continue; 177 | if (i >= _scrollItems.Count) continue; 178 | var item = _scrollItems[i]; 179 | if (item.ScrollIndex < startScrollIndex) continue; 180 | if (item.ScrollIndex >= endScrollIndex) continue; 181 | 182 | result += item.Size; 183 | count--; 184 | } 185 | 186 | result += count*DefaultSize; 187 | return result; 188 | } 189 | 190 | public int GetSizeByModelIndex(int modelIndex) 191 | { 192 | if (_sizeOverridesByModelIndex.ContainsKey(modelIndex)) return _sizeOverridesByModelIndex[modelIndex]; 193 | return DefaultSize; 194 | } 195 | 196 | public int GetSizeByScrollIndex(int scrollIndex) 197 | { 198 | return GetSizeByRealIndex(scrollIndex + FrozenCount); 199 | } 200 | 201 | public int GetSizeByRealIndex(int realIndex) 202 | { 203 | int modelIndex = RealToModel(realIndex); 204 | return GetSizeByModelIndex(modelIndex); 205 | } 206 | 207 | public void RemoveSizeOverride(int realIndex) 208 | { 209 | int modelIndex = RealToModel(realIndex); 210 | _sizeOverridesByModelIndex.Remove(modelIndex); 211 | } 212 | 213 | public int GetScroll(int sourceScrollIndex, int targetScrollIndex) 214 | { 215 | if (sourceScrollIndex < targetScrollIndex) 216 | { 217 | return -Enumerable.Range(sourceScrollIndex, targetScrollIndex - sourceScrollIndex).Select(GetSizeByScrollIndex).Sum(); 218 | } 219 | else 220 | { 221 | return Enumerable.Range(targetScrollIndex, sourceScrollIndex - targetScrollIndex).Select(GetSizeByScrollIndex).Sum(); 222 | } 223 | } 224 | 225 | public bool ModelIndexIsInScrollArea(int modelIndex) 226 | { 227 | var realIndex = ModelToReal(modelIndex); 228 | return realIndex >= FrozenCount; 229 | } 230 | 231 | public int GetTotalScrollSizeSum() 232 | { 233 | var scrollSizeOverrides = _sizeOverridesByModelIndex.Where(x => ModelIndexIsInScrollArea(x.Key)).ToList(); 234 | return scrollSizeOverrides.Select(x => x.Value).Sum() + (Count - scrollSizeOverrides.Count)*DefaultSize; 235 | } 236 | 237 | public int GetPositionByRealIndex(int realIndex) 238 | { 239 | if (realIndex < 0) return 0; 240 | if (realIndex < FrozenCount) return _frozenItems[realIndex].Position; 241 | return GetPositionByScrollIndex(realIndex - FrozenCount); 242 | } 243 | 244 | public int GetPositionByScrollIndex(int scrollIndex) 245 | { 246 | int order = _scrollIndexes.BinarySearch(scrollIndex); 247 | if (order >= 0) return _scrollItems[order].Position; 248 | order = ~order; 249 | order--; 250 | if (order < 0) return scrollIndex*DefaultSize; 251 | return _scrollItems[order].EndPosition + (scrollIndex - _scrollItems[order].ScrollIndex - 1)*DefaultSize; 252 | } 253 | 254 | public int GetVisibleScrollCount(int firstVisibleIndex, int viewportSize) 255 | { 256 | int res = 0; 257 | int index = firstVisibleIndex; 258 | int count = 0; 259 | while (res < viewportSize && index <= Count) 260 | { 261 | res += GetSizeByScrollIndex(index); 262 | index++; 263 | count++; 264 | } 265 | return count; 266 | } 267 | 268 | public int GetVisibleScrollCountReversed(int lastVisibleIndex, int viewportSize) 269 | { 270 | int res = 0; 271 | int index = lastVisibleIndex; 272 | int count = 0; 273 | while (res < viewportSize && index >= 0) 274 | { 275 | res += GetSizeByScrollIndex(index); 276 | index--; 277 | count++; 278 | } 279 | return count; 280 | } 281 | 282 | public void InvalidateAfterScroll(int oldFirstVisible, int newFirstVisible, Action invalidate, int viewportSize) 283 | { 284 | //int oldFirstVisible = FirstVisibleColumn; 285 | //FirstVisibleColumn = column; 286 | //int visibleCols = VisibleColumnCount; 287 | 288 | if (newFirstVisible > oldFirstVisible) 289 | { 290 | int oldVisibleCount = GetVisibleScrollCount(oldFirstVisible, viewportSize); 291 | int newVisibleCount = GetVisibleScrollCount(newFirstVisible, viewportSize); 292 | 293 | for (int i = oldFirstVisible + oldVisibleCount - 1; i <= newFirstVisible + newVisibleCount; i++) 294 | { 295 | invalidate(i + FrozenCount); 296 | } 297 | } 298 | else 299 | { 300 | for (int i = newFirstVisible; i <= oldFirstVisible; i++) 301 | { 302 | invalidate(i + FrozenCount); 303 | } 304 | } 305 | } 306 | 307 | public bool IsWholeInView(int firstVisibleIndex, int index, int viewportSize) 308 | { 309 | int res = 0; 310 | int testedIndex = firstVisibleIndex; 311 | while (res < viewportSize && testedIndex < Count) 312 | { 313 | res += GetSizeByScrollIndex(testedIndex); 314 | if (testedIndex == index) return res <= viewportSize; 315 | testedIndex++; 316 | } 317 | return false; 318 | } 319 | 320 | public int ScrollInView(int firstVisibleIndex, int scrollIndex, int viewportSize) 321 | { 322 | if (IsWholeInView(firstVisibleIndex, scrollIndex, viewportSize)) 323 | { 324 | return firstVisibleIndex; 325 | } 326 | 327 | if (scrollIndex < firstVisibleIndex) 328 | { 329 | return scrollIndex; 330 | } 331 | 332 | // scroll to the end 333 | int res = 0; 334 | int testedIndex = scrollIndex; 335 | while (res < viewportSize && testedIndex >= 0) 336 | { 337 | int size = GetSizeByScrollIndex(testedIndex); 338 | if (res + size > viewportSize) return testedIndex + 1; 339 | testedIndex--; 340 | res += size; 341 | } 342 | 343 | if (res >= viewportSize && testedIndex < scrollIndex) return testedIndex + 1; 344 | return firstVisibleIndex; 345 | //if (testedIndex < index) return testedIndex + 1; 346 | //return index; 347 | } 348 | 349 | public void Resize(int realIndex, int newSize) 350 | { 351 | if (realIndex < 0) return; 352 | int modelIndex = RealToModel(realIndex); 353 | if (modelIndex < 0) return; 354 | // can be done more effectively 355 | _sizeOverridesByModelIndex[modelIndex] = newSize; 356 | BuildIndex(); 357 | } 358 | 359 | public void SetExtraordinaryIndexes(HashSet hidden, HashSet frozen) 360 | { 361 | _hiddenAndFrozenModelIndexes = new List(hidden); 362 | _frozenModelIndexes = new List(frozen.Where(x => !hidden.Contains(x))); 363 | _hiddenAndFrozenModelIndexes.AddRange(_frozenModelIndexes); 364 | 365 | _frozenModelIndexes.Sort(); 366 | _hiddenAndFrozenModelIndexes.Sort(); 367 | 368 | if (!_hiddenAndFrozenModelIndexes.Any()) _hiddenAndFrozenModelIndexes = null; 369 | if (!_frozenModelIndexes.Any()) _frozenModelIndexes = null; 370 | 371 | BuildIndex(); 372 | } 373 | 374 | public int RealToModel(int realIndex) 375 | { 376 | if (_hiddenAndFrozenModelIndexes == null && _frozenModelIndexes == null) return realIndex; 377 | if (realIndex < 0) return -1; 378 | if (realIndex < FrozenCount && _frozenModelIndexes != null) return _frozenModelIndexes[realIndex]; 379 | if (_hiddenAndFrozenModelIndexes == null) return realIndex; 380 | 381 | realIndex -= FrozenCount; 382 | foreach (int hidItem in _hiddenAndFrozenModelIndexes) 383 | { 384 | if (realIndex < hidItem) return realIndex; 385 | realIndex++; 386 | } 387 | return realIndex; 388 | } 389 | 390 | public int ModelToReal(int modelIndex) 391 | { 392 | if (_hiddenAndFrozenModelIndexes == null && _frozenModelIndexes == null) return modelIndex; 393 | if (modelIndex < 0) return -1; 394 | int frozenIndex = _frozenModelIndexes != null ? _frozenModelIndexes.IndexOf(modelIndex) : -1; 395 | if (frozenIndex >= 0) return frozenIndex; 396 | if (_hiddenAndFrozenModelIndexes == null) return modelIndex; 397 | int hiddenIndex = _hiddenAndFrozenModelIndexes.BinarySearch(modelIndex); 398 | if (hiddenIndex >= 0) return -1; 399 | hiddenIndex = ~hiddenIndex; 400 | if (hiddenIndex >= 0) return modelIndex - hiddenIndex + FrozenCount; 401 | return modelIndex; 402 | } 403 | 404 | public int GetFrozenPosition(int frozenIndex) 405 | { 406 | return _frozenItems[frozenIndex].Position; 407 | } 408 | 409 | /// 410 | /// Determines whether [has size override] [the specified model index]. 411 | /// 412 | /// Index of the model. 413 | /// 414 | /// true if [has size override] [the specified model index]; otherwise, false. 415 | /// 416 | public bool HasSizeOverride(int modelIndex) 417 | { 418 | return _sizeOverridesByModelIndex.ContainsKey(modelIndex); 419 | } 420 | 421 | public bool IsVisible(int testedRealIndex, int firstVisibleScrollIndex, int viewportSize) 422 | { 423 | if (testedRealIndex<0) return false; 424 | if (testedRealIndex>=0 && testedRealIndex= 0 && onPageIndex < GetVisibleScrollCount(firstVisibleScrollIndex, viewportSize); 428 | } 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /FastWpfGridTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FastWpfGridTest/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /FastWpfGridTest/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace FastWpfGridTest 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /FastWpfGridTest/FastWpfGridTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A1D9E1CA-885D-40E7-A2C1-006E5D59EA2E} 8 | WinExe 9 | Properties 10 | FastWpfGridTest 11 | FastWpfGridTest 12 | v4.5 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 4.0 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | MSBuild:Compile 54 | Designer 55 | 56 | 57 | 58 | 59 | MSBuild:Compile 60 | Designer 61 | 62 | 63 | App.xaml 64 | Code 65 | 66 | 67 | 68 | 69 | MainWindow.xaml 70 | Code 71 | 72 | 73 | 74 | 75 | Code 76 | 77 | 78 | True 79 | True 80 | Resources.resx 81 | 82 | 83 | True 84 | Settings.settings 85 | True 86 | 87 | 88 | ResXFileCodeGenerator 89 | Resources.Designer.cs 90 | 91 | 92 | SettingsSingleFileGenerator 93 | Settings.Designer.cs 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | {e9d6f03c-bfb5-4d31-a15e-7f789b4bbcc3} 103 | FastWpfGrid 104 | 105 | 106 | {B0AA6A94-6784-4221-81F0-244A68C374C0} 107 | WriteableBitmapEx.Wpf 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 133 | -------------------------------------------------------------------------------- /FastWpfGridTest/GridModel1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using FastWpfGrid; 8 | 9 | namespace FastWpfGridTest 10 | { 11 | public class GridModel1 : FastGridModelBase 12 | { 13 | private Dictionary, string> _editedCells = new Dictionary, string>(); 14 | private static string[] _columnBasicNames = new[] { "", "Value:", "Long column value:" }; 15 | 16 | public override int ColumnCount 17 | { 18 | get { return 100; } 19 | } 20 | 21 | public override int RowCount 22 | { 23 | get { return 1000; } 24 | } 25 | 26 | public override string GetCellText(int row, int column) 27 | { 28 | var key = Tuple.Create(row, column); 29 | if (_editedCells.ContainsKey(key)) return _editedCells[key]; 30 | 31 | 32 | return String.Format("{0}{1},{2}", _columnBasicNames[column % _columnBasicNames.Length], row + 1, column + 1); 33 | } 34 | 35 | public override void SetCellText(int row, int column, string value) 36 | { 37 | var key = Tuple.Create(row, column); 38 | _editedCells[key] = value; 39 | } 40 | 41 | public override IFastGridCell GetGridHeader(IFastGridView view) 42 | { 43 | var impl = new FastGridCellImpl(); 44 | var btn = impl.AddImageBlock(view.IsTransposed ? "/Images/flip_horizontal_small.png" : "/Images/flip_vertical_small.png"); 45 | btn.CommandParameter = FastWpfGrid.FastGridControl.ToggleTransposedCommand; 46 | btn.ToolTip = "Swap rows and columns"; 47 | impl.AddImageBlock("/Images/foreign_keysmall.png").CommandParameter = "FK"; 48 | impl.AddImageBlock("/Images/primary_keysmall.png").CommandParameter = "PK"; 49 | return impl; 50 | } 51 | 52 | public override void HandleCommand(IFastGridView view, FastGridCellAddress address, object commandParameter, ref bool handled) 53 | { 54 | base.HandleCommand(view, address, commandParameter, ref handled); 55 | if (commandParameter is string) MessageBox.Show(commandParameter.ToString()); 56 | } 57 | 58 | public override int? SelectedRowCountLimit 59 | { 60 | get { return 100; } 61 | } 62 | 63 | public override void HandleSelectionCommand(IFastGridView view, string command) 64 | { 65 | MessageBox.Show(command); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /FastWpfGridTest/GridModel2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | using FastWpfGrid; 8 | 9 | namespace FastWpfGridTest 10 | { 11 | public class GridModel2 : FastGridModelBase 12 | { 13 | public override int ColumnCount 14 | { 15 | get { return 5; } 16 | } 17 | 18 | public override int RowCount 19 | { 20 | get { return 100; } 21 | } 22 | 23 | public override IFastGridCell GetColumnHeader(IFastGridView view, int column) 24 | { 25 | string image = null; 26 | if (column == 0) image = "/Images/primary_keysmall.png"; 27 | if (column == 2) image = "/Images/foreign_keysmall.png"; 28 | 29 | var res = new FastGridCellImpl(); 30 | if (image != null) 31 | { 32 | res.Blocks.Add(new FastGridBlockImpl 33 | { 34 | BlockType = FastGridBlockType.Image, 35 | ImageWidth = 16, 36 | ImageHeight = 16, 37 | ImageSource = image, 38 | }); 39 | } 40 | 41 | res.Blocks.Add(new FastGridBlockImpl 42 | { 43 | IsBold = true, 44 | TextData = String.Format("Column {0}", column), 45 | }); 46 | 47 | return res; 48 | } 49 | 50 | public override IFastGridCell GetCell(IFastGridView view, int row, int column) 51 | { 52 | if ((row+column) % 4 == 0) 53 | { 54 | return new FastGridCellImpl 55 | { 56 | SetBlocks = new[] 57 | { 58 | new FastGridBlockImpl 59 | { 60 | IsItalic = true, 61 | FontColor = Colors.Gray, 62 | TextData = "(NULL)", 63 | } 64 | }, 65 | Decoration = CellDecoration.StrikeOutHorizontal, 66 | DecorationColor = Colors.Red, 67 | }; 68 | } 69 | var impl = new FastGridCellImpl(); 70 | impl.AddTextBlock(GetCellText(row, column)); 71 | var btn = impl.AddImageBlock("/Images/foreign_keysmall.png"); 72 | btn.MouseHoverBehaviour = MouseHoverBehaviours.HideWhenMouseOut; 73 | btn.CommandParameter = "TEST"; 74 | impl.RightAlignBlockCount = 1; 75 | return impl; 76 | } 77 | 78 | public override string GetCellText(int row, int column) 79 | { 80 | return String.Format("{0}{1}", row + 1, column + 1); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /FastWpfGridTest/GridModel3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media; 7 | using FastWpfGrid; 8 | 9 | namespace FastWpfGridTest 10 | { 11 | public class GridModel3 : FastGridModelBase 12 | { 13 | private string _longText = 14 | "Lewis Hamilton took a stunning pole position for Mercedes at the season-opening Australian Grand Prix as McLaren-Honda qualified last. The world champion beat team-mate Nico Rosberg by more than half a second as Mercedes utterly dominated. Williams's Felipe Massa beat Ferrari's Sebastian Vettel to third. Meanwhile, McLaren produced their worst performance for six years as their big-budget new engine partnership with Honda got off to a terrible start. "; 15 | 16 | public override int ColumnCount 17 | { 18 | get { return 5; } 19 | } 20 | 21 | public override int RowCount 22 | { 23 | get { return 1000; } 24 | } 25 | 26 | public override IFastGridCell GetCell(IFastGridView view, int row, int column) 27 | { 28 | var cell = new FastGridCellImpl(); 29 | switch (column) 30 | { 31 | case 2: 32 | var lines = new List(); 33 | for (int i = 0; i <= row%5; i++) 34 | { 35 | lines.Add(String.Format("Line {0}", i)); 36 | } 37 | 38 | if (view.FlexibleRows) 39 | { 40 | cell.AddTextBlock(String.Join("\n", lines)); 41 | } 42 | else 43 | { 44 | cell.AddTextBlock(String.Format("({0} lines)", lines.Count)).FontColor = Colors.Gray; 45 | cell.ToolTipText = String.Join("\n", lines); 46 | } 47 | return cell; 48 | 49 | case 3: 50 | cell.AddTextBlock(_longText); 51 | cell.ToolTipText = _longText; 52 | cell.ToolTipVisibility = TooltipVisibilityMode.OnlyWhenTrimmed; 53 | return cell; 54 | 55 | default: 56 | return base.GetCell(view, row, column); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /FastWpfGridTest/Images/flip_horizontal_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/flip_horizontal_small.png -------------------------------------------------------------------------------- /FastWpfGridTest/Images/flip_transform_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/flip_transform_small.png -------------------------------------------------------------------------------- /FastWpfGridTest/Images/flip_vertical_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/flip_vertical_small.png -------------------------------------------------------------------------------- /FastWpfGridTest/Images/foreign_keysmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/foreign_keysmall.png -------------------------------------------------------------------------------- /FastWpfGridTest/Images/grid1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/grid1.png -------------------------------------------------------------------------------- /FastWpfGridTest/Images/primary_keysmall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/FastWpfGridTest/Images/primary_keysmall.png -------------------------------------------------------------------------------- /FastWpfGridTest/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /FastWpfGridTest/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | using System.Windows.Threading; 18 | 19 | namespace FastWpfGridTest 20 | { 21 | /// 22 | /// Interaction logic for MainWindow.xaml 23 | /// 24 | public partial class MainWindow : Window 25 | { 26 | private GridModel1 _model1; 27 | private GridModel2 _model2; 28 | private GridModel3 _model3; 29 | 30 | public MainWindow() 31 | { 32 | InitializeComponent(); 33 | grid1.Model = _model1 = new GridModel1(); 34 | grid2.Model = _model2 = new GridModel2(); 35 | grid3.Model = _model3 = new GridModel3(); 36 | } 37 | 38 | private void tabChanged(object sender, SelectionChangedEventArgs e) 39 | { 40 | if (tab.SelectedIndex == 2) Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action) SetBitmap); 41 | } 42 | 43 | private void SetBitmap() 44 | { 45 | int width = (int) textbox.ActualWidth; 46 | int height = (int) textbox.ActualHeight; 47 | 48 | if (width <= 0 || height <= 0) return; 49 | WriteableBitmap buffer; 50 | 51 | var bgColor = Color.FromRgb(0xF9, 0xF9, 0xF9); 52 | 53 | textImage1.Width = width; 54 | textImage1.Height = height; 55 | buffer = BitmapFactory.New(width, height); 56 | buffer.Clear(bgColor); 57 | //buffer.DrawString(0, 0, Colors.Black, new Typeface("Arial"), 12, "ABC 123 Gfijdr"); 58 | buffer.DrawString(0, 0, Colors.Black, new PortableFontDesc(isbold: true), "Transparency"); 59 | buffer.DrawLine(0, 10, 50, 10, Colors.Black); 60 | buffer.DrawLine(50, 0, 50, 40, Colors.Black); 61 | textImage1.Source = buffer; 62 | 63 | textImage2.Width = width; 64 | textImage2.Height = height; 65 | buffer = BitmapFactory.New(width, height); 66 | buffer.Clear(bgColor); 67 | buffer.DrawString(0, 0, Colors.Black, bgColor, new PortableFontDesc(cleartype: true), "ABC 123 Gfijdr"); 68 | textImage2.Source = buffer; 69 | 70 | textImage3.Width = width; 71 | textImage3.Height = height; 72 | 73 | 74 | var text = new FormattedText("ABC l 123 Gfijdr", 75 | CultureInfo.InvariantCulture, 76 | FlowDirection.LeftToRight, 77 | new Typeface("Arial"), 78 | 12, 79 | new SolidColorBrush(Colors.Black)); 80 | 81 | 82 | var drawingVisual = new DrawingVisual(); 83 | RenderOptions.SetBitmapScalingMode(drawingVisual, BitmapScalingMode.NearestNeighbor); 84 | RenderOptions.SetClearTypeHint(drawingVisual, ClearTypeHint.Enabled); 85 | RenderOptions.SetEdgeMode(drawingVisual, EdgeMode.Aliased); 86 | TextOptions.SetTextRenderingMode(drawingVisual, TextRenderingMode.ClearType); 87 | TextOptions.SetTextFormattingMode(drawingVisual, TextFormattingMode.Display); 88 | TextOptions.SetTextHintingMode(drawingVisual, TextHintingMode.Fixed); 89 | 90 | using (var drawingContext = drawingVisual.RenderOpen()) 91 | { 92 | 93 | //var brush = new VisualBrush(new TextBlock {Text = "ABC 123 Gfijdr"}); 94 | //drawingContext.DrawRectangle(brush, null, new Rect(0, 0, width, height)); 95 | 96 | //Double halfPenWidth = 0.5; 97 | 98 | //var face = new Typeface("Arial"); 99 | //GlyphTypeface gt; 100 | //face.TryGetGlyphTypeface(out gt); 101 | //var indexes = new ushort[] {gt.CharacterToGlyphMap['A']}; 102 | //var widths = new double[] {gt.AdvanceWidths['A']*12}; 103 | 104 | //var glyphRun = new GlyphRun(gt, 0, false, 12, indexes, new Point(0, 14), widths, null, null, null, null, null, null); 105 | //var rect = glyphRun.ComputeAlignmentBox(); 106 | 107 | //Double halfPenWidth = 0.5; 108 | ////var rect = new Rect(0, 0, width, height); 109 | //GuidelineSet guidelines = new GuidelineSet(); 110 | //guidelines.GuidelinesX.Add(rect.Left + halfPenWidth); 111 | //guidelines.GuidelinesX.Add(rect.Right + halfPenWidth); 112 | //guidelines.GuidelinesY.Add(rect.Top + halfPenWidth); 113 | //guidelines.GuidelinesY.Add(rect.Bottom + halfPenWidth); 114 | 115 | drawingContext.DrawRectangle(new SolidColorBrush(bgColor), null, new Rect(0, 0, width, height)); 116 | 117 | drawingContext.DrawText(text, new Point(0, 0)); 118 | drawingContext.DrawLine(new Pen(Brushes.Black, 1), new Point(0.5, 10.5), new Point(50.5, 10.5)); 119 | drawingContext.DrawLine(new Pen(Brushes.Black, 1), new Point(35.5, 0.5), new Point(35.5, 40.5)); 120 | 121 | //drawingContext.PushGuidelineSet(guidelines); 122 | //drawingContext.DrawGlyphRun(Brushes.Black, glyphRun); 123 | //drawingContext.Pop(); 124 | } 125 | //drawingVisual.Transform = new ScaleTransform(0.9, 0.9); 126 | //drawingVisual.Transform = new ScaleTransform(1, 1); 127 | //drawingVisual.Transform = new TranslateTransform(0, -0.25); 128 | 129 | var tb = new TextBlock { Text = "ABC l 123 Gfijdr" }; 130 | tb.Measure(new Size(width, height)); 131 | tb.Arrange(new Rect(new Point(0, 0), new Size(width, height))); 132 | 133 | var bmp = new RenderTargetBitmap(width, height, DpiDetector.DpiXKoef * 96, DpiDetector.DpiYKoef * 96, PixelFormats.Pbgra32); 134 | 135 | RenderOptions.SetBitmapScalingMode(bmp, BitmapScalingMode.NearestNeighbor); 136 | RenderOptions.SetClearTypeHint(bmp, ClearTypeHint.Enabled); 137 | RenderOptions.SetEdgeMode(bmp, EdgeMode.Aliased); 138 | TextOptions.SetTextRenderingMode(bmp, TextRenderingMode.ClearType); 139 | TextOptions.SetTextFormattingMode(bmp, TextFormattingMode.Display); 140 | TextOptions.SetTextHintingMode(bmp, TextHintingMode.Fixed); 141 | 142 | //RenderOptions.SetCachingHint(bmp, CachingHint.Cache); 143 | //RenderOptions.SetBitmapScalingMode(bmp, BitmapScalingMode.LowQuality); 144 | bmp.Render(drawingVisual); 145 | //bmp.Render(tb); 146 | //bmp.Freeze(); 147 | 148 | 149 | BitmapEncoder encoder = new PngBitmapEncoder(); 150 | encoder.Frames.Add(BitmapFrame.Create(bmp)); 151 | 152 | var ms = new MemoryStream(); 153 | encoder.Save(ms); 154 | 155 | BitmapImage bi = new BitmapImage(); 156 | bi.BeginInit(); 157 | bi.StreamSource = ms; 158 | bi.EndInit(); 159 | 160 | encoder = new PngBitmapEncoder(); 161 | encoder.Frames.Add(BitmapFrame.Create(bmp)); 162 | using (var fs = System.IO.File.OpenWrite("c:/test/file1.png")) 163 | { 164 | encoder.Save(fs); 165 | } 166 | 167 | textImage3.Stretch = Stretch.None; 168 | textImage3.Source = bmp; 169 | } 170 | 171 | private void columnsCfgChanged(object sender, RoutedEventArgs e) 172 | { 173 | var hidden = new HashSet(); 174 | var frozen = new HashSet(); 175 | 176 | if (chbHideColumn3.IsChecked == true) hidden.Add(2); 177 | if (chbFreezeColumn5.IsChecked == true) frozen.Add(4); 178 | 179 | _model1.SetColumnArrange(hidden, frozen); 180 | } 181 | 182 | private void grid1_SelectedCellsChanged(object sender, FastWpfGrid.SelectionChangedEventArgs e) 183 | { 184 | var view = (FastWpfGrid.IFastGridView)grid1; 185 | if (view.GetSelectedModelCells().Count > 1) 186 | { 187 | view.ShowSelectionMenu(new string[] { "CMD1", "CMD2" }); 188 | } 189 | else 190 | { 191 | view.ShowSelectionMenu(null); 192 | } 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /FastWpfGridTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("FastWpfGridTest")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Microsoft")] 14 | [assembly: AssemblyProduct("FastWpfGridTest")] 15 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /FastWpfGridTest/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18063 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace FastWpfGridTest.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FastWpfGridTest.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /FastWpfGridTest/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /FastWpfGridTest/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18063 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace FastWpfGridTest.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FastWpfGridTest/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /FastWpfGridTest/TextRenderPanel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Media; 10 | 11 | namespace FastWpfGridTest 12 | { 13 | public class TextRenderPanel : Panel 14 | { 15 | protected override void OnRender(System.Windows.Media.DrawingContext dc) 16 | { 17 | base.OnRender(dc); 18 | var bgColor = Color.FromRgb(0xF9, 0xF9, 0xF9); 19 | dc.DrawRectangle(new SolidColorBrush(bgColor), null, new Rect(0, 0, ActualWidth, ActualHeight)); 20 | var text = new FormattedText("ABC l 123 Gfijdr", CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface("Arial"), 12, Brushes.Black); 21 | dc.DrawText(text, new Point(0, 0)); 22 | dc.DrawLine(new Pen(Brushes.Black, 1), new Point(0.5, 10.5), new Point(50.5, 10.5)); 23 | dc.DrawLine(new Pen(Brushes.Black, 1), new Point(30.5, 0.5), new Point(30.5, 40.5)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /FastWpfGridUnitTest/FastWpfGridUnitTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {B94371DD-DDC9-4889-B49F-7D76E3E52B01} 7 | Library 8 | Properties 9 | FastWpfGridUnitTest 10 | FastWpfGridUnitTest 11 | v4.5 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {E9D6F03C-BFB5-4D31-A15E-7F789B4BBCC3} 59 | FastWpfGrid 60 | 61 | 62 | 63 | 64 | 65 | 66 | False 67 | 68 | 69 | False 70 | 71 | 72 | False 73 | 74 | 75 | False 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /FastWpfGridUnitTest/GridTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FastWpfGrid; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace FastWpfGridUnitTest 7 | { 8 | [TestClass] 9 | public class GridTest 10 | { 11 | [TestMethod] 12 | public void TestSeriesHide() 13 | { 14 | var sizes = new SeriesSizes(); 15 | sizes.Count = 10; 16 | for (int i = 0; i < sizes.Count; i++) 17 | { 18 | sizes.PutSizeOverride(i, i); 19 | } 20 | sizes.SetExtraordinaryIndexes(new HashSet {2, 9}, new HashSet()); 21 | 22 | Assert.AreEqual(0, sizes.ModelToReal(0)); 23 | Assert.AreEqual(1, sizes.ModelToReal(1)); 24 | Assert.AreEqual(-1, sizes.ModelToReal(2)); 25 | Assert.AreEqual(2, sizes.ModelToReal(3)); 26 | Assert.AreEqual(7, sizes.ModelToReal(8)); 27 | Assert.AreEqual(-1, sizes.ModelToReal(9)); 28 | 29 | Assert.AreEqual(0, sizes.RealToModel(0)); 30 | Assert.AreEqual(1, sizes.RealToModel(1)); 31 | Assert.AreEqual(3, sizes.RealToModel(2)); 32 | Assert.AreEqual(8, sizes.RealToModel(7)); 33 | 34 | sizes.SetExtraordinaryIndexes(new HashSet(), new HashSet {3}); 35 | Assert.AreEqual(1, sizes.ModelToReal(0)); 36 | Assert.AreEqual(2, sizes.ModelToReal(1)); 37 | Assert.AreEqual(3, sizes.ModelToReal(2)); 38 | Assert.AreEqual(0, sizes.ModelToReal(3)); 39 | Assert.AreEqual(4, sizes.ModelToReal(4)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /FastWpfGridUnitTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("FastWpfGridUnitTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("FastWpfGridUnitTest")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("af6b046f-ba19-4c66-9160-623a0665a54c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jan Prochazka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Project Description 2 | Fast WPF datagrid control designed to work with large datasets, with mandatory data virtualization. 3 | 4 | fast DataGrid control for .NET - WPF 5 | - designed for large data sets 6 | - both columns and rows are defined in model 7 | - uses MVVM design pattern, but (for performance reasons) does not use classic WPF binding 8 | - works only with data virtualization (UI virtualization is not needed as in other WPF datagrid controls) 9 | - for rendering is used WriteableBitmapEx library 10 | 11 | ### Features 12 | 13 | - Fast scrolling and rendering 14 | - Excel-like mouse-drag selecting 15 | - Hide columns/rows 16 | - Frozen columns/rows 17 | - Own rendering, WPF templates are not used. Supported objects - text (with - italic, bold attributes), images, image buttons 18 | 19 | ![grid1](https://raw.githubusercontent.com/dbshell/fastwpfgrid/master/FastWpfGridTest/Images/grid1.png) 20 | ![grid2](https://raw.githubusercontent.com/dbshell/fastwpfgrid/master/FastWpfGridTest/Images/grid2.png) 21 | 22 | ### References 23 | 24 | FastWPFGrid is used in DbMouse project ( http:///www.jenasoft.com/dbmouse ) 25 | 26 | ### Model implementation 27 | 28 | Grid control is bind to model, which controls displayed data. Below is example of model implementation. 29 | ```c# 30 | using System; 31 | using System.Collections.Generic; 32 | using System.Linq; 33 | using System.Text; 34 | using System.Threading.Tasks; 35 | using FastWpfGrid; 36 | 37 | namespace FastWpfGridTest 38 | { 39 | public class GridModel1 : FastGridModelBase 40 | { 41 | private Dictionary, string> _editedCells = newDictionary, string>(); 42 | private static string[] _columnBasicNames = new[] { "","Value:", "Long column value:" }; 43 | 44 | public override int ColumnCount 45 | { 46 | get { return 100; } 47 | } 48 | 49 | public override int RowCount 50 | { 51 | get { return 1000; } 52 | } 53 | 54 | public override string GetCellText(int row, int column) 55 | { 56 | var key = Tuple.Create(row, column); 57 | if (_editedCells.ContainsKey(key)) return _editedCells[key]; 58 | 59 | 60 | return String.Format("{0}{1},{2}", _columnBasicNames[column % _columnBasicNames.Length], row + 1, column + 1); 61 | } 62 | 63 | public override void SetCellText(int row, int column, string value) 64 | { 65 | var key = Tuple.Create(row, column); 66 | _editedCells[key] = value; 67 | } 68 | 69 | public override IFastGridCell GetGridHeader(IFastGridView view) 70 | { 71 | var impl = new FastGridCellImpl(); 72 | var btn = impl.AddImageBlock (view.IsTransposed ? "/Images/flip_horizontal_small.png" : "/Images/flip_vertical_small.png"); 73 | btn.CommandParameter = FastWpfGrid.FastGridControl. ToggleTransposedCommand; 74 | btn.ToolTip = "Swap rows and columns"; 75 | return impl; 76 | } 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /WriteableBitmapEx/BitmapFactory.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | // 3 | // Project: WriteableBitmapEx - WriteableBitmap extensions 4 | // Description: Collection of extension methods for the WriteableBitmap class. 5 | // 6 | // Changed by: $Author$ 7 | // Changed on: $Date$ 8 | // Changed in: $Revision$ 9 | // Project: $URL$ 10 | // Id: $Id$ 11 | // 12 | // 13 | // Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors 14 | // 15 | // This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;) 16 | // 17 | #endregion 18 | 19 | using System; 20 | 21 | #if NETFX_CORE 22 | namespace Windows.UI.Xaml.Media.Imaging 23 | #else 24 | namespace System.Windows.Media.Imaging 25 | #endif 26 | { 27 | /// 28 | /// Cross-platform factory for WriteableBitmaps 29 | /// 30 | public static class BitmapFactory 31 | { 32 | /// 33 | /// Creates a new WriteableBitmap of the specified width and height 34 | /// 35 | /// For WPF the default DPI is 96x96 and PixelFormat is Pbgra32 36 | /// 37 | /// 38 | /// 39 | public static WriteableBitmap New(int pixelWidth, int pixelHeight) 40 | { 41 | if (pixelHeight < 1) pixelHeight = 1; 42 | if (pixelWidth < 1) pixelWidth = 1; 43 | 44 | #if SILVERLIGHT 45 | return new WriteableBitmap(pixelWidth, pixelHeight); 46 | #elif WPF 47 | return new WriteableBitmap(pixelWidth, pixelHeight, DpiDetector.DpiXKoef * 96.0, DpiDetector.DpiYKoef * 96.0, PixelFormats.Pbgra32, null); 48 | #elif NETFX_CORE 49 | return new WriteableBitmap(pixelWidth, pixelHeight); 50 | #endif 51 | } 52 | 53 | #if WPF 54 | /// 55 | /// Converts the input BitmapSource to the Pbgra32 format WriteableBitmap which is internally used by the WriteableBitmapEx. 56 | /// 57 | /// The source bitmap. 58 | /// 59 | public static WriteableBitmap ConvertToPbgra32Format(BitmapSource source) 60 | { 61 | // Convert to Pbgra32 if it's a different format 62 | if (source.Format == PixelFormats.Pbgra32) 63 | { 64 | return new WriteableBitmap(source); 65 | } 66 | 67 | var formatedBitmapSource = new FormatConvertedBitmap(); 68 | formatedBitmapSource.BeginInit(); 69 | formatedBitmapSource.Source = source; 70 | formatedBitmapSource.DestinationFormat = PixelFormats.Pbgra32; 71 | formatedBitmapSource.EndInit(); 72 | return new WriteableBitmap(formatedBitmapSource); 73 | } 74 | #endif 75 | } 76 | } -------------------------------------------------------------------------------- /WriteableBitmapEx/ClearTypeLetterGlyph.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace System.Windows.Media.Imaging 9 | { 10 | public class ClearTypeLetterGlyph 11 | { 12 | public struct Item 13 | { 14 | public short X; 15 | public short Y; 16 | public int Color; 17 | } 18 | 19 | public char Ch; 20 | public int Width; 21 | public int Height; 22 | 23 | //// instruction: 24 | //// y, x0, xcount, [color 1...color xcount] 25 | //// end: y=-1 26 | //public int[] Instructions; 27 | 28 | public Item[] Items; 29 | 30 | public static ClearTypeLetterGlyph CreateSpaceGlyph(GlyphTypeface glyphTypeface, double size) 31 | { 32 | int spaceWidth = (int)Math.Ceiling(glyphTypeface.AdvanceWidths[glyphTypeface.CharacterToGlyphMap[' ']] * size); 33 | return new ClearTypeLetterGlyph 34 | { 35 | Ch = ' ', 36 | Height = (int)Math.Ceiling(glyphTypeface.Height * size), 37 | Width = spaceWidth, 38 | }; 39 | } 40 | 41 | 42 | public static ClearTypeLetterGlyph CreateGlyph(GlyphTypeface glyphTypeface, System.Drawing.Font font, double size, char ch, Color fontColor, Color bgColor) 43 | { 44 | if (ch == ' ') return CreateSpaceGlyph(glyphTypeface, size); 45 | 46 | int width; 47 | int height; 48 | 49 | using (var bmp1 = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) 50 | { 51 | using (var g = Graphics.FromImage(bmp1)) 52 | { 53 | //var sizef = g.MeasureString("" + ch, font, new PointF(0, 0), StringFormat.GenericTypographic); 54 | var sizef = g.MeasureString("" + ch, font, new PointF(0, 0), StringFormat.GenericTypographic); 55 | width = (int) Math.Ceiling(sizef.Width); 56 | height = (int) Math.Ceiling(sizef.Height); 57 | } 58 | } 59 | 60 | if (width == 0 || height == 0) return null; 61 | 62 | var res = new List(); 63 | 64 | using (var bmp = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) 65 | { 66 | var fg2 = System.Drawing.Color.FromArgb(fontColor.A, fontColor.R, fontColor.G, fontColor.B); 67 | var bg2 = System.Drawing.Color.FromArgb(bgColor.A, bgColor.R, bgColor.G, bgColor.B); 68 | 69 | using (var g = System.Drawing.Graphics.FromImage(bmp)) 70 | { 71 | g.FillRectangle(new System.Drawing.SolidBrush(bg2), new Rectangle(0, 0, width, height)); 72 | g.DrawString("" + ch, font, new System.Drawing.SolidBrush(fg2), 0, 0, StringFormat.GenericTypographic); 73 | } 74 | 75 | for (int y = 0; y < height; y++) 76 | { 77 | for (int x = 0; x < width; x++) 78 | { 79 | var color = bmp.GetPixel(x, y); 80 | if (color != bg2) 81 | { 82 | res.Add(new Item 83 | { 84 | X = (short)x, 85 | Y = (short)y, 86 | Color = WriteableBitmapExtensions.ConvertColor(Color.FromArgb(color.A, color.R, color.G, color.B)), 87 | }); 88 | } 89 | } 90 | } 91 | } 92 | 93 | //var res = new List(); 94 | 95 | //using (var bmp = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)) 96 | //{ 97 | // var fg2 = System.Drawing.Color.FromArgb(fontColor.A, fontColor.R, fontColor.G, fontColor.B); 98 | // var bg2 = System.Drawing.Color.FromArgb(bgColor.A, bgColor.R, bgColor.G, bgColor.B); 99 | 100 | // using (var g = System.Drawing.Graphics.FromImage(bmp)) 101 | // { 102 | // g.FillRectangle(new System.Drawing.SolidBrush(bg2), new Rectangle(0, 0, width, height)); 103 | // g.DrawString("" + ch, font, new System.Drawing.SolidBrush(fg2), 0, 0, StringFormat.GenericTypographic); 104 | // } 105 | 106 | // int bgint = WriteableBitmapExtensions.ConvertColor(Color.FromArgb(bgColor.A, bgColor.R, bgColor.G, bgColor.B)); 107 | // for (int y = 0; y < height; y++) 108 | // { 109 | // var line = new List(); 110 | // for (int x = 0; x < width; x++) 111 | // { 112 | // var color = bmp.GetPixel(x, y); 113 | // line.Add(WriteableBitmapExtensions.ConvertColor(Color.FromArgb(color.A, color.R, color.G, color.B))); 114 | // } 115 | // if (line.All(x => x == bgint)) continue; // all pixels filled with BG color 116 | // int minx = line.FindIndex(x => x != bgint); 117 | // int maxx = line.FindLastIndex(x => x != bgint); 118 | // res.Add(y); 119 | // res.Add(minx); 120 | // res.Add(maxx - minx + 1); 121 | // for (int i = minx; i <= maxx; i++) res.Add(line[i]); 122 | // } 123 | //} 124 | 125 | //res.Add(-1); // end mark 126 | 127 | return new ClearTypeLetterGlyph 128 | { 129 | Width = width, 130 | Height = height, 131 | Ch = ch, 132 | //Instructions = res.ToArray(), 133 | Items = res.ToArray(), 134 | }; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /WriteableBitmapEx/DpiDetector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Windows.Forms; 7 | 8 | namespace System.Windows.Media.Imaging 9 | { 10 | public static class DpiDetector 11 | { 12 | private static double? _dpiXKoef; 13 | 14 | public static double DpiXKoef 15 | { 16 | get 17 | { 18 | if (_dpiXKoef == null) 19 | { 20 | using (var graphics = Graphics.FromHwnd(IntPtr.Zero)) 21 | { 22 | _dpiXKoef = graphics.DpiX / 96.0; 23 | } 24 | } 25 | return _dpiXKoef ?? 1; 26 | } 27 | } 28 | 29 | private static double? _dpiYKoef; 30 | 31 | public static double DpiYKoef 32 | { 33 | get 34 | { 35 | if (_dpiYKoef==null) 36 | { 37 | using (var graphics = Graphics.FromHwnd(IntPtr.Zero)) 38 | { 39 | _dpiYKoef= graphics.DpiY / 96.0; 40 | } 41 | } 42 | return _dpiYKoef ?? 1; 43 | 44 | //return Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width; 45 | //WantGlobalTransformMatrix(); 46 | //if (_globalTransformPatrix.HasValue) return _globalTransformPatrix.Value.M22; 47 | //return 1; 48 | } 49 | } 50 | 51 | //private static void WantGlobalTransformMatrix() 52 | //{ 53 | // if (_globalTransformPatrix != null) return; 54 | // try 55 | // { 56 | // _globalTransformPatrix = 57 | // PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice; 58 | // } 59 | // catch 60 | // { 61 | // _globalTransformPatrix = null; 62 | // } 63 | //} 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /WriteableBitmapEx/GrayScaleLetterGlyph.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Windows; 7 | using System.Windows.Media; 8 | using System.Windows.Media.Imaging; 9 | 10 | namespace System.Windows.Media.Imaging 11 | { 12 | public class GrayScaleLetterGlyph 13 | { 14 | public struct Item 15 | { 16 | public short X; 17 | public short Y; 18 | public int Alpha; 19 | } 20 | 21 | public char Ch; 22 | public int Width; 23 | public int Height; 24 | 25 | public Item[] Items; 26 | 27 | 28 | public static GrayScaleLetterGlyph CreateSpaceGlyph(GlyphTypeface glyphTypeface, double size) 29 | { 30 | int spaceWidth = (int)Math.Ceiling(glyphTypeface.AdvanceWidths[glyphTypeface.CharacterToGlyphMap[' ']] * size); 31 | return new GrayScaleLetterGlyph 32 | { 33 | Ch = ' ', 34 | Height = (int)Math.Ceiling(glyphTypeface.Height * size), 35 | Width = spaceWidth, 36 | }; 37 | } 38 | 39 | public static unsafe GrayScaleLetterGlyph CreateGlyph(Typeface typeface, GlyphTypeface glyphTypeface, double size, char ch) 40 | { 41 | if (ch == ' ') return CreateSpaceGlyph(glyphTypeface, size); 42 | 43 | FormattedText text = new FormattedText("" + ch, 44 | CultureInfo.InvariantCulture, 45 | FlowDirection.LeftToRight, 46 | typeface, 47 | size, 48 | Brushes.White); 49 | 50 | int width = (int)Math.Ceiling(DpiDetector.DpiXKoef * text.Width); 51 | int height = (int)Math.Ceiling(DpiDetector.DpiYKoef * text.Height); 52 | if (width == 0 || height == 0) return null; 53 | 54 | DrawingVisual drawingVisual = new DrawingVisual(); 55 | DrawingContext drawingContext = drawingVisual.RenderOpen(); 56 | drawingContext.DrawRectangle(Brushes.Black, new Pen(), new Rect(0, 0, width, height)); 57 | drawingContext.DrawText(text, new Point(0, 0)); 58 | drawingContext.Close(); 59 | 60 | RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, DpiDetector.DpiXKoef * 96, DpiDetector.DpiYKoef * 96, PixelFormats.Pbgra32); 61 | bmp.Render(drawingVisual); 62 | 63 | var res = new List(); 64 | 65 | var pixbmp = new WriteableBitmap(bmp); 66 | using (var ctx = new BitmapContext(pixbmp)) 67 | { 68 | var pixels = ctx.Pixels; 69 | 70 | for (int y = 0; y < height; y++) 71 | { 72 | for (int x = 0; x < width; x++) 73 | { 74 | int color = pixels[y*width + x]; 75 | 76 | byte r, g, b; 77 | double avg; 78 | 79 | r = (byte) ((color >> 16) & 0xFF); 80 | g = (byte) ((color >> 8) & 0xFF); 81 | b = (byte) ((color) & 0xFF); 82 | 83 | avg = 0.299*r + 0.587*g + 0.114*b; 84 | 85 | if (avg >= 1) 86 | { 87 | res.Add(new Item 88 | { 89 | X = (short) x, 90 | Y = (short) y, 91 | Alpha = (int) Math.Round(avg/255.0*0x1000), 92 | }); 93 | } 94 | } 95 | } 96 | } 97 | 98 | return new GrayScaleLetterGlyph 99 | { 100 | Width = width, 101 | Height = height, 102 | Ch = ch, 103 | Items = res.ToArray(), 104 | }; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /WriteableBitmapEx/IntGeometry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace System.Windows.Media.Imaging 7 | { 8 | public struct IntRect 9 | { 10 | public int Left, Top, Right, Bottom; 11 | 12 | public int Width 13 | { 14 | get { return Right - Left + 1; } 15 | set { Right = Left + value - 1; } 16 | } 17 | 18 | public int Height 19 | { 20 | get { return Bottom - Top + 1; } 21 | set { Bottom = Top + value - 1; } 22 | } 23 | 24 | public IntPoint TopLeft 25 | { 26 | get { return new IntPoint(Left, Top); } 27 | set 28 | { 29 | Left = value.X; 30 | Top = value.Y; 31 | } 32 | } 33 | 34 | public IntPoint BottomRight 35 | { 36 | get { return new IntPoint(Right, Bottom); } 37 | set 38 | { 39 | Right = value.X; 40 | Bottom = value.Y; 41 | } 42 | } 43 | 44 | public IntSize Size 45 | { 46 | get { return new IntSize(Width, Height); } 47 | set 48 | { 49 | Width = value.Width; 50 | Height = value.Height; 51 | } 52 | } 53 | 54 | public IntRect(IntPoint topLeft, IntSize size) 55 | { 56 | Left = topLeft.X; 57 | Top = topLeft.Y; 58 | Right = topLeft.X + size.Width - 1; 59 | Bottom = topLeft.Y + size.Height - 1; 60 | } 61 | 62 | public IntRect(IntPoint topLeft, IntPoint bottomRight) 63 | { 64 | Left = topLeft.X; 65 | Top = topLeft.Y; 66 | Right = bottomRight.X; 67 | Bottom = bottomRight.Y; 68 | } 69 | 70 | public IntRect GrowSymmetrical(int dWidth2, int dHeight2) 71 | { 72 | return new IntRect( 73 | new IntPoint(Left - dWidth2, Top - dHeight2), 74 | new IntSize(Width + 2*dWidth2, Height + 2*dHeight2) 75 | ); 76 | } 77 | 78 | public Rect ToRect() 79 | { 80 | return new Rect(Left, Top, Width, Height); 81 | } 82 | 83 | public IntRect Translate(IntPoint pt) 84 | { 85 | return new IntRect(TopLeft.Translate(pt), Size); 86 | } 87 | 88 | public bool Contains(Point pt) 89 | { 90 | return pt.X >= Left && pt.X <= Right && pt.Y >= Top && pt.Y <= Bottom; 91 | } 92 | } 93 | 94 | public struct IntPoint 95 | { 96 | public int X, Y; 97 | 98 | public IntPoint(int x, int y) 99 | { 100 | X = x; 101 | Y = y; 102 | } 103 | 104 | public IntPoint Translate(IntPoint pt) 105 | { 106 | return new IntPoint(X + pt.X, Y + pt.Y); 107 | } 108 | } 109 | 110 | public struct IntSize 111 | { 112 | public int Width, Height; 113 | 114 | public IntSize(int width, int height) 115 | { 116 | Width = width; 117 | Height = height; 118 | } 119 | } 120 | 121 | static partial class WriteableBitmapExtensions 122 | { 123 | public static void FillRectangle(this WriteableBitmap bmp, IntRect rect, Color color) 124 | { 125 | var col = ConvertColor(color); 126 | bmp.FillRectangle(rect.Left, rect.Top, rect.Right + 1, rect.Bottom + 1, col); 127 | } 128 | 129 | public static void DrawRectangle(this WriteableBitmap bmp, IntRect rect, Color color) 130 | { 131 | var col = ConvertColor(color); 132 | bmp.DrawRectangle(rect.Left, rect.Top, rect.Right, rect.Bottom, col); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /WriteableBitmapEx/LetterGlyphTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Windows.Media; 7 | using System.Windows.Media.Imaging; 8 | 9 | namespace System.Windows.Media.Imaging 10 | { 11 | public static class LetterGlyphTool 12 | { 13 | public static Dictionary FontCache = new Dictionary(); 14 | 15 | public static unsafe void DrawLetter(this BitmapContext context, int x0, int y0, IntRect cliprect, Color fontColor, GrayScaleLetterGlyph glyph) 16 | { 17 | if (glyph.Items == null) return; 18 | 19 | // Use refs for faster access (really important!) speeds up a lot! 20 | int w = context.Width; 21 | int h = context.Height; 22 | var pixels = context.Pixels; 23 | 24 | int fr = fontColor.R; 25 | int fg = fontColor.G; 26 | int fb = fontColor.B; 27 | 28 | int xmin = cliprect.Left; 29 | int ymin = cliprect.Top; 30 | int xmax = cliprect.Right; 31 | int ymax = cliprect.Bottom; 32 | 33 | if (xmin < 0) xmin = 0; 34 | if (ymin < 0) ymin = 0; 35 | if (xmax >= w) xmax = w - 1; 36 | if (ymax >= h) ymax = h - 1; 37 | 38 | fixed (GrayScaleLetterGlyph.Item* items = glyph.Items) 39 | { 40 | int itemCount = glyph.Items.Length; 41 | GrayScaleLetterGlyph.Item* currentItem = items; 42 | for (int i = 0; i < itemCount; i++, currentItem++) 43 | { 44 | int x = x0 + currentItem->X; 45 | int y = y0 + currentItem->Y; 46 | int alpha = currentItem->Alpha; 47 | if (x < xmin || y < ymin || x > xmax || y > ymax) continue; 48 | 49 | int color = pixels[y * w + x]; 50 | int r = ((color >> 16) & 0xFF); 51 | int g = ((color >> 8) & 0xFF); 52 | int b = ((color) & 0xFF); 53 | 54 | r = (((r << 12) + (fr - r) * alpha) >> 12) & 0xFF; 55 | g = (((g << 12) + (fg - g) * alpha) >> 12) & 0xFF; 56 | b = (((b << 12) + (fb - b) * alpha) >> 12) & 0xFF; 57 | 58 | pixels[y * w + x] = (0xFF << 24) | (r << 16) | (g << 8) | (b); 59 | } 60 | } 61 | } 62 | 63 | public static unsafe void DrawLetter(this BitmapContext context, int x0, int y0, IntRect cliprect, ClearTypeLetterGlyph glyph) 64 | { 65 | //if (glyph.Instructions == null) return; 66 | if (glyph.Items == null) return; 67 | 68 | // Use refs for faster access (really important!) speeds up a lot! 69 | int w = context.Width; 70 | int h = context.Height; 71 | var pixels = context.Pixels; 72 | 73 | int xmin = cliprect.Left; 74 | int ymin = cliprect.Top; 75 | int xmax = cliprect.Right; 76 | int ymax = cliprect.Bottom; 77 | 78 | if (xmin < 0) xmin = 0; 79 | if (ymin < 0) ymin = 0; 80 | if (xmax >= w) xmax = w - 1; 81 | if (ymax >= h) ymax = h - 1; 82 | 83 | fixed (ClearTypeLetterGlyph.Item* items = glyph.Items) 84 | { 85 | int itemCount = glyph.Items.Length; 86 | ClearTypeLetterGlyph.Item* currentItem = items; 87 | //if (x0 >= xmin && y0 >= ymin && x0 + glyph.Width < xmax && y0 + glyph.Height < ymax) 88 | //{ 89 | // for (int i = 0; i < itemCount; i++, currentItem++) 90 | // { 91 | // pixels[(y0 + currentItem->Y) * w + x0 + currentItem->X] = currentItem->Color; 92 | // } 93 | //} 94 | //else 95 | //{ 96 | for (int i = 0; i < itemCount; i++, currentItem++) 97 | { 98 | int x = x0 + currentItem->X; 99 | int y = y0 + currentItem->Y; 100 | int color = currentItem->Color; 101 | if (x < xmin || y < ymin || x > xmax || y > ymax) continue; 102 | 103 | pixels[y * w + x] = color; 104 | } 105 | //} 106 | } 107 | 108 | //fixed (int *instructions = glyph.Instructions) 109 | //{ 110 | // int* current = instructions; 111 | // while (*current != -1) 112 | // { 113 | // int dy = *current++; 114 | // int dx = *current++; 115 | // int count0 = *current++; 116 | 117 | // int y = y0 + dy; 118 | // if (y >= ymin && y <= ymax) 119 | // { 120 | // int x = x0 + dx; 121 | // int* dst = pixels + y*w + x; 122 | // int* src = current; 123 | // int count = count0; 124 | 125 | // if (x < xmin) 126 | // { 127 | // int dmin = xmin - x; 128 | // x += dmin; 129 | // dst += dmin; 130 | // src += dmin; 131 | // count -= dmin; 132 | // } 133 | 134 | // if (x + count - 1 > xmax) 135 | // { 136 | // int dmax = x + count - 1 - xmax; 137 | // count -= dmax; 138 | // } 139 | 140 | // if (count > 0) 141 | // { 142 | // NativeMethods.memcpy(dst, src, count * 4); 143 | 144 | // //if (count < 10) 145 | // //{ 146 | // // while (count > 0) 147 | // // { 148 | // // *dst++ = *src++; 149 | // // count--; 150 | // // } 151 | // //} 152 | // //else 153 | // //{ 154 | // // NativeMethods.memcpy(dst, src, count*4); 155 | // //} 156 | // } 157 | // } 158 | 159 | // current += count0; 160 | // } 161 | //} 162 | } 163 | 164 | public static int DrawString(this WriteableBitmap bmp, int x0, int y0, IntRect cliprect, Color fontColor, GlyphFont font, string text) 165 | { 166 | return DrawString(bmp, x0, y0, cliprect, fontColor, null, font, text); 167 | } 168 | 169 | public static int DrawString(this WriteableBitmap bmp, int x0, int y0, IntRect cliprect, Color fontColor, Color? bgColor, GlyphFont font, string text) 170 | { 171 | if (text == null) return 0; 172 | int dx = 0, dy = 0; 173 | int textwi = 0; 174 | 175 | using (var context = bmp.GetBitmapContext()) 176 | { 177 | foreach (char ch in text) 178 | { 179 | if (ch == '\n') 180 | { 181 | if (dx > textwi) textwi = dx; 182 | dx = 0; 183 | dy += font.TextHeight; 184 | } 185 | if (x0 + dx <= cliprect.Right) 186 | { 187 | if (font.IsClearType) 188 | { 189 | if (!bgColor.HasValue) 190 | throw new Exception("Clear type fonts must have background specified"); 191 | var letter = font.GetClearTypeLetter(ch, fontColor, bgColor.Value); 192 | if (letter == null) continue; 193 | context.DrawLetter(x0 + dx, y0 + dy, cliprect, letter); 194 | dx += letter.Width; 195 | } 196 | else 197 | { 198 | var letter = font.GetGrayScaleLetter(ch); 199 | if (letter == null) continue; 200 | context.DrawLetter(x0 + dx, y0 + dy, cliprect, fontColor, letter); 201 | dx += letter.Width; 202 | } 203 | } 204 | } 205 | } 206 | 207 | if (dx > textwi) textwi = dx; 208 | return textwi; 209 | } 210 | 211 | public static int DrawString(this WriteableBitmap bmp, int x0, int y0, IntRect cliprect, Color fontColor, PortableFontDesc typeface, string text) 212 | { 213 | var font = GetFont(typeface); 214 | return bmp.DrawString(x0, y0, cliprect, fontColor, font, text); 215 | } 216 | 217 | public static int DrawString(this WriteableBitmap bmp, int x0, int y0, Color fontColor, PortableFontDesc typeface, string text) 218 | { 219 | var font = GetFont(typeface); 220 | return bmp.DrawString(x0, y0, new IntRect(new IntPoint(0, 0), new IntSize(bmp.PixelWidth, bmp.PixelHeight)), fontColor, font, text); 221 | } 222 | 223 | public static int DrawString(this WriteableBitmap bmp, int x0, int y0, Color fontColor, Color? bgColor, PortableFontDesc typeface, string text) 224 | { 225 | var font = GetFont(typeface); 226 | return bmp.DrawString(x0, y0, new IntRect(new IntPoint(0, 0), new IntSize(bmp.PixelWidth, bmp.PixelHeight)), fontColor, bgColor, font, text); 227 | } 228 | 229 | public static GlyphFont GetFont(PortableFontDesc typeface) 230 | { 231 | lock (FontCache) 232 | { 233 | if (FontCache.ContainsKey(typeface)) return FontCache[typeface]; 234 | } 235 | var fontFlags = System.Drawing.FontStyle.Regular; 236 | if (typeface.IsItalic) fontFlags |= System.Drawing.FontStyle.Italic; 237 | if (typeface.IsBold) fontFlags |= System.Drawing.FontStyle.Bold; 238 | var font = new GlyphFont 239 | { 240 | Typeface = new Typeface(new FontFamily(typeface.FontName), 241 | typeface.IsItalic ? FontStyles.Italic : FontStyles.Normal, 242 | typeface.IsBold ? FontWeights.Bold : FontWeights.Normal, 243 | FontStretches.Normal), 244 | EmSize = typeface.EmSize, 245 | Font = new Font(typeface.FontName, typeface.EmSize * 76.0f / 92.0f, fontFlags), 246 | IsClearType = typeface.IsClearType, 247 | }; 248 | font.Typeface.TryGetGlyphTypeface(out font.GlyphTypeface); 249 | lock (FontCache) 250 | { 251 | FontCache[typeface] = font; 252 | } 253 | return font; 254 | } 255 | } 256 | 257 | public struct ColorGlyphKey : IEquatable 258 | { 259 | public int FontColor; 260 | public int BackgroundColor; 261 | public char Char; 262 | 263 | public ColorGlyphKey(Color fontColor, Color backgroundColor, char @char) 264 | { 265 | FontColor = (fontColor.A << 24) | (fontColor.R << 16) | (fontColor.G << 8) | fontColor.B; 266 | BackgroundColor = (backgroundColor.A << 24) | (backgroundColor.R << 16) | (backgroundColor.G << 8) | backgroundColor.B; 267 | Char = @char; 268 | } 269 | 270 | public bool Equals(ColorGlyphKey other) 271 | { 272 | return FontColor.Equals(other.FontColor) && BackgroundColor.Equals(other.BackgroundColor) && Char == other.Char; 273 | } 274 | 275 | public override bool Equals(object obj) 276 | { 277 | if (ReferenceEquals(null, obj)) return false; 278 | return obj is ColorGlyphKey && Equals((ColorGlyphKey)obj); 279 | } 280 | 281 | public override int GetHashCode() 282 | { 283 | unchecked 284 | { 285 | var hashCode = FontColor.GetHashCode(); 286 | hashCode = (hashCode * 397) ^ BackgroundColor.GetHashCode(); 287 | hashCode = (hashCode * 397) ^ Char.GetHashCode(); 288 | return hashCode; 289 | } 290 | } 291 | } 292 | 293 | public class GlyphFont 294 | { 295 | public Dictionary Glyphs = new Dictionary(); 296 | public Dictionary ColorGlyphs = new Dictionary(); 297 | public Typeface Typeface; 298 | public double EmSize; 299 | public GlyphTypeface GlyphTypeface; 300 | public System.Drawing.Font Font; 301 | public bool IsClearType; 302 | 303 | public GrayScaleLetterGlyph GetGrayScaleLetter(char ch) 304 | { 305 | lock (Glyphs) 306 | { 307 | if (!Glyphs.ContainsKey(ch)) 308 | { 309 | Glyphs[ch] = GrayScaleLetterGlyph.CreateGlyph(Typeface, GlyphTypeface, EmSize, ch); 310 | } 311 | return Glyphs[ch]; 312 | } 313 | } 314 | 315 | public ClearTypeLetterGlyph GetClearTypeLetter(char ch, Color fontColor, Color bgColor) 316 | { 317 | lock (ColorGlyphs) 318 | { 319 | var key = new ColorGlyphKey(fontColor, bgColor, ch); 320 | ClearTypeLetterGlyph glyph; 321 | 322 | if (!ColorGlyphs.TryGetValue(key, out glyph)) 323 | { 324 | glyph = ClearTypeLetterGlyph.CreateGlyph(GlyphTypeface, Font, EmSize, ch, fontColor, bgColor); 325 | ColorGlyphs[key] = glyph; 326 | } 327 | return glyph; 328 | } 329 | } 330 | 331 | public int GetTextWidth(string text, int? maxSize = null) 332 | { 333 | int maxLineWidth = 0; 334 | int curLineWidth = 0; 335 | if (text == null) return 0; 336 | foreach (var ch in text) 337 | { 338 | if (ch == '\n') 339 | { 340 | if (curLineWidth > maxLineWidth) maxLineWidth = curLineWidth; 341 | curLineWidth = 0; 342 | } 343 | 344 | if (IsClearType) 345 | { 346 | var letter = GetClearTypeLetter(ch, Colors.Black, Colors.White); 347 | if (letter == null) continue; 348 | curLineWidth += letter.Width; 349 | } 350 | else 351 | { 352 | var letter = GetGrayScaleLetter(ch); 353 | if (letter == null) continue; 354 | curLineWidth += letter.Width; 355 | } 356 | if (maxSize.HasValue && maxLineWidth >= maxSize.Value) 357 | { 358 | return maxSize.Value; 359 | } 360 | if (maxSize.HasValue && curLineWidth >= maxSize.Value) 361 | { 362 | return maxSize.Value; 363 | } 364 | } 365 | if (curLineWidth > maxLineWidth) maxLineWidth = curLineWidth; 366 | return maxLineWidth; 367 | } 368 | 369 | public int GetTextHeight(string text) 370 | { 371 | if (text == null) return 0; 372 | int lines = text.Count(x => x == '\n') + 1; 373 | return lines * TextHeight; 374 | } 375 | 376 | public int TextHeight 377 | { 378 | get { return (int)Math.Ceiling(GlyphTypeface.Height * EmSize * DpiDetector.DpiYKoef); } 379 | } 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /WriteableBitmapEx/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace System.Windows.Media.Imaging 6 | { 7 | internal static class NativeMethods 8 | { 9 | [TargetedPatchingOptOut("Internal method only, inlined across NGen boundaries for performance reasons")] 10 | internal static unsafe void CopyUnmanagedMemory(byte* srcPtr, int srcOffset, byte* dstPtr, int dstOffset, int count) 11 | { 12 | srcPtr += srcOffset; 13 | dstPtr += dstOffset; 14 | 15 | memcpy(dstPtr, srcPtr, count); 16 | } 17 | 18 | [TargetedPatchingOptOut("Internal method only, inlined across NGen boundaries for performance reasons")] 19 | internal static void SetUnmanagedMemory(IntPtr dst, int filler, int count) 20 | { 21 | memset(dst, filler, count); 22 | } 23 | 24 | // Win32 memory copy function 25 | //[DllImport("ntdll.dll")] 26 | [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] 27 | public static extern unsafe void* memcpy( 28 | void* dst, 29 | void* src, 30 | int count); 31 | 32 | // Win32 memory set function 33 | //[DllImport("ntdll.dll")] 34 | //[DllImport("coredll.dll", EntryPoint = "memset", SetLastError = false)] 35 | [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] 36 | private static extern void memset( 37 | IntPtr dst, 38 | int filler, 39 | int count); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /WriteableBitmapEx/PortableFontDesc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace System.Windows.Media.Imaging 7 | { 8 | public class PortableFontDesc 9 | { 10 | public readonly string FontName; 11 | public readonly int EmSize; 12 | public readonly bool IsBold; 13 | public readonly bool IsItalic; 14 | public readonly bool IsClearType; 15 | 16 | public PortableFontDesc(string name = "Arial", int emsize = 12, bool isbold = false, bool isitalic = false, bool cleartype = false) 17 | { 18 | FontName = name; 19 | EmSize = emsize; 20 | IsBold = isbold; 21 | IsItalic = isitalic; 22 | IsClearType = cleartype; 23 | } 24 | 25 | public override int GetHashCode() 26 | { 27 | unchecked 28 | { 29 | return FontName.GetHashCode() ^ EmSize.GetHashCode() ^ IsBold.GetHashCode() ^ IsItalic.GetHashCode() ^ IsClearType.GetHashCode(); 30 | } 31 | } 32 | 33 | public override bool Equals(object obj) 34 | { 35 | var other = obj as PortableFontDesc; 36 | if (other == null) return false; 37 | return FontName == other.FontName && EmSize == other.EmSize && IsBold == other.IsBold && IsItalic == other.IsItalic && IsClearType == other.IsClearType; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WriteableBitmapEx/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | // 3 | // Project: WriteableBitmapEx - WriteableBitmap extensions 4 | // Description: Assembly Infos. 5 | // 6 | // Changed by: $Author$ 7 | // Changed on: $Date$ 8 | // Changed in: $Revision$ 9 | // Project: $URL$ 10 | // Id: $Id$ 11 | // 12 | // 13 | // Copyright © 2009-2012 Rene Schulte and WriteableBitmapEx Contributors 14 | // 15 | // This Software is weak copyleft open source. Please read the License.txt for details. 16 | // 17 | #endregion 18 | 19 | using System.Reflection; 20 | using System.Runtime.CompilerServices; 21 | using System.Runtime.InteropServices; 22 | 23 | // General Information about an assembly is controlled through the following 24 | // set of attributes. Change these attribute values to modify the information 25 | // associated with an assembly. 26 | [assembly: AssemblyTitle("WriteableBitmapEx.Wpf")] 27 | [assembly: AssemblyConfiguration("")] 28 | [assembly: AssemblyDescription("The WriteableBitmapEx library is a collection of extension methods for the WriteableBitmap. The extension methods are easy to use like built-in methods and offer GDI+ like functionality for Silverlight web, Windows Phone, WPF and WinRT.")] 29 | 30 | [assembly: Guid("2f062d6e-c93d-4300-b408-2d612aa9b885")] 31 | -------------------------------------------------------------------------------- /WriteableBitmapEx/Properties/WBX_key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/WriteableBitmapEx/Properties/WBX_key.snk -------------------------------------------------------------------------------- /WriteableBitmapEx/ScrollingTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media.Imaging; 6 | 7 | namespace System.Windows.Media.Imaging 8 | { 9 | public static class ScrollingTool 10 | { 11 | /// 12 | /// scrolls content of given rectangle 13 | /// 14 | /// 15 | /// if greater than 0, scrolls down, else scrolls up 16 | /// 17 | public static unsafe void ScrollY(this WriteableBitmap bmp, int dy, IntRect rect, Color? background = null) 18 | { 19 | int bgcolor = WriteableBitmapExtensions.ConvertColor(background ?? Colors.White); 20 | 21 | using (var context = bmp.GetBitmapContext()) 22 | { 23 | // Use refs for faster access (really important!) speeds up a lot! 24 | int w = context.Width; 25 | int h = context.Height; 26 | var pixels = context.Pixels; 27 | int xmin = rect.Left; 28 | int ymin = rect.Top; 29 | int xmax = rect.Right; 30 | int ymax = rect.Bottom; 31 | 32 | if (xmin < 0) xmin = 0; 33 | if (ymin < 0) ymin = 0; 34 | if (xmax >= w) xmax = w - 1; 35 | if (ymax >= h) ymax = h - 1; 36 | int xcnt = xmax - xmin + 1; 37 | int ycnt = ymax - ymin + 1; 38 | if (xcnt <= 0) return; 39 | 40 | if (dy > 0) 41 | { 42 | for (int y = ymax; y >= ymin + dy; y--) 43 | { 44 | int ydstidex = y; 45 | int ysrcindex = y - dy; 46 | if (ysrcindex < ymin || ysrcindex > ymax) continue; 47 | 48 | NativeMethods.memcpy(pixels + ydstidex*w + xmin, pixels + ysrcindex*w + xmin, xcnt*4); 49 | } 50 | } 51 | if (dy < 0) 52 | { 53 | for (int y = ymin; y <= ymax - dy; y++) 54 | { 55 | int ysrcindex = y - dy; 56 | int ydstidex = y; 57 | if (ysrcindex < ymin || ysrcindex > ymax) continue; 58 | NativeMethods.memcpy(pixels + ydstidex*w + xmin, pixels + ysrcindex*w + xmin, xcnt*4); 59 | } 60 | } 61 | 62 | if (dy < 0) 63 | { 64 | bmp.FillRectangle(xmin, ymax + dy + 1, xmax, ymax, bgcolor); 65 | } 66 | if (dy > 0) 67 | { 68 | bmp.FillRectangle(xmin, ymin, xmax, ymin + dy - 1, bgcolor); 69 | } 70 | } 71 | } 72 | 73 | /// 74 | /// scrolls content of given rectangle 75 | /// 76 | /// 77 | /// if greater than 0, scrolls right, else scrolls left 78 | /// 79 | public static unsafe void ScrollX(this WriteableBitmap bmp, int dx, IntRect rect, Color? background = null) 80 | { 81 | int bgcolor = WriteableBitmapExtensions.ConvertColor(background ?? Colors.White); 82 | using (var context = bmp.GetBitmapContext()) 83 | { 84 | // Use refs for faster access (really important!) speeds up a lot! 85 | int w = context.Width; 86 | int h = context.Height; 87 | var pixels = context.Pixels; 88 | int xmin = rect.Left; 89 | int ymin = rect.Top; 90 | int xmax = rect.Right; 91 | int ymax = rect.Bottom; 92 | 93 | if (xmin < 0) xmin = 0; 94 | if (ymin < 0) ymin = 0; 95 | if (xmax >= w) xmax = w - 1; 96 | if (ymax >= h) ymax = h - 1; 97 | int xcnt = xmax - xmin + 1; 98 | int ycnt = ymax - ymin + 1; 99 | 100 | int srcx = xmin, dstx = xmin; 101 | if (dx < 0) 102 | { 103 | xcnt += dx; 104 | dstx = xmin; 105 | srcx = xmin - dx; 106 | } 107 | if (dx > 0) 108 | { 109 | xcnt -= dx; 110 | srcx = xmin; 111 | dstx = xmin + dx; 112 | } 113 | 114 | if (xcnt <= 0) return; 115 | 116 | int* yptr = pixels + w*ymin; 117 | for (int y = ymin; y <= ymax; y++, yptr += w) 118 | { 119 | NativeMethods.memcpy(yptr + dstx, yptr + srcx, xcnt*4); 120 | } 121 | 122 | if (dx < 0) 123 | { 124 | bmp.FillRectangle(xmax + dx + 1, ymin, xmax, ymax, bgcolor); 125 | } 126 | if (dx > 0) 127 | { 128 | bmp.FillRectangle(xmin, ymin, xmin + dx - 1, ymax, bgcolor); 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapAntialiasingExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | // 3 | // Project: WriteableBitmapEx - WriteableBitmap extensions 4 | // Description: Collection of internal anti-aliasing helper methods for the WriteableBitmap class. 5 | // 6 | // Changed by: $Author: unknown $ 7 | // Changed on: $Date: 2015-02-24 20:36:41 +0100 (Di, 24 Feb 2015) $ 8 | // Changed in: $Revision: 112951 $ 9 | // Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs $ 10 | // Id: $Id: WriteableBitmapTransformationExtensions.cs 112951 2015-02-24 19:36:41Z unknown $ 11 | // 12 | // 13 | // Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors 14 | // 15 | // This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;) 16 | // 17 | #endregion 18 | using System; 19 | 20 | #if NETFX_CORE 21 | using Windows.Foundation; 22 | using Windows.UI.Xaml.Media.Imaging; 23 | using Windows.UI; 24 | 25 | namespace Windows.UI.Xaml.Media.Imaging 26 | #else 27 | namespace System.Windows.Media.Imaging 28 | #endif 29 | { 30 | /// 31 | /// Collection of draw extension methods for the Silverlight WriteableBitmap class. 32 | /// 33 | public 34 | #if !SILVERLIGHT 35 | unsafe 36 | #endif 37 | static partial class WriteableBitmapExtensions 38 | { 39 | private static readonly int[] leftEdgeX = new int[8192]; 40 | private static readonly int[] rightEdgeX = new int[8192]; 41 | 42 | private static void AAWidthLine(int width, int height, BitmapContext context, float x1, float y1, float x2, float y2, float lineWidth, Int32 color) 43 | { 44 | // Perform cohen-sutherland clipping if either point is out of the viewport 45 | if (!CohenSutherlandLineClip(new Rect(0, 0, width, height), ref x1, ref y1, ref x2, ref y2)) return; 46 | 47 | if (lineWidth <= 0) return; 48 | 49 | var buffer = context.Pixels; 50 | 51 | if (y1 > y2) 52 | { 53 | Swap(ref x1, ref x2); 54 | Swap(ref y1, ref y2); 55 | } 56 | 57 | if (x1 == x2) 58 | { 59 | x1 -= (int)lineWidth / 2; 60 | x2 += (int)lineWidth / 2; 61 | 62 | if (x1 < 0) 63 | x1 = 0; 64 | if (x2 < 0) 65 | return; 66 | 67 | if (x1 >= width) 68 | return; 69 | if (x2 >= width) 70 | x2 = width - 1; 71 | 72 | if (y1 >= height || y2 < 0) 73 | return; 74 | 75 | if (y1 < 0) 76 | y1 = 0; 77 | if (y2 >= height) 78 | y2 = height - 1; 79 | 80 | for (var x = (int)x1; x <= x2; x++) 81 | { 82 | for (var y = (int)y1; y <= y2; y++) 83 | { 84 | var a = (byte)((color & 0xff000000) >> 24); 85 | var r = (byte)((color & 0x00ff0000) >> 16); 86 | var g = (byte)((color & 0x0000ff00) >> 8); 87 | var b = (byte)((color & 0x000000ff) >> 0); 88 | 89 | byte rs, gs, bs; 90 | byte rd, gd, bd; 91 | 92 | int d; 93 | 94 | rs = r; 95 | gs = g; 96 | bs = b; 97 | 98 | d = buffer[y * width + x]; 99 | 100 | rd = (byte)((d & 0x00ff0000) >> 16); 101 | gd = (byte)((d & 0x0000ff00) >> 8); 102 | bd = (byte)((d & 0x000000ff) >> 0); 103 | 104 | rd = (byte)((rs * a + rd * (0xff - a)) >> 8); 105 | gd = (byte)((gs * a + gd * (0xff - a)) >> 8); 106 | bd = (byte)((bs * a + bd * (0xff - a)) >> 8); 107 | 108 | buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0); 109 | } 110 | } 111 | 112 | return; 113 | } 114 | if (y1 == y2) 115 | { 116 | if (x1 > x2) Swap(ref x1, ref x2); 117 | 118 | y1 -= (int)lineWidth / 2; 119 | y2 += (int)lineWidth / 2; 120 | 121 | if (y1 < 0) y1 = 0; 122 | if (y2 < 0) return; 123 | 124 | if (y1 >= height) return; 125 | if (y2 >= height) x2 = height - 1; 126 | 127 | if (x1 >= width || y2 < 0) return; 128 | 129 | if (x1 < 0) x1 = 0; 130 | if (x2 >= width) x2 = width - 1; 131 | 132 | for (var x = (int)x1; x <= x2; x++) 133 | { 134 | for (var y = (int)y1; y <= y2; y++) 135 | { 136 | var a = (byte)((color & 0xff000000) >> 24); 137 | var r = (byte)((color & 0x00ff0000) >> 16); 138 | var g = (byte)((color & 0x0000ff00) >> 8); 139 | var b = (byte)((color & 0x000000ff) >> 0); 140 | 141 | Byte rs, gs, bs; 142 | Byte rd, gd, bd; 143 | 144 | Int32 d; 145 | 146 | rs = r; 147 | gs = g; 148 | bs = b; 149 | 150 | d = buffer[y * width + x]; 151 | 152 | rd = (byte)((d & 0x00ff0000) >> 16); 153 | gd = (byte)((d & 0x0000ff00) >> 8); 154 | bd = (byte)((d & 0x000000ff) >> 0); 155 | 156 | rd = (byte)((rs * a + rd * (0xff - a)) >> 8); 157 | gd = (byte)((gs * a + gd * (0xff - a)) >> 8); 158 | bd = (byte)((bs * a + bd * (0xff - a)) >> 8); 159 | 160 | buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0); 161 | } 162 | } 163 | 164 | return; 165 | } 166 | 167 | y1 += 1; 168 | y2 += 1; 169 | 170 | float slope = (y2 - y1) / (x2 - x1); 171 | float islope = (x2 - x1) / (y2 - y1); 172 | 173 | float m = slope; 174 | float w = lineWidth; 175 | 176 | float dx = x2 - x1; 177 | float dy = y2 - y1; 178 | 179 | var xtot = (float)(w * dy / Math.Sqrt(dx * dx + dy * dy)); 180 | var ytot = (float)(w * dx / Math.Sqrt(dx * dx + dy * dy)); 181 | 182 | float sm = dx * dy / (dx * dx + dy * dy); 183 | 184 | // Center it. 185 | 186 | x1 += xtot / 2; 187 | y1 -= ytot / 2; 188 | x2 += xtot / 2; 189 | y2 -= ytot / 2; 190 | 191 | // 192 | // 193 | 194 | float sx = -xtot; 195 | float sy = +ytot; 196 | 197 | var ix1 = (int)x1; 198 | var iy1 = (int)y1; 199 | 200 | var ix2 = (int)x2; 201 | var iy2 = (int)y2; 202 | 203 | var ix3 = (int)(x1 + sx); 204 | var iy3 = (int)(y1 + sy); 205 | 206 | var ix4 = (int)(x2 + sx); 207 | var iy4 = (int)(y2 + sy); 208 | 209 | if (lineWidth == 2) 210 | { 211 | if (Math.Abs(dy) < Math.Abs(dx)) 212 | { 213 | if (x1 < x2) 214 | { 215 | iy3 = iy1 + 2; 216 | iy4 = iy2 + 2; 217 | } 218 | else 219 | { 220 | iy1 = iy3 + 2; 221 | iy2 = iy4 + 2; 222 | } 223 | } 224 | else 225 | { 226 | ix1 = ix3 + 2; 227 | ix2 = ix4 + 2; 228 | } 229 | } 230 | 231 | int starty = Math.Min(Math.Min(iy1, iy2), Math.Min(iy3, iy4)); 232 | int endy = Math.Max(Math.Max(iy1, iy2), Math.Max(iy3, iy4)); 233 | 234 | if (starty < 0) starty = -1; 235 | if (endy >= height) endy = height + 1; 236 | 237 | for (int y = starty + 1; y < endy - 1; y++) 238 | { 239 | leftEdgeX[y] = -1 << 16; 240 | rightEdgeX[y] = 1 << 16 - 1; 241 | } 242 | 243 | 244 | AALineQ1(width, height, context, ix1, iy1, ix2, iy2, color, sy > 0, false); 245 | AALineQ1(width, height, context, ix3, iy3, ix4, iy4, color, sy < 0, true); 246 | 247 | if (lineWidth > 1) 248 | { 249 | AALineQ1(width, height, context, ix1, iy1, ix3, iy3, color, true, sy > 0); 250 | AALineQ1(width, height, context, ix2, iy2, ix4, iy4, color, false, sy < 0); 251 | } 252 | 253 | if (x1 < x2) 254 | { 255 | if (iy2 >= 0 && iy2 < height) rightEdgeX[iy2] = Math.Min(ix2, rightEdgeX[iy2]); 256 | if (iy3 >= 0 && iy3 < height) leftEdgeX[iy3] = Math.Max(ix3, leftEdgeX[iy3]); 257 | } 258 | else 259 | { 260 | if (iy1 >= 0 && iy1 < height) rightEdgeX[iy1] = Math.Min(ix1, rightEdgeX[iy1]); 261 | if (iy4 >= 0 && iy4 < height) leftEdgeX[iy4] = Math.Max(ix4, leftEdgeX[iy4]); 262 | } 263 | 264 | //return; 265 | 266 | for (int y = starty + 1; y < endy - 1; y++) 267 | { 268 | leftEdgeX[y] = Math.Max(leftEdgeX[y], 0); 269 | rightEdgeX[y] = Math.Min(rightEdgeX[y], width - 1); 270 | 271 | for (int x = leftEdgeX[y]; x <= rightEdgeX[y]; x++) 272 | { 273 | var a = (byte)((color & 0xff000000) >> 24); 274 | var r = (byte)((color & 0x00ff0000) >> 16); 275 | var g = (byte)((color & 0x0000ff00) >> 8); 276 | var b = (byte)((color & 0x000000ff) >> 0); 277 | 278 | Byte rs, gs, bs; 279 | Byte rd, gd, bd; 280 | 281 | Int32 d; 282 | 283 | rs = r; 284 | gs = g; 285 | bs = b; 286 | 287 | d = buffer[y * width + x]; 288 | 289 | rd = (byte)((d & 0x00ff0000) >> 16); 290 | gd = (byte)((d & 0x0000ff00) >> 8); 291 | bd = (byte)((d & 0x000000ff) >> 0); 292 | 293 | rd = (byte)((rs * a + rd * (0xff - a)) >> 8); 294 | gd = (byte)((gs * a + gd * (0xff - a)) >> 8); 295 | bd = (byte)((bs * a + bd * (0xff - a)) >> 8); 296 | 297 | buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0); 298 | } 299 | } 300 | } 301 | 302 | private static void Swap(ref T a, ref T b) 303 | { 304 | T t = a; 305 | a = b; 306 | b = t; 307 | } 308 | 309 | private static void AALineQ1(int width, int height, BitmapContext context, int x1, int y1, int x2, int y2, Int32 color, bool minEdge, bool leftEdge) 310 | { 311 | Byte off = 0; 312 | 313 | if (minEdge) off = 0xff; 314 | 315 | if (x1 == x2) return; 316 | if (y1 == y2) return; 317 | 318 | var buffer = context.Pixels; 319 | 320 | if (y1 > y2) 321 | { 322 | Swap(ref x1, ref x2); 323 | Swap(ref y1, ref y2); 324 | } 325 | 326 | int deltax = (x2 - x1); 327 | int deltay = (y2 - y1); 328 | 329 | if (x1 > x2) deltax = (x1 - x2); 330 | 331 | int x = x1; 332 | int y = y1; 333 | 334 | UInt16 m = 0; 335 | 336 | if (deltax > deltay) m = (ushort)(((deltay << 16) / deltax)); 337 | else m = (ushort)(((deltax << 16) / deltay)); 338 | 339 | UInt16 e = 0; 340 | 341 | var a = (byte)((color & 0xff000000) >> 24); 342 | var r = (byte)((color & 0x00ff0000) >> 16); 343 | var g = (byte)((color & 0x0000ff00) >> 8); 344 | var b = (byte)((color & 0x000000ff) >> 0); 345 | 346 | Byte rs, gs, bs; 347 | Byte rd, gd, bd; 348 | 349 | Int32 d; 350 | 351 | Byte ta = a; 352 | 353 | e = 0; 354 | 355 | if (deltax >= deltay) 356 | { 357 | while (deltax-- != 0) 358 | { 359 | if ((UInt16)(e + m) <= e) // Roll 360 | { 361 | y++; 362 | } 363 | 364 | e += m; 365 | 366 | if (x1 < x2) x++; 367 | else x--; 368 | 369 | if (y < 0 || y >= height) continue; 370 | 371 | if (leftEdge) leftEdgeX[y] = Math.Max(x + 1, leftEdgeX[y]); 372 | else rightEdgeX[y] = Math.Min(x - 1, rightEdgeX[y]); 373 | 374 | if (x < 0 || x >= width) continue; 375 | 376 | // 377 | 378 | ta = (byte)((a * (UInt16)(((((UInt16)(e >> 8))) ^ off))) >> 8); 379 | 380 | rs = r; 381 | gs = g; 382 | bs = b; 383 | 384 | d = buffer[y * width + x]; 385 | 386 | rd = (byte)((d & 0x00ff0000) >> 16); 387 | gd = (byte)((d & 0x0000ff00) >> 8); 388 | bd = (byte)((d & 0x000000ff) >> 0); 389 | 390 | rd = (byte)((rs * ta + rd * (0xff - ta)) >> 8); 391 | gd = (byte)((gs * ta + gd * (0xff - ta)) >> 8); 392 | bd = (byte)((bs * ta + bd * (0xff - ta)) >> 8); 393 | 394 | buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0); 395 | 396 | // 397 | } 398 | } 399 | else 400 | { 401 | off ^= 0xff; 402 | 403 | while (--deltay != 0) 404 | { 405 | if ((UInt16)(e + m) <= e) // Roll 406 | { 407 | if (x1 < x2) x++; 408 | else x--; 409 | } 410 | 411 | e += m; 412 | 413 | y++; 414 | 415 | if (x < 0 || x >= width) continue; 416 | if (y < 0 || y >= height) continue; 417 | 418 | // 419 | 420 | ta = (byte)((a * (UInt16)(((((UInt16)(e >> 8))) ^ off))) >> 8); 421 | 422 | rs = r; 423 | gs = g; 424 | bs = b; 425 | 426 | d = buffer[y * width + x]; 427 | 428 | rd = (byte)((d & 0x00ff0000) >> 16); 429 | gd = (byte)((d & 0x0000ff00) >> 8); 430 | bd = (byte)((d & 0x000000ff) >> 0); 431 | 432 | rd = (byte)((rs * ta + rd * (0xff - ta)) >> 8); 433 | gd = (byte)((gs * ta + gd * (0xff - ta)) >> 8); 434 | bd = (byte)((bs * ta + bd * (0xff - ta)) >> 8); 435 | 436 | buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0); 437 | 438 | if (leftEdge) leftEdgeX[y] = x + 1; 439 | else rightEdgeX[y] = x - 1; 440 | } 441 | } 442 | } 443 | } 444 | } -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapContextExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | // 3 | // Project: WriteableBitmapEx - WriteableBitmap extensions 4 | // Description: Collection of extension methods for the WriteableBitmap class. 5 | // 6 | // Changed by: $Author$ 7 | // Changed on: $Date$ 8 | // Changed in: $Revision$ 9 | // Project: $URL$ 10 | // Id: $Id$ 11 | // 12 | // 13 | // Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors 14 | // 15 | // This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;) 16 | // 17 | #endregion 18 | 19 | using System; 20 | 21 | #if NETFX_CORE 22 | namespace Windows.UI.Xaml.Media.Imaging 23 | #else 24 | namespace System.Windows.Media.Imaging 25 | #endif 26 | { 27 | /// 28 | /// Provides the WriteableBitmap context pixel data 29 | /// 30 | public static partial class WriteableBitmapContextExtensions 31 | { 32 | /// 33 | /// Gets a BitmapContext within which to perform nested IO operations on the bitmap 34 | /// 35 | /// For WPF the BitmapContext will lock the bitmap. Call Dispose on the context to unlock 36 | /// 37 | /// 38 | public static BitmapContext GetBitmapContext(this WriteableBitmap bmp) 39 | { 40 | return new BitmapContext(bmp); 41 | } 42 | 43 | /// 44 | /// Gets a BitmapContext within which to perform nested IO operations on the bitmap 45 | /// 46 | /// For WPF the BitmapContext will lock the bitmap. Call Dispose on the context to unlock 47 | /// The bitmap. 48 | /// The ReadWriteMode. If set to ReadOnly, the bitmap will not be invalidated on dispose of the context, else it will 49 | /// 50 | public static BitmapContext GetBitmapContext(this WriteableBitmap bmp, ReadWriteMode mode) 51 | { 52 | return new BitmapContext(bmp, mode); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapConvertExtensions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/WriteableBitmapEx/WriteableBitmapConvertExtensions.cs -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapEx.Wpf.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {B0AA6A94-6784-4221-81F0-244A68C374C0} 9 | Library 10 | Properties 11 | System.Windows.Media.Imaging 12 | WriteableBitmapEx.Wpf 13 | v4.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | ..\..\Build\Debug\ 21 | TRACE;DEBUG;WPF 22 | prompt 23 | 4 24 | true 25 | 26 | 27 | pdbonly 28 | true 29 | ..\..\Build\Release\ 30 | TRACE;WPF 31 | prompt 32 | 4 33 | true 34 | ..\..\Build\Release\WriteableBitmapEx.Wpf.XML 35 | 36 | 37 | true 38 | 39 | 40 | Properties\WBX_key.snk 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapFillExtensions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/WriteableBitmapEx/WriteableBitmapFillExtensions.cs -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapFilterExtensions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/WriteableBitmapEx/WriteableBitmapFilterExtensions.cs -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapSplineExtensions.cs: -------------------------------------------------------------------------------- 1 | #region Header 2 | // 3 | // Project: WriteableBitmapEx - WriteableBitmap extensions 4 | // Description: Collection of draw spline extension methods for the WriteableBitmap class. 5 | // 6 | // Changed by: $Author$ 7 | // Changed on: $Date$ 8 | // Changed in: $Revision$ 9 | // Project: $URL$ 10 | // Id: $Id$ 11 | // 12 | // 13 | // Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors 14 | // 15 | // This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;) 16 | // 17 | #endregion 18 | 19 | using System; 20 | 21 | #if NETFX_CORE 22 | namespace Windows.UI.Xaml.Media.Imaging 23 | #else 24 | namespace System.Windows.Media.Imaging 25 | #endif 26 | { 27 | /// 28 | /// Collection of draw spline extension methods for the WriteableBitmap class. 29 | /// 30 | public 31 | #if WPF 32 | unsafe 33 | #endif 34 | static partial class WriteableBitmapExtensions 35 | { 36 | #region Fields 37 | 38 | private const float StepFactor = 2f; 39 | 40 | #endregion 41 | 42 | #region Methods 43 | 44 | #region Beziér 45 | 46 | /// 47 | /// Draws a cubic Beziér spline defined by start, end and two control points. 48 | /// 49 | /// The WriteableBitmap. 50 | /// The x-coordinate of the start point. 51 | /// The y-coordinate of the start point. 52 | /// The x-coordinate of the 1st control point. 53 | /// The y-coordinate of the 1st control point. 54 | /// The x-coordinate of the 2nd control point. 55 | /// The y-coordinate of the 2nd control point. 56 | /// The x-coordinate of the end point. 57 | /// The y-coordinate of the end point. 58 | /// The color. 59 | public static void DrawBezier(this WriteableBitmap bmp, int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2, Color color) 60 | { 61 | var col = ConvertColor(color); 62 | bmp.DrawBezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2, col); 63 | } 64 | 65 | /// 66 | /// Draws a cubic Beziér spline defined by start, end and two control points. 67 | /// 68 | /// The WriteableBitmap. 69 | /// The x-coordinate of the start point. 70 | /// The y-coordinate of the start point. 71 | /// The x-coordinate of the 1st control point. 72 | /// The y-coordinate of the 1st control point. 73 | /// The x-coordinate of the 2nd control point. 74 | /// The y-coordinate of the 2nd control point. 75 | /// The x-coordinate of the end point. 76 | /// The y-coordinate of the end point. 77 | /// The color. 78 | public static void DrawBezier(this WriteableBitmap bmp, int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2, int color) 79 | { 80 | // Determine distances between controls points (bounding rect) to find the optimal stepsize 81 | var minX = Math.Min(x1, Math.Min(cx1, Math.Min(cx2, x2))); 82 | var minY = Math.Min(y1, Math.Min(cy1, Math.Min(cy2, y2))); 83 | var maxX = Math.Max(x1, Math.Max(cx1, Math.Max(cx2, x2))); 84 | var maxY = Math.Max(y1, Math.Max(cy1, Math.Max(cy2, y2))); 85 | 86 | // Get slope 87 | var lenx = maxX - minX; 88 | var len = maxY - minY; 89 | if (lenx > len) 90 | { 91 | len = lenx; 92 | } 93 | 94 | // Prevent divison by zero 95 | if (len != 0) 96 | { 97 | using (var context = bmp.GetBitmapContext()) 98 | { 99 | // Use refs for faster access (really important!) speeds up a lot! 100 | int w = context.Width; 101 | int h = context.Height; 102 | 103 | // Init vars 104 | var step = StepFactor / len; 105 | int tx1 = x1; 106 | int ty1 = y1; 107 | int tx2, ty2; 108 | 109 | // Interpolate 110 | for (var t = step; t <= 1; t += step) 111 | { 112 | var tSq = t * t; 113 | var t1 = 1 - t; 114 | var t1Sq = t1 * t1; 115 | 116 | tx2 = (int)(t1 * t1Sq * x1 + 3 * t * t1Sq * cx1 + 3 * t1 * tSq * cx2 + t * tSq * x2); 117 | ty2 = (int)(t1 * t1Sq * y1 + 3 * t * t1Sq * cy1 + 3 * t1 * tSq * cy2 + t * tSq * y2); 118 | 119 | // Draw line 120 | DrawLine(context, w, h, tx1, ty1, tx2, ty2, color); 121 | tx1 = tx2; 122 | ty1 = ty2; 123 | } 124 | 125 | // Prevent rounding gap 126 | DrawLine(context, w, h, tx1, ty1, x2, y2, color); 127 | } 128 | } 129 | } 130 | 131 | /// 132 | /// Draws a series of cubic Beziér splines each defined by start, end and two control points. 133 | /// The ending point of the previous curve is used as starting point for the next. 134 | /// Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). 135 | /// 136 | /// The WriteableBitmap. 137 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). 138 | /// The color for the spline. 139 | public static void DrawBeziers(this WriteableBitmap bmp, int[] points, Color color) 140 | { 141 | var col = ConvertColor(color); 142 | bmp.DrawBeziers(points, col); 143 | } 144 | 145 | /// 146 | /// Draws a series of cubic Beziér splines each defined by start, end and two control points. 147 | /// The ending point of the previous curve is used as starting point for the next. 148 | /// Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). 149 | /// 150 | /// The WriteableBitmap. 151 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). 152 | /// The color for the spline. 153 | public static void DrawBeziers(this WriteableBitmap bmp, int[] points, int color) 154 | { 155 | int x1 = points[0]; 156 | int y1 = points[1]; 157 | int x2, y2; 158 | for (int i = 2; i + 5 < points.Length; i += 6) 159 | { 160 | x2 = points[i + 4]; 161 | y2 = points[i + 5]; 162 | bmp.DrawBezier(x1, y1, points[i], points[i + 1], points[i + 2], points[i + 3], x2, y2, color); 163 | x1 = x2; 164 | y1 = y2; 165 | } 166 | } 167 | 168 | #endregion 169 | 170 | #region Cardinal 171 | 172 | /// 173 | /// Draws a segment of a Cardinal spline (cubic) defined by four control points. 174 | /// 175 | /// The x-coordinate of the 1st control point. 176 | /// The y-coordinate of the 1st control point. 177 | /// The x-coordinate of the 2nd control point. 178 | /// The y-coordinate of the 2nd control point. 179 | /// The x-coordinate of the 3rd control point. 180 | /// The y-coordinate of the 3rd control point. 181 | /// The x-coordinate of the 4th control point. 182 | /// The y-coordinate of the 4th control point. 183 | /// The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. 184 | /// The color. 185 | /// The pixel context. 186 | /// The width of the bitmap. 187 | /// The height of the bitmap. 188 | private static void DrawCurveSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float tension, int color, BitmapContext context, int w, int h) 189 | { 190 | // Determine distances between controls points (bounding rect) to find the optimal stepsize 191 | var minX = Math.Min(x1, Math.Min(x2, Math.Min(x3, x4))); 192 | var minY = Math.Min(y1, Math.Min(y2, Math.Min(y3, y4))); 193 | var maxX = Math.Max(x1, Math.Max(x2, Math.Max(x3, x4))); 194 | var maxY = Math.Max(y1, Math.Max(y2, Math.Max(y3, y4))); 195 | 196 | // Get slope 197 | var lenx = maxX - minX; 198 | var len = maxY - minY; 199 | if (lenx > len) 200 | { 201 | len = lenx; 202 | } 203 | 204 | // Prevent divison by zero 205 | if (len != 0) 206 | { 207 | // Init vars 208 | var step = StepFactor / len; 209 | int tx1 = x2; 210 | int ty1 = y2; 211 | int tx2, ty2; 212 | 213 | // Calculate factors 214 | var sx1 = tension * (x3 - x1); 215 | var sy1 = tension * (y3 - y1); 216 | var sx2 = tension * (x4 - x2); 217 | var sy2 = tension * (y4 - y2); 218 | var ax = sx1 + sx2 + 2 * x2 - 2 * x3; 219 | var ay = sy1 + sy2 + 2 * y2 - 2 * y3; 220 | var bx = -2 * sx1 - sx2 - 3 * x2 + 3 * x3; 221 | var by = -2 * sy1 - sy2 - 3 * y2 + 3 * y3; 222 | 223 | // Interpolate 224 | for (var t = step; t <= 1; t += step) 225 | { 226 | var tSq = t * t; 227 | 228 | tx2 = (int)(ax * tSq * t + bx * tSq + sx1 * t + x2); 229 | ty2 = (int)(ay * tSq * t + by * tSq + sy1 * t + y2); 230 | 231 | // Draw line 232 | DrawLine(context, w, h, tx1, ty1, tx2, ty2, color); 233 | tx1 = tx2; 234 | ty1 = ty2; 235 | } 236 | 237 | // Prevent rounding gap 238 | DrawLine(context, w, h, tx1, ty1, x3, y3, color); 239 | } 240 | } 241 | 242 | /// 243 | /// Draws a Cardinal spline (cubic) defined by a point collection. 244 | /// The cardinal spline passes through each point in the collection. 245 | /// 246 | /// The WriteableBitmap. 247 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). 248 | /// The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. 249 | /// The color for the spline. 250 | public static void DrawCurve(this WriteableBitmap bmp, int[] points, float tension, Color color) 251 | { 252 | var col = ConvertColor(color); 253 | bmp.DrawCurve(points, tension, col); 254 | } 255 | 256 | /// 257 | /// Draws a Cardinal spline (cubic) defined by a point collection. 258 | /// The cardinal spline passes through each point in the collection. 259 | /// 260 | /// The WriteableBitmap. 261 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). 262 | /// The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. 263 | /// The color for the spline. 264 | public static void DrawCurve(this WriteableBitmap bmp, int[] points, float tension, int color) 265 | { 266 | using (var context = bmp.GetBitmapContext()) 267 | { 268 | // Use refs for faster access (really important!) speeds up a lot! 269 | int w = context.Width; 270 | int h = context.Height; 271 | 272 | // First segment 273 | DrawCurveSegment(points[0], points[1], points[0], points[1], points[2], points[3], points[4], points[5], tension, color, context, w, h); 274 | 275 | // Middle segments 276 | int i; 277 | for (i = 2; i < points.Length - 4; i += 2) 278 | { 279 | DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 4], points[i + 5], tension, color, context, w, h); 280 | } 281 | 282 | // Last segment 283 | DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 2], points[i + 3], tension, color, context, w, h); 284 | } 285 | } 286 | 287 | /// 288 | /// Draws a closed Cardinal spline (cubic) defined by a point collection. 289 | /// The cardinal spline passes through each point in the collection. 290 | /// 291 | /// The WriteableBitmap. 292 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). 293 | /// The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. 294 | /// The color for the spline. 295 | public static void DrawCurveClosed(this WriteableBitmap bmp, int[] points, float tension, Color color) 296 | { 297 | var col = ConvertColor(color); 298 | bmp.DrawCurveClosed(points, tension, col); 299 | } 300 | 301 | /// 302 | /// Draws a closed Cardinal spline (cubic) defined by a point collection. 303 | /// The cardinal spline passes through each point in the collection. 304 | /// 305 | /// The WriteableBitmap. 306 | /// The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). 307 | /// The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. 308 | /// The color for the spline. 309 | public static void DrawCurveClosed(this WriteableBitmap bmp, int[] points, float tension, int color) 310 | { 311 | using (var context = bmp.GetBitmapContext()) 312 | { 313 | // Use refs for faster access (really important!) speeds up a lot! 314 | int w = context.Width; 315 | int h = context.Height; 316 | 317 | int pn = points.Length; 318 | 319 | // First segment 320 | DrawCurveSegment(points[pn - 2], points[pn - 1], points[0], points[1], points[2], points[3], points[4], points[5], tension, color, context, w, h); 321 | 322 | // Middle segments 323 | int i; 324 | for (i = 2; i < pn - 4; i += 2) 325 | { 326 | DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 4], points[i + 5], tension, color, context, w, h); 327 | } 328 | 329 | // Last segment 330 | DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[0], points[1], tension, color, context, w, h); 331 | 332 | // Last-to-First segment 333 | DrawCurveSegment(points[i], points[i + 1], points[i + 2], points[i + 3], points[0], points[1], points[2], points[3], tension, color, context, w, h); 334 | } 335 | } 336 | 337 | #endregion 338 | 339 | #endregion 340 | } 341 | } -------------------------------------------------------------------------------- /WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janproch/fastwpfgrid/8bcfaf37df343e0b7ccffb3718d9a973a1f58b40/WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs --------------------------------------------------------------------------------