WPF – Custom control

During creating user interface situation when you have to use same elements is very common.

To prevent duplicate code and reduce time needed for modifications I prefer to use User Controls

Our goal will be creating user control which will contain TextBox and Label for description. Such a bunch of elements is used usually in forms.
Let’s start from creating a view of our control in XAML language.

<UserControl x:Class="RadekKodujeBinding.CustomControls.LabeledTextBox" 
             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:RadekKodujeBinding.CustomControls" 
             mc:Ignorable="d" d:DesignHeight="25" d:DesignWidth="300" Name="labeledTextBox">
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="Auto"/>
             <ColumnDefinition Width="*"/>
         </Grid.ColumnDefinitions>
         <Label Content="{Binding ElementName=labeledTextBox, Path=LabelText}" Margin="5" />
         <TextBox Text="{Binding ElementName=labeledTextBox, Path=Text}" TextChanged="TextBox_TextChanged" Grid.Column="1" Margin="5" VerticalContentAlignment="Center" />
     </Grid>
</UserControl>

If you create your own user control you have to remember that you will lose control of all properties specific for elements which are inside.
In our case we want to modify Label content and TextBox text from outside of user control. These properties should be also bindable.
To achieve that we have to use Dependency Properties.

Implementation of single dependency property usually requires documentation because is it a lot of code but in Visual Studio you can use snippet which saves your time.

Dependency Properties can be only defined in class which derives from DependencyObject class because they expect SetValue and GetValue methods.

Whole “code behind” of our control is presented below.

using System.Windows;
using System.Windows.Controls;

namespace RadekKodujeBinding.CustomControls
{
     public partial class LabeledTextBox : UserControl
     {
         public LabeledTextBox()
         {
             InitializeComponent();
         }

         public string LabelText
         {
             get { return (string)GetValue(LabelTextProperty); }
             set { SetValue(LabelTextProperty, value); }
         }

         public static readonly DependencyProperty LabelTextProperty =
             DependencyProperty.Register("LabelText", typeof(string), typeof(LabeledTextBox), new PropertyMetadata(string.Empty));

         public string Text
         {
             get { return (string)GetValue(TextProperty); }
             set { SetValue(TextProperty, value); }
         }

         public static readonly DependencyProperty TextProperty =
             DependencyProperty.Register("Text", typeof(string), typeof(LabeledTextBox), new PropertyMetadata(string.Empty));

         private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
         {
             var expression = (sender as TextBox).GetBindingExpression(TextBox.TextProperty);
             expression.UpdateSource();
         }
     }
}

Dependency Properties must be public, static and readonly. They also have to be registered by calling DependencyProperty.Register method with following parameters: name of property, data type of property, data type of parent.
The last parameter is PropertyMetadata object which allows you to declare default value of property or callback method.

Ok. How to use it?

You have to add custom namespace which contains newly created control to view. In our case it is just controls.


<Window x:Class="RadekKodujeBinding.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:local="clr-namespace:RadekKodujeBinding" 
        xmlns:controls="clr-namespace:RadekKodujeBinding.CustomControls" 
        mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
    <Grid>
    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200">
        <controls:LabeledTextBox Text="{Binding TypedText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" LabelText="{Binding LabelText}" />
    </StackPanel>
</Grid>
</Window>

As you can see there is binding again, but this time to ViewModel of main window. You can create it in this way:

using System.Windows;

namespace RadekKodujeBinding
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DataContext = new MainViewModel();
        }
    }
}

and a MainViewModel class:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace RadekKodujeBinding
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private string labelText = "Email:";

        private string typedText;

        public string LabelText
        {
            get { return labelText; }
            set { labelText = value; RaisePropertyChanged(); }
        }

        public string TypedText
        {
            get { return typedText; }
            set
            {
                typedText = value;
                RaisePropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

If your assumption is to inform ViewModel every time when user changes text inside TextBox set Mode and UpdateSoruceTrigger in Binding Markup Extension of Text property.

Of course, newly created user control needs some styling to fit rest of project. It is not very attractive now.

Creating user control in WPF is very easy and can be achieved in few steps, but I am sure it will help with arrange your code better.

Leave a Reply

Your email address will not be published. Required fields are marked *