Ignore global style for specific control and it's children - c#

I've done my best to ensure this isn't an exact duplicate of other questions and have tried quite a few possible solutions. Maybe I'm just not doing the right searches.
The problem
I have a resource dictionary with a bunch of default styles. For example, most control types have a default height of 26 to provide some consistency in my layout.
So for example, I have the following style for TextBlock
<Style TargetType="TextBlock">
<Setter Property="Height" Value="26"/>
</Style>
The problem I have is that the Telerik RadGridView uses TextBlock controls to display the column header text and these controls are adopting the default style and end up with a height of 26.
I would like to get the RadGridView, and all of it's child controls, to ignore the styles in my resource dictionary.
I tried this but it didn't work
I found quite a few suggestions to set the style to Null for controls that you want to ignore global styles. I tried the following but it didn't work as it doesn't seem to apply to the child controls inside of the RadGridView.
<telerik:RadGridView Style="{x:Null}">
...
</telerik:RadGridView>
This works but may not be the best solution
I found the following question which had a solution I was able modify and use
Setting style based on existence of an ancestor type
Using the answers in that question I created a converter to check if a control has an ancestor of a specific type. The code is pretty much the same as in the above question so to keep this question from getting too long I won't paste it here.
I modified my style to this
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource HasAncestorTypeConverter},
ConverterParameter={x:Type telerik:RadGridView}}"
Value="False">
<Setter Property="Height" Value="26"/>
</DataTrigger>
</Style.Triggers>
</Style>
Although this works, I was concerned about performance as my application grows and there's more and more controls. I have similar triggers for many control types that could be used in the RadGridView.
Every control is going to be recursively checking if it has a RadGridView as an ancestor. Most won't, so they need to search all the way up to their base container before knowing they don't have a RadGridView as an ancestor.
My question
So after that the lead up, my questions is whether there's a better way to do this? Is there something I can wrap the RadGridView with that will tell it, and it's child elements, to ignore the styles in my resource dictionary?

Yes there is better way to do this: define empty style either in Resources of RadGridView itself, or if you want to apply that to all RadGridViews - define it in resources of RadGridView style itself, like this:
<Style TargetType="telerik:RadGridView">
<Style.Resources>
<Style TargetType="TextBlock" />
</Style.Resources>
</Style>
That will prevent all children of RadGridView to inherit global TextBlock style (instead they will use "default" TextBlock style)

Related

How to apply style in WPF to all controls without overriding their original styles?

I want to create a style, which will normalize margins for all controls in a specific scenario:
<Style TargetType="FrameworkElement" x:Key="MyStyle">
<Setter Property="Margin" Value="{StaticResource DialogItemsExceptTopMargin}" />
</Style>
I have then a couple of different controls: textboxes, comboboxes, checkboxes etc., to which I want to apply this style.
However, when I do that, their look is immediately reverted to the platform style (I am applying a style from 3rd party library). How can I define my style so that the original styles are kept intact?
Mind: I know, that I can use BasedOn for specific type:
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
...
</Style>
This however would require me to create completely separate styles for all possible controls, which I may use. I tried this trick with FrameworkElement and Control, but I got an error, that there is no such StaticResource.

Override specific properties of a style

I'm creating a new style for the TabControl including a new ItemContainerStyle for the items. The new style works fine, except that I need the possibility to add features to the ItemContainerStyle when using the style. In detail this is the Header property
<TabControl x:Name="myTabControl" SelectionChanged="myTabControl_SelectionChanged">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Title}"></Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
which works OK. But it overrides the style completely. the BasedOn property would help, but I don't have access to the key of the ItemContainerStyle since it is embedded in the TabControls' style. How can I simply update a property of the style without overriding the style completely?
Thanks
There is a bit of a difference in the way styles in WPF compared to css. In Wpf they work completely off of inheritance, this is the basic document on how styles work. So if a key is not provided for you I think you are out of luck when using the BasedOn inheritance.
However, Microsoft does provide a useful utility in Visual Studio Blend. In the Objects and Timelines Window you right click then select an "edit a style", This will do all the heavy lifting for you. If you are going to be doing a lot of small changes on the style I would suggest you make a copy, and give it a Key, then use the BasedOn property to make your small changes that you want.
I Hope this helps.
You can base your style on the implicitly applied default style:
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">

Hiding TreeView items

I've been trying to just hide items from a TreeView. I'm using a custom data type as source (called SettingsMenuItem) which inherits from FrameworkElement (currently FrameworkContentElement, because otherwise the TreeView renders them wrong).
My goal is by setting the VisibilityProperty of these FrameworkElements to either Collapsed or Visible that I'm able to hide certain items (including their children). I know that this can be done by deleting items from the source collection. But that's not what I want. It would mean that I have to mirror each collection in order to keep track of it's actual items, bind to each one in order to be notified about Visibility-changes and create a new collection each time one changes. A lot of overhead for this.
Right now I have no clue how I could accomplish that. I figure it's related to the ItemsGenerator, but I haven't seen any possibility to override it's behaviour. I thought TreeView would be able to detect Visibility, but obviously it doesn't. As alternative I thought of a custom TreeViewItem (maybe even TreeView if necessary) - but at this point the abstraction of this whole system overwhelms me. I don't know where to start and what is actually necessary to solve the problem.
Tips what I have to change or implement by myself would be more than enough. A complete solution would be nice.
You can do this using a data trigger bound to a property (e.g. "IsVisible") in you tree data nodes:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
While this technically answers your question I would be wary of actually doing it. User3690202's comment is correct, it's the sort of thing you would normally do via filtering in your view model.
For alternate solution using code behind xaml.cs:
To Remove a specific TreeViewItem from a TreeView which is created from a code behind.
TreeViewItem treeViewItem1 = new TreeViewItem
{
Visibility = Visibility.Collapsed,
};
use the code with TreeViewItem you want to hide in a if condition to hide specific TreeViewItem Header let say "Cars" and you want to hide it and use the code with if condition to hide "Cars" TreeViewItem.

Unexpected behavior of AlternativeRow BackColor in WPF

I'm styling my DataGrid using an style provided by Microsoft at this page. I'm not changing this style except a single line as following that enables alternative row back color.
<!--Style and template for the DataGrid.-->
<Style TargetType="{x:Type DataGrid}">
<Setter Property="AlternationCount" Value="2"/>
... and the rest of the style
I realized a very strange behavior of this style. When you scroll up/down the data grid for few times and then you see that alternative row back color is mixed! such as following image:
I expect a sequence of light-colored + dark-colored rows which is true before scrolling; but after scrolling everything is randomly mixed-up. Before applying this style I was explicitly setting alternative row color at DataGrid definition like following which I never experienced such behavior.
<DataGrid ItemsSource="{Binding Source={StaticResource itemsSource}}" AutoGenerateColumns="False" AlternatingRowBackground="#FF58C9FD" RowBackground="#00000000"/>
Does anyone have a suggestion where should I be looking for the problem ?
That's a known virtualization problem on WPF DataGrid.
Here is a workaround, but be aware that it can cause serious performance problems if your dealing with large amounts of data on that DataGrid.
There is a good explanation on the mechanics behind this behavior here.
Gabriel mentioned that this behavior is a known issue that raises when you try to create a brand-new style for data grid (or in general any item collection).
I tried creating an style based on default style as suggested by Gabriel like following:
<Style TargetType="{x:Type DataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="AlternationCount" Value="2"/>
<Setter Property="AlternatingRowBackground" Value="OrangeRed"/>
</Style>
I tried overriding all properties I need via this style, and it works fine. Maybe there is a trick/tweak missing in styles provided by MSDN (which is duplicated in most of styles found on net) that fixes this issue. We still appreciate if anyone updates us.
Meanwhile, I suggest to override default styles by your customization and avoid any brand-new style.

How to add a trigger to a WPF custom control without overriding the existing style?

I am creating a simple custom control extending from toggle button that allows the user to specify checked and unchecked content directly in XAML. It works well but it is based on a trigger, and I don't know how to define the trigger except in a style. If I define the style, then I lose anything set outside of the custom control.
What I would like to be able to do is just append this trigger to any existing style set elsewhere on the control.
Here's the XAML for the style/trigger.
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=UncheckedContent}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content"
Value="{Binding RelativeSource={RelativeSource Self}, Path=CheckedContent}" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
I tried inheriting the style via the BasedOn with a default type but it won't work if the custom control has an explicit style set by its parent. I also considered EventTriggers but I do not believe there would be an event to initialize the control.
Thanks for any help anyone can offer. :)
Just to clear things up on the terminology here: A user control is a control that derives from the UserControl class. If I understood you right you derived from ToggleButton to add the UncheckedContent and CheckedContent properties. In that case you have created a custom control. It's always easier to follow if we agree on common terminology :)
As far as I know you can not do such a generic style inheritance in XAML. You always have to specify explicitly what style a another style is based upon. Your style can either be based on the default style for ToggleButton or on a specific other style. If you can't build a style inheritance chain that respects that, this approach won't work.
But since you have a custom control, couldn't you write a default style for it that is based on the default toggle button style like this?
<Style TargetType="{x:Type CustomToggleButton}"
BasedOn="{StaticResource {x:Type ToggleButton}}">
Then whenever you apply an explicit style to a toggle button you would specify that it is based on the default toggle button style.
Also you could write a (default) control template for your new toggle button in Themes\Generic.xaml that contains the above triggers. In blend you can get a copy of the default template for toggle button ("Edit Template"->"Edit a Copy") so you can make sure that your toggle button looks exactly like the normal toggle button. Then incorporate the triggers above into that template.
BTW: you do not have to create a new control just to add new properties. You can add new properties to an existing control using attached properties. They can be used from XAML just like normal properties.

Categories

Resources