Silverlight 4: ContentTemplate Background Change - silverlight

I have a Header style for my datagrid custom header. I am using theme for my application. The problem here is the header background of the datagrid is not changing however when I remove the styles, the header background has no problem, it change.
Here's the themes
Here's my sample application, right click the grid and context menu will appear for the list of themes, select the different themes. I have two columns namely, with header style and without header style. See the difference. Thank you for your help.
<Style x:Key="DataGridHeaderStyle" TargetType="primitive:DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" Grid.Column="0" HorizontalAlignment="Left" />
<filter:DataGridColumnFilter Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Thank you

I did a quick test, and it does not seem to work with either the tag approach or the ImplicitStyleManager attribute approach. This is probably because the style setter is outside the scope and therefore gets applied without a theme.
Suggestion 1: Look at the way that the Jet Pack theme handles it with resource dictionaries in App.xaml, and try to do something similar. (You will need the theme.xaml file for this. EDIT: This link might help.) If you want to change the theme dynamically, then consider this (old) post about swapping themes in resource dictionaries (I haven't tried it, but it should work).
Suggestion 2: You need to think about style inheritance. Since "BasedOn" needs an x:Key tag, you could instead copy the theme's implicit DataGridColumnHeader style and only modify the parts you are interested in. (I don't know if there's a more elegant way.)
If this is not what you meant, then please provide more sample code.
EDIT [2010-12-09]:
I looked at the code, and I believe the root of the problem is the absence of style inheritance. Your "DataGridHeaderStyle" is saying "Do not use the normal DataGridColumnHeader style, but instead use this TextBlock inside this Grid." So Silverlight does just that: it gives you a styled TextBlock in an unstyled default DataGridColumnHeader.
Proof: Update your "Home.xaml" and add a TextBox in the second column of your "DataGridHeaderStyle" style, next to the current TextBlock. Notice how the theme for the TextBox in the header is changing every time you change the theme (look at the TextBox backgroud), but the background of the column header is stuck on the default colour. Like I said, your custom style is telling Silverlight to ignore the implicit style.
FIX: I do not know how to inherit from implicit styles without an "x:Key" attribute. I googled quite a bit but could not find anything helpful. You will either have to a) create a custom column definition style for all your datagrid headers, or you will have to b) bother the Silverlight team for a new feature in the next version of Silverlight. Or c) pick one theme to stick with and edit a copy of the theme's implicit column header style as your new "DataGridHeaderStyle" style.
My holiday is about to start, so I hope this helped. At least now you know where the problem lies.

Related

ContentPresenter Resources not applied when added as LogicalChild

My custom control is derived from ContentControl and has an additional dependency property 'AdditionalContent' of type FrameworkElement.
This property is bound to a ContentPresenter in style that has custom style resources:
<ContentPresenter ContentSource="AdditionalContent">
<ContentPresenter.Resources>
<Style TargetType="{x:Type Button}">
... some setters ...
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
As I learned from other questions here, I have to add this object as logical child of my control by calling AddLogicalChild(AdditionalContent) and overriding LogicalChildren property.
Now, if I use my control like this
<MyControl>
<MyControl.AdditionalContent>
<Button .../>
</MyControl.AdditionalContent>
</MyControl>
The style for Button is not applied. And that's the correct behaviour, because of style inheritance (see this answer). So I have to apply the style in the place where I define the AdditionalContent. So far so good.
But strange behaviour: when I leave out adding the object as logical child, the styles are applied.
Why does this happen? And is there a proper way to provide styles for all contents inside AdditionalContent similar to define Toolbar styles?
It's hard to tell since you have left out much of the button definition, but try setting the style of the button to a dynamic resource with the button type as the resource key.
<Button Style="{DynamicResource {x:Type Button}}"/>
When adding a default style with no resource key like you have done, the implicit key is the data type.
By setting the style to a dynamic resource you are indicating that the resource could change during runtime, which is the case when you are inserting it into the tree at runtime like you are doing.

WPF style in Application resources

