Here's the code for EditText:  
class EditText : FrameworkElement
{
    TextBlock hintBlock, errorBlock, lengthBlock;
    TextBox inputBox;
    Separator separator;
    Path leftIcon, invalidIcon, validIcon;
    Grid container, infoContainer;
    TranslateTransform translateHint;
    ScaleTransform scaleHint;
    DoubleAnimation translateHintAnim, scaleHintAnim;
    ColorAnimation brushAnim;
    SolidColorBrush brush;
    bool isHintMoved;
    public string Icon { get; set; }
    public string Hint { get; set; }
    public bool IsRequired { get; set; }
    public bool IsMultiline { get; set; }
    public int MaxLength { get; set; }
    public bool IsTrimBottomRequested { get; set; }
    public EditText() {
        Margin = new Thickness(5);
        brush = new SolidColorBrush(Colors.SkyBlue);
        initializeControls();
        container = new Grid {
            Margin = new Thickness(0, 5, 0, 0),
            RowDefinitions = {
                new RowDefinition(),
                new RowDefinition(){Height = GridLength.Auto},
                new RowDefinition(){Height = GridLength.Auto}
            },
            ColumnDefinitions = {
                new ColumnDefinition(){Width = GridLength.Auto},
                new ColumnDefinition(),
                new ColumnDefinition(){Width = GridLength.Auto}
            },
            Children = { leftIcon, inputBox, hintBlock, invalidIcon, validIcon, separator, infoContainer }
        };
        AddVisualChild(container);
        initializeAnimations();
        inputBox.TextChanged += onInputTextChanged;
        Loaded += onLoaded;
    }
    void onLoaded(object sender, RoutedEventArgs e) {
        hintBlock.Text = Hint;
        if (Icon != null) {
            leftIcon.Data = Geometry.Parse(Icon);
            leftIcon.Visibility = Visibility.Visible;
        }
        if (IsRequired) {
            Grid.SetColumnSpan(inputBox, 1);
            errorBlock.Visibility = Visibility.Visible;
            invalidIcon.Visibility = Visibility.Visible;
        }
        if (MaxLength > 0) {
            inputBox.MaxLength = MaxLength;
            lengthBlock.Text = "0/" + MaxLength;
            lengthBlock.Visibility = Visibility.Visible;
        }
        if (IsMultiline) {
            inputBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            inputBox.Resources.Add(typeof(ScrollBar), App.multilineTextScrollStyle);
            inputBox.AcceptsReturn = true;
            inputBox.TextWrapping = TextWrapping.Wrap;
            inputBox.VerticalAlignment = VerticalAlignment.Top;
            leftIcon.VerticalAlignment = VerticalAlignment.Top;
            invalidIcon.VerticalAlignment = VerticalAlignment.Top;
            validIcon.VerticalAlignment = VerticalAlignment.Top;
            hintBlock.VerticalAlignment = VerticalAlignment.Top;
        }
        inputBox.SetBinding(TextBox.TextProperty, new Binding() {
            Path = new PropertyPath(nameof(Text)),
            RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(EditText), 1),
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        });
        errorBlock.SetBinding(TextBlock.TextProperty, new Binding() {
            Path = new PropertyPath(nameof(Error)),
            RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(EditText), 1)
        });
        if (IsTrimBottomRequested) {
            infoContainer.Visibility = Visibility.Collapsed;
        }
    }
    void initializeControls() {
        translateHint = new TranslateTransform();
        scaleHint = new ScaleTransform();
        hintBlock = new TextBlock {
            Padding = new Thickness(5, 0, 5, 0),
            Foreground = Brushes.Gray,
            Background = Brushes.White,
            HorizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment = VerticalAlignment.Center,
            RenderTransform = new TransformGroup {
                Children = { scaleHint, translateHint }
            }
        };
        inputBox = new TextBox {
            BorderThickness = new Thickness(0),
            Margin = new Thickness(5, 0, 5, 0),
            VerticalAlignment = VerticalAlignment.Bottom
        };
        separator = new Separator { Background = brush };
        leftIcon = new Path {
            Fill = brush,
            Width = 18,
            Height = 18,
            Stretch = Stretch.Uniform,
            HorizontalAlignment = HorizontalAlignment.Left,
            Visibility = Visibility.Collapsed
        };
        invalidIcon = new Path {
            Data = Geometry.Parse(Icons.Info),
            Fill = Brushes.Coral,
            Width = 16,
            Height = 16,
            Stretch = Stretch.Uniform,
            Visibility = Visibility.Collapsed,
            HorizontalAlignment = HorizontalAlignment.Right
        };
        validIcon = new Path {
            Data = Geometry.Parse(Icons.Checked),
            Fill = Brushes.Green,
            Width = 16,
            Height = 16,
            Stretch = Stretch.Uniform,
            Visibility = Visibility.Collapsed
        };
        Grid.SetRow(separator, 1);
        Grid.SetColumnSpan(separator, 3);
        Grid.SetColumn(inputBox, 1);
        Grid.SetColumnSpan(inputBox, 2);
        Grid.SetColumn(hintBlock, 1);
        Grid.SetColumn(invalidIcon, 2);
        Grid.SetColumn(validIcon, 2);
        errorBlock = new TextBlock {
            Foreground = Brushes.Gray,
            Margin = new Thickness(5, 0, 0, 0),
            TextWrapping = TextWrapping.Wrap,
            Visibility = Visibility.Hidden,
            FontSize = 11,
            FontStyle = FontStyles.Italic
        };
        lengthBlock = new TextBlock() {
            Foreground = Brushes.Gray,
            Visibility = Visibility.Collapsed,
            HorizontalAlignment = HorizontalAlignment.Right,
            FontSize = 11,
            FontStyle = FontStyles.Italic
        };
        Grid.SetColumn(lengthBlock, 1);
        infoContainer = new Grid() {
            ColumnDefinitions = {
                new ColumnDefinition(),
                new ColumnDefinition(){Width = GridLength.Auto}
            },
            Children = { errorBlock, lengthBlock }
        };
        Grid.SetRow(infoContainer, 2);
        Grid.SetColumnSpan(infoContainer, 3);
    }
    void initializeAnimations() {
        var duration = TimeSpan.FromMilliseconds(250);
        var ease = new CubicEase { EasingMode = EasingMode.EaseInOut };
        scaleHintAnim = new DoubleAnimation {
            Duration = duration,
            EasingFunction = ease
        };
        translateHintAnim = new DoubleAnimation {
            Duration = duration,
            EasingFunction = ease
        };
        brushAnim = new ColorAnimation {
            Duration = duration,
            EasingFunction = ease
        };
    }
    void onInputTextChanged(object sender, TextChangedEventArgs e) {
        lengthBlock.Text = inputBox.Text.Length + "/" + MaxLength;
    }
    void animateHint() {
        translateHint.BeginAnimation(TranslateTransform.YProperty, translateHintAnim);
        scaleHint.BeginAnimation(ScaleTransform.ScaleYProperty, scaleHintAnim);
        scaleHint.BeginAnimation(ScaleTransform.ScaleXProperty, scaleHintAnim);
    }
    void moveHintUp() {
        isHintMoved = true;
        scaleHintAnim.To = 0.9;
        translateHintAnim.To = -15;
        animateHint();
    }
    void moveHintDown() {
        isHintMoved = false;
        scaleHintAnim.To = 1;
        translateHintAnim.To = 0;
        animateHint();
    }
    #region Overrides
    protected override void OnMouseEnter(MouseEventArgs e) {
        if (string.IsNullOrWhiteSpace(inputBox.Text)) {
            inputBox.Focus();
            brushAnim.To = Colors.CornflowerBlue;
            brush.BeginAnimation(SolidColorBrush.ColorProperty, brushAnim);
        }
    }
    protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) {
        if (inputBox.Text.Length == 0 && !isHintMoved) moveHintUp();
        brushAnim.To = Colors.CornflowerBlue;
        brush.BeginAnimation(SolidColorBrush.ColorProperty, brushAnim);
    }
    protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e) {
        if (string.IsNullOrWhiteSpace(Text) && isHintMoved) moveHintDown();
        brushAnim.To = Colors.SkyBlue;
        brush.BeginAnimation(SolidColorBrush.ColorProperty, brushAnim);
    }
    protected override Size MeasureOverride(Size availableSize) {
        if (IsMultiline) {
            hintBlock.Measure(availableSize);
            separator.Measure(availableSize);
            leftIcon.Measure(availableSize);
            double height = 0d;
            if (double.IsInfinity(availableSize.Height)) height = 100;
            else {
                height = availableSize.Height - hintBlock.DesiredSize.Height - separator.DesiredSize.Height - Margin.Top;
                height = height > 0 ? height : 0;
            }
            inputBox.Height = height;
        }
        container.Width = availableSize.Width;
        container.Measure(availableSize);
        return container.DesiredSize;
    }
    protected override Size ArrangeOverride(Size finalSize) {
        container.Arrange(new Rect(container.DesiredSize));
        return finalSize;
    }
    protected override Visual GetVisualChild(int index) => container;
    protected override int VisualChildrenCount => 1;
    #endregion
    #region DependencyProperty
    public string Text {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
    public string Error {
        get { return (string)GetValue(ErrorProperty); }
        set { SetValue(ErrorProperty, value); }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(EditText), new FrameworkPropertyMetadata() {
            DefaultValue = null,
            BindsTwoWayByDefault = true,
            PropertyChangedCallback = onTextChanged
        });
    static void onTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var o = d as EditText;
        if (e.NewValue == null && o.Error == string.Empty ||
            e.NewValue == null && o.Error == null)
            o.OnPreviewLostKeyboardFocus(null);
        else {
            o.moveHintUp();
        }
    }
    public static readonly DependencyProperty ErrorProperty =
        DependencyProperty.Register("Error", typeof(string), typeof(EditText), new PropertyMetadata(null, onErrorChanged));
    static void onErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var o = d as EditText;
        var err = e.NewValue == null ? null : e.NewValue.ToString();
        if (err == string.Empty) {
            o.invalidIcon.Visibility = Visibility.Hidden;
            o.validIcon.Visibility = Visibility.Visible;
            o.errorBlock.Visibility = Visibility.Hidden;
        }
        else {
            o.invalidIcon.Visibility = Visibility.Visible;
            o.validIcon.Visibility = Visibility.Hidden;
            o.errorBlock.Visibility = Visibility.Visible;
        }
    }
    #endregion      
}