C# 未在控件模板中为datagrid设置依赖项属性

C# 未在控件模板中为datagrid设置依赖项属性,c#,wpf,xaml,datagrid,wpfdatagrid,C#,Wpf,Xaml,Datagrid,Wpfdatagrid,我需要定制datagrid,所以我创建了自己的datagrid扩展WPF datagrid。小相关代码贴在下面- public class ExtendedDataGrid : DataGrid { public ExtendedDataGrid() { this.SelectionMode = DataGridSelectionMode.Extended; } } 我在一个窗口中创建了它的实例,并将SelectionMode设置为Single,这可以很

我需要定制datagrid,所以我创建了自己的datagrid扩展WPF datagrid。小相关代码贴在下面-

public class ExtendedDataGrid : DataGrid
{
    public ExtendedDataGrid()
    {
        this.SelectionMode = DataGridSelectionMode.Extended;
    }
}
我在一个窗口中创建了它的实例,并将
SelectionMode
设置为
Single
,这可以很好地工作,并且为dataGrid将属性设置为
Single
。到目前为止一切都很好

但如果我将数据网格放在ControlTemplate中,
SelectionMode
永远不会设置为
Single
。SelectionMode只是一个例子,若我在DataGrid的构造函数中显式设置了该值,那个么就并没有通过XAML设置DP

小样本复制问题就在这里-

 <Grid>
    <Grid.Resources>
        <ControlTemplate x:Key="MyTemplate">
            <local:ExtendedDataGrid ItemsSource="{Binding Collection,
                                                RelativeSource={RelativeSource   
                                                  Mode=FindAncestor, 
                                                  AncestorType=Window}}"
                                    SelectionMode="Single">
                <local:ExtendedDataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding}"/>
                </local:ExtendedDataGrid.Columns>
            </local:ExtendedDataGrid>
        </ControlTemplate>
    </Grid.Resources>
    <ContentControl Template="{StaticResource MyTemplate}"/>
    <local:ExtendedDataGrid ItemsSource="{Binding Collection,
                                          RelativeSource={RelativeSource 
                                          Mode=FindAncestor,
                                          AncestorType=Window}}"
                            Grid.Row="1" SelectionMode="Single">
        <local:ExtendedDataGrid.Columns>
            <DataGridTextColumn Binding="{Binding}"/>
        </local:ExtendedDataGrid.Columns>
    </local:ExtendedDataGrid>
</Grid>

对于第二个DataGrid,其工作正常,但对于放置在ControlTemplate中的DataGrid不起作用为什么会有这种奇怪的行为?这是DataGrid代码中的错误吗?

注意-如果我对DataGrid中的行进行注释,它将正常工作 构造函数,其中我显式地将SelectionMode设置为 延长。我知道这是默认值,删除它之后 在这两种情况下都可以正常工作(还有很多方法可以设置默认值),但我想知道为什么它在一种情况下工作,而在另一种情况下不工作


很抱歉,我无法回答您的问题,即为什么在使用
ControlTemplate
时它不起作用,但我可以为您提供更好的方法,为扩展类中的继承属性设置默认值,这可以解决您的问题

可以使用
dependencProperty.OverrideMetadata
方法为继承的
dependencProperty
提供具有默认值的新元数据。您可以使用
static
构造函数为
SelectionMode
属性设置自己的默认值,如下所示:

static ExtendedDataGrid()
{
    SelectionModeProperty.OverrideMetadata(typeof(ExtendedDataGrid), 
        new FrameworkPropertyMetadata(2));
}

更新>>>

如果将
SelectionMode
枚举替换为表示所需值的整数,则代码将编译。我只使用了
SelectionMode.Extended
值(现在替换为其整数值-2),因为这就是您在示例中使用的值


我建议使用这种设置默认值的替代方法,因为您说过,如果在构造函数中将
SelectionMode
属性的默认值设置为
SelectionMode.Extended
的那一行注释掉,问题就会消失。我认为,如果您使用该行替换该行,那么您的问题可能会消失。

这是一个好问题,要回答它,需要了解WPF引擎是如何创建这两个
DataGrid
实例的

对于
DataGrid
的第一个实例,它是
窗口的直接子对象,当调用
窗口的构造函数中的
InitializeComponents()
时,将创建该实例。我不会深入讨论
初始化组件的工作原理,但只是一个jist,它调用方法
System.Windows.Application.LoadComponent()
,并
LoadComponent()
加载位于传入的
URI
上的
XAML
文件,并将其转换为由
XAML
文件的根元素指定的对象实例。执行此操作时,它首先调用要创建的元素的默认构造函数,然后再次设置属性中提到的
dependancProperties

现在,您在
ControlTemplate
中放置的第二个实例。当将
ControlTemplate
应用于元素时,将创建该实例。如果未应用
模板
,则永远不会创建实例。在应用
模板
时,调用
ControlTemplate.LoadContent()
来创建
ControlTemplate
的根元素。现在
LoadContent()
对创建
controlTemplate
中定义的
ui元素采取不同的粗略方法。它确实为每个元素调用默认构造函数,但在设置
dependandcroperties
时,它会运行大量检查以确定属性值。简而言之,它检查在元素实例上是否已经为特定的<代码>依赖项属性< /代码>设置了任何值(即,值不是默认值,它是“代码> RealCasyObjult<代码> >的ValueDeCigIcript中的实例的本地值条目”,它不考虑XAML中指定的值。因此,在这种情况下,当
LoadComponents
调用DataGrid的默认构造函数时,我们设置
SelectionModeProperty
值。加载内容时,
ControlTemplate
检查内容并返回相同的值,并忽略xaml中指定的值


所有的控件都是如此,不仅仅是
DataGrid

最后,在
PresentationFramework assembly
中使用
reflector
挖掘了一些代码之后,我能够找到这个问题的确切RCA。如所述,此行为适用于所有DP和所有控件,而不仅仅是DataGrid

这一切都与此有关 设置DP时,哪个值优先于其他值。 (枚举是BaseValueSourceInternal,它在WindowsBase.dll程序集中存储DP的优先顺序)

DependencyObject类
包含方法
UpdateEffectiveValue
,该方法负责通过调用DataGrid实例上的
SetValue
方法来设置任何DP上的最终实际值。
UpdateEffectiveValue
方法在实际调用DP上的
SetValue
方法之前包含大量逻辑

通过ControlTemplate停止设置的有趣的检查是这个(if ((newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Unknown) && (newEntry.BaseValueSourceInternal < oldEntry.BaseValueSourceInternal)) { return (UpdateResult) 0; }
<ControlTemplate x:Key="MyTemplate">
    <local:ExtendedDataGrid ItemsSource="{Binding Collection,
                                         RelativeSource={RelativeSource
                                       Mode=FindAncestor, AncestorType=Window}}">
         <local:ExtendedDataGrid.Columns>
             <DataGridTextColumn Binding="{Binding}"/>
         </local:ExtendedDataGrid.Columns>
         <local:ExtendedDataGrid.Triggers>
             <EventTrigger RoutedEvent="FrameworkElement.Loaded">
                 <BeginStoryboard>
                     <Storyboard>
                        <ObjectAnimationUsingKeyFrames 
                             Storyboard.TargetProperty="SelectionMode">
                            <DiscreteObjectKeyFrame KeyTime="00:00:00"
                              Value="{x:Static DataGridSelectionMode.Single}"/>
                        </ObjectAnimationUsingKeyFrames>
                     </Storyboard>
                 </BeginStoryboard>
             </EventTrigger>
          </local:ExtendedDataGrid.Triggers>
    </local:ExtendedDataGrid>
</ControlTemplate>