1

I am trying to tweak a style for an Infragistics DataRecordCellArea for a XamDataGrid to be largely based on the currently assigned style (via ThemeManager, but potentially on a parent element). Normally, if the theme were being applied in the same way as the system themes, I would do this using something like:

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

with the style being injected as a resource on the XamDataGrid, in the hope that the implicit style would be picked up based on the BasedOn attribute. However, this isn't what's happening: using this approach, I get a XamlParseException related to StaticResource being unable to find the resource - i.e. there is no style to base things on.

"Great", you might think, "Just get rid of the based on and it'll work." This would be true except that doing so with an empty style clearly DOES affect the control's appearance as a deviation from what the theme provides.

My intended solution is a custom MarkupExtension that takes a named FrameworkElement and a target type and attempts to find the implicit style that WOULD be in use were I to create an instance of the target type as a child of the context-providing object. If that turns out to be the default style (or even a null value), then so be it. This should be safe for sealing when the style starts to be used, too, because I do not need to respond to changes in context, just get the value at the point when the style is constructed. I think.

However, what I can't work out is HOW to retrieve the IMPLICIT style for an element when no explicit value is found for the actual style property. Finding the default style seems easy enough, but since there is no context providable to the Application.Current.FindResource() method, I am convinced this will be the equivalent of leaving the BasedOn attribute blank. Conversely, I expect to get false nulls if I simply get var implicit = Context.Resources.Contains(TargetType) ? Context.Resources[TargetType] : null;, then I expect to only capture styles that are explicit members of that ResourceDictionary and its collection of MergedDictionaries.

There is a possibility of a further complication, too: namely the possibility that the style I actually want to replace may NOT be keyed only with the type. For instance, imagine that the theme may use the same object for multiple purposes, and each of those purposes has a specific key name, with the style then assigned during the ControlTemplate being configured for the parent object, i.e.

<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>

For the time being, I am hoping that this is NOT what is happening, but there is at least some evidence that indicates it might be. If anyone knows of a way to hook onto and override THAT style when the ControlTemplate is rendered, this would also answer my problem - possibly better than finding the implicit style (because then I'm not actually overriding an implicit style).

Community
  • 1
  • 1
tobriand
  • 1,095
  • 15
  • 29
  • In last case ("further complication") style will always be `SomeActualKeyedValue` I think, no matter what you do. – Evk Mar 22 '17 at 11:19
  • Meh - was hoping there might be a way to say "Given this object, for any created instance of my targetted type, take the style that would be applied left to its own devices and construct a new, overriding, style in its place and apply that instead". I guess the sealed nature of styles means that you'd need a new one for every instance, or at least for every identified candidate style to base the new one on, but still. If it's not possible, it's not possible. – tobriand Mar 22 '17 at 11:26
  • Hmm... thinking about it, I wonder if I could override the `FrameworkPropertyMetaData` for the target type to respond to change events on some base property, and then apply my style there... Might be overkill, but I guess it bears consideration... – tobriand Mar 22 '17 at 11:28
  • You can override metadata for `Style` property of target control type, then in callback do `var style = LoadYourStyle(); style.BasedOn = (Style) e.NewValue; targetControl.Style = style;`. This will work, but only if style is explicitly set on that control. If it's not - callback will not be called. On the other hand you can combine that with other approaches. – Evk Mar 22 '17 at 12:07

1 Answers1

0

The approach I ended up taking (ignoring the complicating case for now) was to write a markup extension that took a FrameworkElement for context and a target type. From there, it turns out that all FrameworkElements have a FindResource() method (not just the Application), so it was pretty straightforward to just call FindResource as it pertained to the context object.

The result is xaml that looks something like:

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

... and C# that looks something like:

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;
    }
}

(NB the above is an approximation of my solution, since I don't have the code-base to hand, and haven't tried compiling it for now, but it should provide a fair idea as to the approach that worked.)

This evaluates nicely at the point when the style would be created, finds the implicit style, and bases it off that. It doesn't find styles with an unknown (or known) key and replace them though (per the complicating case).

tobriand
  • 1,095
  • 15
  • 29