xmal UI:
<Style TargetType="{x:Type Button}" x:Key="btnConfigStyle">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="Transparent">
<Path Fill="Gray" Height="{TemplateBinding Height}" Stretch="Fill" Width="{TemplateBinding Width}" Data="M46.494725 395.728342C20.816807 395.728342 0.023276 416.38232 0.023276 442.246309l0 139.5539c0 25.677918 20.607459 46.517967 46.471449 46.517967l136.297642 0c4.140099 11.722528 8.884932 23.119429 14.234498 34.237223l-96.431745 96.385227c-18.142007 18.142007-18.281561 47.448326 0.046518 65.729887l98.711125 98.711125c18.142007 18.188525 47.448326 18.281561 65.729887 0.046518l96.617817-96.617817c11.024758 5.25653 22.375142 9.954845 34.004634 14.048426l0 136.623268c0 25.677918 20.653977 46.471449 46.517967 46.471449l139.5539 0c25.677918 0 46.517967-20.607459 46.517967-46.471449l0-136.80934c11.582974-4.093581 22.88684-8.791896 33.86508-14.048426l96.757371 96.757371c18.142007 18.142007 47.448326 18.281561 65.729887-0.046518l98.711125-98.711125c18.188525-18.142007 18.281561-47.494844 0.046518-65.729887l-96.757371-96.757371c5.25653-10.97824 9.954845-22.282106 14.048426-33.86508l136.80934 0c25.677918 0 46.471449-20.653977 46.471449-46.517967l0-139.5539c0-25.677918-20.607459-46.517967-46.471449-46.517967l-136.623268 0c-4.093581-11.629492-8.791896-22.979876-14.048426-34.004634l96.571299-96.617817c18.142007-18.142007 18.281561-47.448326-0.046518-65.729887l-98.711125-98.711125c-18.188525-18.188525-47.494844-18.281561-65.729887-0.046518l-96.385227 96.431745c-11.071276-5.303048-22.514696-10.094399-34.237223-14.234498l0-136.297642c0-25.677918-20.653977-46.471449-46.517967-46.471449l-139.5539 0c-25.677918 0-46.517967 20.607459-46.517967 46.471449l0 136.11157c-11.769046 4.140099-23.258983 8.884932-34.376777 14.234498L265.08265 100.618362c-18.142007-18.142007-47.448326-18.281561-65.729887 0.046518L100.641638 199.329487c-18.142007 18.142007-18.281561 47.448326-0.046518 65.729887l96.245673 96.245673c-5.349566 11.164312-10.094399 22.607732-14.234498 34.376777L46.494725 395.681824zM511.720909 279.154318c128.436106 0 232.589833 104.153727 232.589833 232.589833s-104.153727 232.589833-232.589833 232.589833c-128.436106 0-232.589833-104.153727-232.589833-232.589833S383.284803 279.154318 511.720909 279.154318z" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<Style x:Key="ColumnHeaderGripperStyle"
TargetType="{x:Type Thumb}">
<Setter Property="Width"
Value="8" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Cursor"
Value="SizeWE" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DataGridColumnHeaderSettingStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="Background" Value="#FF004475"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="BorderThickness" Value="0,0,1,0" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="Height" Value="45"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<!--<TextBlock Text="{Binding}" VerticalAlignment="Center" TextWrapping="Wrap" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>-->
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="Center"/>
</Border>
<Thumb x:Name="PART_LeftHeaderGripper" Style="{StaticResource ColumnHeaderGripperStyle}" HorizontalAlignment="Left" />
<Thumb x:Name="PART_RightHeaderGripper" Style="{StaticResource ColumnHeaderGripperStyle}" HorizontalAlignment="Right" />
<Button Margin="0,2,2,0" Style="{StaticResource btnConfigStyle}" x:Name="btnSetting" Visibility="Collapsed" Command="{Binding Command,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" Width="15" Height="15" HorizontalAlignment="Right" VerticalAlignment="Top" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="DisplayIndex" Value="0">
<Setter TargetName="btnSetting" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type local:CustomDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGridColumnHeaderSettingStyle}"/>
<Setter Property="SelectionUnit" Value="FullRow"/>
<Setter Property="MinRowHeight" Value="40"/>
<Setter Property="AutoGenerateColumns" Value="False"/>
<Setter Property="SelectionMode" Value="Single"/>
<Setter Property="GridLinesVisibility" Value="All"/>
<Setter Property="CanUserAddRows" Value="False"/>
<Setter Property="CanUserDeleteRows" Value="False"/>
<Setter Property="CanUserResizeColumns" Value="True"/>
</Style>
Custom Control:
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnRaisePropertyChange(string name)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class DataGridHeaderInfo : BaseModel
{
public bool UsedColumnName { set; get; }
public int DisplayIndex { set; get; }
public string Header { set; get; }
private bool _IsShow;
public bool IsShow
{
get { return _IsShow; }
set
{
_IsShow = value;
OnRaisePropertyChange("IsShow");
}
}
private bool isEnable = true;
public bool IsEnable
{
get { return isEnable; }
set
{
isEnable = value;
OnRaisePropertyChange("IsEnable");
}
}
public DataGridLength Width { set; get; }
}
public class CustomDataGridTextColumn : DataGridTextColumn
{
public string ColumnName
{
get { return (string)GetValue(ColumnNameProperty); }
set { SetValue(ColumnNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ColumnName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnNameProperty =
DependencyProperty.Register("ColumnName", typeof(string), typeof(CustomDataGridTemplateColumn), new PropertyMetadata(null, (o, e) =>
{
var column = o as CustomDataGridTemplateColumn;
column.ColumnName = e.NewValue as string;
}));
public bool FixedColumn
{
get { return (bool)GetValue(FixedColumnProperty); }
set { SetValue(FixedColumnProperty, value); }
}
// Using a DependencyProperty as the backing store for FixedColumn. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FixedColumnProperty =
DependencyProperty.Register("FixedColumn", typeof(bool), typeof(CustomDataGridTextColumn), new PropertyMetadata(false, (o, e) =>
{
var column = o as CustomDataGridTextColumn;
column.FixedColumn = Convert.ToBoolean(e.NewValue);
}));
}
public class CustomDataGridTemplateColumn : DataGridTemplateColumn
{
public string ColumnName
{
get { return (string)GetValue(ColumnNameProperty); }
set { SetValue(ColumnNameProperty, value); }
}
// Using a DependencyProperty as the backing store for ColumnName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ColumnNameProperty =
DependencyProperty.Register("ColumnName", typeof(string), typeof(CustomDataGridTemplateColumn), new PropertyMetadata(null,(o, e) =>
{
var column = o as CustomDataGridTemplateColumn;
column.ColumnName =e.NewValue as string;
}));
public bool FixedColumn
{
get { return (bool)GetValue(FixedColumnProperty); }
set { SetValue(FixedColumnProperty, value); }
}
// Using a DependencyProperty as the backing store for FixedColumn. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FixedColumnProperty =
DependencyProperty.Register("FixedColumn", typeof(bool), typeof(CustomDataGridTemplateColumn), new PropertyMetadata(false, (o, e) =>
{
var column = o as CustomDataGridTemplateColumn;
column.FixedColumn = Convert.ToBoolean(e.NewValue);
}));
}
public class CustomDataGrid : DataGrid
{
DataGridConfigColumnSettingRepository repository = null;
DataGridConfigColumnSetting setting = null;
public CustomDataGrid()
{
repository = new DataGridConfigColumnSettingRepository();
HeadersVisibility = DataGridHeadersVisibility.Column;
HorizontalGridLinesBrush = VerticalGridLinesBrush = new SolidColorBrush(Colors.LightGray);
this.Loaded += MyDataGrid_Loaded;
Command = new RelayCommand(OpenConfigWindow);
}
void MyDataGrid_Loaded(object sender, RoutedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
try
{
if (string.IsNullOrEmpty(KeyName))
{
MessageBox.Show("The 'KeyName' of Custom DataGrid is null !");
return;
}
var settingColumn = this.Columns[0] as CustomDataGridTemplateColumn;
if(settingColumn==null ||settingColumn.FixedColumn==false)
{
MessageBox.Show("The first column of Custom DataGrid is invalid !");
return;
}
//Init data from database
setting = repository.GetSetting(this.KeyName);
if (setting != null && !string.IsNullOrEmpty(setting.Json))
{
List<DataGridHeaderInfo> list = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DataGridHeaderInfo>>(setting.Json);
foreach (var column in this.Columns)
{
string header=string.Empty;
if (column.Header is string)
{
header = column.Header.ToString();
}else if (column.Header is TextBlock)
{
var c = column as CustomDataGridTemplateColumn;
header = c.ColumnName;
}
//else
//{
// throw new Exception("Unknow Column Header Type!" + column.Header.ToString());
//}
var item = list.FirstOrDefault(m => m.Header == header);
if (item != null)
{
column.Visibility = item.IsShow ? Visibility.Visible : Visibility.Collapsed;
int idx = 0;
if (item.DisplayIndex > this.Columns.Count - 1)
{
idx = this.Columns.Count - 1;
}
else
{
idx = item.DisplayIndex;
}
column.DisplayIndex = idx;
if (item.Width != DataGridLength.Auto)
{
column.Width = item.Width;
}
}
}
}
else
{
if (this.KeyName == "CheckList")
{
var levelColumn = this.Columns.FirstOrDefault(m =>m.Header!=null&& m.Header.ToString() == "Level");
if(null!=levelColumn)
{
levelColumn.Visibility = Visibility.Collapsed;
}
var planSampleSizeColumn = this.Columns.FirstOrDefault(m => m.Header != null && m.Header.ToString() == " Planned \r Sample Size");
if (null != planSampleSizeColumn)
{
planSampleSizeColumn.Visibility = Visibility.Collapsed;
}
}
}
}
catch (Exception ex)
{
throw new Exception("CustomDataGrid MyDataGrid_Loaded error:" + ex.StackTrace);
}
}
}
private List<DataGridHeaderInfo>GetColumnInfos()
{
List<DataGridHeaderInfo> list = new List<DataGridHeaderInfo>();
foreach (var column in this.Columns)
{
int dispalyIdx = column.DisplayIndex;
DataGridLength width = 0;
bool isEnable = true;
if (column is CustomDataGridTextColumn)
{
var c = column as CustomDataGridTextColumn;
if (c.FixedColumn)
{
isEnable = !c.FixedColumn;
}
width = c.Width;
}
else if (column is CustomDataGridTemplateColumn)
{
var c = column as CustomDataGridTemplateColumn;
if (c.FixedColumn)
{
isEnable = !c.FixedColumn;
}
width = c.Width;
}
bool usedColumnName = false;
string header = string.Empty;
if (column.Header is TextBlock)
{
var c = column as CustomDataGridTemplateColumn;
header = c.ColumnName;
usedColumnName = true;
width = c.Width;
}
else if (column.Header is string)
{
header = column.Header.ToString();
width = column.Width;
}
//else
//{
// throw new Exception("Unknow Column Header Type!" + column.Header.ToString());
//}
if (isEnable)
{
list.Add(new DataGridHeaderInfo()
{
DisplayIndex = dispalyIdx,
Width=width,
UsedColumnName=usedColumnName,
Header = header,
IsShow = column.Visibility == Visibility.Visible ? true : false,
IsEnable = isEnable
});
}
}
return list;
}
private void OpenConfigWindow()
{
try
{
if (string.IsNullOrEmpty(KeyName))
{
MessageBox.Show("The 'KeyName' of CustomDataGrid is null !");
return;
}
var source = GetColumnInfos();
#region pop up window to config setting
var list = source.OrderBy(m => m.DisplayIndex).ToList();
CustomDataGridColumnSetting w = new CustomDataGridColumnSetting( list);
if (true == w.ShowDialog())
{
foreach (DataGridHeaderInfo item in list)
{
dynamic column = null;
if (item.UsedColumnName)
{
column = this.Columns.FirstOrDefault(m => m.Header != null && m.Header is TextBlock && (m as CustomDataGridTemplateColumn).ColumnName == item.Header);
}
else
{
column = this.Columns.FirstOrDefault(m => m.Header != null && m.Header.ToString() == item.Header);
}
if (column != null)
{
column.DisplayIndex = item.DisplayIndex;
column.Visibility = item.IsShow ? Visibility.Visible : System.Windows.Visibility.Collapsed;
column.Width = item.Width;
}
}
this.UpdateLayout();
//save to database
if (setting == null)
{
setting = new DataGridConfigColumnSetting();
}
setting.KeyName = this.KeyName;
setting.UpdatedTimeUTC = DateTime.UtcNow;
setting.UserID = App.UserAccount.UserID;
setting.Json = Newtonsoft.Json.JsonConvert.SerializeObject(list);
repository.Update(setting);
}
#endregion pop up window to config setting
}
catch (Exception ex)
{
App.Log.Error("CustomDataGrid OpenConfigWindow error:"+ex.StackTrace);
}
}
#region Base Property
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Using a DependencyProperty as the backing store for Command. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(CustomDataGrid), new PropertyMetadata(null, (o, e) =>
{
var dg = o as CustomDataGrid;
dg.Command = e.NewValue as ICommand;
}));
/// <summary>
/// 数据表格唯一标志名
/// </summary>
public string KeyName
{
get { return (string)GetValue(KeyNameProperty); }
set { SetValue(KeyNameProperty, value); }
}
// Using a DependencyProperty as the backing store for KeyName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty KeyNameProperty =
DependencyProperty.Register("KeyName", typeof(string), typeof(CustomDataGrid), new PropertyMetadata(null, (o, e) =>
{
var dg = o as CustomDataGrid;
dg.KeyName = e.NewValue as string;
}));
#endregion Base Property
}
效果图: