【问题标题】:Cannot get attached behavior property value when control is unloaded卸载控件时无法获取附加的行为属性值
【发布时间】:2016-02-02 12:35:45
【问题描述】:

我正在使用 ContentControl 根据用户通过 DataTemplateSelector 选择的对象类型来更改 UI 中显示的控件。

当控件被卸载时,基于替换下面 ContentControl 中的内容,我无法再访问 附加行为的 Unloaded 事件处理程序中附加行为的属性。

例如,如果用户在 UI 中单击钻机单元,然后单击燃料卡车单元,则我无法访问控件中的附加属性。就像卸载控件时为时已晚,无法读取附加属性 当 DateTemplateSelector 触发时。

如果控件被卸载是因为我关闭了父窗口或其他一些原因,比如隐藏了父文档选项卡,那么我 读取附加的行为属性值没有问题。

这是失败的代码片段:

代码试图从下面找到附加的行为属性:“behaviours:PersistUiBehavior.PersistanceChildCode” 我期望“结果”等于 17 或 18,具体取决于控件何时处于活动状态。

public static void GetPersistenceRequiredControls(DependencyProperty property, DependencyObject root, int code, List<object> sources)
{
  if ((property != null) && (root != null))
  {
    // property = behaviours:PersistUiBehavior.PersistanceChildCode does not exist here when unloaded
    var result = (int) root.GetValue(property);
    if (result == code)
    {
      sources.Add(root);
    }
  }
 }

这是一个显示各个部分的 XAML 片段:

<UserControl x:Class="FleetControl.Editors.Views.PropertyEditor">
 <UserControl.Resources>
  <ResourceDictionary>

  <templates:PropertyEditorTemplateSelector x:Key="PropertyEditorTemplateSelector">

    <templates:PropertyEditorTemplateSelector.DrillUnitEditorTemplate>
      <DataTemplate>
        <telerik:RadPropertyGrid 
          x:Name="DrillPropertyGrid"
          behaviours:PersistUiBehavior.FileName="Test1.txt"
          behaviours:PersistUiBehavior.PersistanceParentCode="5"
          behaviours:PersistUiBehavior.PersistanceChildCode="17"
          behaviours:PersistUiBehavior.Name="DrillPropertyGrid">
          <telerik:RadPropertyGrid.PropertyDefinitions>
            <telerik:PropertyDefinition DisplayName="Equipment Ident" Binding="{Binding DisplayName, Mode=OneWay}" GroupName="Description" IsReadOnly="True" />
            <telerik:PropertyDefinition DisplayName="Equipment Description" Binding="{Binding Description, Mode=OneWay}" GroupName="Description" IsReadOnly="True"/>
            <telerik:PropertyDefinition DisplayName="Equipment Type Ident" Binding="{Binding EquipmentType.Ident, Mode=OneWay}" GroupName="Description" IsReadOnly="True"/>
          </telerik:RadPropertyGrid.PropertyDefinitions>
        </telerik:RadPropertyGrid>
      </DataTemplate>
    </templates:PropertyEditorTemplateSelector.DrillUnitEditorTemplate>
    <templates:PropertyEditorTemplateSelector.FuelTruckUnitEditorTemplate>
      <DataTemplate>
        <telerik:RadPropertyGrid 
          x:Name="FuelTruckUnitPropertyGrid"
          behaviours:PersistUiBehavior.FileName="Test2.txt"
          behaviours:PersistUiBehavior.PersistanceParentCode="6"
          behaviours:PersistUiBehavior.PersistanceChildCode="18"
          behaviours:PersistUiBehavior.Name="FuelTruckUnitPropertyGrid">
          <telerik:RadPropertyGrid.PropertyDefinitions>
            <telerik:PropertyDefinition DisplayName="Location" Binding="{Binding Location.LocationName, Mode=OneWay}" GroupName="Realtime" IsReadOnly="True"/>
            <telerik:PropertyDefinition DisplayName="Location Code" Binding="{Binding Location.Code, Mode=OneWay}" GroupName="Realtime" IsReadOnly="True"/>
            <telerik:PropertyDefinition DisplayName="Location Description" Binding="{Binding Location.Description, Mode=OneWay}" GroupName="Realtime" IsReadOnly="True"/>
          </telerik:RadPropertyGrid.PropertyDefinitions>
        </telerik:RadPropertyGrid>
      </DataTemplate>
    </templates:PropertyEditorTemplateSelector.FuelTruckUnitEditorTemplate>
  </templates:PropertyEditorTemplateSelector>

 </ResourceDictionary>
