I am new to WPF and some help would be appreciated.
I got a resource Dictionary where I am trying to style a ListViewItem or ItemTemplate ListView's Property, But both ways have failed.
First way:
Dictionary
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Height="45" Width="45" Fill="Gray"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow
<ListView Grid.Row="1" Background="LightSkyBlue">
<ListViewItem>Text cannot be seen</ListViewItem>
</ListView>
"This one doesn't show any text"
Second Way:
Dictionary
<DataTemplate x:Key="LeftMenuButtons">
<StackPanel Orientation="Horizontal">
<Ellipse Height="45" Width="45" Fill="Gray"/>
<ContentPresenter/>
</StackPanel>
</DataTemplate>
MainWindow
<ListView ItemTemplate="{StaticResource LeftMenuButtons}" Grid.Row="1" >
<ListViewItem>No Effect for DataTemplate Only Text Appears</ListViewItem>
</ListView>
This Doesn't apply anything in DataTemplate.
If possible I would like to know what is going wrong in both two ways ... Thanks in advance.
First Way - Control Template
Please specify the TargetType of the control template explicity.
<ControlTemplate TargetType="{x:Type ListViewItem}">
You could also give the list view item style a name, so it is not applied on all items in your scope, but only to the target list view.
<Style x:Key="ListViewItemStyle"
TargetType="ListViewItem">
Make sure, that you assign the style to your target list view.
<ListView Grid.Row="1"
Background="LightSkyBlue"
ItemContainerStyle="{StaticResource ListViewItemStyle}">
Second Way - Data Template
First, data templates only work if your items are assigned via the ItemsSource property. If you directly assign ListViewItems to your list view, they will not be affected. You have to bind the ItemsSource to a collection. Second, you do not use a ContentPresenter in the data template, you bind to properties of the objects in the ItemsSource collection. In this example you would have a collection of strings that are assigned to the Text property of the TextBlock. Maybe this is beyond the scope of your question.
<ListView Grid.Row="1"
ItemsSource="{Binding StringCollection}"
ItemTemplate="{StaticResource LeftMenuButtons}"/>
<DataTemplate x:Key="LeftMenuButtons"
<StackPanel Orientation="Horizontal">
<Ellipse Height="45"
Width="45"
Fill="Gray"/>
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
The code below works. You just forgot to write ControlTemplate TargetType.
<Style x:Key="ListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<StackPanel Orientation="Horizontal">
<Ellipse Height="45" Width="45" Fill="Gray"/>
<ContentPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So, I have a datagrid that has different colour cells depending on the cell's value.
I also have a tooltip that displays some further information. This all works fine.
I, however, would like to alter the tooltip to show further information and also to be the same colour as the cell. So, I thought it would be wise to create a custom style for my tool tips. So, I have the below code.
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"/>
<TextBlock Grid.Row="1"/>
<TextBlock Grid.Row="2"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have an object shown below that is bound to my datagrid. I want to bind the three properties to the three textboxes in my tooltip.
class MyTask
{
public string Name;
public int Code;
public string Description;
}
In my DataGrid I do the following to bind my data to my datagrid
ItemsSource="{Binding TaskList}"
Then in the DataGridTextColumn I bind to a property like below
DataGridTextColumn Header="Code" Binding="{Binding Code}"
This makes sense to me. I am however at a loss to see how I use binding when creating my custom tooltip. I read that I can use templatebinding. I still don't understand how my tooltip will bind to my object of type MyTask in my xaml above?
Update - hopefully make my question clearer
I want to know how to create the bindings in my control template (for the 3 textboxes) and then in the main part of my code how I bind to these text boxes. I then would like to know how to create a binding for the background colour of my control template, I believe this is something to do with relativesource?
When I'm reading other examples (changing the Template Property) I see lines like below. I don't really understand why you have to do it? Is it a case of if you didn't right the line below you wouldn't be able to create a binding on the Padding property?
<Border Padding="{Binding Padding}" ...>
You don't need TemplateBindng, as that is used for setting up the resulting template object to layout based on dynamically using the properties of the implementing control. See this CodePlex article for a good example of when you'd need such functionality.
You simply need to set the bindings of your TextBlock elements within your ToolTip. You don't really need a Template at all in this case, except that since you are using the same ToolTip across all your column cells, it will help you out as you don't need to copy-paste the same code three times. You are after something similar to this article, Tooltip in DataGrid in WPF.
A solution which would work specifically to your case would be like:
<DataGrid Name="TestGrid1" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Code">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Code}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Description">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}">
<TextBlock.ToolTip>
<ToolTip />
</TextBlock.ToolTip>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.Resources>
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Name}"/>
<TextBlock Grid.Row="1" Text="{Binding Code}"/>
<TextBlock Grid.Row="2" Text="{Binding Description}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
You set the ToolTip property within the CellTemplate so that the resulting ToolTip that pops up has the same DataContext as the active row in the DataGrid. This way, you can simply do your property bindings as normal within your ToolTip ContentTemplate, since it has access to all the same properties as does your DataGrid for the row.
To use underlying DataGridCell Background as ToolTip Background, bind your Border Background as Background="{Binding PlacementTarget.Background, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}" .
You are trying to show all the fields in the tooltip of a cell. That doesn't make sense. But still you can do that easily using
PlacementTarget property, which gives you the underlying Visual element. ContextMenu, Popup too expose this property.
PlacementTarget.DataContext will give you the underlying MyTask object.
PlacementTarget.Content will give you the content of the corresponding DataGridCell, in your case it will be TextBlock.
So, if you want to show 3 fields in your cell's tooltip, below code will work for you using point number 2 above.
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Tomato"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<ToolTip.Style>
<Style TargetType="ToolTip">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="{Binding PlacementTarget.Background, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Name, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Code, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
<TextBlock Grid.Row="0" Text="{Binding PlacementTarget.DataContext.Description, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}"
/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToolTip.Style>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
And, if you want to show only corresponding cell's field in your cell's tooltip, then remove the remaining 2 textblocks, and use only one as :
<TextBlock Text="{Binding PlacementTarget.Content.Text, RelativeSource={RelativeSource AncestorType=ToolTip, Mode=FindAncestor}}" />
If you want to show all 3 fields, then apply the ToolTip to DataGridRow using DataGrid.RowStyle. No change in code would be needed.
What about using a color property in MyTask?
class MyTask
{
public string Name { get; set; }
public int Code { get; set; }
public string Description { get; set; }
public SolidColorBrush Color { get; set; }
}
And binding to the color property:
<Style TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border Name="Border" Background="{Binding Color}" BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1" Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Code}" />
<TextBlock Text="{Binding Description}" />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why don't you go for DataTemplate of tool tip like give it's a key and apply it in your cell tool tip style.
<Style TargetType="ToolTip" x:Key="ToolTipStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate TargetType="ToolTip">
<Border CornerRadius="15,15,15,15"
BorderThickness="3,3,3,3"
Background="#AA000000"
BorderBrush="#99FFFFFF"
RenderTransformOrigin="0.5,0.5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"/>
<TextBlock Grid.Row="1"/>
<TextBlock Grid.Row="2"/>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now, you can bind your property to textblock too.
Currently, I have three different types of objects that I draw to the screen (I'm using a ZoomableCanvas, if that makes a difference): beacons (concentric blue circles), tables (black rectangles), and debugRectangles (gold rectangles. The objects are displayed/layered on the Z-axis according to the order in which they're added to the ItemSource, but it's not always possible for me to add shapes in Z-ordering.
This image shows how it looks, depending on the order of objects being added. I'd like for the shapes to respect the Panel.ZIndexes I've set, and in doing so, look like the top image (except with the gold rectangle in the back).
<Style.Triggers>
<!-- Gold rectangles drawn here (color set in code) -->
<DataTrigger Binding="{Binding type}" Value="rectangle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Rectangle Fill="{Binding fill}" Stroke="{Binding border}" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="-1"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- Black rectangles drawn here -->
<DataTrigger Binding="{Binding type}" Value="tableBlock">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Rectangle Fill="{Binding fill}" Stroke="Black" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="50"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- Blue circles drawn here -->
<DataTrigger Binding="{Binding type}" Value="beacon">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Ellipse Fill="DodgerBlue" Width="{Binding outerRadius}" Height="{Binding outerRadius}" Panel.ZIndex="97"/>
<Ellipse Fill="SkyBlue" Width="{Binding innerRadius}" Height="{Binding innerRadius}" Panel.ZIndex="98"/>
<TextBlock Text="{Binding id}" HorizontalAlignment="Center" VerticalAlignment="Center" Panel.ZIndex="99"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
Within a template, they follow the order (I can rearrange the components of a beacon), but relative to each other, no dice. Can anyone identify the issue?
ZoomableCanvas relies upon Panel to render, which means it uses the standard ordering for ZIndex.
<Window x:Class="ZoomableApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ZoomableCanvas>
<Rectangle Fill="Green" Height="250" Width="250" />
<Rectangle Fill="Red" Height="200" Width="400" Panel.ZIndex="2" />
<Rectangle Fill="Blue" Height="400" Width="200" />
</ZoomableCanvas>
</DockPanel>
</Window>
The problem you are having is that your visual tree looks like this:
ZoomableCanvas
Grid
Ellipse with Panel.ZIndex="98"
Since Ellipse is a child of Grid, the ZOrder doesn't affect the ZoomableCanvas, but instead sets the ZIndex of the Ellipse relative to the other children of the grid. In order to change the layering of the ZoomableCanvas, you need to set the ZOrder property on the child of the ZoomableCanvas – the Grid:
ZoomableCanvas
Grid with Panel.ZIndex="98"
Ellipse
Example:
<DataTemplate>
<Grid Panel.ZIndex="100">
<Rectangle Fill="{Binding fill}" Stroke="Black" StrokeThickness="5"
Width="{Binding width}" Height="{Binding height}" Panel.ZIndex="50"/>
<TextBlock Text="{Binding i}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
If you are using a ListBox, you end up with additional levels in your tree:
ZoomableCanvas
ListBoxItem with ItemContainerStyle setting Panel.ZIndex="98"
Grid
Ellipse
Example of use:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Panel.ZIndex" Value="98" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
If you have multiple varying ZIndexes, you could expose a property on the data item and bind to that, or use a ItemContainerStyleSelector to do the logic in code-behind.
You can also use Snoop to look at the created tree to identify these sorts of issues.
Have you tried setting the Panel.ZIndex on the Grids?
I am a XAML newbie and was looking at this blog
http://jeremybytes.blogspot.com/2009/03/wpf-xaml-sample.html
He mentions "Notice that they do not have any content (Button1a, Button1b, etc.). This is because our template does not contain an element for the Content." I am looking to figure out how ensure the content remains even after the new button template is applied. Here is the XAML
<Page.Resources>
<ControlTemplate TargetType="Button" x:Key="targetButton">
<Canvas Width="100" Height="100" Name="mainCanvas">
<Rectangle Height="100" Width="100" Fill="Aqua" Canvas.Top="1"/>
</Canvas>
</ControlTemplate>
<Style TargetType="Button">
<Setter Property="Margin" Value="5, 5, 5, 5"/>
<Setter Property="Template" Value="{StaticResource targetButton}"/>
</Style>
</Page.Resources>
<StackPanel DataContext="{Binding Board}">
<StackPanel Orientation="Horizontal">
<Button Name="testButton1a" Click="testButton1a_Click" Content="testButton1a"/>
<Button Name="testButton1b" Click="testButton1a_Click" Content="testButton1b"/>
<Button Name="testButton1c" Click="testButton1a_Click" Content="testButton1c"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="testButton2a" Click="testButton1a_Click" Content="testButton2a"/>
<Button x:Name="testButton2b" Click="testButton1a_Click" Content="testButton2b"/>
<Button x:Name="testButton2c" Click="testButton1a_Click" Content="testButton2c"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Name="testButton3a" Click="testButton1a_Click" Content="testButton3a"/>
<Button Name="testButton3b" Click="testButton1a_Click" Content="testButton3b"/>
<Button Name="testButton3c" Click="testButton1a_Click" Content="testButton3c"/>
</StackPanel>
</StackPanel>
I have tried to add a TextBlock in the template but I see the same textblock overlayed across all buttons. All I want is to render testButton* content displayed on the rectangle button matrix so I can change the content once clicked.
Thanks, any help will be appreciated
You can use the ContentPresenter to display the content:
<ControlTemplate TargetType="Button" x:Key="targetButton">
<Canvas Width="100" Height="100" Name="mainCanvas">
<Rectangle Height="100" Width="100" Fill="Aqua" Canvas.Top="1"/>
<ContentPresenter/>
</Canvas>
</ControlTemplate>
I want to template a Control and include the Control itself into the template, not only the control's content.
For example, I have a button:
<Grid x:Name="LayoutRoot" Background="White">
<Button Name="hello" Content="123" Width="100" Height="100" >
</Button>
</Grid>
I want to make a broader to surround this button, so it will look like this:
<Grid x:Name="LayoutRoot" Background="White" Width="120" Height="120">
<Rectangle Fill="AliceBlue"/>
<Button Name="hello" Content="123" Width="100" Height="100" >
</Button>
</Grid>
But I want to make this more genric, not only for button, but for some other control.
i.e Image, TreeViewItem etc.
So I created a template:
<UserControl.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Width="120" Height="120">
<Rectangle Fill="AliceBlue"/>
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button Name="hello" Content="123" Width="100" Height="100" >
</Button>
</Grid>
Now the ContentPresenter only display the content of the Button, but not include the button itself, how can I include the templated control itself too?