C# 如何基于绑定属性动态更改ContentTemplate?

C# 如何基于绑定属性动态更改ContentTemplate?,c#,wpf,C#,Wpf,我有一个问题困扰了我一段时间,但我相信我最终找到了它。症状是,当绑定属性之一触发交换ContentTemplate的DataTrigger时,WPF控件将无法正确呈现。堆栈跟踪: System.ArgumentNullException: Value cannot be null. Parameter name: d at MS.Internal.Data.ElementObjectRef.GetObject(DependencyObject d, ObjectRefArgs

我有一个问题困扰了我一段时间,但我相信我最终找到了它。症状是,当绑定属性之一触发交换
ContentTemplate
DataTrigger
时,WPF控件将无法正确呈现。堆栈跟踪:

  System.ArgumentNullException: Value cannot be null.
  Parameter name: d
     at MS.Internal.Data.ElementObjectRef.GetObject(DependencyObject d, ObjectRefArgs args)
     at MS.Internal.Data.ObjectRef.GetDataObject(DependencyObject d, ObjectRefArgs args)
     at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.VerifySourceReference(Boolean lastChance)
     at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
     at MS.Internal.Data.DataBindEngine.Run(Object arg)
     at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
     at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
     at System.Windows.ContextLayoutManager.UpdateLayout()
     at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
     at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
     at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
     at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
     at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
     at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
     at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
调试器根本没有帮助,因为它只是在
application.Run()上中断。以下是我在实际xaml方面所做的工作:

  <CollectionViewSource x:Key="SomeCollectionView"
                        Source="{Binding StatsByUser}"
                        IsLiveSortingRequested="True">
      <CollectionViewSource.SortDescriptions>
          <scm:SortDescription PropertyName="Amount" Direction="Descending"/>
          <scm:SortDescription PropertyName="Name" Direction="Ascending"/>
      </CollectionViewSource.SortDescriptions>
  </CollectionViewSource>

  <ItemsControl Background="Transparent" Width="{StaticResource Width}"
                ItemsSource="{Binding Source={StaticResource SomeCollectionView}}">
      <ItemsControl.Resources>
          <DataTemplate x:Key="FullViewTemplate">
              <Border Style="{StaticResource BorderStyle}">
                  <controls:FullCustomEntityControl CustomEntityObject="{Binding}"
                                                  Style="{StaticResource PanelStyle}"
                                                  MouseDown="Info_OnMouseDown"/>
              </Border>
          </DataTemplate>
          <DataTemplate x:Key="CompactViewTemplate">
              <Border Style="{StaticResource BorderStyle}">
                  <controls:CompactCustomEntityControl CustomEntityObject="{Binding}"
                                                     Style="{StaticResource PanelStyle}"
                                                     MouseDown="Info_OnMouseDown"/>
              </Border>
          </DataTemplate>
      </ItemsControl.Resources>
      <ItemsControl.ItemTemplate>
          <DataTemplate>
              <ContentControl Content="{Binding}">
                  <ContentControl.Style>
                      <Style TargetType="{x:Type ContentControl}">
                          <Setter Property="ContentTemplate" Value="{StaticResource FullViewTemplate}"/>
                          <Style.Triggers>
                              <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=c:ShellView}, Path=ViewModel.ShowCompactView}" Value="True">
                                  <Setter Property="ContentTemplate" Value="{StaticResource CompactViewTemplate}"/>
                              </DataTrigger>
                          </Style.Triggers>
                      </Style>
                  </ContentControl.Style>
              </ContentControl>
          </DataTemplate>
      </ItemsControl.ItemTemplate>
  </ItemsControl>

每当
ViewModel.showcomactview
引发
PropertyChanged
事件并启动
DataTrigger
时,它将切换
ContentTemplate
,然后引发此错误。有没有一种方法可以解决这个问题,或者有更好的方法来设计
ContentTemplate
交换,而这不会导致这个问题

编辑:可能相关的支持文章