</UserControl.Resources>

 <Grid>
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
  </Grid.ColumnDefinitions>

  <ContentControl 
    ContentTemplateSelector="{StaticResource PropertyEditorTemplateSelector}"
    Content="{Binding Path=SelectedPropertyEditor, Mode=OneWay}" />
 </Grid>
</UserControl>

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    是的,你是对的。当 ContentPresenter 的模板更改时,附加属性(实际上是所有属性)将被清除。这是由System.Windows.StyleHelper.DoTemplateInvalidations() 方法执行的操作的一部分,在这种情况下由ContentPresenter.OnTemplateChanged() 方法调用。这最终导致System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode() 被调用,这是实际工作完成的地方。

    例如,这是我在调试器中看到的调用堆栈:

    WindowsBase.dll!System.Windows.DependencyObject.InvalidateProperty(System.Windows.DependencyProperty dp, bool preserveCurrentValue) Unknown
    PresentationFramework.dll!System.Windows.StyleHelper.InvalidatePropertiesOnTemplateNode(System.Windows.DependencyObject container, MS.Internal.FrameworkObject child, int childIndex, ref MS.Utility.FrugalStructList<System.Windows.ChildRecord> childRecordFromChildIndex, bool isDetach, System.Windows.FrameworkElementFactory templateRoot)    Unknown
    PresentationFramework.dll!System.Windows.StyleHelper.ClearTemplateChain(System.Collections.Specialized.HybridDictionary[] instanceData, System.Windows.FrameworkElement feContainer, System.Windows.FrameworkContentElement fceContainer, System.Collections.Generic.List<System.Windows.DependencyObject> templateChain, System.Windows.FrameworkTemplate oldFrameworkTemplate)    Unknown
    PresentationFramework.dll!System.Windows.StyleHelper.ClearGeneratedSubTree(System.Collections.Specialized.HybridDictionary[] instanceData, System.Windows.FrameworkElement feContainer, System.Windows.FrameworkContentElement fceContainer, System.Windows.FrameworkTemplate oldFrameworkTemplate) Unknown
    PresentationFramework.dll!System.Windows.StyleHelper.DoTemplateInvalidations(System.Windows.FrameworkElement feContainer, System.Windows.FrameworkTemplate oldFrameworkTemplate)    Unknown
    PresentationFramework.dll!System.Windows.Controls.ContentPresenter.OnTemplateChanged(System.Windows.DependencyObject d, System.Windows.DependencyPropertyChangedEventArgs e)    Unknown
    

    这一切都发生在Unloaded 事件被调用之前。无论好坏,这完全是设计使然。

    如果没有一个很好的 Minimal, Complete, and Verifiable example 可以清楚地显示您的代码的作用,并准确解释您的最终目标是让该代码做什么,我真的不能说最好的替代方法是什么。

    我想到了至少几个明显的变通方法,都涉及在较早的时间点执行您在 Unloaded 事件处理程序中尝试执行的工作:

    • 在模型数据引用发生更改时执行该工作。 IE。处理对 SelectedPropertyEditor 属性的更改。
    • 在附加属性本身无效时执行该工作。例如。将 PropertyChangedCallback 委托添加到附加属性元数据并在那里执行工作(即,当您看到附加属性从以前的某个值更改为 null 时)。

    我会注意到,在您发布的一小段代码中,您似乎正试图保留在模板本身中声明的 DependencyObject(例如 telerik:RadPropertyGrid目的)。恕我直言,这可能会在其他地方引起其他问题。我承认,我不知道究竟是什么问题,但是在 WPF 已经卸载它之后尝试进入模板化子图并复活它似乎是错误的。因此,即使您解决了属性无效问题,当您尝试重用该对象时,也可能会遇到更多问题。


    如果您需要比这更具体的帮助,请发布一个新问题,其中您提供了一个很好的代码示例,以及关于在卸载模板时您希望发生什么具体的详细信息,以及在模板化对象卸载后,您希望以后如何使用它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-06
      • 1970-01-01
      • 1970-01-01
      • 2019-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多