How to set the margin on a internal TextBoxView in

2019-06-18 03:55发布

问题:

I have a case where I want to minimize the horizontal padding of a textbox.

Using snoop I found that the textbox consists of a multiple sub-controls. One of them is a TextBoxView with a margin of 2,0,2,0

The TextBoxView is an internal wpf component and has no public API.

How would you approach getting rid of the "internal padding"??

回答1:

Set the outer margin to -2,0,-2,0 to compensate for the padding.



回答2:

I created a custom control that removes that internal padding.

public class MyTextBox : TextBox
{
    public MyTextBox()
    {
        Loaded += OnLoaded;
    }                 

    void OnLoaded(object sender, RoutedEventArgs e)
    {
        // the internal TextBoxView has a margin of 2,0,2,0 that needs to be removed
        var contentHost = Template.FindName("PART_ContentHost", this) as ScrollViewer;
        if (contentHost != null && contentHost.Content != null && contentHost.Content is FrameworkElement)
        {
            var textBoxView = contentHost.Content as FrameworkElement;
            textBoxView.Margin = new Thickness(0,0,0,0);
        }
    }       
}


回答3:

Here is a dirty way of doing it:

public static class TextBoxView
{
    public static readonly DependencyProperty MarginProperty = DependencyProperty.RegisterAttached(
        "Margin",
        typeof(Thickness?),
        typeof(TextBoxView),
        new PropertyMetadata(null, OnTextBoxViewMarginChanged));

    public static void SetMargin(TextBox element, Thickness? value)
    {
        element.SetValue(MarginProperty, value);
    }

    [AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
    [AttachedPropertyBrowsableForType(typeof(TextBox))]
    public static Thickness? GetMargin(TextBox element)
    {
        return (Thickness?)element.GetValue(MarginProperty);
    }

    private static void OnTextBoxViewMarginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBox = (TextBox)d;
        OnTextBoxViewMarginChanged(textBox, (Thickness?)e.NewValue);
    }

    private static void OnTextBoxViewMarginChanged(TextBox textBox, Thickness? margin)
    {
        if (!textBox.IsLoaded)
        {
            textBox.Dispatcher.BeginInvoke(
                DispatcherPriority.Loaded,
                new Action(() => OnTextBoxViewMarginChanged(textBox, margin)));
            return;
        }

        var textBoxView = textBox.NestedChildren()
                                 .SingleOrDefault(x => x.GetType().Name == "TextBoxView");
        if (margin == null)
        {
            textBoxView?.ClearValue(FrameworkElement.MarginProperty);
        }
        else
        {
            textBoxView?.SetValue(FrameworkElement.MarginProperty, margin);
        }
    }

    private static IEnumerable<DependencyObject> NestedChildren(this DependencyObject parent)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            yield return child;
            if (VisualTreeHelper.GetChildrenCount(child) == 0)
            {
                continue;
            }

            foreach (var nestedChild in NestedChildren(child))
            {
                yield return nestedChild;
            }
        }
    }
}

It allows setting the margin on textboxes:

<Style TargetType="{x:Type TextBox}">
    <Setter Property="demo:TextBoxView.Margin" Value="1,0" />
</Style>

Not optimized for performance at all.