为什么WPF PasswordBox无法滚动

问题描述

有人可以向我解释为什么PasswordBox控件不可滚动。

Preview

如您所见,当鼠标光标移到PasswordBox上时,滚动不起作用。我添加认和自定义的PasswordBox,两者的行为相同。

这是我创建的一个示例项目,用于更好的演示。

<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>

解决方法

这似乎在内部抑制了密码框中的滚动鼠标事件。您可以执行将IsHitTestVisible设置为false的操作,但这也会阻止其他鼠标事件的发生。

如果您不需要任何多余的装饰,请为PreviewMouseWheel事件添加处理程序,如下所示:

<PasswordBox PreviewMouseWheel="PasswordBox_PreviewMouseWheel" />

为您的父级ScrollViewer命名:

<ScrollViewer Name="scrollViewer1">

然后在代码中实现处理程序回调,如下所示:

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

这很丑陋,但是可行。

,

我提出了另一种方法来向PasswordBox控件添加滚动行为。我认为这样比较干净。

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>