选择一系列文本框

时间:2015-03-11 05:57:17

标签: c# wpf

我有一系列可选的TextBoxes。我需要能够单击其中一个并选择鼠标移动的所有文本框,直到释放单击。

我使用了以下代码但是我无法在其他TextBox上点击MouseMove。它总是击中发出Click的TextBox。

class SelectableTextBox: TextBox
{
    public Boolean IsSelected { get; protected set; }

    public void select(Boolean value)
    {
        this.IsSelected = value;
        if (value)
        {
            this.Background = System.Windows.Media.Brushes.Aqua;
        }
        else
        {
            this.Background = System.Windows.Media.Brushes.White;
        }
    }
}

private void onPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    SelectableTextBox textBox = (SelectableTextBox)sender;

    this.SelectionStartedRight = !textBox.IsSelected;
    textBox.select(!textBox.IsSelected);
}

private void onPreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
    SelectableTextBox textBox = (SelectableTextBox)sender;

    if (this.SelectionStartedRight)
    {
        textBox.select(true);
    }
}

private void onPreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    SelectableTextBox textBox = (SelectableTextBox)sender;

    this.SelectionStartedRight = false;
}

1 个答案:

答案 0 :(得分:4)

尝试使用MouseEnter事件代替MouseMove。将MouseEnter附加到您选择的文本框中。这将确保只触发所需的事件处理程序及其代码。

如果您决定继续使用全局处理程序,请在将sender转换为特定控件类型时小心。当不是“预期”控制时,你需要考虑那些时间:

SelectableTextBox textBox = sender as SelectableTextBox;

if (textBox != null)
{
    // The rest of the code here...
}

select是一个Linq关键字,因此您可能希望重命名该方法,以避免任何冲突。虽然我认为这不会引起任何问题,但我会改变它。

您不必遵循此约定,但在C#习惯使用大写字母作为方法的第一个字母。另请注意,类的默认访问修饰符为internal ...只是为了确保您知道这一点,因为您继续开发。

您的上一个方法onPreviewMouseLeftButtonUp(...)中包含以下代码:

SelectableTextBox textBox = (SelectableTextBox)sender;

正如我上面所描述的那样,它不仅不安全,而且它也绝对没有。

最后......这就是我,我可能会移动代码来处理将可选文本框(选中或未选中)的状态更改为其类,因为它属于那里。为什么其他任何东西应该负责它如何处理它的状态。保留他们所属的东西,您可以更轻松地测试,调试和维护代码。不要陷入“我稍后会重构它”陷阱......它很少会发生。

这是我粗略的例子。我让该类处理其MouseEnter事件,并检查当时Mouse.LeftButton是否已关闭。你必须扩展它,但它应该是一个坚实的开始:

在评论中对每个OP的请求进行了一些修改。

预览

enter image description here

<强> XAML:

<Window x:Class="SelectableTextBoxes.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SelectableTextBoxes"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <local:SelectableTextBox Height="20" Width="100" Margin="10"/>
        <local:SelectableTextBox Height="20" Width="100" Margin="10"/>
        <local:SelectableTextBox Height="20" Width="100" Margin="10"/>
        <local:SelectableTextBox Height="20" Width="100" Margin="10"/>
        <local:SelectableTextBox Height="20" Width="100" Margin="10"/>
    </StackPanel>
</Window>

C#(请原谅我将SelectableTextBox放入同一个文件中......):

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SelectableTextBoxes
{
    // Move this class into its own file (it's here for prototyping).
    public class SelectableTextBox : TextBox
    {
        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                if (_isSelected != value)
                {
                    _isSelected = value;
                    // If the value changes, sets new color.
                    SetSelectionColor();
                }
            }
        }

        public SelectableTextBox()
        {
            // For handling an initial click if it happens in the textbox.
            PreviewMouseDown += SelectableTextBox_PreviewMouseDown;
            // For handling selection when mouse enters the textbox
            // and left mouse button is down.
            MouseEnter += SelectableTextBox_MouseEnter;    
            // To handle mouse capture (release it).     
            GotMouseCapture += SelectableTextBox_GotMouseCapture;
        }

        // Handles the mouse down event within the textbox.
        void SelectableTextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!IsSelected)
            {
                IsSelected = true;
            }

            // If one of the Shift keys is down, return, since
            // we don't want to deselect others.
            if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {
                return;
            }

            // This part makes a poor assumption that the parent is
            // always going to be a Panel... expand on this code to
            // cover other types that may contain more than one element.
            var parent = VisualTreeHelper.GetParent(this) as Panel;

            if (parent != null)
            {
                foreach (var child in parent.Children)
                {
                    // If a child is not of a correct type, it'll be null.
                    var tbx = child as SelectableTextBox;
                    // This is where we check to see if it's null or this instance.
                    if (tbx != null && tbx != this)
                    {
                        tbx.IsSelected = false;
                    }
                }
            }
        }

        // When textbox receives focus, this event fires... we need to release
        // the mouse to continue selection.
        void SelectableTextBox_GotMouseCapture(object sender, MouseEventArgs e)
        {
            ReleaseMouseCapture();
        }

        // Sets selection state to true if the left mouse button is 
        // down while entering.
        void SelectableTextBox_MouseEnter(object sender, MouseEventArgs e)
        {
            if (Mouse.LeftButton == MouseButtonState.Pressed)
            {
                IsSelected = true;
            }
        }

        // Sets the background color based on selection state.
        private void SetSelectionColor()
        {
            if (IsSelected)
            {
                Background = Brushes.LightCyan;
            }
            else
            {
                Background = Brushes.White;
            }
        }
    }

    // Window code... should be on its own, but I placed the two
    // classes together while prototyping.
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}