I've been trying (failing) to create a button template, to be shared between libraries. The template is (currently) basically a bordered circle inside a bordered circle. In order to make the inner border's size smaller than the outer, I use a Converter on the binding. I'd like to pass in a property of the TemplatedParent as a ConverterParameter but it just doesn't work, apparently by design. The bit that doesn't work is because I'm trying to bind a ConveterParameter to a TemplatedParent property.
Here's my Style def (in a ResourceDictionary):
<SolidColorBrush x:Key="MyBorderFillColour">Yellow</SolidColorBrush>
<SolidColorBrush x:Key="MyBorderEdgeColour">#ff652f00</SolidColorBrush>
<SolidColorBrush x:Key="MyGeneralFillColour">#ffffffbd</SolidColorBrush>
<s:Int32 x:Key="MyBorderThickness">10</s:Int32>
<l:RelativeSizeConverter x:Key="RelativeSizeConverter" />
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Ellipse
Fill="{StaticResource MyBorderFillColour}"
StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}" />
<Ellipse StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"
Fill="{StaticResource MyGeneralFillColour}"
Height="{Binding Path=Height,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeSizeConverter},
ConverterParameter={StaticResource MyBorderThickness}}"
Width="{Binding Path=Width,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeSizeConverter},
ConverterParameter={TemplateBinding BorderThickness}}" />
<TextBlock
Text="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black"
FontFamily="Calibri"
FontWeight="Bold"
FontSize="17" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The second Ellipse's Height works using a StaticResource, but Width doesn't work using the TemplateBinding approach. I've also tried
ConverterParameter={Binding Path=BorderThickness,RelativeSource={RelativeSource TemplatedParent}}
Any idea how to achieve my aim?!?
Thanks for any help,
This does not work because in order to provide a binding expression for a value, the value must be a dependency property. ConverterParameter is not a dependency property so you cannot bind its value.
How about using a slightly less generic approach, where you create a few slightly more specific value converters. You can then use a 'dot' path, so that your Button is passed as the value to the converter, you can then access teh width, border width etc... directly.
Width="{Binding Path=.,
RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource RelativeWidthWidthBorderSizeConverter}}"
You don't need to do anything so fancy here; the layout system will handle it for you (using the Margin property and the stretching behavior of the Ellipse in a container):
<Grid Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Ellipse
Fill="{StaticResource MyBorderFillColour}"
StrokeThickness="2"
Stroke="{StaticResource MyBorderEdgeColour}"/>
<Ellipse StrokeThickness="2" Margin="4"
Stroke="{StaticResource MyBorderEdgeColour}"
Fill="{StaticResource MyGeneralFillColour}"/>
<TextBlock
Text="{TemplateBinding Content}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Black"
FontFamily="Calibri"
FontWeight="Bold"
FontSize="17" />
</Grid>
Note also that you should use a ContentPresenter for the Content instead of a TextBlock for more flexibility; this currently fails the Button contract if the user of the templated button supplies more complex Content.
Related
I have a control template like below, and I want to get IsChecked property when user selects a radio button.
But when user select radio button "A" it's IsChecked property still show false. Why?
<ControlTemplate x:Key="RadioBtnTemplate" TargetType="{x:Type RadioButton}">
<Grid>
<StackPanel Margin="5">
<RadioButton Name="tempbtn" IsChecked="{TemplateBinding IsChecked}" FontFamily="Segoe UI" FontSize="18.667" Content="{TemplateBinding Content}" GroupName="{TemplateBinding GroupName}"/>
</StackPanel>
</Grid>
</ControlTemplate>
and I use this template:
<RadioButton GroupName="CG" x:Name="_rdoBtnA" Content="A" Template="{DynamicResource RadioBtnTemplate}" IsChecked="True"/>
<RadioButton GroupName="CG" x:Name="_rdoBtnB" Content="B" Template="{DynamicResource RadioBtnTemplate}" />
<RadioButton GroupName="CG" x:Name="_rdoBtnC" Content="C" Template="{DynamicResource RadioBtnTemplate}" />
If we take your example as is then you have two problems which cause the problems you are seeing.
Issue 1:
Firstly your design has created six not three <RadioButton> controls. The three in the <StackPanel> and then three that are created as part of the control template. All six radio buttons are now linked as part of the GroupName="CG" group.
As you know because they all belong to the same CG group only one of the six radio buttons can have the IsChecked property set to True. The three named controls _rdoBtnA, _rdoBtnB and _rdoBtnC are not even visible on the screen so they can never be set to True (and in the case of _rdoBtnA is promptly set to False from the XAML declared True the moment the template control is bound).
To resolve this situation, remove the GroupName="{TemplateBinding GroupName}" from the control template definition leaving only the three top level radio buttons in the group.
Issue 2: This is the issue I thought was the root of your problem to begin with. IsChecked={TemplateBinding IsChecked} is only OneWay binding and will not update the other way. To make the binding TwoWay you need to use the long-hand version of the binding definition, IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
The control template now becomes this by making those two changes.
<ControlTemplate x:Key="RadioBtnTemplate" TargetType="{x:Type RadioButton}">
<Grid>
<StackPanel Margin="5">
<RadioButton Name="tempbtn" IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" FontFamily="Segoe UI" FontSize="18.667" Content="{TemplateBinding Content}" />
</StackPanel>
</Grid>
</ControlTemplate>
you can use it this way:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<ControlTemplate x:Key="ButtonAsSwatchTemplate">
<Border x:Name="SwatchBorder" BorderThickness="1">
<Rectangle Fill="{TemplateBinding Property=Background}" Width="15" Height="15" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="SwatchBorder" Property="BorderBrush" Value="Yellow" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</StackPanel.Resources>
<RadioButton Template="{StaticResource ButtonAsSwatchTemplate}"
GroupName="CropGuidesColourRadioButtonGroup"
IsChecked="{Binding Checked}" Margin="2" Background="Red" />
<RadioButton Template="{StaticResource ButtonAsSwatchTemplate}"
GroupName="CropGuidesColourRadioButtonGroup"
IsChecked="{Binding Checked}" Margin="2" Background="Black" />
</StackPanel>
taken from StackOverFlow
I have a button using the ControlTemplate below.
<ControlTemplate x:Key="ControlTemplate" TargetType="{x:Type Button}">
<Grid>
<Path x:Name="ButtonPath" Fill="{Binding IsSelected, Converter={StaticResource ArrowBackgroundColorConverter}, UpdateSourceTrigger=PropertyChanged}"
Data="{Binding Converter={StaticResource ArrowPathSelector}}" Stretch="UniformToFill" Margin="0 0 -35 0"></Path>
<TextBlock Grid.Column="0" Grid.Row="0" FontFamily="{StaticResource ApplicationFont}" FontSize="{StaticResource Heading3}" HorizontalAlignment="Center" Margin="35 0 0 0" VerticalAlignment="Center" Text="{Binding Title}" Foreground="White"/>
</Grid>
</ControlTemplate>
but when I click the Button in my app it is not outlining the Path but instead the original Button.
I cant quite figure out how to get the button to reflect the Path itself. Any help is greatly appreciated, Thanks!
If you want to change the shape of the focus rectangle (the dotted border indicating keyboard focus), you need to create a custom FocusVisualStyle that draws a dotted path in the same shape as your button content:
<Style x:Key="ButtonFocusRectangle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Path Stroke="Black"
StrokeDashArray="2 2"
Data="M 0,0 50,50 100,0 Z"
Stretch="UniformToFill"
Margin="0 0 -35 0" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then, on the button, set FocusVisualStyle="{StaticResource ButtonFocusRectangle}". Note that it would probably be cleaner to define a custom Style for the button, and have it apply both the Template and FocusVisualStyle for you.
Alternatively, you can just get rid of the focus visual entirely by setting FocusVisualStyle="{x:Null}". You might opt to do this, and simply draw a focus hint in your regular Template by, for example, changing the path's stroke with an IsKeyboardFocused trigger.
As an aside, you probably want to set Stretch to Uniform on the paths in both templates. UniformToFill can cause clipping.
Have you tried putting your Path and your textblock the other way around:
<ControlTemplate x:Key="ControlTemplate" TargetType="{x:Type Button}">
<Grid>
<TextBlock Grid.Column="0" Grid.Row="0" FontFamily="{StaticResource ApplicationFont}" FontSize="{StaticResource Heading3}" HorizontalAlignment="Center" Margin="35 0 0 0" VerticalAlignment="Center" Text="{Binding Title}" Foreground="White"/>
<Path x:Name="ButtonPath" Fill="{Binding IsSelected, Converter={StaticResource ArrowBackgroundColorConverter}, UpdateSourceTrigger=PropertyChanged}"
Data="{Binding Converter={StaticResource ArrowPathSelector}}" Stretch="UniformToFill" Margin="0 0 -35 0"></Path>
</Grid>
</ControlTemplate>
I need to "decorate" a textBlock with a custom background (say, when IsSelected)
Actually I have two separate elements textBlock and its background rectangle:
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
LayoutTransform="{Binding ElementName=StopText, Path=LayoutTransform}"/>
Now, when I want to Hide the textBlock I need to hide the rectangle too, etc. I think there is should be a more elegant way to do it.
Any remarks appreciated.
EDIT
Border approach
<Border x:Name="ShadowRectangle"
Background="Transparent"
Canvas.Left="18"
Canvas.Top="-17"
CornerRadius="5"
Opacity="0.2"
LayoutTransform="{Binding ElementName=StopText,
Path=LayoutTransform}">
<TextBlock x:Name="StopText" Text="Eiffel Tower"
FontSize="14" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" FontFamily="Giddyup Std"
Canvas.Top="-16" Padding="5">
</TextBlock>
</Border>
In a solution similar to your border approach, if you want no dependencies between the opacity of the border and the textblock, you could go with something like this:
<grid>
<rectangle />
<textblock />
<grid>
this should put the textblock over the rectangle since they are on the same grid cell.
They you only have to change the Rectangle.Fill when it's selected. You could do that using a DataTrigger on the Rectangle style.
You can make a Custom Control instead.
Here is a link for some ideers:
http://www.codeguru.com/cpp/i-n/internet/xml/article.php/c12521
Bind the visibility property for both the textblock and Rectangle to a bool in your viewmodel and use the BoolToVisibilityConverter to convert the bool value to a visibility value. Your XAML will look something like this:
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5"
Visibility="{Binding IsVis, Converter={StaticResource BooleanToVisibilityConverter}}">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
Visibility="{Binding IsVis, Converter={StaticResource BooleanToVisibilityConverter}}"/>
Using either the Border or Rectangle approach, you should be able to bind the Visibility of the decorator (Border or Rectangle) to the Visibility of the TextBlock.
<TextBlock x:Name="StopText" Text="Eiffel Tower"
Canvas.Left="17"
FontSize="14"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
Canvas.Top="-16"
Padding="5">
</TextBlock>
<Rectangle x:Name="ShadowRectangle"
Fill="Red"
Canvas.Left="18"
Width="{Binding ElementName=StopText, Path=ActualWidth}"
Height="{Binding ElementName=StopText, Path=ActualHeight}"
RadiusX="5" RadiusY="5" Opacity="0.2" Canvas.Top="-17"
LayoutTransform="{Binding ElementName=StopText, Path=LayoutTransform}"
Visibility="{Binding ElementName=StopText}"/>
Let me suggest to use templated label (textblock wont accept templates and took more characters to type -). For your convenience, I wrapped it in a style:
<Window x:Class="_4203457.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">
<Window.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontFamily" Value="Giddyup Std"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<TextBlock Text="{TemplateBinding Content}"
FontSize="14"
Padding="5"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
FontFamily="Giddyup Std"
/>
<Rectangle x:Name="ShadowRectangle"
Fill="SkyBlue"
RadiusX="5" RadiusY="5" Opacity="0.5"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Height="27" Width="454">
<Label Content="Eiffel Tower"/>
</Grid>
I want to remove the border of image which is displayed when i click on it .
**<Image x:Name="ImagePresenter" Source="{StaticResource IconDocument}" />**
<Rectangle x:Name="focusElement" Opacity="0" Stroke="{StaticResource color_506_brush}" StrokeThickness="1" IsHitTestVisible="False" RadiusX="4" RadiusY="4"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Adding an image to a Silverlight UserControl results in just the image with no border displaying for me.
Xaml:
<Image x:Name="ImageText" Source="TestImage.png"/>
I assume that isn't going to help you. Could you give a bit more detail in your question and see if I can find a better answer?
Your question is not quite clear to me as silverlight doesnt add any border to image unless something in your code tells it to.
Try setting StrokeThickness to zero. (If required modify the template to set the strokethickness to zero).
Hope this helps
Yes you all were right by default the image doesn't have border. As i had two columns in my defined style it was creating two rectangles for each(one for my textblock another for image).To get rid of this I used one column defination and it worked. :)
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="ImagePresenter" Grid.Column="0" Source="{StaticResource IconDocument}" Width="15" HorizontalAlignment="Left"/>
<TextBlock x:Name="TextPresenter" Grid.Column="0" Text="{Binding DocumentName}" Style="{StaticResource DocumentViewerOutputTextItem}" Margin="15 4 4 0"
Visibility="{Binding IsSelected, Converter={StaticResource InvertVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Grid.Column="0" Text="{Binding DocumentName}" Style="{StaticResource DocumentViewerOutputTextItem_MouseOver}" Margin="15 4 4 0"
Visibility="{Binding IsSelected, Converter={StaticResource VisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}"/>
<Rectangle x:Name="focusElement" Opacity="0" Stroke="{StaticResource color_506_brush}" StrokeThickness="1" IsHitTestVisible="False" RadiusX="4" RadiusY="4"/>
</Grid>
</Grid>
</ControlTemplate>
I have a ListBox with it's ItemTemplate bound to a DataTemplate. My problem is I cannot get the elements in the template to stretch to the full width of the ListBox.
<ListBox x:Name="listPeople" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,0,0,0" Background="{x:Null}" SelectionMode="Extended" Grid.Row="1"
ItemTemplate="{StaticResource PersonViewModel.BrowserDataTemplate}"
ItemsSource="{Binding Mode=OneWay, Path=SearchResults}" >
</ListBox>
<DataTemplate x:Key="PersonViewModel.BrowserDataTemplate">
<ListBoxItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5">
<Border Opacity=".1" x:Name="itemBorder" Background="#FF000000"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
CornerRadius="5,5,5,5" MinWidth="100" Height="50"/>
</Grid>
</ListBoxItem>
</DataTemplate>
As you can see, I have added a border within the grid to indicate the width of the template. My goal is to see this border expand to the full width of the listbox. Currently its width is determined by its contents or MinWidth, which is the only thing at the moment keeping it visible at all.
I spent an hour trying to resolve this one. Very very frustrasting. You don't have to override the entire default style for the ListBoxItem. I couldn't get this to work. In the end I resolved the issue by simply overriding just the HorizontalContentAlignment property in my ListBox.ItemContainerStyle section e.g:
<ListBox x:Name="ClassList" ItemsSource="{Binding LineClasses}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch" Loaded="ClassList_Loaded"
VerticalAlignment="Stretch" Grid.Row="0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" CornerRadius="3" Background="#FFE88D34"
BorderThickness="1" HorizontalAlignment="Stretch" >
<Grid Background="Transparent" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0" HorizontalAlignment="Stretch"
Margin="2"
FontSize="10"
Text="{Binding DisplayClassNm}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
This worked a treat for me.
Myles
When creating Data Templates for ListBox, you should not incldue <ListBoxItem>. The contents of the DataTemplate will be placed inside of a generated container. You can control how that container is constructed using ItemContainerStyle.
The default control style for ListBoxItem is used to define the ItemContainerStyle by default. This style sets the ListBoxItem.HorizontalContentAlignment property to 'Left'. Notice how the ContentPresenter binds its HorizontalAlignment to this property.
You need to override the style of the ListBoxItem container that is being generated when you bind to your ListBox. This can be done by setting the ItemContainerStyle. Set the HorizontalContentAlignment property to be "Stretch".
Below is the default ListBoxItem Style. Included for reference.
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<!-- VSM excluded for readability -->
<Rectangle x:Name="fillColor" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<Rectangle x:Name="fillColor2" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Rectangle x:Name="FocusVisualElement" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="1" RadiusY="1" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is an example of using the control templates and data templates for the listbox control. Refer to the XAML markup which streaches the border for the listbox items.
My ListBoxItem contained a CheckBox and the above solutions did not work for me (most likely due to the nature of a CheckBox, not those solutions) I was able to coerce this functionality by not binding to the "Content" property of the checkbox, but explicitely defining the XAML inline:
<CheckBox HorizontalAlignment="Stretch" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<TextBlock
MinWidth="130"
Margin="0,-2,0,0"
HorizontalAlignment="Stretch"
Text="{Binding Path=DisplayText}" />
</CheckBox>
The margin is needed because the TextBox text did not align with the CheckBox's checkmark. The MinWidth was also necessary.
I am working with Silverlight 5 with VS 2012. I have the same issue. I have horizontal listbox with my own custom objects as ItemsSource. I want listbox items to expand. But they are not expanding. I donot want to give any hard coded width for every listbox item. I tried all answers here but nothing works. I just gave ItemsSource ={Binding Persons} DisplayMemberPath="First Name". Thats all
Two things I noticed here, because I had the same issue and wasn't able to solve it the way you're trying.
First, you don't need to explicitly put a ListBoxItem in your DataTemplate. This is created for you automatically, so you actually have your ListBoxItem inside of the one that was created for you. I checked this out in Snoop to confirm.
Second, and I don't know exactly why, but I wasn't able to get the stretching behavior out of the alignment attributes either. I changed it to use RelativeSource binding on the Width attribute to the ActualWidth property of the containing ListBoxItem. This worked for me.
Width="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListBoxItem}}, Path=ActualWidth}"
If you need to set style properties on the ListBoxItem that is implicitly created for you, use a Style element inside of the ListBox.ItemContainerStyle element.
Hope this helps...