Edit2:UI发生了什么的示例:。您可以看到大插槽是
FullCustomEntityControl
,小插槽是
CompactCustomEntityControl
。将它们保留在任一模式而不更改不会导致任何问题,但让数据触发器更改它们会导致类似这样的问题。此外,所使用的控件应该是一致的,而不是这里看起来像分裂的控件。通过让它们处于任一模式,我的意思是移除数据触发器并选择其中一个

Edit3:一篇关于类似问题的帖子,有来自Microsoft的人回应:

有关资料:

如果堆栈上的ArgumentNullException带有VerifySourceReference,那么它肯定是由Connect 561752中描述的问题引起的。即使你的应用程序没有直接使用ElementName绑定,它也可能间接使用它们——一些内置控件使用ElementName绑定:ComboBox、ContextMenu、MenuItem等


每个ItemsControl都有属性,您可以利用它。

我不知道我是否理解错了问题,但当我将代码复制到解决方案时,它工作得非常好。下面是找我的零钱

我做的一件事是,将Path=ViewModel.ShowCompactView替换为Path=DataContext.ShowCompactView,relativesource是我的主窗口

<ItemsControl Background="Transparent" 
            ItemsSource="{Binding Source={StaticResource SomeCollectionView}}">
        <ItemsControl.Resources>
            <DataTemplate x:Key="FullViewTemplate">
                <Border >
                    <Label Content="{Binding}"
                                              />
                </Border>
            </DataTemplate>
            <DataTemplate x:Key="CompactViewTemplate">
                <Border >
                    <Button Content="{Binding}"
                                                />
                </Border>
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ContentControl Content="{Binding}">
                    <ContentControl.Style>
                        <Style TargetType="{x:Type ContentControl}">
                            <Setter Property="ContentTemplate" Value="{StaticResource FullViewTemplate}"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.ShowCompactView}" Value="True">
                                    <Setter Property="ContentTemplate" Value="{StaticResource CompactViewTemplate}"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </ContentControl.Style>
                </ContentControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

我对您的设置进行了修改,似乎一切正常。错误似乎与您的
CompactCustomEntityControl
/
FullCustomEntityControl
与PresentationCore交互时执行的操作有关

尝试在调试->选项中启用.NET Framework源代码单步执行以查看发生了什么:


据我所知,
ItemTemplateSelector
只执行一次选择。在我的示例中,当绑定变量发生变化时,模板会得到更新。我很好奇,使用两个带有可见性绑定的
ItemsControl
是否有效。即使控件不可见,也会有额外的处理开销吗?更改
ContentTemplate
是解决此问题的可靠方法,并且不会导致
ArgumentNullException
。问题可能来自您的控件
CompactCustomEntityControl
FullCustomEntityControl
。您可以获得异常在VS debug中引发的实际位置。我尝试用简化的示例(删除了自定义控件、CollectionViewSource等)重新创建您的问题,并且一切似乎都正常-它确实将我的TextBox模板与我的TextBlock模板切换。。。我建议您尝试创建一个简单的示例,并从中添加缺少的部分,直到找到真正的根cause@AmittaiShapira您要绑定到的集合中有多少个元素?当问题发生时,所发生的情况是,它中断的控件只是在正常情况下显示为空白。集合中其他确实绑定正确的元素没有出现问题。我还添加了一个可能相关的支持链接,似乎相关,但我怀疑这是原因,因为我的版本是18402,而修补程序版本是395。不幸的是,那个dll不再可以下载,所以我没有真正的方法来测试它是否是一个回归问题;但是,在某些情况下,会发生我提到的情况,而不是完全呈现控件,控件所在的位置只有空白。设置所有这些选项,使用Microsoft Symbol server,仍然没有可用的源。不确定在框架本身内调试会有多大帮助。@Lunyx尝试在选项->调试->符号作为源添加中关闭默认的“Microsoft符号服务器”。打开.net framework调试可能会有点麻烦,因为mscorlib版本通常与“Microsoft Symbol Servers”源代码版本不匹配,但我认为在您的情况下,这可能会有所帮助-您应该看到抛出的确切代码。以我的经验来看