I'm attempting to create a control template for the Silverlight RadioButton, using a ToggleButton for each item. The problem I'm running into is that the selection mechanism appears to be broken. Here's the (simplified) style I'm using:
<Style TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid>
<ToggleButton x:Name="toggle"
IsChecked="{TemplateBinding IsChecked}">
<ContentPresenter x:Name="contentPresenter" />
</ToggleButton>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The test is simply whether the radio selection actually works -- so for example, the text blocks below don't show the correct value when you click on the radio buttons:
<StackPanel>
<RadioButton x:Name="radio1" GroupName="Test" Content="1" />
<RadioButton x:Name="radio2" GroupName="Test" Content="2" />
<TextBlock Text="{Binding ElementName=radio1,Path=IsChecked,StringFormat='Radio 1 checked: {0}'}" />
<TextBlock Text="{Binding ElementName=radio2,Path=IsChecked,StringFormat='Radio 2 checked: {0}'}" />
</StackPanel>
You would think that maybe there is a named part in the control template, which the control uses to update the selection -- however, the docs indicate no named parts. So what is going on here, and how can I get my example working?
You would think that maybe there is a named part in the control template
Yes ... it turns out there is an "undocumented feature". Trial and error shows that the "BoxMiddleLine" is the named part that controls the selection. So adding to the control template a clickable element with that name, fixes the issue:
<Grid>
<Border x:Name="BoxMiddleLine" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#0000" />
<ToggleButton x:Name="toggle"
IsChecked="{TemplateBinding IsChecked}"
IsHitTestVisible="False">
<ContentPresenter x:Name="contentPresenter" />
</ToggleButton>
</Grid>
Related
Trying to stay on the MVVM road, I keep struggling with the following task for hours:
I want to show the String-value of a specific Item (in a TextBlock), which is part of the UserCollection (ObservableCollection<Tuple<int, string>>). The selection should take place via the Int-property of the item in the Collection, matching the bound IdCreatedByUser-Property in MyOrder.
To make things more clear:
An UserCollection that holds an ID (int) and NAME (string):
public ObservableCollection<Tuple<int, string>> UserCollection;
A MyOrder-Property holding an Instance of the Orders-Class:
public Order MyOrder;
Here an example of the Orders-class.
public class Order: INotifyPropertyChanged
{
public string Comment;
public int IdCreatedByUser;
public bool IsComplete;
}
Please note that this is just an example for the properties..knowing that get,set are missing here..
The only solution I came up with is to hijack a Combox like this:
<ComboBox ItemsSource="{Binding UserCollection}"
DisplayMemberPath="Item2"
SelectedValue="{Binding MyOrder.IdCreatedByUser}"
SelectedValuePath="Item1">
<ComboBox.Template>
<ControlTemplate>
<TextBlock Text="{Binding SelectedItem.Item2,RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
The fact that I can use ItemsSource, SelectedValue and SelectedValuePath makes it possible for me to select and show the desired Item. Any solutions for the use of TextBlocks with this one?
I was also thinking about a converter or extra property..but maybe you can show me a way to design this in a better way..
Thanks!
This essentially makes your specialized ComboBox easily reusable. Stuff like FontWeight will be inherited by the ContentPresenter.
<Style x:Key="CollectionLookupComboBox" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<!--
Default to readonly, but you can override that for particular instances
if that's useful somewhere.
-->
<Setter Property="IsReadOnly" Value="True" />
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Border
x:Name="OuterBorder"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
>
<!--
The margin here keeps the text in the same spot when I toggle IsReadOnly,
with the default theme I have. May need to fiddle with that to get it to
look right for you.
-->
<ContentPresenter
Margin="3,2,2,0"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
VerticalAlignment="Stretch"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Example -- this is overkill; you don't need an ItemTemplate or boldface, but it demonstrates how all the usual ComboBox stuff is supported:
<StackPanel
Orientation="Vertical"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
>
<CheckBox
x:Name="ReadOnlyCheckBox"
IsChecked="True"
Margin="1"
Content="Read-Only"
/>
<ComboBox
Margin="1"
Style="{StaticResource CollectionLookupComboBox}"
IsReadOnly="{Binding IsChecked, ElementName=ReadOnlyCheckBox}"
MinWidth="80"
SelectedIndex="0"
FontWeight="Bold"
Foreground="Green"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="DeepSkyBlue" BorderThickness="2" CornerRadius="4">
<Label Content="{Binding}" />
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
<sys:String>First Item</sys:String>
<sys:String>Second Item</sys:String>
</ComboBox>
</StackPanel>
I'm applying this style to an infragistics control XamDateTimeEditor.
The new error icon displays as expected.
issue: The original error style is still displaying when a validation error occurs. Thus making the control to have two error icon styles.
<Style TargetType="{x:Type XamDateTimeEditor}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<DockPanel>
<Border
Background="Red"
Width="25"
Height="25"
CornerRadius="10"
>
<TextBlock
Text="Error"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontWeight="Bold"
Foreground="White"
/>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using a view model that implements IDataErrorInfo is the best approach when customizing an error template that targets XamDateTimeEditor.
This was previously discussed and demonstrated on our forums at: http://www.infragistics.com/community/forums/t/109152.aspx
https://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
I use telerik, but that should not mean much for this question. My application is WPF (.Net 4.5).
I have a style, that I use for all comboboxes, which has an errortemplate. The style looks like this:
<Style TargetType="{x:Type telerik:RadComboBox}" x:Key="RadComboBoxStyle" >
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Background" Value="{StaticResource InputBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource InputBorderBrush}" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource RadComboBoxValidationErrorTemplate}" />
</Style>
My ErrorTemplate looks like this:
<ControlTemplate TargetType="{x:Type Control}" x:Key="RadComboBoxValidationErrorTemplate">
<Grid ToolTipService.IsEnabled="True" ToolTipService.ShowOnDisabled="True"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors), Converter={StaticResource ValidationErrorsConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border BorderBrush="{StaticResource ErrorBrush}" BorderThickness="3" Panel.ZIndex="999" HorizontalAlignment="Right" Margin="0,0,10,0"
Background="Transparent" DockPanel.Dock="right" Width="16" Height="16" CornerRadius="10">
<Rectangle Fill="{StaticResource ErrorBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="3" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="315" />
</Rectangle.RenderTransform>
</Rectangle>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="{StaticResource ErrorBrush}" BorderThickness="1" />
</AdornedElementPlaceholder>
</Grid>
</ControlTemplate>
The entire thing is defined in a global ResourceDictionary.
What this code does, it to put a "forbidden sign" on top of the combobox (Panel.ZIndex="999"), just before the dropdown button (using margins). The Border and the Rectangle makes a sign much like this: Picture.
The combobox itself must be able to hold a tooltip, set locally. So the error-message cannot be shown in the tooltip of the combobox - unless I find a way to combine the two without having to resolve it locally (I want that code in my resourcedictionary).
I also do not want the "forbidden sign" to handle mouse clicks (it gobbles up the click and prevent the combobox from dropping down, if the user clicks on the "forbidden sign".
I tried setting IsHitTestVisible to false on the grid and on the border inside the ErrorTemplate, but that caused the Tooltip to never show.
I also tried setting IsEnabled to false on the same two constrols, but that would not send the mouseclick on to the combobox itself (the list in the combobox does not drop down).
Is there any way to do this directly in the combobox-style or errortemplate? I do not even mind having a code behind - but I really do not want to add code locally where the combobox-style is used.
Based on the following code :
<GroupBox>
<GroupBox.Template>
<ControlTemplate TargetType="{x:Type GroupBox}">
<ContentPresenter TextElement.FontSize="28" />
</ControlTemplate>
</GroupBox.Template>
<TextBlock>Test</TextBlock>
</GroupBox>
I was expecting "Test" to be displayed with FontSize=28. But it uses the default size instead.
If I remove the TextBlock like this :
<GroupBox>
<GroupBox.Template>
<ControlTemplate TargetType="{x:Type GroupBox}">
<ContentPresenter TextElement.FontSize="28" />
</ControlTemplate>
</GroupBox.Template>
Test
</GroupBox>
The text is now the displayed with 28 as FontSize.
Shouldn't the property value be inherited when I use a TextBlock ?
This other question How do I Change the FontFamily on a ContentPresenter? doesn't help, as it works only for default content too.
This question also : How do I Change the FontFamily on a ContentPresenter?.
Both works whe you use the default content handler, but fails when you manually create a textblock.
Edit: As demonstrated in this other question, I've tried by simply using a ContentControl :
<StackPanel>
<StackPanel.Resources>
<ControlTemplate x:Key="UsingBorderTemplate" TargetType="{x:Type ContentControl}">
<Border BorderBrush="Red" BorderThickness="1" TextElement.FontFamily="Courier New" Margin="5">
<ContentPresenter/>
</Border>
</ControlTemplate>
<ControlTemplate x:Key="MyTemplate" TargetType="{x:Type ContentControl}">
<ContentPresenter TextElement.FontFamily="Courier New" Margin="5" />
</ControlTemplate>
</StackPanel.Resources>
<ContentControl Template="{StaticResource MyTemplate}">
I'm courier new!
</ContentControl>
<ContentControl Template="{StaticResource MyTemplate}">
<TextBlock>I'm default!</TextBlock>
</ContentControl>
</StackPanel>
You can change the template from "MyTemplate" to "UsingBorderTemplate" with the same result.
I had an odd problem with ContentPresenter. I remember that I have analyzed the source of the problem and have found out that it was by design - Probably you have here the same issue.
Look at this post, maybe it helps you.
I think the text that the content presenter is presenting is the GroupBox.Header, and you may just be tacking another TextBox in there that isn't part of the Group Box.
In your first code block, add the line below and see if that works:
<GroupBox.Header>Test</GroupBox.Header>
HTH,
Berryl
Im quite new to wpf and have to following problem.
I need to create a List (i am using a listbox) of items that can be expanded (expander).
The problem is, that they can be expanded, only if they have been 'selected'.
Each listboxitem should have a checkbox and some text.
So very basic example to illustrate what i mean:
<listbox>
<item>(checkbox) John Doe</item>
<item>(checkbox) Mike Murray</item>
</listbox>
If any (so multiple is allowed) of the checkboxes in the listbox are checked, then
the item expands showing more data.
Again an example:
<listbox>
<item>
(checkbox-checked) John Doe
Some extra data shown in expanded area
</item>
<item>
(checkbox-unchecked) Mike Murray</item>
</listbox>
I cant get a expander to use a checkbox as 'togglebutton'.
Could anyone help me out? Some example code would be very welcome...
This should do the trick:
<ListBox>
<ListBox.Resources>
<Style TargetType="Expander">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Expander">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Content="{TemplateBinding Header}"
/>
<ContentControl
x:Name="body"
Grid.Row="1" Content="{TemplateBinding Content}"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="body" Property="Visibility" Value="Collapsed" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
<Expander Header="One">
Content one
</Expander>
<Expander Header="Two">
Content two
</Expander>
</ListBox>
I've defined a Style here that changes the Template of any Expander controls to which the Style is applied. (And since I've put the Style in the ListBox.Resources it'll automatically apply to an Expander controls in the list.)
The trick to getting the CheckBox to work is that when you put it (or indeed any ToggleButton based control) into an Expander template, you need to use a data binding configured with its RelativeSource set to the TemplatedParent. This enables two-way binding - it means that not only does the CheckBox reflect the current state of the expander, it is also able to change the current state.
All you need to add a check box in the header is this code:
<telerik:RadExpander.Header>
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center"/>
<TextBlock Margin="5,0,0,0">Legend</TextBlock>
</StackPanel>
</telerik:RadExpander.Header>
I am using Rad Control, The same can be done using the standard expander