Skip to content

Instantly share code, notes, and snippets.

@sergey-miryanov
Created October 21, 2015 05:31
Show Gist options
  • Save sergey-miryanov/ee8dfca7e71e6cc27395 to your computer and use it in GitHub Desktop.
Save sergey-miryanov/ee8dfca7e71e6cc27395 to your computer and use it in GitHub Desktop.
DynamicTableControl
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using DevExpress.Xpf.Grid;
using GUI.Params.DynamicTable.Impl;
namespace GUI.Params.DynamicTable.Control
{
public class DynamicCellTemplateSelector : DataTemplateSelector
{
public DataTemplate NumericCellTemplate { get; set; }
public DataTemplate StringCellTemplate { get; set; }
public DataTemplate TitleCellTemplate { get; set; }
public DataTemplate NullCellTemplate { get; set; }
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
var cellData = item as GridCellData;
if(cellData != null)
{
if(cellData.Value == null)
{
return NullCellTemplate;
}
else if(cellData.Column.Tag is TitleColumn)
{
return TitleCellTemplate;
}
var column = cellData.Column.Tag as IParamsColumn;
var row = cellData.RowData.Row as ParamsRow;
if(row != null && column != null)
{
var cell = row.GetValue(column.ColumnName);
if (cell is NumericCell || cell is NumericPropertyCell)
{
return NumericCellTemplate;
}
else if (cell is StringCell)
{
return StringCellTemplate;
}
}
if(cellData.Column.Tag is NumericColumn)
{
return NumericCellTemplate;
}
else if(cellData.Column.Tag is StringColumn)
{
return StringCellTemplate;
}
}
return NullCellTemplate;
}
}
}
<dxg:GridControl x:Class="GUI.Params.DynamicTable.Control.DynamicTableControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<dxg:GridControl.View>
<dxg:TableView AllowPerPixelScrolling="True" ShowTotalSummary="True"/>
</dxg:GridControl.View>
</dxg:GridControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DevExpress.Xpf.Grid;
using GUI.Params.DynamicTable;
namespace GUI.Params.DynamicTable.Control
{
/// <summary>
/// Логика взаимодействия для DynamicTableControl.xaml
/// </summary>
public partial class DynamicTableControl : GridControl
{
public static readonly DependencyProperty CustomColumnsProperty =
DependencyProperty.Register("CustomColumns", typeof(IEnumerable<IParamsColumn>),
typeof(DynamicTableControl), new PropertyMetadata(null, OnCustomColumnsChanged));
public static readonly DependencyProperty CellTemplateSelectorProperty =
DependencyProperty.Register("CellTemplateSelector", typeof(DataTemplateSelector),
typeof(DynamicTableControl), new PropertyMetadata(null));
public IEnumerable<IParamsColumn> CustomColumns
{
get { return GetValue(CustomColumnsProperty) as IEnumerable<IParamsColumn>; }
set { SetValue(CustomColumnsProperty, value); }
}
public DataTemplateSelector CellTemplateSelector
{
get { return GetValue(CellTemplateSelectorProperty) as DataTemplateSelector; }
set { SetValue(CellTemplateSelectorProperty, value); }
}
public DynamicTableControl()
{
InitializeComponent();
}
private static void OnCustomColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = d as DynamicTableControl;
var columns = e.NewValue as IEnumerable<IParamsColumn>;
if(grid != null && columns != null)
{
grid.Columns.Clear();
foreach(var column in columns)
{
var c = new GridColumn();
c.Header = column.ColumnName;
c.Binding = new Binding(column.BindingName)
{
Mode = column.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay,
};
c.CellTemplateSelector = grid.CellTemplateSelector;
c.Tag = column;
grid.Columns.Add(c);
}
}
}
}
}
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:table="clr-namespace:GUI.Params.DynamicTable.Control"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
>
<DataTemplate x:Key="NumericCellTemplate">
<dxe:TextEdit
EditValue="{Binding Path=Value.NumericValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayFormatString="N2"
MaskType="Numeric"
HorizontalContentAlignment="Right"
ShowBorder="False"
/>
</DataTemplate>
<DataTemplate x:Key="StringCellTemplate">
<dxe:TextEdit
EditValue="{Binding Value.StringValue}"
MaskType="None"
ShowBorder="False"
/>
</DataTemplate>
<DataTemplate x:Key="TitleCellTemplate">
<TextBlock Text="{Binding Value.StringValue}"/>
</DataTemplate>
<DataTemplate x:Key="NullCellTemplate">
<Grid HorizontalAlignment="Stretch" Background="#e4e4e4">
<TextBlock Text="-" HorizontalAlignment="Center" />
</Grid>
</DataTemplate>
<table:DynamicCellTemplateSelector x:Key="DynamicCellTemplateSelector"
NullCellTemplate="{StaticResource NullCellTemplate}"
NumericCellTemplate="{StaticResource NumericCellTemplate}"
StringCellTemplate="{StaticResource StringCellTemplate}"
TitleCellTemplate="{StaticResource TitleCellTemplate}"
/>
</ResourceDictionary>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GUI.Params.DynamicTable
{
public interface IParamsCell : INotifyPropertyChanged
{
Int32 RowOrder { get; }
String RowName { get; }
String RowTitle { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GUI.Params.DynamicTable
{
public interface IParamsColumn
{
Boolean IsReadOnly { get; }
Int32 Order { get; }
String ColumnName { get; }
String ColumnTitle { get; }
String BindingName { get; }
IEnumerable<IParamsCell> Cells { get; }
}
}
<Window x:Class="TestDXTable.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxci="http://schemas.devexpress.com/winfx/2008/xaml/core/internal"
xmlns:table="clr-namespace:GUI.Params.DynamicTable.Control"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<table:DynamicTableControl
Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
CustomColumns="{Binding Columns}"
CellTemplateSelector="{DynamicResource DynamicCellTemplateSelector}"
ItemsSource="{Binding Rows}">
<table:DynamicTableControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DynamicTableTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</table:DynamicTableControl.Resources>
<table:DynamicTableControl.View>
<dxg:TableView AllowPerPixelScrolling="True" ShowTotalSummary="True"/>
</table:DynamicTableControl.View>
</table:DynamicTableControl>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Series1.Prop1"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Series1.Prop1, Mode=TwoWay}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Series1.Prop2"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Series1.Prop2, Mode=TwoWay}"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GUI.Params.DynamicTable;
using GUI.Params.DynamicTable.Impl;
using DevExpress.Mvvm;
namespace TestDXTable
{
/// <summary>
/// Логика взаимодействия для MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var o1 = new Series1
{
Prop1 = 2000,
Prop2 = 20,
};
var o2 = new Series2
{
Prop1 = 3333,
Prop3 = 10,
Prop4 = 100,
};
var columnSeries1 = new NumericColumn
{
Order = 10,
ColumnName = "Series1",
ColumnTitle = "Series 1",
Cells = new List<IParamsCell>
{
new NumericCell
{
RowOrder = 10,
RowName = "Row1",
RowTitle = "Row 1",
NumericValue = 123,
},
new StringCell
{
RowOrder = 20,
RowName = "Row2",
RowTitle = "Row 2",
StringValue = "345",
},
new StringCell
{
RowOrder = 30,
RowName = "Row4",
RowTitle = "Row 4",
StringValue = "345d",
},
new NumericPropertyCell(o1, () => o1.Prop1, _ => o1.Prop1 = _)
{
RowOrder = 40,
RowName = "Prop1",
RowTitle = "Prop 1",
},
new NumericPropertyCell(o1, () => o1.Prop2, _ => o1.Prop2 = _)
{
RowOrder = 50,
RowName = "Prop2",
RowTitle = "Prop 2",
},
},
};
var columnSeries2 = new NumericColumn
{
Order= 20,
ColumnName = "Series2",
ColumnTitle = "Series 2",
Cells = new List<IParamsCell>
{
new NumericCell
{
RowOrder = 10,
RowName = "Row1",
RowTitle = "Row 1",
NumericValue = 678,
},
new NumericCell
{
RowOrder = 20,
RowName = "Row3",
RowTitle = "Row 3",
NumericValue = 555,
},
new StringCell
{
RowOrder = 30,
RowName = "Row4",
RowTitle = "Row 4",
StringValue = "dasdsa",
},
new NumericPropertyCell(o2, () => o2.Prop1, _ => o2.Prop1 =_)
{
RowOrder = 40,
RowName = "Prop1",
RowTitle = "Prop 1",
},
new NumericPropertyCell(o2, () => o2.Prop3, _ => o2.Prop3=_)
{
RowOrder = 41,
RowName = "Prop3",
RowTitle = "Prop 3",
},
new NumericPropertyCell(o2, () => o2.Prop4, _ => o2.Prop4 =_)
{
RowOrder = 60,
RowName = "Prop4",
RowTitle = "Prop 4",
},
},
};
var vm = new Tbl(new[] { columnSeries1, columnSeries2 }, o1);
DataContext = vm;
}
}
public class Tbl : ParamsTableViewModel
{
public Series1 Series1 { get; set; }
public Tbl(IEnumerable<IParamsColumn> columns, Series1 series1)
:base(columns)
{
Series1 = series1;
}
}
public class Series1 : BindableBase
{
public Double Prop1
{
get { return _prop1; }
set { SetProperty(ref _prop1, value, () => Prop1); }
}
public Double Prop2
{
get { return _prop2; }
set { SetProperty(ref _prop2, value, () => Prop2); }
}
private Double _prop1;
private Double _prop2;
}
public class Series2 : BindableBase
{
public Double Prop1
{
get { return _prop1; }
set { SetProperty(ref _prop1, value, () => Prop1); }
}
public Double Prop3
{
get { return _prop3; }
set { SetProperty(ref _prop3, value, () => Prop3); }
}
public Double Prop4
{
get { return _prop4; }
set { SetProperty(ref _prop4, value, () => Prop4); }
}
private Double _prop1;
private Double _prop3;
private Double _prop4;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DevExpress.Mvvm;
using System.ComponentModel;
namespace GUI.Params.DynamicTable.Impl
{
public abstract class BaseCell : BindableBase, IParamsCell
{
public int RowOrder
{
get;
set;
}
public string RowName
{
get;
set;
}
public string RowTitle
{
get;
set;
}
}
public class NumericCell : BaseCell
{
public double NumericValue
{
get { return _numericValue; }
set
{
SetProperty(ref _numericValue, value, () => NumericValue);
}
}
private Double _numericValue;
}
public class StringCell : BaseCell
{
public string StringValue
{
get { return _stringValue; }
set
{
SetProperty(ref _stringValue, value, () => StringValue);
}
}
private String _stringValue;
}
public class NumericPropertyCell : BaseCell
{
public Double NumericValue
{
get
{
return _getter();
}
set
{
_setter(value);
RaisePropertyChanged(() => NumericValue);
}
}
public NumericPropertyCell(INotifyPropertyChanged owner, Func<Double> getter, Action<Double> setter)
{
_getter = getter;
_setter = setter;
owner.PropertyChanged += OnOwnerPropertyChanged;
}
void OnOwnerPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
RaisePropertyChanged();
}
private readonly Func<Double> _getter;
private readonly Action<Double> _setter;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GUI.Params.DynamicTable.Impl
{
public abstract class BaseColumn : IParamsColumn
{
public Boolean IsReadOnly
{
get;
set;
}
public int Order
{
get;
set;
}
public string ColumnName
{
get;
set;
}
public string ColumnTitle
{
get;
set;
}
public IEnumerable<IParamsCell> Cells
{
get;
set;
}
public abstract String BindingName { get; }
}
public class NumericColumn : BaseColumn
{
public override String BindingName
{
get { return String.Format("RowData.Row.{0}", ColumnName); }
}
}
public class StringColumn : BaseColumn
{
public override string BindingName
{
get { return String.Format("RowData.Row.{0}", ColumnName); }
}
}
public class TitleColumn : IParamsColumn
{
public Boolean IsReadOnly
{
get { return true; }
}
public int Order
{
get;
set;
}
public string ColumnName
{
get;
set;
}
public string ColumnTitle
{
get;
set;
}
public String BindingName
{
get { return String.Format("RowData.Row.{0}", ColumnName); }
}
public IEnumerable<IParamsCell> Cells
{
get;
private set;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GUI.Params.DynamicTable.Impl
{
public class ParamsRow : DynamicObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public readonly IList<IParamsColumn> Columns;
private readonly IParamsCell[,] _cells;
private readonly Int32 _rowIndex;
public ParamsRow(IParamsCell[,] cells, Int32 row, IList<IParamsColumn> columns)
{
Columns = columns;
_rowIndex = row;
_cells = cells;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return Columns.Select(_ => _.ColumnName);
}
//public void SetValue(string propertyName, object value)
//{
// TrySetMember(new SetMemberValueBinder(propertyName), value);
//}
public object GetValue(string propertyName)
{
object value = null;
TryGetMember(new GetMemberValueBinder(propertyName), out value);
return value;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
for (var idx = 0; idx < Columns.Count;++idx)
{
if(Columns[idx].ColumnName == binder.Name)
{
result = _cells[_rowIndex, idx];
return true;
}
}
result = null;
return false;
}
//public override bool TrySetMember(SetMemberBinder binder, object value)
//{
// dictionary[binder.Name.ToLower()] = value;
// NotifyPropertyChanged(binder.Name);
// return true;
//}
void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
class SetMemberValueBinder : SetMemberBinder
{
public SetMemberValueBinder(string propertyName)
: base(propertyName, false)
{
}
public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion)
{
return errorSuggestion;
}
}
class GetMemberValueBinder : GetMemberBinder
{
public GetMemberValueBinder(string propertyName)
: base(propertyName, false)
{
}
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
return errorSuggestion;
}
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TestDXTable;
namespace GUI.Params.DynamicTable.Impl
{
[Export]
public class ParamsTableViewModel
{
public IList<ParamsRow> Rows { get; private set; }
public IList<IParamsColumn> Columns { get; private set; }
[ImportingConstructor]
public ParamsTableViewModel([ImportMany] IEnumerable<IParamsColumn> columns)
{
var rows = new Dictionary<String, IParamsCell>();
foreach(var column in columns)
{
foreach(var cell in column.Cells)
{
rows[cell.RowName] = cell;
}
}
var orderedRows = rows.Values.OrderBy(_ => _.RowOrder).ToList();
var cells = new IParamsCell[orderedRows.Count, columns.Count() + 1];
for (var idx = 0; idx < orderedRows.Count; ++idx)
{
var row = orderedRows[idx];
cells[idx, 0] = new StringCell
{
RowOrder = row.RowOrder,
RowName = "Params",
RowTitle = "Params",
StringValue = row.RowTitle,
};
var columnIndex = 1;
foreach(var column in columns)
{
foreach(var cell in column.Cells)
{
if(cell.RowName == row.RowName)
{
cells[idx, columnIndex] = cell;
break;
}
}
columnIndex += 1;
}
}
var cs = new List<IParamsColumn>
{
new TitleColumn
{
ColumnName = "Params",
ColumnTitle= "Params",
Order = 0,
},
};
cs.AddRange(columns);
Columns = cs;
Rows = new List<ParamsRow>();
for (var idx = 0; idx < orderedRows.Count; ++idx)
{
Rows.Add(new ParamsRow(cells, idx, Columns));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment