Apply base style of window to inherited window - wpf

I have defined a style for the Window class in my ResourceDictionary:
<Style x:Key="windowStyle"
TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Width="300" Height="300" BorderBrush="Red" BorderThickness="3">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want a specialized window. To this end I inherit a new class from Window and add custom DependecyProperties etc.
My goal is to reuse the base style, ie. I pretty much only want to fill the ContentPresenter from the base style with a grid and add a new ContentPresenter in one of its cells, so I tried this:
<Style x:Key="customWindowStyle"
BasedOn="{StaticResource windowStyle}"
TargetType="{x:Type local:CustomWindow}">
<Setter Property="ContentTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomWindow}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{TemplateBinding Source}" />
<ContentPresenter Grid.Column="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which gives me an exception at runtime, since the ContentTemplate is of type DataTemplate as opposed to ControlTemplate. Of course if I Set the Template instead of ContentTemplate in the customWindowStyle, I lose the style of the base window.
Is there another way to achieve this goal?

Related

WPF Datagrid HeaderColumn Content Alignment

The ContentTemplate code I use for the Datagrid Header is included in the relevant style file as follows:
<Style TargetType="{x:Type DataGrid}">
...
<Style.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" Margin="10 5" Grid.Column="0" />
<Button x:Name="btnFilter" Content="" FontFamily="{StaticResource FontAwesome}" FontSize="16" HorizontalAlignment="Right" Grid.Column="1" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
...
</Style>
The resulting image from this code is as follows:
But I want to align the arrow icons to the right side as in the picture below. How can I do that?
Set the HorizontalContentAlignment property of the DataGridColumnHeaders to Stretch:
<Style TargetType="DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ContentTemplate">
...
</Setter>
</Style>

WPF Creating DataTemplate for a control that doesn't have a DataTemplate property

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.

How can I access child element of any object by using style?

I'm faced with a problem about to find child element. I want to access TextBlock element inside Label. But I can't find it.
Here is my MainWindow.xaml code:
<Label x:Name="text" Style="{DynamicResource labelstyle}">
<TextBlock>asdasdasd</TextBlock>
</Label>
Here is my style code:
<Style x:Key="labelstyle" TargetType="Label">
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border BorderThickness="2" BorderBrush="Red">
<TextBox x:Name="textBox" Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TextBlock},
AncestorLevel=2},Path=Text}">
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to bind TextBox's Text property to Label inside TextBlock's Text property. What should I do ?
I hope I made my self clear.
Thank you.
Below is a sample image:
Instead of using a TextBlock in your label, just leave it in your template and have it reference the Label Content for the text to be displayed.
Below is an example:
<Label x:Name="text" Content="asdasdasd" Style="{StaticResource labelstyle}"/>
and for the styling/template
<Style x:Key="labelstyle" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Border BorderThickness="2" BorderBrush="Red">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style>
This should provide you with the centered text in the red border you seem to be trying to achieve.
Hopefully this helps you a bit.
This will let you display text through a binding and the user will be able to select it, but not type in the TextBox. If you want to be able to also type in the TextBox, remove the IsReadOnly="True"
<Label Height="30" Width="150">
<Label.Template>
<ControlTemplate TargetType="{x:Type Label}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
MinWidth="26"
Margin="2"
Source="{Binding myImageSource}"/>
<TextBox Grid.Column="1"
IsReadOnly="True"
Text="{Binding myTextValue}"
Margin="5,2"/>
</Grid>
</ControlTemplate>
</Label.Template>
</Label>

Column header background -- but not twice

I have the usual style:
<Style TargetType="{x:Type DataGrid}">
<Setter Property="ColumnHeaderStyle">
<Setter.Value>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="{x:Null}" /> <!-- this doesn't help, either -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Border Background="SomeTransparentColor" CornerRadius="20" Margin="5" />
...
The problem is that the background is applied twice: once for all the headers together, for the complete header row, and once for each of the headers separately. Because I use a partly transparent color, this looks rather ugly.
How can I keep WPF from applying this style to the whole header row background as well?
The Default style of the DataGrid column Header puts one layout below of all column headers having the same style as the column headers. This base layer is added so that the header area visibly fills up the full horizontal space even if the sum of all column headers is smaller. The following style changes the approach but keeps the requirement and removes the base layer:
<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ItemsPresenter />
<DataGridColumnHeader x:Name="PART_FillerColumnHeader"
Grid.Column="1"
IsHitTestVisible="False" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

wpf textbox flat border style

need to have flat border style for wpf based textbox... really surprised to see there is no easy way to get this like was in winforms textbox BorderStyle.FixedSingle
is there any easy way to get this done for wpf textbox
The way to do this is to use a control template to draw the border yourself. You can do this in many different ways, heres a couple for you to look at.
The quick hack approach:
<TextBox>
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="1"/>
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
</Grid>
</ControlTemplate>
</TextBox.Template>
</TextBox>
and then theres using resources...
<ResourceDictionary>
<Color x:Key="detailMark">#FFA1A9B3</Color>
<SolidColorBrush x:Key="detailMarkBrush" Color="{StaticResource ResourceKey=detailMark}" />
<Style x:Key="flatTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Rectangle Stroke="{StaticResource ResourceKey=detailMarkBrush}" StrokeThickness="1"/>
<TextBox Margin="1" Text="{TemplateBinding Text}" BorderThickness="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
and then you can use the style:
<TextBox Style="{StaticResource ResourceKey=flatTextBox}" />
<TextBox BorderThickness="1" BorderBrush="Black">
just try this by black or gray
This is better way to me, make a custom template with border, to override the default one.
And most important make ScrollViewer named PART_ContentHost, to fit inner TemplatePart, and for any other features work like default.
simular to example from MSDN:
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border CornerRadius="2" Padding="2" Background="#19212F" BorderBrush="Red" BorderThickness="1">
<ScrollViewer Margin="0" x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources