【问题标题】:MVVM- How can I select text in a textbox?MVVM-如何在文本框中选择文本?
【发布时间】:2011-02-05 12:11:09
【问题描述】:

是否有一种 MVVM 方法来选择文本框中的文本?我使用的 MVVM 框架是 Laurent Bugnion 的 MVVM Light Toolkit。

【问题讨论】:

    标签: c# wpf mvvm textbox mvvm-light


    【解决方案1】:

    每当我试图直接影响“纯”MVVM 应用程序中的视图(视图中没有代码隐藏)时,我将使用Attached Properties 来封装我想要实现的任何效果。我将创建一个界面来定义我希望使用自定义事件执行的操作。然后我在每个将在视图上“运行”这些命令的视图模型中实现这个接口。最后,我将 ViewModel 绑定到 View 定义中的附加属性。以下代码显示了如何为 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(参见 this page 上的图 3 中的代码)。在本例中,它在 MyViewModel 中使用(以及我几乎所有的 MVVM 代码)。

    【讨论】:

    • 我收到一个错误:“Delegate 'System.Action' 不接受 1 个参数”?
    • 错误在哪里?确保事件处理程序定义中的参数数量与您定义处理程序(并使用它)的方式一致。在我的示例中,我使用 Lambda 和 RelayCommand 来引发事件。
    • 我有一个中继命令,就像你的例子和我的视图模型的构造函数中的这段代码: SelectAllCommand = new RelayCommand(p => SelectAll(this) 错误在 lambda 表达式中的 p . 顺便说一句,感谢您继续帮助我。
    • 啊哈!我刚刚查看了 RelayCommand 的 MVVM Light 定义。它不需要参数(我使用的 Josh Smith 的参数)。这意味着你只需要定义你的 lambda 而不需要这样的参数: SelectAllCommand = new RelayCommand(() => { if(SelectAll != null) SelectAll(this); } )
    • 要注意的另一件事:如果您引发 SelectAll 事件而不检查它是否为空(如您在评论中显示的那样),如果没有人注册监听该事件,您将面临 NullReferenceException .
    【解决方案2】:

    在此处找到有关附加属性的良好介绍: http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-18
      • 2014-03-03
      • 2011-01-28
      • 1970-01-01
      相关资源
      最近更新 更多