【问题标题】:How can I access the implicit (not default) style for a UIElement from code behind?如何从后面的代码访问 UIElement 的隐式(非默认)样式?
【发布时间】:2017-03-22 11:08:44
【问题描述】:

我正在尝试调整 Infragistics DataRecordCellArea 的样式,以使 XamDataGrid 主要基于当前分配的样式(通过 ThemeManager,但可能在父元素上)。通常,如果主题的应用方式与系统主题相同,我会使用以下方式:

<!-- with xmlns:idp="http://infragistics.com/DataPresenter" -->
<Style 
    TargetType="{x:Type idp:DataRecordCellArea}" 
    BasedOn={StaticResource {x:Type idp:DataRecordCellArea}}
    >
    <!-- Style overrides -->
</Style>

将样式作为资源注入XamDataGrid,希望基于BasedOn 属性获取隐式样式。但是,这不是正在发生的事情:使用这种方法,我得到一个与 StaticResource 相关的 XamlParseException 无法找到资源 - 即没有样式可以作为基础。

“太棒了”,你可能会想,“只要去掉 based 就可以了。”这将是正确的,除非使用空样式这样做会明显影响控件的外观,因为它偏离了主题提供的内容。

我的预期解决方案是自定义 MarkupExtension,它采用命名 FrameworkElement 和目标类型,并尝试找到将要使用的隐式样式,如果我创建目标类型的实例作为上下文的子级 -提供对象。如果结果是默认样式(甚至是空值),那就这样吧。这对于开始使用样式时的密封也应该是安全的,因为我不需要响应上下文的变化,只需在构建样式时获取值即可。我想。

但是,当没有为实际样式属性找到显式值时,我无法解决的是如何检索元素的隐式样式。 Finding the default style 看起来很简单,但由于没有可提供给 Application.Current.FindResource() 方法的上下文,我相信这相当于将 BasedOn 属性留空。相反,如果我只是得到var implicit = Context.Resources.Contains(TargetType) ? Context.Resources[TargetType] : null;,我希望得到假空值,然后我希望只捕获ResourceDictionary 及其MergedDictionaries 集合的显式成员的样式。

还有可能更复杂:即我实际想要替换的样式可能不会仅使用类型键入。例如,假设主题可能将同一个对象用于多个目的,并且每个目的都有一个特定的键名,然后在为父对象配置 ControlTemplate 期间分配样式,即

<Style TargetType={XamDataGridOrSomeUnknownChildOfXamDataGrid}>
    <Style.Setters>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ControlTemplate.Resources>
                        <Style TargetType="{TheTypeICareAbout}" x:Key="SomeActualKeyedValue" BasedOn="{StaticResource SomeThemeStyleOrKeyToTheDefaultOne}">
                        <!-- Style definition -->
                        </Style>
                    </ControlTemplate.Resources>
                    ...
                    <TheTypeICareAbout Style="{StaticResource SomeActualKeyedValue}" />
                    ...
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>

目前,我希望这不是正在发生的事情,但至少有一些证据表明它可能是。如果有人知道在呈现ControlTemplate 时挂钩并覆盖该样式的方法,这也将回答我的问题-可能比找到隐式样式更好(因为那时我实际上并没有覆盖隐式样式)。

【问题讨论】:

  • 在最后一种情况下(“进一步的复杂化”),我认为无论你做什么,风格都将永远是SomeActualKeyedValue
  • 嗯 - 希望有一种方法可以说“给定这个对象,对于我的目标类型的任何创建实例,采用将应用于其自己设备的样式并构造一个新的,压倒一切,在其位置设置样式并应用它”。我猜样式的密封性质意味着您需要为每个实例创建一个新样式,或者至少对于每个已确定的候选样式都需要一个新样式来作为新样式的基础,但仍然如此。如果不可能,那就不可能。
  • 嗯...考虑一下,我想知道我是否可以覆盖目标类型的FrameworkPropertyMetaData 以响应某些基本属性上的更改事件,然后在此处应用我的样式...可能有点矫枉过正,但我​​想这值得考虑......
  • 您可以覆盖目标控件类型的Style 属性的元数据,然后在回调中执行var style = LoadYourStyle(); style.BasedOn = (Style) e.NewValue; targetControl.Style = style;。这将起作用,但前提是在该控件上显式设置样式。如果不是 - 将不会调用回调。另一方面,您可以将其与其他方法结合使用。

标签: c# wpf infragistics


【解决方案1】:

我最终采用的方法(暂时忽略了复杂的情况)是编写一个标记扩展,它采用一个 FrameworkElement 作为上下文和一个目标类型。从那里可以看出,所有 FrameworkElements 都有一个 FindResource() 方法(不仅仅是应用程序),因此只需调用与上下文对象相关的 FindResource 非常简单。

结果是看起来像这样的 xaml:

<Style 
    TargetType={x:Type TheTypeIWant} 
    BasedOn={BasedOnWithContext Context={x:ref NameOfContextProvidingObject},TargetType={x:Type TheTypeIWant}} 
    />

... 和 C# 类似:

public class BasedOnWithContextExtension : MarkupExtension 
{
    public BasedOnWIthContextExtension() 
    {
    }

    public DependencyObject Context {get; set;} 

    public Type TargetType {get; set;} 

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return this.Context?.FindResource(this.TargetType) as Style;
    }
}

(注意以上是我的解决方案的近似值,因为我手头没有代码库,也没有尝试编译它,但它应该提供一个关于可行方法的公平想法.)

这可以很好地在创建样式时进行评估,找到隐式样式并以此为基础。它不会找到具有未知(或已知)键的样式并替换它们(根据复杂情况)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 2013-02-13
    • 2012-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    相关资源
    最近更新 更多