How do I apply a DataTemplate in a Dynamic Grid? - wpf

I have a Grid. The grid's columns are auto-generated at run-time based on the user's selection.
I need the cells in the grid to be red if the content is a negative number.
I have created a DataTemplateSelector. The DataTemplateSelector get's correctly called and returns my template if the cell is negative.
Since my columns are auto-generated, I cannot simply put the correct field in the binding in my template.
<DataTemplate x:Key="MontantNegatifTemplate">
<TextBlock Foreground="Red" Text="{Binding}" />
</DataTemplate>
If I do a Template like this the text is the name of the object the grid is bound on.
If I do something like:
<DataTemplate x:Key="MontantNegatifTemplate">
<TextBlock Foreground="Red" />
</DataTemplate>
The cell is empty since the Textblock seems to overwrite the standard auto-generated cell.
Is there a way to make this work? Should I use another approach?

I finally found the awnser to my question.
I needed to use a StyleSelector rather than a DataTemplateSelector.
In the same way I needed to define a Style instead of a DataTemplate in my Grid resources.
<style:NegativeStyleSelector x:Key="NegativeStyleSelector">
<style:NegativeStyleSelector.NegativeStyle>
<Style TargetType="GridViewCell">
<Setter Property="Foreground" Value="Red"/>
</Style>
</style:NegativeStyleSelector.NegativeStyle>
</style:NegativeStyleSelector>

Related

Binding to Template's parent's parent's child's property

I have an Expander style which applied template on both Header and Content
I wish to have one of the TextBlock inside content's template to match the Header's TextBlock's Foreground color
<Style TargetType="Expander">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Foreground="Blue"/> <!--Header TextBlock-->
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock/> <!--Match Header TextBlock's Foreground-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I have tried ElemenName binding but it seems like the name scope is different since I am 2 template level deep.
I thought about TemplateBinding but I only want one of the column in the content to match the color of header instead of the whole expander.
I could apply the same trigger for the Header TextBlock on the Content TextBlock too but I am trying to see if there is a way to avoid duplicating the code.
ElementName can't work across templates; with a template, you could have multiple elements with the same name.
Anything with different template instances reaching out to grope each other via the visual tree is going to be fraught with nameless horrors, whatever you do.
Instead, I would suggest that they both get their brushes from the same source. This is much more in line with how WPF is happy doing things.
If the color won't change, use an appropriately-named Brush resource for both.
If it will change, bind both to a viewmodel Brush property (kinda squicky, but not the end of the world), or use triggers driven by some other viewmodel property which represents the state being indicated by the color. The triggers would reference any number of appropriately-named Brush resources: ErrorBrush, HappyBrush, SadBrush, etc. By "name" I mean x:Key of course:
<SolidColorBrush x:Key="HappyBrush">GreenYellow</SolidColorBrush>
...etc.

WPF ComboBox: How to you utilise a generic ItemContainerStyle with binding

I want to utilise a generic style for my ComboBoxItem content and have the text content bound to different properties on my underlying class. So this is the best I can come up with but the bindings are hard coded. So for every class bound to a combobox using this ItemContainerStyle I'd have to implement a "MainText" and "SubText" property.
Question is, is there a way to have the binding soft coded so where the style referenced from a combobox I can specify which string properties of the underlying class are used.
<Style TargetType="{x:Type ComboBoxItem}" x:Key="ComboBoxItemStyleA1">
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="BB" Padding="8,3,8,3" Background="DarkGreen">
<StackPanel Margin="0">
<TextBlock Foreground="White" FontSize="16" Text="{Binding MainText}"/>
<TextBlock Foreground="White" FontSize="8" Text="{Binding SubText}"/>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="BB" Value="#FF256294"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And to use the style...
<ComboBox ItemsSource="{Binding Items}"
ItemContainerStyle="{StaticResource ComboBoxItemStyleA1}" />
Further to dowhilefor's answer (many many thanks - WPF is great but sure is a voyage of discovery)
I used a data template to define the cell look originally - and then wanted to use a comboboxitem based style with a control template defined where I could specify the onmouseover triggers. i.e. these were to change the background color etc.
Butj
a) I couldn't remove the Border section of the template above - the triggers are tied to it by targettype="BB". so I kind of wanted to get the trigger bound to the container such that the datatemplate would pick up the background from the template binding but not sure how to get this plumbed in.
b) I realised that even if I comment out the BB specific bindings on the triggers just to get it to run - the combobox doesn't find and use the DataTemplate I defined. Seems that defining the controltemplate in my comboboxitemstyle stops it picking up the datatemplate.
I hope I make sense here - bottom line is I just want a style that I can apply with triggers in that set the background color of my cobobox item. It should not know what the data is - i.e. be able to plug in a datatemplate that will (template ?) bind to this background color.
Many thanks for the very fast response.
btw I'm using ItemContainerStyle in conjuction with ItemTemplate so I can have a different representation in the dropdown to what appears in the combobox list
First of all don't use the ItemContainerStyle for that. To be more precise never have any Bindings to the datacontext inside an ItemContainerStyle, at least try not. Why? The Style is used for defining the appearance of a combobox item disregarding the content. If you want to define how the content should look like, you use a DataTemplate for that. There are multiple ways to tell the combobox where he can find a proper DataTemplate for the Data you supply. Checkout the property ItemTemplate, ItemTemplateSelector and search for implicit styles, to find out more about them.
So to your problem, create one ItemContainerStyle for you combobox (if you really have to anymore) which doesn't care about the object that will be put into. Now you still need to provide multiple DataTemplates each and everyone with the knowledge of the data object that you want to be templated. There is no way around it, there is no soft databinding. Just try to keep your templates small and simple. If for some reason you need the exact same template, but your properties are just named differently, why not use a wrapper item for the DataContext with the properties Caption, Description and you can decide in code how these properties are filled with your real data wrapped into this object.

