MVVM- 我该如何在文本框中选择文本?

14

在使用Laurent Bugnion的MVVM Light Toolkit框架时,是否有一种MVVM方式来选择文本框中的文本?

2个回答

20
每当我尝试在“纯”MVVM应用程序中直接影响视图(没有视图中的代码后台),我会使用附加属性来封装我想要实现的效果。我将创建一个定义了我想要采取的操作的接口,使用自定义事件。然后,我在将运行这些命令的每个ViewModel中实现此接口。最后,在我的View定义中将ViewModel绑定到附加属性。以下代码显示了如何为SelectAll和TextBox执行此操作。这段代码可以轻松扩展以对视图中的任何组件执行任何操作。
我的附加属性和接口定义:
using System.Windows;
using System.Windows.Controls;
using System;
using System.Collections.Generic;

namespace SelectAllSample
{
    public static class TextBoxAttach
    {
        public static readonly DependencyProperty TextBoxControllerProperty = DependencyProperty.RegisterAttached(
            "TextBoxController", typeof(ITextBoxController), typeof(TextBoxAttach),
            new FrameworkPropertyMetadata(null, OnTextBoxControllerChanged));
        public static void SetTextBoxController(UIElement element, ITextBoxController value)
        {
            element.SetValue(TextBoxControllerProperty, value);
        }
        public static ITextBoxController GetTextBoxController(UIElement element)
        {
            return (ITextBoxController)element.GetValue(TextBoxControllerProperty);
        }

        private static readonly Dictionary<ITextBoxController, TextBox> elements = new Dictionary<ITextBoxController, TextBox>();
        private static void OnTextBoxControllerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as TextBox;
            if (element == null)
                throw new ArgumentNullException("d");

            var oldController = e.OldValue as ITextBoxController;
            if (oldController != null)
            {
                elements.Remove(oldController);
                oldController.SelectAll -= SelectAll;
            }

            var newController = e.NewValue as ITextBoxController;
            if (newController != null)
            {
                elements.Add(newController, element);
                newController.SelectAll += SelectAll;
            }
        }
        private static void SelectAll(ITextBoxController sender)
        {
            TextBox element;
            if (!elements.TryGetValue(sender, out element))
                throw new ArgumentException("sender");
            element.Focus();
            element.SelectAll();
        }
    }

    public interface ITextBoxController
    {
        event SelectAllEventHandler SelectAll;
    }

    public delegate void SelectAllEventHandler(ITextBoxController sender);
}

我的ViewModel定义:

public class MyViewModel : ITextBoxController
{
    public MyViewModel()
    {
        Value = "My Text";
        SelectAllCommand = new RelayCommand(p =>
        {
            if (SelectAll != null)
                SelectAll(this);
        });
    }

    public string Value { get; set; }
    public RelayCommand SelectAllCommand { get; private set; }

    public event SelectAllEventHandler SelectAll;
}

我的视图定义:

<Window x:Class="SelectAllSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:loc="clr-namespace:SelectAllSample"
    Title="Window1" Height="150" Width="150">
    <x:Code><![CDATA[
        public Window1()
        {
            InitializeComponent();
            DataContext = new MyViewModel();
        }
    ]]></x:Code>
    <StackPanel>
        <TextBox Text="{Binding Value}" loc:TextBoxAttach.TextBoxController="{Binding}" />
        <Button Content="Select All" Command="{Binding SelectAllCommand}" />
    </StackPanel>
</Window>

注意:感谢Josh Smith提供的RelayCommand(见此页面上图3中的代码)。在本示例中(以及我几乎所有的MVVM代码中)都使用了它。


1
错误出在哪里?请确保您事件处理程序定义中的参数数量与您定义和使用处理程序的方式一致。我在我的示例中使用Lambda和RelayCommand来触发事件。 - Joseph Sturtevant
1
啊哈!我刚刚查看了MVVM Light中RelayCommand的定义。它不需要参数(我使用的是Josh Smith的,它需要参数)。这意味着你只需要像这样定义lambda而不需要参数:SelectAllCommand = new RelayCommand(() => { if(SelectAll != null) SelectAll(this); } ) - Joseph Sturtevant
1
另一个需要注意的事情是:如果你在不检查它是否为空的情况下引发了SelectAll事件(就像你在评论中展示的那样),那么如果没有人注册来监听该事件,你就会面临NullReferenceException的风险。 - Joseph Sturtevant
一种优雅的解决方案是定义两个附加属性(SelectionStart,SelectionEnd),这样可以简化过程很多。我理解您正在尝试在控制器中封装尽可能多的文本框功能,但如果您想添加新功能,我担心它的可维护性。 - Gusdor
我知道这是一个问题,但我希望还有人能回答! :)我如何调整此解决方案,以便我可以有两个不同的按钮来选择两个不同文本框中的文本?谢谢。 - Andy
显示剩余5条评论

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接