I have a style in application resources which I want to apply to many different pie charts. The style looks like this:
<Style x:Key="aaa" TargetType="{x:Type nm:CustomChartControl}">
<Setter Property="..." Value="..." />
<!-- etc -->
<nm:CustomChartControl.Series>
<nm:PieSeries /> <!-- PROBLEM -->
</nm:CustomChartControl.Series>
</Style>
There is a lot more properties which I exluded for simplicity. This all works well. Now, some of my pies need to have a different "model" for paitning background for a slice (ex dashed), and this is were the problem occurs.
When I set a model (at runtime) for nm:PieSeries in a particular chart, then this model is also applied to all other pies that are shown in application. As if there is only one instance of that is used by all pies that applied the style.
Is there some way I can tell it to create a new instance of nm:PieSeries each time a Style is applied to new control?
You might try creating the PieSeries as a separate, non-shared resource:
<nm:PieSeries x:Shared="False" x:Key="NonSharedPieSeries" />
And then use that resource in the style:
Value="{Binding Source={StaticResource NonSharedPieSeries}}"
(...and thanks OP for correcting my error in how to bind it to Value).

Collapse ContentControl if Content is Collapsed

I've got a ContentControl which has a style containing a border and other visual decorations. I want these decorations to disappear when the content is collapsed, so I figured I have to set the visibility of the ContentControl to collapsed in this case. I got this style for my ContentControl decoration:
<Style x:Key="DecoratedItem1" TargetType="{x:Type c:DecoratedItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:DecoratedItem}">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="2">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/file.png"/>
<ContentPresenter Name="wContent"/>
</StackPanel>
</Border>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
<DataTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger.Setters>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The DecoratedItem class is just a subclass of ContentControl with additional DependencyProperties which are not relevant to this issue, I just wanted to note that I already have a subclass to which I could add code, if necessary.
This works when the content of the ContentControl is a UIElement, however if the content is generated by a DataTemplate it complains about not being able to find the Visibility property.
<!-- works -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
<!-- doesn't work -->
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
<c:DecoratedItem.Resources>
<DataTemplate DataType="{x:Type clr:String}">
<TextBlock Text="{Binding}" Visibility="Collapsed"/>
</DataTemplate>
</c:DecoratedItem.Resources>
</c:DecoratedItem>
The error for the second case diplayed in the debug output window is:
System.Windows.Data Error: 40 : BindingExpression path error:
'Visibility' property not found on 'object' ''String' (HashCode=-885832486)'.
BindingExpression:Path=Content.Visibility;
DataItem='ContentPresenter' (Name='wContent');
target element is 'DecoratedItem' (Name='');
target property is 'NoTarget' (type 'Object')
I understand why this happens, but don't know how to fix my style to work as I want it. I don't mind adding helper code to the DecoratedItem subclass if necessary. Any idea how to fix this?
[edit 1]
Some more explanation in regard to the proposed answer:
I can't enforce that the Content is always an UIElement. This is a model-view design after all, and of course I simplified the example a lot. In the real project the content is a model selected from the DataContext, which can be of several different types, and the DataTemplate builds a presentation for that model. Some of the DataTemplates decide (depending on model-state) that there is nothing to present and switch Visibility to Collapsed. I would like to propagate that information to the decorating container. The example above really just presents the problem and not the motivation, sorry.
[edit 2]
Not sure how knowing more about the model would help the problem, but here we go. The data in the Content field doesn't have much in common since it can be a lot of things, this DecoratedItem is supposed to be reusable to give a common visual style to items shown on some forms. Content can be stuff like work items whose DataTemplate collapses them if they are disabled; other kinds of Content can be incomplete and get collapsed. Of course other kinds never may get collapsed.
But note that the data model doesn't really have much to do with the question, which still is how to bind against the Visibility of the expanded content element (after possibly exposing it through the subclass in a bindable way).
There are a couple of ways of describing what's wrong. In the first, working example:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}">
<TextBlock Text="ABC" Visibility="Collapsed"/>
</c:DecoratedItem>
the Content property of the ContentControl is set to be a TextBlock, which is a UIElement with a Visibility property. (This assumes that you have not changed the ContentPropertyAttribute of your derived class DecoratedItem to be something other than Content). Thus, your DataTrigger binding can correctly evaluate:
<DataTrigger Binding="{Binding ElementName=wContent, Path=Content.Visibility}" Value="Collapsed">
Contrast the working case with the failing one:
<c:DecoratedItem Style="{StaticResource DecoratedItem1}" Content="ABC">
in which the Content property is set to an instance of a String, which does not have a Visibility property.
The other way to describe what's wrong is to note that, even though you supply a DataTemplate for the case of Content being a String, Content is still a String and still does not have a Visibility property. In other words, the statement that the Content is generated by the DataTemplate is incorrect -- your DataTemplate just told the control how to display Content of type String.
The general answer to your question is that, if you want the DataTrigger in DecoratedItem1 bound with a Path of Content.Visibility, you need to make sure the content you put in it is always a UIElement. Conversely, if you want to be able to put any sort of Content into the control, you need to trigger off of something else.
The specific answer to your question, strictly, relies on your broader intent (in particular, on how the Visibility of the Content of your control will be set/modified). A couple of possibilities:
if you really want your DataTrigger binding of the form, "Content.Visibility", make sure that the Content is always a UIElement. For instance, use the working form of the style and then bind the Text of TextBlock to something appropriate. However, this doesn't fit so well with the idea of your derived control as a ContentControl, so...
your DataTrigger could probably bind to something else. It seems like, from the way the question is formed, that there is some other property or code-behind that will control whether the various content entities are Visible or not.
finally, you could add an additional DataTrigger to the TextBlock. This DataTrigger would set the visibility of its parent based on its own visibility. Then, bind the DataTrigger in style DecoratedItem1 with Path "Visibility" instead of "Content.Visibility", essentially chaining together Visibilities manually.
Edit
Based on what you've described about how you want to use this, it sounds like you need to consider the visual tree. You might augment your DecoratedItem control to have the following functionality: if all its visual children that are UIElments have a visibility of Collapsed (or if it has no visual children), it is also Collapsed (or, whatever logic makes sense for the desired functionality in terms of the Visibility of its visual children). You'd need to use the VisualTreeHelper class from code -- in particular, the GetChildrenCount and GetChild methods. You'd also, in your DecoratedItem class, override OnVisualChildrenChanged (while still calling the base class method) so that you can get UIElement.IsVisibleChanged events for the visible children.

Apply a style to multiple controls without using a key

In .NET WPF, I have the following XAML code:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="5" />
</Style>
</StackPanel.Resources>
<CheckBox>Check 1</CheckBox>
<TextBox>Some text...</TextBox>
</StackPanel>
The controls do not have any margins applied to them.
Is it possible to apply a style to multiple controls (of different types) without using a key to set the style explicitly on each control?
Styles are not inherited, you can base the subclasses' styles on that one though using BasedOn.
Another method in this case should be using an ItemsControl with an ItemContainerStyle set to this style.
There are examples for both methods in this answer.
Sorry, I misread the question before I wrote this out. My answer is useful if you want to style multiple checkboxes within the StackPanel.
Implicitly style the entire application by placing this into your app.xaml's merged dictionaries.
<Style TargetType="CheckBox" BasedOn="{DynamicResource YourBaseStyle}"/>
This also works on a much smaller scope. Reducing the scope to just that StackPanel simply requires that you add that same line of code to your StackPanel.Resources tag.

how to reference current control in grid?

I want to creat my own control:
public class DataGrid : System.Windows.Controls.DataGrid
In the style definition, I want to add a button above the grid, so I wrote:
<Style TargetType="local:DataGrid">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:DataGrid">
<Grid>
<Button Content="Addnew"></Button>
<?????>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But how can I tell the xaml to put the grid at postion ????? ?
Thank you all!!
Are you sure that you want to use inheritance here? You should consider creating another control that contains a DataGrid rather than inheriting from DataGrid and use the default Template.
If you decide that you do need to customize the Template of the DataGrid you will need to recreate the entire DataGrid template. You can find the original DataGrid template by opening the DataGrid's assembly in .net reflector or a similar application and opening the embedded resource "generic.xaml". This file will contain a ResourceDictionary defining all the default styles for the Controls defined in the assembly. You can copy the default Template from here and modify it as necessary.
Alternatively, if you have Expression Blend you can have it do this automatically by right clicking on the DataGrid control and choosing "edit a copy of this template" (or something like that, I can't remember the exact wording off the top of my head).

Resources