Question about the structure of the new control extending the existing style - wpf

I trying to use the Style to reuse.
I saw the article as follows.
https://charlesk.tistory.com/66
The article shows how to extend the control to use the existing style.
I became curious about how the style structure to be when a new control is created by extending the existing style.
For example, in the article defined the HeaderedContentControl that has "Style_HeaderedContentControl" key as below.
<Style x:Key="Style_HeaderedContentControl" TargetType="{x:Type HeaderedContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type HeaderedContentControl}">
<Border Background="WhiteSmoke"
BorderBrush="DarkGray"
BorderThickness="1"
Margin="5">
<StackPanel Background="WhiteSmoke"
Margin="5">
<ContentPresenter ContentSource="Header"
TextBlock.FontWeight="Bold"
Margin="0,0,0,5"/>
<ContentPresenter />
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And create a new control by extending the above style as below.
<HeaderedContentControl Header="재생" Grid.Column="1"
Style="{DynamicResource Style_HeaderedContentControl}">
<StackPanel>
<Button Content="시작"/>
<Button Content="정지"/>
</StackPanel>
</HeaderedContentControl>
Here, I wonder how the HeaderedContentControl structure to be.
The candidates I think are:
<Border Background="WhiteSmoke"
BorderBrush="DarkGray"
BorderThickness="1"
Margin="5">
<StackPanel Background="WhiteSmoke"
Margin="5">
<ContentPresenter ContentSource="Header"
TextBlock.FontWeight="Bold"
Margin="0,0,0,5"/>
<StackPanel>
<Button Content="시작"/>
<Button Content="정지"/>
</StackPanel>
<ContentPresenter />
</StackPanel>
</Border>
or
<Border Background="WhiteSmoke"
BorderBrush="DarkGray"
BorderThickness="1"
Margin="5">
<StackPanel Background="WhiteSmoke"
Margin="5">
<ContentPresenter ContentSource="Header"
TextBlock.FontWeight="Bold"
Margin="0,0,0,5"/>
<ContentPresenter />
<StackPanel>
<Button Content="시작"/>
<Button Content="정지"/>
</StackPanel>
</StackPanel>
</Border>
or
<Border Background="WhiteSmoke"
BorderBrush="DarkGray"
BorderThickness="1"
Margin="5">
<StackPanel Background="WhiteSmoke"
Margin="5">
<ContentPresenter ContentSource="Header"
TextBlock.FontWeight="Bold"
Margin="0,0,0,5"/>
<ContentPresenter />
</StackPanel>
<StackPanel>
<Button Content="시작"/>
<Button Content="정지"/>
</StackPanel>
</Border>
How is the right answer? and could you tell me why the structure to be?
Thanks for reading.

When you do this...
<HeaderedContentControl Header="재생" Grid.Column="1"
Style="{DynamicResource Style_HeaderedContentControl}">
<StackPanel>
<Button Content="시작"/>
<Button Content="정지"/>
</StackPanel>
</HeaderedContentControl>
.. you are setting the content of the control to be that StackPanel. The style, however, sets the template, which dictates the overall look of the control and specifies where the content should go with this line:
<ContentPresenter />
So what you get in the end is everything in Style_HeaderedContentControl, but with that <ContentPresenter /> replaced with the content that you provided.
A more simple example is what happens when you declare a ContentControl with some content, all you see is the content you've provided:
<ContentControl HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="This is the content" Background="CornflowerBlue" />
</ContentControl>
Which shows this:
Now let's declare a template for it, and set it with Template="{StaticResource MyTemplate}":
<ControlTemplate x:Key="MyTemplate" TargetType="{x:Type ContentControl}">
<GroupBox Header="This is the template">
<ContentPresenter />
</GroupBox>
</ControlTemplate>
Now you've completely changed the whole look of the control, but your content appears where you've specified with <ContentPresenter />:

Related

Making the header of a group box unique for each instance

I have created a Groupbox resource directory, and created a style that affects the groupbox.
Here is my Resource Directory code:
<Style x:Key="grpNumbers" TargetType="GroupBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Border
BorderThickness="1"
BorderBrush="#25A0DA" CornerRadius="10">
<Label HorizontalAlignment="Left" Content="Carrier" Foreground="White" Background="#151515" Height="38"
Margin="30,-195,0,0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The issue is that I want the group box style to not be coupled with the Content in the Label.
Currently it is getting instantiated in the mainwindow like this:
<GroupBox Style="{StaticResource ResourceKey=grpNumbers}" Grid.Column="1" BorderBrush="#272727" Grid.Row="1" Height="200" Margin="20" HorizontalAlignment="Stretch" Header="Carrier information" Foreground="White" FontSize="20" BorderThickness="0.2">
</GroupBox>
But obviously the above groupboxes' Header tag isn't overriding the header of my custom groupbox but I need it too, can this be done?
Label inside ContentTemplate can use TemplateBinding to bind Header:
<Label Content="{TemplateBinding Header}"/>
You should modify your Style too look like this:
<Style x:Key="grpNumbers" TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<Grid>
<Border BorderThickness="1"
BorderBrush="#25A0DA"
CornerRadius="10">
<Label HorizontalAlignment="Left"
Content="{TemplateBinding Header}"
Foreground="White"
Background="#151515"
Height="38"
Margin="30,-195,0,0"/>
</Border>
<!-- Responsible for displaying what you put inside of your GroupBox -->
<ContentPresenter Margin="5,25,5,5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
which can then be used like this:
<GroupBox Style="{StaticResource grpNumbers}"
Grid.Column="1"
BorderBrush="#272727"
Grid.Row="1"
Height="200"
Margin="20"
HorizontalAlignment="Stretch"
Header="Carrier information"
Foreground="White"
FontSize="20"
BorderThickness="0.2">
<TextBox /> <!-- or whatever you want inside your GroupBox -->
</GroupBox>

Passing in an Image.Source to a XAML style

I am trying to implement a button style that has both an image and a caption that are specified when the style is used.
Here is my style:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Margin="0,0,0,0" Background="#FF9E9FA3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{TemplateBinding Tag}" Grid.Column="0"/>
<Label Content="{TemplateBinding Content}" Grid.Column="1" FontSize="15" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I am using it like this:
<Button Tag="edit.png" Content="Edit" Style="{StaticResource ButtonStyle}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Tag="save.png" Content="Save" Style="{StaticResource ButtonStyle}" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"/>
Content works as expected and two buttons show up with the correct words in the label, but the images do not show up. If I use the ControlTemplate directly in (no style) and replace {TemplateBinding} with the image file name, the images do appear.
Obviously I am using the Tag wrong or the Image wrong.
Help?
It works if you replace
<Image Source="{TemplateBinding Tag}" />
by
<Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
because then the necessary type conversion from string to ImageSource is performed automatically.

Tabitem header image

When I set image as Header of TabItem it looks like image in some gray area. How remove this surrouding area?
<TabControl TabStripPlacement="Left"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Top">
<TabItem ToolTip="Элементы, в которых найдены несоответствия" Height="200"
Style="{StaticResource TabItemHeaderImageStyle}">
<TabItem.Header>
<Image Source="{StaticResource CheckImage}"
Margin="0"
Height="25" Width="25" />
</TabItem.Header>
<ListBox ItemsSource="{Binding unmatched.vs}"
MaxHeight="200"
ItemTemplate="{StaticResource VertexPresenterNotConformTemplate}" />
</TabItem>
<TabItem ToolTip="Элементы, соответствующие шаблону"
Style="{StaticResource TabItemHeaderImageStyle}">
<TabItem.Header>
<Image Source="{StaticResource UncheckImage}"
Height="25" Width="25" />
</TabItem.Header>
<ListBox ItemsSource="{Binding unmatched.vs}"
MaxHeight="200"
ItemTemplate="{StaticResource VertexPresenterConformTemplate}" />
</TabItem>
<Style x:Key="TabItemHeaderImageStyle" TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border Name="Border"
Margin="0,0,-4,0"
BorderThickness="1,1,1,1"
CornerRadius="2,12,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
What I wish(TabItem's headers under each other without a gray area around it):
TabItem probably has a border around it's header that's gray (thus making a shadow around an image). See this link for TabItem ControlTemplate.
I'd try fiddling with the controltemplate or use an app like WpfInspector to see what's wrong with the template and get correct values.

Template Binding in Control template

I have the following control template.
I wish to set the source property for the image control in the control
template using Template Binding.
But since this is a control template for button control and the button control doesn't
have source property, i can't use TemplateBinding in this case.
<ControlTemplate x:Key="BtnTemplate" TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel>
<Image Name="Img" Style="{StaticResource ImageStyle}" Source="temp.jpg" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
Since i have to set different images for different instances of button, i can't hardcode the path as well.
Please let me know how to tackle this situation.
I'd suggest using dynamic resources, e.g. define the template as follows:
<ControlTemplate x:Key="buttonTemplate" TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel Orientation="Horizontal" Background="Yellow">
<Image Source="{DynamicResource ResourceKey=Img}" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
And use it like this:
<Button Content="Button" Template="{StaticResource ResourceKey=buttonTemplate}">
<Button.Resources>
<ImageSource x:Key="Img">SomeUri.png/</ImageSource>
</Button.Resources>
</Button>
TemplateBinding is a lightweight "binding", it doesn't support some features of traditional Binding, such as automatically type conversion using the known type converters associated with the target property (such as converting the string URI into a BitmapSource instance).
The following code can work properly:
<Window x:Class="GridScroll.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="5" Margin="15" Cursor="Hand" Background="Red">
<StackPanel Orientation="Horizontal" Background="White">
<Image Name="Img" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Button Style="{StaticResource ButtonStyle}" Tag="a.jpeg" Content="a"/>
<Button Style="{StaticResource ButtonStyle}" Tag="b.png" Content="b"/>
</StackPanel>
You haven't really said how you expect consumers of your button to set the source. You could use the Button.Tag property, for example, and then bind to that in your template. Or you could define your own control:
public class ImageButton : Button
{
// add Source dependency property a la Image
}
And then the template:
<ControlTemplate TargetType="ImageButton">
<Border CornerRadius="5" Margin="15" Cursor="Hand">
<StackPanel>
<Image Name="Img" Style="{StaticResource ImageStyle}" Source="{TempateBinding Source}" Height="100" Width="100" Margin="5"></Image>
<Label Content="{TemplateBinding Content}" Background="Transparent" Margin="2"></Label>
</StackPanel>
</Border>
</ControlTemplate>
I'm not sure that I understood your problem very well but why don't you use ContentPresenter? It allows to move the code for your Image at the higher level.
<ControlTemplate x:Key="BtnTemplate" TargetType="Button">
...
<ContentPresenter/>
</ControlTemplate>
...
<Button Template="{StaticResource BtnTemplate}">
<Image .../>
</Button>

WPF Buttons Style

I have WPF Form which has many buttons with the same code. Appearance of all buttons must be the same
For example, code for one of these buttons
<Button x:Name="btnAddRelative" Width="120" Click="btnAddRelative_Click" >
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Height="26" HorizontalAlignment="Left">
<Image.Source>
<BitmapImage UriSource="images/add.png" />
</Image.Source>
</Image>
<TextBlock Text=" Add Relative" Height="20" VerticalAlignment="Center"/>
</StackPanel>
</Button.Content>
</Button>
How can I create one style and use it for all my buttons. All buttons has the same png image, only their text different. How can I do this.
I tried to do this with Style object in Resource Section:
<UserControl.Resources>
<Style TargetType="Button" x:Key="AddStyle">
<Setter Property="Content">
<Setter.Value>
<StackPanel Orientation="Horizontal">
<Image Height="26" HorizontalAlignment="Left">
<Image.Source>
<BitmapImage UriSource="images/add.png" />
</Image.Source>
</Image>
<TextBlock Text=" " Height="20" VerticalAlignment="Center"/>
</StackPanel>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
But this code not work. Can any body know how can I do this?
If the image is fix you can hard-code it in the style, and use the Content property of Button bin to the Content of TextBox
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel
Orientation="Horizontal">
<!--<Image Height="26" HorizontalAlignment="Left">
<Image.Source>
<BitmapImage UriSource="images/add.png" />
</Image.Source>
</Image>-->
<TextBlock
Foreground="{TemplateBinding Foreground}"
Text="{TemplateBinding Content}"
Height="20"
VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
just try this
<Window.Resources>
<Style TargetType="Button"
x:Key="AddStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<StackPanel Orientation="Horizontal">
<Image Height="26"
Width="20"
HorizontalAlignment="Left">
<Image.Source>
<BitmapImage UriSource="/WpfApplication33;component/Images/MoveLeft.png" />
</Image.Source>
</Image>
<TextBlock Text ="{TemplateBinding Content}"
Height="20"
/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<Button Style="{StaticResource AddStyle}"
Height="25" Width="100"
Content="Button1"></Button>
<Button Style="{StaticResource AddStyle}"
Height="25"
Width="100"
Content="Button22"></Button>
<Button Style="{StaticResource AddStyle}"
Height="25"
Width="100"
Content="Button2233"></Button>
<Button Style="{StaticResource AddStyle}"
Height="25"
Width="100"
Content="Button2332"></Button>
</StackPanel>
</Grid>
Note: Use ContentPresenter instead of TextBlock if you have to display anything other than flat text
Try changing your style as follows
<UserControl.Resources>
<Style
TargetType="Button"
x:Key="AddStyle">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel
Orientation="Horizontal">
<Image
Height="26"
HorizontalAlignment="Left">
<Image.Source>
<BitmapImage
UriSource="images/add.png" />
</Image.Source>
</Image>
<TextBlock
Text=" "
Height="20"
VerticalAlignment="Center" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[Edit inspired by comment]
You could create a new UserControl, lets call it AddButtonContent containing your Stackpanel and such, then include that within your button, like so:
<Button>
<local:AddButtonContent
ButtonText="Testing, one, two, three" />
</Button>
You'll need to add a xmlns reference called local (or whatever you'd like to call it) to the UserControl with all of the buttons.
The codebehind portion of your AddButtonContent UserControl will need the following code, and you'll need to name your TextBlock (I used testText for this example).
public static DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText",
typeof(string),
typeof(AddButtonContent),
new PropertyMetadata("", onTextChangedCallback));
public string ButtonText
{
get { return (string)GetValue(ButtonTextProperty); }
set
{
SetValue(ButtonTextProperty, value);
}
}
static void onTextChangedCallback(
DependencyObject dobj,
DependencyPropertyChangedEventArgs args)
{
AddButtonContent abc = dobj as AddButtonContent;
abc.testText.Text = args.NewValue.ToString();
}

Resources