Binding to DataRowView with a DataTrigger inside of a DataGrid

I am trying to implement a cell template for a datagrid. The problem is, when I try to bind to the Object within the datagridview, I am not getting the values that I am expecting.
This is what the datagrid looks like with no styling:
datagrid plain
Pretty simple, but when I try to style it using a cell template with triggers, this is what I am getting (Each cell that has DAL.Task above should contain an orange circle):
datagrid styled
here is the xaml that I am using to style the grid:
<DataTemplate x:Key="MiddleDataGridCellTemplate">
<Grid>
<Image Name="CenterImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Row.ItemArray/TaskStatusName}"
Value="In Progress">
<Setter TargetName="CenterImage"
Property="Source"
Value="/besoControlLibrary;component/Resources/Spreadsheet_CheckedOut.png" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I am guessing that "{Binding Row.ItemArray/TaskStatusName}" is causing the problem, what is the correct way to pull out the TaskStatusName property of the DAL.Task within the datagridview?
I found out that the values of the blank cells are DBNull if that helps at all.
Also, these values are coming from a DataTable that is the context of the the DataGrid.
The DataContext should already be your object you are showing so you should be able to bind right to whatever property you want:
{Binding TaskStatusName}
You can get more information from looking at your output window when running your app. Also here is an example of a DataGridTemplateColumn which is probably what you want to mimic:
http://msdn.microsoft.com/en-us/library/system.windows.controls.datagridtemplatecolumn%28v=vs.95%29.aspx
I solved the problem, the solution is here:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7fe562f4-739c-45df-bea3-557abd80c63d

WPF how to make a listbox/listview unfocusable

I've been trying for a while to display some data in a listbox/listview that would be unfocusable (I mean not only the list, but also the items in it).
I tried with both types of list (listbox and listview), and I used their ItemTemplate and ItemContainerStyle. Everywhere I could, I set the Focusable property to false.
I don't see any other way than disabling the list, but then I have to change all its style, to make it appear not disabled.
Have I missed something? Is there a read-only type of list that I don't know about?
Thank you for your ideas :)
The problem you're probably seeing is that each individual item in the list is focusable. However, you can override that... Try adding this to your listbox:
<ListBox.ItemContainerStyle>
<Style TargetType="Control">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.ItemContainerStyle>
Note however that this makes the items unselectable (by keyboard or by mouse). You can set the selected item programmatically, but it doesn't appear to be highlighted automatically any more - so really, this behaves almost the same as an ItemsControl.
Use an ItemsControl with TextBlocks instead of a ListBox
<ItemsControl ItemsSource="{Binding MyListBoxItemsSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyDisplayName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Applying TextDecoration to an WPF ListViewItem

I can see how to apply a text decoration to a GridViewColumn.
<GridViewColumn Header="Tool" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Entity.ToolId}" TextDecorations="{Binding Path=TextDecoration}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
But if I want the TextDecoration to apply to the entire row (in my case a strikethrough), then I have to duplicate the above code to every GridViewColumn.
What I can not figure out is how to apply the TextDecoration to the entire listview item possibly via the ItemContainerStyle.
Can anyone give me a head up on how this could be done?
You should not define the binding per column, if the cells of each column support TextDecoration, because then you need to repeat it for every column. Better apply it to DataGrid.CellStyle property:
<DataGrid.CellStyle>
<Style TargetType="TextBlock">
<Setter Property="TextDecorations"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}},
Path =Item.TextDecoration}" />
</Style>
</DataGrid.CellStyle>
The challenge here is to access the property TextDecoration. Use relative source binding to find the parent DataGridRow which has an Item property which contains the data for that row.
Formatting WPF DataGrid using binding can be rather perplexing. See my article on CodeProject where I cover the most important cases, including applying TextDecoration on single cells (or all cells in your case): Guide to WPF DataGrid Formatting Using Bindings
use GridViewRow.CellTemplate

Resources