I have a ListBoxItem Style that I am trying to modify so that it will show character ellipsis when the list box is made to small. To do that I've had to get rid of the ContentPresenter in our code and replace it with a TextBlock. The ListBoxes that this is applied to are all bound via the ItemSource property.
Here is my code.
<Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="White"/>
<Setter Property="Margin" Value="0,0,0,0"/>
<Setter Property="Padding" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid>
<Border x:Name="Bd" SnapsToDevicePixels="true">
<!-- Before this used to be ContentPresenter but I switched it to TextBlock to get it the TextTrimming property. I can't find the right way to bind the data though.-->
<TextBlock Text="{TemplateBinding DisplayMemberPath}" TextTrimming="CharacterEllipsis" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<Rectangle x:Name="HoverRectangle"
Stroke="{StaticResource Gold}"
StrokeDashCap="Square"
StrokeThickness="0"
SnapsToDevicePixels="True" />
<Rectangle x:Name="KeyboardFocusRectangle"
Height="Auto"
SnapsToDevicePixels="True"
Stroke="{StaticResource BrightBlue}"
StrokeDashCap="Square"
StrokeThickness="0" />
</Grid>
<ControlTemplate.Triggers>
<!-- Bunch of Triggers in here -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My current TextBlock Text binding (Text="{TemplateBinding DisplayMemberPath}") is not working. What should the binding be in order to work correctly?
Your only reasonable choice here is to assume the data context of the ListBoxItem is a string, or can be displayed as such:
<TextBlock Text="{Binding}" .../>
Related
I've noticed that TextBoxes are very slow and create performance issues when the Text is changed dynamically by code (I need to change the Text continuosly to 10-15 TextBoxes at the same time), so, as a workaround, I've created a custom control with a TextBlock and a TextBox:
The TextBlock is used in almost all time.
The TextBox is used only when I need to edit the Text inside the control with keyboard.
My solution is to change the template and use the TextBox when the control is focused:
(Value is a string Dependency Property)
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Value" Value="Val"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Value}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
But when I click on the control nothing happens.
I think that the problem is that the "focus state" is passed to the internal TextBox, and the control loses the "focus state".
There is a better way to create a custom "TextBox" control like this, or a way to resolve this problem?
You don't need a custom control for this, that's just adding unnecessary overhead. What you're trying to create is still a TextBox, with all the usual behavior of a TextBox (focus etc). All you need to do is change the template to a TextBlock when it's not in focus:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Text="Hello World" />
<TextBox Text="Goodbye World" />
</StackPanel>
I have simple ToggleButton and when IsChecked i want to change only the text.
I want all other properties like border and background will be Transparent but it seems that i still have this background style:
<ToggleButton x:Name="changeButBorderedBlinky" Content="EDIT" Width="40" Height="40" Background="Transparent">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Content" Value="Done"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
One thing that is often annoying is some visual aspects of the default WPF controls are coded in a way so as they are not configurable. The MouseOver Background is an example (scrollbar sizes is another... grrr!!!). You can solve this though by defining your own Template for the ToggleButton and eliminate that MouseOver trigger. Here is a simple example:
<ToggleButton x:Name="changeButBorderedBlinky" Width="40" Height="40" Background="Transparent">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="EDIT"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
ContentStringFormat="{TemplateBinding ContentStringFormat}"
Focusable="False"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Content" Value="Done"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Also, in order to change a property in a trigger it has to be set in a style, not directly. That is why your Content doesn't change in your MouseOver Trigger. If you remove the property setting for it and
add it into the Style with a Setter, it will allow the trigger to change it.
One downside to all this is it overrides all the default template triggers so you won't see when the ToggleButton is checked anymore unless you add a trigger for "IsChecked" too. (and if you need to alter the Background in the trigger, move the Background Property to a Setter like I did for Content)
Hope that helps...
Sorry to revive an old thread, but (for someone struggling) the following worked for me. This can be in App.xaml file under Application.Resources tag or directly in the Window.Resources. I originally needed it for a UWP application, then had to adapt for WPF for a different project:
<Style TargetType="ToggleButton" x:Key="TransparentToggleButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid x:Name="RootGrid" Background="Transparent">
<ContentPresenter x:Name="ContentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then just style your ToggleButton:
<ToggleButton Style="{StaticResource TransparentToggleButtonStyle}">
(whatever content you want to present)
</ToggleButton>
Hope this helps someone,
I'm creating a custom TickBar for a Slider. This CustomTickBar allows me to put different markers on the Slider. I'm gonna use the following model:
Interface IModel
{
string Id;
}
Class Model1 : IModel
{
string Id;
string SomeProperty;
}
Class Model2 : IModel
{
string Id;
string SomeOtherProperty;
}
The idea is I provide a List<IModel> to this TickBar control and based on the type of IModel the marker icon would change; e.g. for Model1 it would be a triangle and for Model2 it would be a rectangle. I understand this would be possible using a DataTemplate. But WPF TickBar doesn't have a DataTemplate property. Now is there a way I can do this using a DataTemplate property and subclassing TickBar?
Note: I understand I can create custom tick using OnRender(), but I'm trying to check if there's a way to do it by writing as less code-behind as possible.
TickBar does not have default style, so it looks like using OnRender is the way they designed it.
Another solution I'm think about, would be:
Create custom control, you own TickBar. Maybe inherit from TickBar.
You can set your own style for this custom TickBar, even based on some of your model data.
Use themes/generic.xaml and this code to apply custom style for your control:
static void MyCustomTickBar() {
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomTickBar), new FrameworkPropertyMetadata(typeof(MyCustomTickBar)));
}
Edit template of Slider and use you new TickBar instead of built in one.
Here is default template for Slider. I used style snooper to extract it. Sorry, I could not provide it in my answer, it's too long.
Try to create a style for the slider first.
e.g. Something like this:
<Window.Resources>
<SolidColorBrush x:Key="HorizontalSliderTrackNormalBackground" Color="#FFE7EAEA"/>
<LinearGradientBrush x:Key="HorizontalSliderTrackNormalBorder" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFAEB1AF" Offset="0.1"/>
<GradientStop Color="#FFAEB1AF" Offset=".9"/>
</LinearGradientBrush>
<Style x:Key="SliderRepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle Fill="Transparent"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CustomThumbForSlider" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse Fill="#009EFF" Stroke="#009EFF" Height="14" Width="14"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MyCustomStyleForSlider" TargetType="{x:Type Slider}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="Auto" MinHeight="{TemplateBinding MinHeight}"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TickBar x:Name="TopTick" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Placement="Top" Height="10" Grid.Row="2"/>
<TickBar x:Name="BottomTick" Visibility="Collapsed" Fill="{TemplateBinding Foreground}" Placement="Bottom" Height="10" Grid.Row="2"/>
<Border x:Name="TrackBackground"
Background="{StaticResource HorizontalSliderTrackNormalBackground}"
BorderBrush="{StaticResource HorizontalSliderTrackNormalBorder}"
BorderThickness="2" CornerRadius="1"
Margin="5,0" VerticalAlignment="Center" Height="10.0" Grid.Row="1" >
<Canvas Margin="-6,-2">
<Rectangle Visibility="Hidden" x:Name="PART_SelectionRange" Height="6.0"
Fill="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"
Stroke="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}"
StrokeThickness="2.0"/>
</Canvas>
</Border>
<Track x:Name="PART_Track" Grid.Row="1" >
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource SliderRepeatButtonStyle}" Command="{x:Static Slider.DecreaseLarge}"/>
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource SliderRepeatButtonStyle}" Command="{x:Static Slider.IncreaseLarge}"/>
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb x:Name="Thumb" Style="{StaticResource CustomThumbForSlider}" Background="Black"/>
</Track.Thumb>
</Track>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
You can then define a Slider which uses the style:
<Slider Name="CustomSlider" Style="{StaticResource MyCustomStyleForSlider}"/>
In order to change the style depending on some properties you can add a datatrigger. just replace the existing style with a new one:
<Style x:Key="CustomThumbForSlider" TargetType="{x:Type Thumb}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse Fill="#009EFF" Stroke="#009EFF" Height="14" Width="14"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsDifferent}" Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse Fill="#009055" Stroke="#009055" Height="14" Width="14"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
This new Style is going to change the appearance when the datacontext of slider has a different value for the property "IsDifferent".
<Slider Name="CustomSlider" Style="{StaticResource MyCustomStyleForSlider}" DataContext="{Binding Path=MyContext}"/>
Of course it would be possible to replace the green ellipse with a different shape of your liking and also use a different property.
For the templating problem it is usally best to use a ControlTemplate or alternativly a ContentControl whose DataTemplate can be set freely and which will act as a parent for your own controls.
I've recently tried overriding onrender for a custom slider myself and it's tricky. I wouldn't go that route.
I suggest you consider adding another control to hold the markers and make that match the height or width of your slider.
If your "ticks" at fixed then that could be just a uniformgrid containing paths and you use a resource to define their data using a DynamicResource geometry. You can switch out the geometry by merging different ones or datatemplate it.
I have a simple xaml example and I want to change CornerRadius property, which is set through ControlTemplate of Button. I need a way to change it through Button. One use case is: I have 2 buttons, one sets to "BigRadius" and another one sets to "SmallRadius". If I change the default one on Border, then both buttons will have the same CornerRadius. Is there a way to do this in XAML only?
<Window x:Class="StyleDemo.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 Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="5" BorderBrush="Blue"
BorderThickness="5"
Width="80"
Height="40"
x:Name="BaseBorder">
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<!--This button will have a big corner radius-->
<Button Name="Ok" Content="Ok"></Button>
<!--This button will have a small corner radius-->
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>
</Window>
You will need two styles for the Button to achieve what you are doing or create a Custom Button to implement CornerRadius as DependencyProperty, and bind it with CornerRadius of Border in ControlTemplate.
<Style TargetType="Button" x:Key="LargeRadiusStyle">
<Setter Property="Background" Value="White" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="10" Background="White" BorderBrush="Black" BorderThickness="1" >
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Key="SmallRadiusStyle">
<Setter Property="Background" Value="White" />
<Setter Property="TextBlock.TextAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="3" Background="White" BorderBrush="Black" BorderThickness="1" >
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Not sure what you have against DPs. You need SOMETHING to tell the button whether you want the big radius or the small radius. WPF can't read your mind (yet) :). If you don't want a custom button class, then you can use an attached property.
As I've a resource style control template for Button, In addition I've defined a control template in button itself.But the content of the Button didnot displaying. How can I resolve this ?
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<!--Set to true to not get any properties from the themes.-->
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Border TextBlock.Foreground="{TemplateBinding Foreground}"
x:Name="Border"
CornerRadius="10"
BorderThickness="1"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button Name="test" Width="50" Height="60" Content="myvalue">
<Button.Template>
<ControlTemplate>
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
You should set TargetType in ControlTemplate to Button.
<ControlTemplate TargetType="Button">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
Explanation (source msdn):
If you have a standalone ControlTemplate in the resources section with
the TargetType property set to a type, the ControlTemplate does not
get applied to that type automatically. Instead, you need to specify
an x:Key and apply the template explicitly.
Also note that the TargetType property is required on a
ControlTemplate if the template definition contains a
ContentPresenter.