Why WPF PasswordBox is not scrollable

226 Views Asked by At

Can someone explain to me why the PasswordBox control is not scrollable.

Preview

As you can see when mouse cursor comes on PasswordBox, scrolling not work. I added default and custom PasswordBox, both had the same behavior.

This a sample project that I created for better demonstration.

<Window 
    x:Class="PasswordBoxDemo.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"
    mc:Ignorable="d"
    Title="MainWindow" 
    Height="450"
    Width="600"
    WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Padding" Value="12,8"/>
            <Setter Property="Text" Value="Sample text"/>
        </Style>

        <Style TargetType="{x:Type PasswordBox}">
            <Setter Property="Padding" Value="12,8"/>
        </Style>
    </Window.Resources>
    <ScrollViewer>
        <StackPanel Margin="10" Height="800">
            <TextBlock Margin="16" FontSize="32" TextWrapping="Wrap">
                Put your mouse on password box area and try to scroll by mouse wheel.
            </TextBlock>
            <Label>Text:</Label>
            <TextBox />
            <Label>Password:</Label>
            <PasswordBox Password="123456"/>
            <Label>Text:</Label>
            <TextBox />
            <Label>Text:</Label>
            <TextBox />
            <Label>Password:</Label>
            <PasswordBox Password="123456"/>
            <Label>Text:</Label>
            <TextBox />
            <Label>Text:</Label>
            <TextBox />
        </StackPanel>
    </ScrollViewer>
</Window>

2

There are 2 best solutions below

0
On BEST ANSWER

This looks like the scroll mouse event is suppressed on the password box internally. You could do something like setting IsHitTestVisible to false, however this would stop other mouse events from happening as well.

If you just need a no frills work around, add a handler for the PreviewMouseWheel event like so:

<PasswordBox PreviewMouseWheel="PasswordBox_PreviewMouseWheel" />

Give your parent ScrollViewer a name:

<ScrollViewer Name="scrollViewer1">

Then in code implement the handler callback something like this:

private void PasswordBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
    if (e.Delta != 0)
    {
        scrollViewer1.ScrollToVerticalOffset(scrollViewer1.VerticalOffset - e.Delta);
    }
    e.Handled = true;
}

It's a big ugly, but works.

0
On

I put another way to adding scrolling behavior to PasswordBox control. I think this way is cleaner.

public class PasswordBoxAssist
{
    public static ScrollViewer GetParentScrollViewer(DependencyObject obj)
        => (ScrollViewer)obj.GetValue(ParentScrollViewerProperty);

    public static void SetParentScrollViewer(DependencyObject obj, ScrollViewer value)
        => obj.SetValue(ParentScrollViewerProperty, value);

    public static readonly DependencyProperty ParentScrollViewerProperty =
        DependencyProperty.RegisterAttached("ParentScrollViewer", typeof(ScrollViewer),
            typeof(PasswordBoxAssist), new UIPropertyMetadata(default, OnParentScrollViewerChanged));

    private static void OnParentScrollViewerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var passwordBox = (PasswordBox)d;
        var scrollViewer = (ScrollViewer)e.NewValue;

        if (scrollViewer is null)
            passwordBox.PreviewMouseWheel -= OnPasswordBoxPreviewMouseWheel;
        else
            passwordBox.PreviewMouseWheel += OnPasswordBoxPreviewMouseWheel;
    }

    private static void OnPasswordBoxPreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        var passwordBox = (PasswordBox)sender;
        var scrollViewer = GetParentScrollViewer(passwordBox);

        if (e.Delta != 0)
            scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);

        e.Handled = true;
    }
}
<ScrollViewer x:Name="SampleScrollViewer">
    <PasswordBox 
        [...]
        local:PasswordBoxAssist.ParentScrollViewer="{Binding ElementName=SampleScrollViewer}"
        [...]/>
</ScrollViewer>