RadGridView CellTemplate
RadGridView CellTemplate
我需要更改 RadGridView 的 CellTemplate。我可以在网上找到的所有示例都在 Xaml 中静态定义了 Columns,然后在该 Column 标记中,它们定义了 CellTemplate:
<telerik:RadGridView AutoGenerateColumns="False" ItemsSource="{Binding}" RowStyleSelector="{StaticResource styleSelector}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding ID}" CellTemplateSelector="{StaticResource templateSelector}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
以上示例仅为标题为 "ID" 的列加载 CellTemplate,并对该列中的所有单元格执行此操作。
我已经设法在后面的代码中加载了 CellTemplates,但这需要我将整个网格的一个实例传递给 ViewModel,并且还必须向 GridView 添加一个依赖项 属性 以绑定网格的列到 ViewModel 中的 GridViewColumnCollection。
这是一个非常混乱的解决方法,如果被错误的人看到,我肯定会被解雇。我需要做的是类似这样的事情:
<telerik:RadGridView.CellTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.NormalTemplate>
<DataTemplate>
...Some [Normal] template...
</DataTemplate>
</local:MyTemplateSelector.NormalTemplate
<local:MyTemplateSelector.DropdownTemplate>
<DataTemplate>
...Some [ComboBox] template...
</DataTemplate>
</local:MyTemplateSelector.DropdownTemplate>
</local:MyTemplateSelector>
</telerik:RadGridView.CellTemplateSelector>
老实说,我不知道为什么这个 RadGridView 使更改 CellTemplate 变得如此困难,因为这会阻止您更改常见的 属性,例如在 Cell 的 ContentTemplate 中找到的 Label 中的 Foreground Color本身..关于我能做什么有什么想法吗?
所以提前,我不确定 "proper" 方法来做到这一点,因为 WPF 看起来非常上下文相关,它似乎真的是最适合您的特定需求的情况。
我自己的方法可能不是最推荐的方法,但我需要集中所有 styleselector 和 cellselector 变体并使用代码编写的逻辑,而不是 xaml。
我采用的方法类似于 telerik.StyleRule 方法,但更简单,就像在代码隐藏 atm 中一样。我不是在共享 xaml 资源库中定义或在每个视图中定义,而是在单个文件夹中使用 classes 并使用 CLR 命名空间简单地调用它并在 xaml 中调用它(尽管当然可以在代码隐藏中设置)
它使用列名并且非常容易定义条件:
Expression<Func<BaseModelForAllTableObjectClasses,bool>>
它使用的实际资源(数据模板和样式)在共享 xaml 资源库中定义,但如果需要,这些资源可以非常通用。
有点粗糙,有兴趣的可以在评论区留言。
MyProject.CellStyleSelector
/// <summary>
/// Used in CellStyleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
///
/// NOTE - tried extending telerik.StyleRule, issues with setting condition value
/// </summary>
public class CellStyleRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
Style _Style;
public Style Style
{
get
{
return this._Style;
}
set
{
this._Style = value;
}
}
public CellStyleRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Style = d as Style;
else
throw new Exception("No such style resource as '" + s + "'");
// Value = v;
Condition = c;
}
/*
public CellStyleRule(string c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
Style = d as Style;
else
throw new Exception("No such style resource as '" + s + "'");
// TODO - test - guessing at how to do this based on telerik classes
// Value = v;
Telerik.Windows.Data.ExpressionTypeConverter converter = new Telerik.Windows.Data.ExpressionTypeConverter();
Condition = (System.Linq.Expressions.Expression) converter.ConvertFromString(c);
c.ToString();
}
*/
}
// NOTE IMPORTANT - use of xaml defined ConditionalStyleSelectors is not a bad alternative approach, though diferent selector
// for each column it could all be defined in same resource dictionary
// - problem is that issues encountered with Enums, so decided on this approach instead
/// <summary>
/// A variation of StyleSelecter not unlike telerik:ConditionalStyleSelector.
///
/// However, rules are defined using a Dictionary<string,CellStyle> to distinct columns and use same
/// selector, with rules (currently) defined not declarativly (xaml), but in inheriting classes,
/// typically one for each entity type requiring a RadGridView and conditional styling.
/// </summary>
public abstract class CellStyleSelector : StyleSelector
{
// public Dictionary<String, Dictionary<string, Style>> conditions { get; set; }
// Use a Dictionary mapping X columns to individual Lists of rules to check
Dictionary<string, List<CellStyleRule>> _Rules;
public Dictionary<string, List<CellStyleRule>> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new Dictionary<string, List<CellStyleRule>>();
}
return this._Rules;
}
set { this._Rules = value; }
}
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is BaseDataModel)
{
GridViewCell cell = container as GridViewCell;
var currentColumn = cell.Column as GridViewDataColumn;
string key = currentColumn.DataMemberBinding.Path.Path;
if (Rules.ContainsKey(key))
{
foreach (CellStyleRule rule in Rules[key])
{
// string debug = DebugHelper.Debug(rule.Condition);
// REVERTED NOTE - if just Func<>
// if (rule.Condition((BaseDataModel)item))
// NOTE - if Expression<Func<>>, first compile then pass in param
if (rule.Condition.Compile()((BaseDataModel)item))
{
return rule.Style;
}
}
}
}
return null;
}
}
/// <summary>
/// Used in CellCellTemplateRuleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
/// </summary>
public class CellTemplateRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
DataTemplate _Template;
public DataTemplate Template
{
get
{
return this._Template;
}
set
{
this._Template = value;
}
}
public CellTemplateRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Template = d as DataTemplate;
else
throw new Exception("No such template resource as '" + s + "'");
Condition = c;
}
}
// Implementing class, used for any licence table
public class LicenceTableCellStyleSelector : CellStyleSelector
{
public LicenceTableCellStyleSelector()
: base()
{
this.Rules = new Dictionary<string, List<CellStyleRule>>()
{
{ "LicenceStatus" , new List<CellStyleRule>()
{
// Always most specific rules at top
// Error catcher, leave in as visual cue just in case
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < DateTime.Now && ((Licence)x).LicenceStatus != LicenceStatus.Expired, "CellStyle_Licence_ExpiryMismatch") } ,
{ new CellStyleRule(x => ((Licence)x).LicenceStatus == LicenceStatus.Active, "CellStyle_Status_Active") } ,
// Same as != Active, as only those would through this far
{ new CellStyleRule(x => x != null, "CellStyle_Status_Inactive") } ,
}
},
{ "LicenceType" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceType == LicenceType.Full, "CellStyle_Licence_Full") } ,
{ new CellStyleRule(x => ((Licence)x).LicenceType == LicenceType.Upgrade, "CellStyle_Licence_Upgrade") } ,
// Timed, uses fallthrough so no need to actually check unless wanting to distinct types of timed
{ new CellStyleRule(x => x != null, "CellStyle_Licence_Timed") } ,
}
},
{ "LicenceExpired" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < DateTime.Now && ((Licence)x).LicenceStatus != LicenceStatus.Expired, "CellStyle_Licence_ExpiryMismatch") },
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < ((Licence)x).LicenceIssued, "CellStyle_Licence_ExpiryMismatch") } ,
}
},
{ "LicenceIssued" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < ((Licence)x).LicenceIssued, "CellStyle_Licence_ExpiryMismatch") } ,
}
}
};
}
}
CellTemplateSelector
/// <summary>
/// Used in CellCellTemplateRuleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
/// </summary>
public class CellTemplateRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
DataTemplate _Template;
public DataTemplate Template
{
get
{
return this._Template;
}
set
{
this._Template = value;
}
}
public CellTemplateRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Template = d as DataTemplate;
else
throw new Exception("No such template resource as '" + s + "'");
Condition = c;
}
}
// NOTE IMPORTANT - use of xaml defined ConditionalTemplateSelectors is not a bad alternative approach, though diferent selector
// for each column it could all be defined in same resource dictionary
// - problem is that issues encountered with Enums, so decided on this approach instead
/// <summary>
/// See CellStyleSelector, this is a variant used with the CellTemplateSelector property
/// </summary>
public abstract class CellTemplateSelector : DataTemplateSelector
{
// public Dictionary<String, Dictionary<string, Template>> conditions { get; set; }
// Use a Dictionary mapping X columns to individual Lists of rules to check
Dictionary<string, List<CellTemplateRule>> _Rules;
public Dictionary<string, List<CellTemplateRule>> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new Dictionary<string, List<CellTemplateRule>>();
}
return this._Rules;
}
set { this._Rules = value; }
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is BaseDataModel)
{
GridViewCell cell = container as GridViewCell;
var currentColumn = cell.Column as GridViewDataColumn;
string key = currentColumn.DataMemberBinding.Path.Path;
if (Rules.ContainsKey(key))
{
foreach (CellTemplateRule rule in Rules[key])
{
// string debug = DebugHelper.Debug(rule.Condition);
// REVERTED NOTE - if just Func<>
// if (rule.Condition((BaseDataModel)item))
// NOTE - if Expression<Func<>>, first compile then pass in param
if (rule.Condition.Compile()((BaseDataModel)item))
{
return rule.Template;
}
}
}
}
return null;
}
}
// Implementing class for a different table, though RadGridView can use both CellStyleSelector and CellTemplateSelector at same time, i just didnt need yet
public class OrderDongleWrapTableCellTemplateSelector : CellTemplateSelector
{
public OrderDongleWrapTableCellTemplateSelector()
: base()
{
this.Rules = new Dictionary<string, List<CellTemplateRule>>()
{
{ "ReplacedDongle.DongleID" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).IsReplacementDongle == false , "CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField") },
}
},
{ "ReplacedDongleStatus" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).IsReplacementDongle == false , "CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField") },
}
},
/*
* // REVERTED - better to just set to UNKNOWN CUSTOMER explicitly before shown in table, rather than overwrite datatemplate
{ "Customer.CustomerName" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).Customer == null || ((OrderDongleWrap)x).Customer.CustomerID == 1 , "CellTemplate_OrderDongleWrap_UnknownCustomerField") },
}
},
*/
};
}
}
xaml 资源
<!--
The following are styles applied to cells or rows in RadGridViews based on StyleSelectors
First is generic colours, which are inherited by semi-generic flag styles, which optionally
can be further inherited by table-specific styles (which can include tooltips and other properties)
As a general rule, no colour style is applied directly, instead using semi-generic or table-specific
styles so as to make changes easier to manage. Which style actually matches which condition is
entirely the responsibility of the StyleSelector.
https://en.wikipedia.org/wiki/Web_colors
http://www.flounder.com/csharp_color_table.htm
<Style x:Key="CellStyle_" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value=""/>
</Style>
-->
<!--
NOTE: to get cell text underlining to work, see http://www.telerik.com/forums/underline-cell-contents
-->
<Style x:Key="CellStyle_Green" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Green"/>
</Style>
<Style x:Key="CellStyle_DarkGreen" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkGreen"/>
</Style>
<Style x:Key="CellStyle_ExtraDarkGreen" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="#004000"/>
</Style>
<Style x:Key="CellStyle_MediumBlue" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="MediumBlue"/>
</Style>
<Style x:Key="CellStyle_DarkBlue" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkBlue"/>
</Style>
<Style x:Key="CellStyle_Purple" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Purple"/>
</Style>
<Style x:Key="CellStyle_Red" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="CellStyle_Crimson" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Crimson"/>
</Style>
<Style x:Key="CellStyle_DarkOrange" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkOrange"/>
</Style>
<Style x:Key="CellStyle_Silver" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Silver"/>
</Style>
<Style x:Key="CellStyle_DarkGray" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkGray"/>
</Style>
<Style x:Key="CellStyle_Gray" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Gray"/>
</Style>
<Style x:Key="CellStyle_RedBG_WhiteFG" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="CellStyle_Muted" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}" />
<Style x:Key="CellStyle_Status_Active" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_ExtraDarkGreen}" />
<Style x:Key="CellStyle_Status_Inactive" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}" />
<Style x:Key="CellStyle_Status_Warning" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Crimson}" />
<Style x:Key="CellStyle_Status_Error" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Red}" />
<Style x:Key="CellStyle_Status_ErrorBG" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_RedBG_WhiteFG}" />
<Style x:Key="CellStyle_Dongle_InactiveWithActiveLicences" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Status_Error}">
<Setter Property="ToolTip" Value="This Dongle is not Active, but has Licences which are"/>
</Style>
<Style x:Key="CellStyle_Licence_Full" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}" />
<Style x:Key="CellStyle_Licence_Upgrade" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_DarkBlue}" />
<Style x:Key="CellStyle_Licence_Timed" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Purple}" />
<Style x:Key="CellStyle_Licence_ExpiryMismatch" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Status_ErrorBG}">
<Setter Property="ToolTip" Value="Expiry doesnt match Status or Issued Date"/>
</Style>
<Style x:Key="CellStyle_OrderDongleWrap_NotReplacementDongler_NAField" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}">
<Setter Property="Content" Value="N/A"/>
</Style>
<Style x:Key="CellStyle_OrderDongleWrap_UnknownCustomerField" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}">
<Setter Property="Content" Value="N/A"/>
</Style>
<DataTemplate x:Key="CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField">
<TextBlock>N/A</TextBlock>
</DataTemplate>
为 CellStyleSelector
实施 xaml
<telerik:RadGridView.Resources>
<gridviewstyleselectors:LicenceTableCellStyleSelector x:Key="LicenceTableCellStyleSelector" />
</telerik:RadGridView.Resources>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding DongleID}" Header="DongleID" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceSerial}" Header="Serial" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceStatus}" Header="Status" IsReadOnly="True"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Product.ProductName}" Header="Product" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceType}" Header="Licence Type"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceIssued}" Header="Issued"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceExpires}" Header="Expiry"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
</telerik:RadGridView.Columns>
到目前为止,我只有一个 CellTemplateSelector 案例,这里是:
为 CellTemplateSelector 实施 xaml
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Dongle.DongleID}" Header="DongleID">
<telerik:GridViewDataColumn.AggregateFunctions>
<telerik:CountFunction ResultFormatString="{}Dongles: {0}" />
</telerik:GridViewDataColumn.AggregateFunctions>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn DataMemberBinding="{Binding OrderRows.Count}" Header="Licences"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Customer.CustomerName}" Header="Customer"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding IsReplacementDongle}" Header="Replacement?"
IsVisible="False"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding IsEducationalDongle}" Header="Educational?"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding ReplacedDongle.DongleID}" Header="Replaced"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding ReplacedDongleStatus}" Header="Replacement Status"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding OrderRows[0].OrderRowCosts[0].CostValue }" Header="Sub-Total (AUD)">
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
所以这是我自己的方法,但它将条件逻辑和样式集中到每个目标 class(GridView ItemsSource 对象)仅 1 个 cs class 和仅 1 个资源 sheet 对于所有 GridView。
如果你愿意,你可以很容易地重构为声明式的,我看到这是可能的,但不需要或不想这样做,所以我没有。
您还可以重构,以便规则级联所有表的单个 cs class,甚至动态声明样式..? (通过新样式和添加属性?)
无论如何,我希望这能帮助其他人解决这个问题
我需要更改 RadGridView 的 CellTemplate。我可以在网上找到的所有示例都在 Xaml 中静态定义了 Columns,然后在该 Column 标记中,它们定义了 CellTemplate:
<telerik:RadGridView AutoGenerateColumns="False" ItemsSource="{Binding}" RowStyleSelector="{StaticResource styleSelector}">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding ID}" CellTemplateSelector="{StaticResource templateSelector}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
以上示例仅为标题为 "ID" 的列加载 CellTemplate,并对该列中的所有单元格执行此操作。
我已经设法在后面的代码中加载了 CellTemplates,但这需要我将整个网格的一个实例传递给 ViewModel,并且还必须向 GridView 添加一个依赖项 属性 以绑定网格的列到 ViewModel 中的 GridViewColumnCollection。
这是一个非常混乱的解决方法,如果被错误的人看到,我肯定会被解雇。我需要做的是类似这样的事情:
<telerik:RadGridView.CellTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.NormalTemplate>
<DataTemplate>
...Some [Normal] template...
</DataTemplate>
</local:MyTemplateSelector.NormalTemplate
<local:MyTemplateSelector.DropdownTemplate>
<DataTemplate>
...Some [ComboBox] template...
</DataTemplate>
</local:MyTemplateSelector.DropdownTemplate>
</local:MyTemplateSelector>
</telerik:RadGridView.CellTemplateSelector>
老实说,我不知道为什么这个 RadGridView 使更改 CellTemplate 变得如此困难,因为这会阻止您更改常见的 属性,例如在 Cell 的 ContentTemplate 中找到的 Label 中的 Foreground Color本身..关于我能做什么有什么想法吗?
所以提前,我不确定 "proper" 方法来做到这一点,因为 WPF 看起来非常上下文相关,它似乎真的是最适合您的特定需求的情况。
我自己的方法可能不是最推荐的方法,但我需要集中所有 styleselector 和 cellselector 变体并使用代码编写的逻辑,而不是 xaml。
我采用的方法类似于 telerik.StyleRule 方法,但更简单,就像在代码隐藏 atm 中一样。我不是在共享 xaml 资源库中定义或在每个视图中定义,而是在单个文件夹中使用 classes 并使用 CLR 命名空间简单地调用它并在 xaml 中调用它(尽管当然可以在代码隐藏中设置)
它使用列名并且非常容易定义条件:
Expression<Func<BaseModelForAllTableObjectClasses,bool>>
它使用的实际资源(数据模板和样式)在共享 xaml 资源库中定义,但如果需要,这些资源可以非常通用。
有点粗糙,有兴趣的可以在评论区留言。
MyProject.CellStyleSelector
/// <summary>
/// Used in CellStyleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
///
/// NOTE - tried extending telerik.StyleRule, issues with setting condition value
/// </summary>
public class CellStyleRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
Style _Style;
public Style Style
{
get
{
return this._Style;
}
set
{
this._Style = value;
}
}
public CellStyleRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Style = d as Style;
else
throw new Exception("No such style resource as '" + s + "'");
// Value = v;
Condition = c;
}
/*
public CellStyleRule(string c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
Style = d as Style;
else
throw new Exception("No such style resource as '" + s + "'");
// TODO - test - guessing at how to do this based on telerik classes
// Value = v;
Telerik.Windows.Data.ExpressionTypeConverter converter = new Telerik.Windows.Data.ExpressionTypeConverter();
Condition = (System.Linq.Expressions.Expression) converter.ConvertFromString(c);
c.ToString();
}
*/
}
// NOTE IMPORTANT - use of xaml defined ConditionalStyleSelectors is not a bad alternative approach, though diferent selector
// for each column it could all be defined in same resource dictionary
// - problem is that issues encountered with Enums, so decided on this approach instead
/// <summary>
/// A variation of StyleSelecter not unlike telerik:ConditionalStyleSelector.
///
/// However, rules are defined using a Dictionary<string,CellStyle> to distinct columns and use same
/// selector, with rules (currently) defined not declarativly (xaml), but in inheriting classes,
/// typically one for each entity type requiring a RadGridView and conditional styling.
/// </summary>
public abstract class CellStyleSelector : StyleSelector
{
// public Dictionary<String, Dictionary<string, Style>> conditions { get; set; }
// Use a Dictionary mapping X columns to individual Lists of rules to check
Dictionary<string, List<CellStyleRule>> _Rules;
public Dictionary<string, List<CellStyleRule>> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new Dictionary<string, List<CellStyleRule>>();
}
return this._Rules;
}
set { this._Rules = value; }
}
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is BaseDataModel)
{
GridViewCell cell = container as GridViewCell;
var currentColumn = cell.Column as GridViewDataColumn;
string key = currentColumn.DataMemberBinding.Path.Path;
if (Rules.ContainsKey(key))
{
foreach (CellStyleRule rule in Rules[key])
{
// string debug = DebugHelper.Debug(rule.Condition);
// REVERTED NOTE - if just Func<>
// if (rule.Condition((BaseDataModel)item))
// NOTE - if Expression<Func<>>, first compile then pass in param
if (rule.Condition.Compile()((BaseDataModel)item))
{
return rule.Style;
}
}
}
}
return null;
}
}
/// <summary>
/// Used in CellCellTemplateRuleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
/// </summary>
public class CellTemplateRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
DataTemplate _Template;
public DataTemplate Template
{
get
{
return this._Template;
}
set
{
this._Template = value;
}
}
public CellTemplateRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Template = d as DataTemplate;
else
throw new Exception("No such template resource as '" + s + "'");
Condition = c;
}
}
// Implementing class, used for any licence table
public class LicenceTableCellStyleSelector : CellStyleSelector
{
public LicenceTableCellStyleSelector()
: base()
{
this.Rules = new Dictionary<string, List<CellStyleRule>>()
{
{ "LicenceStatus" , new List<CellStyleRule>()
{
// Always most specific rules at top
// Error catcher, leave in as visual cue just in case
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < DateTime.Now && ((Licence)x).LicenceStatus != LicenceStatus.Expired, "CellStyle_Licence_ExpiryMismatch") } ,
{ new CellStyleRule(x => ((Licence)x).LicenceStatus == LicenceStatus.Active, "CellStyle_Status_Active") } ,
// Same as != Active, as only those would through this far
{ new CellStyleRule(x => x != null, "CellStyle_Status_Inactive") } ,
}
},
{ "LicenceType" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceType == LicenceType.Full, "CellStyle_Licence_Full") } ,
{ new CellStyleRule(x => ((Licence)x).LicenceType == LicenceType.Upgrade, "CellStyle_Licence_Upgrade") } ,
// Timed, uses fallthrough so no need to actually check unless wanting to distinct types of timed
{ new CellStyleRule(x => x != null, "CellStyle_Licence_Timed") } ,
}
},
{ "LicenceExpired" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < DateTime.Now && ((Licence)x).LicenceStatus != LicenceStatus.Expired, "CellStyle_Licence_ExpiryMismatch") },
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < ((Licence)x).LicenceIssued, "CellStyle_Licence_ExpiryMismatch") } ,
}
},
{ "LicenceIssued" , new List<CellStyleRule>()
{
{ new CellStyleRule(x => ((Licence)x).LicenceExpires < ((Licence)x).LicenceIssued, "CellStyle_Licence_ExpiryMismatch") } ,
}
}
};
}
}
CellTemplateSelector
/// <summary>
/// Used in CellCellTemplateRuleSelector and its inheriting classes.
///
/// Wouldnt be too hard to modify to allow declarative if highly desired.
/// </summary>
public class CellTemplateRule
{
/*
object _Value;
public object Value
{
get
{
return this._Value;
}
set
{
this._Value = value;
}
}
*/
// NOTE - if needing to widen use case, use <T>, but ONLY if needed
Expression<Func<BaseDataModel, bool>> _Condition;
public Expression<Func<BaseDataModel, bool>> Condition
{
get
{
return this._Condition;
}
set
{
this._Condition = value;
}
}
DataTemplate _Template;
public DataTemplate Template
{
get
{
return this._Template;
}
set
{
this._Template = value;
}
}
public CellTemplateRule(Expression<Func<BaseDataModel, bool>> c, string s)
{
var d = App.Current.FindResource(s);
if (d != null)
this.Template = d as DataTemplate;
else
throw new Exception("No such template resource as '" + s + "'");
Condition = c;
}
}
// NOTE IMPORTANT - use of xaml defined ConditionalTemplateSelectors is not a bad alternative approach, though diferent selector
// for each column it could all be defined in same resource dictionary
// - problem is that issues encountered with Enums, so decided on this approach instead
/// <summary>
/// See CellStyleSelector, this is a variant used with the CellTemplateSelector property
/// </summary>
public abstract class CellTemplateSelector : DataTemplateSelector
{
// public Dictionary<String, Dictionary<string, Template>> conditions { get; set; }
// Use a Dictionary mapping X columns to individual Lists of rules to check
Dictionary<string, List<CellTemplateRule>> _Rules;
public Dictionary<string, List<CellTemplateRule>> Rules
{
get
{
if (this._Rules == null)
{
this._Rules = new Dictionary<string, List<CellTemplateRule>>();
}
return this._Rules;
}
set { this._Rules = value; }
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is BaseDataModel)
{
GridViewCell cell = container as GridViewCell;
var currentColumn = cell.Column as GridViewDataColumn;
string key = currentColumn.DataMemberBinding.Path.Path;
if (Rules.ContainsKey(key))
{
foreach (CellTemplateRule rule in Rules[key])
{
// string debug = DebugHelper.Debug(rule.Condition);
// REVERTED NOTE - if just Func<>
// if (rule.Condition((BaseDataModel)item))
// NOTE - if Expression<Func<>>, first compile then pass in param
if (rule.Condition.Compile()((BaseDataModel)item))
{
return rule.Template;
}
}
}
}
return null;
}
}
// Implementing class for a different table, though RadGridView can use both CellStyleSelector and CellTemplateSelector at same time, i just didnt need yet
public class OrderDongleWrapTableCellTemplateSelector : CellTemplateSelector
{
public OrderDongleWrapTableCellTemplateSelector()
: base()
{
this.Rules = new Dictionary<string, List<CellTemplateRule>>()
{
{ "ReplacedDongle.DongleID" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).IsReplacementDongle == false , "CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField") },
}
},
{ "ReplacedDongleStatus" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).IsReplacementDongle == false , "CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField") },
}
},
/*
* // REVERTED - better to just set to UNKNOWN CUSTOMER explicitly before shown in table, rather than overwrite datatemplate
{ "Customer.CustomerName" , new List<CellTemplateRule>()
{
// Always most specific rules at top
{ new CellTemplateRule(x => ((OrderDongleWrap)x).Customer == null || ((OrderDongleWrap)x).Customer.CustomerID == 1 , "CellTemplate_OrderDongleWrap_UnknownCustomerField") },
}
},
*/
};
}
}
xaml 资源
<!--
The following are styles applied to cells or rows in RadGridViews based on StyleSelectors
First is generic colours, which are inherited by semi-generic flag styles, which optionally
can be further inherited by table-specific styles (which can include tooltips and other properties)
As a general rule, no colour style is applied directly, instead using semi-generic or table-specific
styles so as to make changes easier to manage. Which style actually matches which condition is
entirely the responsibility of the StyleSelector.
https://en.wikipedia.org/wiki/Web_colors
http://www.flounder.com/csharp_color_table.htm
<Style x:Key="CellStyle_" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value=""/>
</Style>
-->
<!--
NOTE: to get cell text underlining to work, see http://www.telerik.com/forums/underline-cell-contents
-->
<Style x:Key="CellStyle_Green" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Green"/>
</Style>
<Style x:Key="CellStyle_DarkGreen" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkGreen"/>
</Style>
<Style x:Key="CellStyle_ExtraDarkGreen" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="#004000"/>
</Style>
<Style x:Key="CellStyle_MediumBlue" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="MediumBlue"/>
</Style>
<Style x:Key="CellStyle_DarkBlue" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkBlue"/>
</Style>
<Style x:Key="CellStyle_Purple" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Purple"/>
</Style>
<Style x:Key="CellStyle_Red" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Red"/>
</Style>
<Style x:Key="CellStyle_Crimson" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Crimson"/>
</Style>
<Style x:Key="CellStyle_DarkOrange" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkOrange"/>
</Style>
<Style x:Key="CellStyle_Silver" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Silver"/>
</Style>
<Style x:Key="CellStyle_DarkGray" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="DarkGray"/>
</Style>
<Style x:Key="CellStyle_Gray" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Foreground" Value="Gray"/>
</Style>
<Style x:Key="CellStyle_RedBG_WhiteFG" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="CellStyle_Muted" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}" />
<Style x:Key="CellStyle_Status_Active" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_ExtraDarkGreen}" />
<Style x:Key="CellStyle_Status_Inactive" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}" />
<Style x:Key="CellStyle_Status_Warning" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Crimson}" />
<Style x:Key="CellStyle_Status_Error" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Red}" />
<Style x:Key="CellStyle_Status_ErrorBG" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_RedBG_WhiteFG}" />
<Style x:Key="CellStyle_Dongle_InactiveWithActiveLicences" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Status_Error}">
<Setter Property="ToolTip" Value="This Dongle is not Active, but has Licences which are"/>
</Style>
<Style x:Key="CellStyle_Licence_Full" TargetType="telerik:GridViewCell" BasedOn="{StaticResource GridViewCellStyle}" />
<Style x:Key="CellStyle_Licence_Upgrade" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_DarkBlue}" />
<Style x:Key="CellStyle_Licence_Timed" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Purple}" />
<Style x:Key="CellStyle_Licence_ExpiryMismatch" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Status_ErrorBG}">
<Setter Property="ToolTip" Value="Expiry doesnt match Status or Issued Date"/>
</Style>
<Style x:Key="CellStyle_OrderDongleWrap_NotReplacementDongler_NAField" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}">
<Setter Property="Content" Value="N/A"/>
</Style>
<Style x:Key="CellStyle_OrderDongleWrap_UnknownCustomerField" TargetType="telerik:GridViewCell" BasedOn="{StaticResource CellStyle_Gray}">
<Setter Property="Content" Value="N/A"/>
</Style>
<DataTemplate x:Key="CellTemplate_OrderDongleWrap_NotReplacementDongler_NAField">
<TextBlock>N/A</TextBlock>
</DataTemplate>
为 CellStyleSelector
实施 xaml<telerik:RadGridView.Resources>
<gridviewstyleselectors:LicenceTableCellStyleSelector x:Key="LicenceTableCellStyleSelector" />
</telerik:RadGridView.Resources>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding DongleID}" Header="DongleID" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceSerial}" Header="Serial" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceStatus}" Header="Status" IsReadOnly="True"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Product.ProductName}" Header="Product" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceType}" Header="Licence Type"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceIssued}" Header="Issued"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding LicenceExpires}" Header="Expiry"
CellStyleSelector="{StaticResource LicenceTableCellStyleSelector}" IsReadOnly="True" />
</telerik:RadGridView.Columns>
到目前为止,我只有一个 CellTemplateSelector 案例,这里是:
为 CellTemplateSelector 实施 xaml
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Dongle.DongleID}" Header="DongleID">
<telerik:GridViewDataColumn.AggregateFunctions>
<telerik:CountFunction ResultFormatString="{}Dongles: {0}" />
</telerik:GridViewDataColumn.AggregateFunctions>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn DataMemberBinding="{Binding OrderRows.Count}" Header="Licences"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Customer.CustomerName}" Header="Customer"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding IsReplacementDongle}" Header="Replacement?"
IsVisible="False"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding IsEducationalDongle}" Header="Educational?"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding ReplacedDongle.DongleID}" Header="Replaced"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding ReplacedDongleStatus}" Header="Replacement Status"
CellStyleSelector="{StaticResource OrderDongleWrapTableCellStyleSelector}"
CellTemplateSelector="{StaticResource OrderDongleWrapTableCellTemplateSelector}"/>
<telerik:GridViewDataColumn DataMemberBinding="{Binding OrderRows[0].OrderRowCosts[0].CostValue }" Header="Sub-Total (AUD)">
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
所以这是我自己的方法,但它将条件逻辑和样式集中到每个目标 class(GridView ItemsSource 对象)仅 1 个 cs class 和仅 1 个资源 sheet 对于所有 GridView。
如果你愿意,你可以很容易地重构为声明式的,我看到这是可能的,但不需要或不想这样做,所以我没有。
您还可以重构,以便规则级联所有表的单个 cs class,甚至动态声明样式..? (通过新样式和添加属性?)
无论如何,我希望这能帮助其他人解决这个问题