Well, look, the main goal is to make two elements inside one and add the ability to use styles. You can go in several ways.
Using styles:
It makes sense here, we make a certain basic style in which we redefine what we need and add a new one.
For example, we have this style for PasswordBox :
<Style x:Key="MainStyle" TargetType="PasswordBox"> <Setter Property="Height" Value="30"/> <Setter Property="Width" Value="150"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Viewbox> <Canvas Width="24" Height="24"> <Path Data="M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z" Fill="Black" /> </Canvas> </Viewbox> <Border Grid.Column="1" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <ScrollViewer x:Name="PART_ContentHost" VerticalAlignment="Center" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
When applying it, we get the following result:
<PasswordBox Style="{StaticResource MainStyle}" Password="1234" />

Now we say we want to set some style for example for errors, then we create another style and set it BaseOn , which will inherit everything from the base style:
<Style x:Key="ErrorStyle" TargetType="PasswordBox" BasedOn="{StaticResource MainStyle}"> <Setter Property="Background" Value="#99DE0000"/> <Setter Property="Foreground" Value="White"/> </Style>
Apply and see the result:
<PasswordBox Style="{StaticResource ErrorStyle}" Password="1234" />

Using UserControl:
It's all about the same thing, but there is still such a thing as DependencyProperty . With its help, we can pass non-standard properties to our Control (as an example, PasswordBox has the Password property, we can do the same, but let's allow the icon to be redefined), UserControl also supports styles. Let's create something simple:
Add a UserControl with this style:
<UserControl x:Class="WpfApp1.CustomPasswordBox" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WpfApp1" mc:Ignorable="d" x:Name="uc" Height="30" Width="150"> <UserControl.Resources> <StreamGeometry x:Key="DefaultData"> M12,17C10.89,17 10,16.1 10,15C10,13.89 10.89,13 12,13A2,2 0 0,1 14,15A2,2 0 0,1 12,17M18,20V10H6V20H18M18,8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H6C4.89,22 4,21.1 4,20V10C4,8.89 4.89,8 6,8H7V6A5,5 0 0,1 12,1A5,5 0 0,1 17,6V8H18M12,3A3,3 0 0,0 9,6V8H15V6A3,3 0 0,0 12,3Z </StreamGeometry> </UserControl.Resources> <UserControl.Template> <ControlTemplate TargetType="UserControl"> <ContentPresenter/> </ControlTemplate> </UserControl.Template> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Viewbox> <Canvas Width="24" Height="24"> <Path Data="{Binding ElementName=uc, Path=Icon, FallbackValue={StaticResource DefaultData}, TargetNullValue={StaticResource DefaultData}}" Fill="Black" /> </Canvas> </Viewbox> <PasswordBox x:Name="PBox" Grid.Column="1" Foreground="{Binding ElementName=uc, Path=Foreground}" Background="{Binding ElementName=uc, Path=Background}" VerticalContentAlignment="Center" /> </Grid> </UserControl>
And define a pair of DP:
public partial class CustomPasswordBox : UserControl { public CustomPasswordBox() { InitializeComponent(); PBox.PasswordChanged += (sender, args) => { Password = ((PasswordBox) sender).SecurePassword; }; } public static readonly DependencyProperty PasswordProperty = DependencyProperty.Register( "Password", typeof(SecureString), typeof(CustomPasswordBox), new PropertyMetadata(default(SecureString))); public SecureString Password { get => (SecureString)GetValue(PasswordProperty); set => SetValue(PasswordProperty, value); } public static readonly DependencyProperty IconProperty = DependencyProperty.Register( "Icon", typeof(StreamGeometry), typeof(CustomPasswordBox), new PropertyMetadata(default(StreamGeometry))); public StreamGeometry Icon { get => (StreamGeometry) GetValue(IconProperty); set => SetValue(IconProperty, value); } }
Here, for example, I set two DependencyProperty:
- Password - there are some difficulties, and to be more precise, in WPF, the password should not be stored in memory in an unprotected form, for this reason it will not be possible to simply bind the password, you have to pass it through the
PasswordChanged event. There are many examples on the Internet how to get around this, it will be interesting, I think you will find ... - Icon - Simple geometry of our vector icon.
Now let's call it and see the result:
<local:CustomPasswordBox />

Create a style for our new UserControl, redefine the colors and icon:
<StreamGeometry x:Key="KeyIcon"> M21,11C21,16.55 17.16,21.74 12,23C6.84,21.74 3,16.55 3,11V5L12,1L21,5V11M12,21C15.75,20 19,15.54 19,11.22V6.3L12,3.18L5,6.3V11.22C5,15.54 8.25,20 12,21M12,6A3,3 0 0,1 15,9C15,10.31 14.17,11.42 13,11.83V14H15V16H13V18H11V11.83C9.83,11.42 9,10.31 9,9A3,3 0 0,1 12,6M12,8A1,1 0 0,0 11,9A1,1 0 0,0 12,10A1,1 0 0,0 13,9A1,1 0 0,0 12,8Z </StreamGeometry> <Style x:Key="ErrorStyle" TargetType="local:CustomPasswordBox"> <Setter Property="Background" Value="#99DE0000"/> <Setter Property="Foreground" Value="White"/> <Setter Property="Icon" Value="{StaticResource KeyIcon}"/> </Style>
Result:
<local:CustomPasswordBox Style="{StaticResource ErrorStyle}" />

Actually, this is how pretty simple we can work with elements.
Good luck learning WPF!
<local:CustomButton Style="{StaticResource TestStyle}"/>). If I'm not mistaken, then any element in WPF accepts Style, including its own. - EvgeniyZBasedOn="///". - EvgeniyZ