Binding with ElementName in Style setter - wpf

I have a base style for Button which contains the ControlTemplate, then I want to create a few styles based on that which just set the Content for commonly used things like "OK", "Cancel", etc.
In those styles I have an image followed by some text, and I want to bind the height of the image to the height of the text, so I've got the following:
<Style x:Key="OkayButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource RoundedButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Grid HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{DynamicResource OkayIcon}" Height="{Binding ActualHeight, ElementName=ButtonText}"/>
<TextBlock x:Name="ButtonText" Grid.Column="1" Margin="5,0,0,0" Text="Okay"/>
</Grid>
</Setter.Value>
</Setter>
</Style>
Doing that gives me the error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=ButtonText'. BindingExpression:Path=ActualHeight; DataItem=null; target element is 'Image' (Name=''); target property is 'Height' (type 'Double')
I'm guessing it's trying to find ButtonText in the ControlTemplate which it obviously can't. Is there a way to accomplish what I'm trying to do here and bind to another element's property from within this style setter?

I don't know why that binding is not working for you; perhaps because name resolution visual tree something or other.
But this works for me, and also addresses the shared-resource issue I mentioned in comments.
<DataTemplate x:Key="ButtonContent" >
<Grid HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="{DynamicResource OkayIcon}"
Height="{Binding ActualHeight, ElementName=ButtonText}"
/>
<TextBlock
x:Name="ButtonText"
Grid.Column="1"
Margin="5,0,0,0"
Text="Okay"
FontSize="32"
/>
</Grid>
</DataTemplate>
<Style
x:Key="OkayButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource RoundedButtonStyle}"
>
<Setter Property="ContentTemplate" Value="{StaticResource ButtonContent}" />
</Style>

Related

How to keep a button to the right of a datagrid header

I want to add a button to the datagrid header column and keep it at the right-most edge of the header column... no matter how wide it is. In my Xaml code I am using a DataTemplate where I attach the header column title and add a toggle button with a popup window.
<DataGrid>
<DataGrid.Resources>
<DataTemplate x:Key="HeaderTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<DataGrid.Resources>
<ContentControl Content="{Binding}" VerticalAlignment="Center"/>
<!--TODO: THIS IS THE BUTTON I WANT TO ANCHOR TO THE RIGHT-->
<ToggleButton Name="FilterButton" Grid.Column="1" Content="▼" Margin="2, 1, 1, 1" Padding="1, 0"/>
<Popup IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}" PlacementTarget="{Binding ElementName=FilterButton}" StaysOpen="False">
<Border Background="White" Padding="3">
<TextBox Width="300"/>
</Border>
</Popup>
</Grid>
</DataTemplate>
</DataGrid.Resources>
When I launch my application I do see the header column with the toggle button right next to it. But I need it to be in the rightmost of any column header. Like this:
Can someone help me understand how to go about doing this with the code I currently have?
After a bit of fiddling, here's what I did to get it to work:
<DataGrid>
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
<DataTemplate x:Key="HeaderTemplate">
<Grid Margin="0, 0, -5, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding}" VerticalAlignment="Center"/>
<!--TODO: THIS IS THE BUTTON I WANT TO ANCHOR TO THE RIGHT-->
<ToggleButton Name="FilterButton" Grid.Column="1" Content="▼" Margin="2, 1, 1, 1" Padding="1, 0"/>
<Popup IsOpen="{Binding ElementName=FilterButton, Path=IsChecked}" PlacementTarget="{Binding ElementName=FilterButton}" StaysOpen="False">
<Border Background="White" Padding="3">
<TextBox Width="300"/>
</Border>
</Popup>
</Grid>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn HeaderTemplate="{StaticResource HeaderTemplate}" Header="Test"/>
</DataGrid.Columns>
</DataGrid>
There are two key changes.
First, I added this to DataGrid.Resources:
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
Which lets the HeaderTemplate fill the the entire header.
Second, I added a negative right margin to the Grid:
<Grid Margin="0, 0, -5, 0">
This compensates for the internal padding of the header's control template, moving the button fully to the right.
The problem here is that the content inside the header is Autosized. What you need to do is to change the Horizontal Alignment of the content. You can define a style for Column header and that will resolve the issue.
<DataGrid.Resources>
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</DataGrid.Resources>

Silverlight Style : custom style based on custom style

Someone has done a custom style for the buttons of the application. Now I want to create a custom style based on this custom style. For example I want to make a "close button" that I want to reuse all other the application. I tried this :
<Style x:Key="GlassButtonClose" TargetType="Button" BasedOn="{StaticResource GlassButton}">
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.Row="0" Source="/Balisage;component/Images/Close.png" Width="24" Height="24" />
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding Source={StaticResource LocCommonStrings}, Path=ButtonLabelClose}" Margin="0" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But this doesn't keep the GlassButton settings. How can I just extend the settings, keeping the existing ones ?
Thanks for your help
Based on the shown template and your answer in the comments it looks like you only need to have a spacific GlassButton with a fixed icon and text. And you want to use this button without the need to specify its content again and again.
Solution:
Prepare your own type GlassStyleCloseButton.
public class GlassStyleCloseButton : Button
{
public GlassStyleCloseButton() {
DefaultStyleKey = typeof(GlassStyleCloseButton); }
}
and a style for the new type (can be placed in app.xaml or generic.xaml):
<Style TargetType="GlassStyleCloseButton" BasedOn="{StaticResource GlassButton}">
<Setter Property="Content" Value="{Binding Path=ButtonLabelClose,
Source={StaticResource LocCommonStrings}}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image
Source="/Balisage;component/Images/Close.png"
Width="24"
Height="24"/>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Text="{TemplateBinding Content}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
and you can use it like this:
<SomewhereInMyApp>
...
<GlassStyleCloseButton/>
</SomewhereInMyApp>

Wpf datagrid column header as Grid

I have DataGrid with customized column like that
<DataGridTemplateColumn Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding SomeProperty}"/>
<TextBlock Grid.Column="0" Text="{Binding OtherProperty}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="*" Header="Const" Binding="{Binding ConstantValue}" />
As you can see, first column has double width of second column and contains two texblocks. So I need make header consists from two words. Every word above corresponding TexBlock. And I try to use Grid with two columns (because I have textblocks in two columns) as HeaderTemplate. I try to set HeaderStyle like that
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Setters>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
and I try to use HeaderTemplate like that
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
But in both cases I have the same wrong result. Namely. Header1 and Header2 placed togeter without any spaces like Header1Header2. And looks like Header1Header2 has HorizontalAligment="Left" relatively whole header.
I.e.
column border-->|Header1Header2___________|<--column border
So Header1 and Header2 do not placed above corresponding TextBlocks in DataGrid.
How can I solve my problem? How should I set HeaderTemplate to take what I want?
Would customizing the ControlTemplate work for you?
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center">Header1</TextBlock>
<TextBlock Grid.Column="1" HorizontalAlignment="Center">Header2</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</DataGridTemplateColumn.HeaderStyle>
Your other option is to create your own DataGridColumnHeadersPresenter which will hint the available size to the headers. By default, headers' ContentPresenters won't be told the available size and will calculate the width from their content (DataTemplate).
In your case, the width is not set explicitly (e.g. <Grid Width="200">) so it'll simply be the combined size of the TextBoxes.

Grid doesn't stretch inside ListPicker item

I have Grid inside ListPicker full mode item. Grid has two columns. First column should be left-aligned and second right-aligned.
Unfortunately this template doesn't work as expected:
<DataTemplate x:Name="ListFullModeItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<TextBlock Grid.Column="1" Text="{Binding Description}" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
Second TextBlock doesn't align to right.
When I set Grid Width property to specific value i.e.
<Grid Width="700">
...
</Grid>
then it works, but I can't do it because user can rotate phone to Portrait/Landscape.
Any ideas?
EDIT:
I also had the same problem in ListBox.
I fixed it by adding:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
I can't use this in ListPicker because it doesn't have ItemContainerStyle element.
set your grid 's width.
and change it by +=:
OrientationChanged += new EventHandler(SecondPage_OrientationChanged);
try adding TextAlignment="Right"

Show "pop up window" when is mouser over listBox item

I bind observable collection on listBox. I have data tempate on listbox item. It consit one image control and som textBlock.
If is mouse over on some listBox item I would like achieve this behavior:
Show PopUp/ToolTip (some "rectangle" with controls) and bind values from listBox current item.
And on textBox in item data template I have style, I would like change color of text in textBlock, for example from black to green.
Style is here:
<Style x:Key="FriedNickStyle" TargetType="TextBlock">
<Setter Property="Margin" Value="2,2,2,2"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="Black"/>
</Style>
Sory for my english, I have problem how describe this behavior correct. I try many thing but any of them doesn’t work good.
Here is it my style:
<DataTemplate x:Key="FriendListBoxItemTemplate">
<Grid Name="RootLayout">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="60"></RowDefinition>
</Grid.RowDefinitions>
<Image Margin="4,4,4,2" Grid.Column="0">
<Image.Source >
<MultiBinding Converter="{StaticResource avatarConverter}">
<Binding Path="ProfilePhoto"></Binding>
<Binding Path="StatusInfo.IsLogged"></Binding>
</MultiBinding>
</Image.Source>
</Image>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock
Text="{Binding Path=Nick}"
Style="{StaticResource FriedNickStyle}"
Grid.Column="0" Grid.Row="0">
</TextBlock>
</Grid>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<!--SHOW SOME POP UP WINDOW and bind properties from ITEM (VALUE)-->
<!--Change color of textBlock-->
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
Thank everybody who help me.
Well, I found this turorial, this article, by the MSDN and another stack overflow's question.
Basically, here's how:
<Popup Margin="10,10,0,13"
Name="Popup1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="194"
Height="200"
IsOpen="True"> // change this to open it
<TextBlock Name="McTextBlock" Background="LightBlue" >
This is popup text
</TextBlock>

Resources