如何(完全)在ListBox中实现就地编辑?

时间:2011-08-03 08:47:03

标签: wpf mvvm

我正在构建一个应用程序,其中ListBox正在显示其项目的Description属性。我想实现与Windows资源管理器中编辑文件名时相同的编辑就地功能,我发现这是一项非常多的工作。

到目前为止我所拥有的是一个ContextMenu来启动编辑。它绑定到视图模型中设置IsEditingDescription属性的命令。对项目模板进行样式设置,以便DescriptionTextBlock为假时显示IsEditingDescription,在TextBoxIsEditingDescription时显示Description。设置说明后,IsEditingDescription上的设置器将{{1}}设置为false。

这非常有效。但有些事情并没有做到这一点:

  • 用户应该可以按F2启动编辑。
  • 用户应该可以通过按ESC取消编辑。
  • 用户应该可以按ENTER确认编辑。
  • 当用户第二次点击当前选定的项目时,它应该启动编辑。
  • 出现文本框时应选择说明文字

我认为我可以使用命令和键绑定处理前三项,但我还不知道如何进行键绑定。但我真的无法找到MVVMish的方法去做其他两个。有吗?

2 个答案:

答案 0 :(得分:2)

这是我不得不反复做的事情所以我决定扩展ListView控件并添加ItemKeyDown,ItemKeyUp和ItemDoubleClick事件,这些事件在ListView中的项目处于焦点时发生这些事件。 ListView是一个ListBox派生的,所以你应该能够轻松地移植它。

<强> /Fx/ListView.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

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

namespace Fx
{
    public static class Func
    {
        /// <summary>
        /// Finds a specific type of parent up the visual tree.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dep"></param>
        /// <returns></returns>
        public static T FindParent<T>(this object obj)
        {
            DependencyObject dep = obj as DependencyObject;
            while ((dep != null) && !(dep is T))
            {
                dep = VisualTreeHelper.GetParent(dep);
            }
            return (dep != null) ? (T)Convert.ChangeType(dep, typeof(T)) : default(T);
        }
    }

    public class ListView:System.Windows.Controls.ListView
    {

        #region public event KeyboardEventHandler ItemKeyDown;
        /// <summary>
        /// Occurs when a key is pressed when a ListViewItem in this
        /// ListView is in focus.
        /// </summary>
        public event KeyEventHandler ItemKeyDown;

        /// <summary>
        /// Raises the ItemKeyDown event for a ListViewitem in this ListView.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="e"></param>
        public void OnItemKeyDown(ListViewItem item, KeyEventArgs e)
        {
            if (ItemKeyDown != null)
                ItemKeyDown(item, e);
        }

        /// <summary>
        /// Handle they KeyDown event on the ListView, find the related
        /// ListViewItem and raise the ItemKeyDown event respectively.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListView_KeyDown(object sender, KeyEventArgs e)
        {
            ListViewItem item = Func.FindParent<ListViewItem>(e.OriginalSource);
            if (item != null)
                OnItemKeyDown(item, e);
        }
        #endregion

        #region public event KeyboardEventHandler ItemKeyUp;
        /// <summary>
        /// Occurs when a key is released when a ListViewItem in this
        /// ListView is in focus.
        /// </summary>
        public event KeyEventHandler ItemKeyUp;

        /// <summary>
        /// Raises the ItemKeyUp event for a ListViewitem in this ListView.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="e"></param>
        public void OnItemKeyUp(ListViewItem item, KeyEventArgs e)
        {
            if (ItemKeyUp != null)
                ItemKeyUp(item, e);
        }

        /// <summary>
        /// Handle they KeyUp event on the ListView, find the related
        /// ListViewItem and raise the ItemKeyUp event respectively.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListView_KeyUp(object sender, KeyEventArgs e)
        {
            ListViewItem item = Func.FindParent<ListViewItem>(e.OriginalSource);
            if (item != null)
                OnItemKeyUp(item, e);
        }
        #endregion

        #region public event MouseButtonEventHandler ItemDoubleClick;
        /// <summary>
        /// Occurs when a ListViewItem in this Listview is double clicked.
        /// </summary>
        public event MouseButtonEventHandler ItemDoubleClick;

        /// <summary>
        /// Raise the ItemDoubleClick event for a ListViewItem.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="e"></param>
        public void OnItemDoubleClick(ListViewItem item, MouseButtonEventArgs e)
        {
            if (ItemDoubleClick != null)
                ItemDoubleClick(item, e);
        }

        /// <summary>
        /// Handle the MouseDoubleClick event for the ListView, find the related
        /// ListViewItem and raise the ItemDoubleClick event respectively.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            ListViewItem item = Func.FindParent<ListViewItem>(e.OriginalSource);
            if (item != null)
                OnItemDoubleClick(item, e);
        }
        #endregion

        public ListView()
        {
            MouseDoubleClick += new MouseButtonEventHandler(ListView_MouseDoubleClick);
            KeyDown += new KeyEventHandler(ListView_KeyDown);
            KeyUp += new KeyEventHandler(ListView_KeyUp);
        }
    }
}

现在用它...... 的 /Pages/EmployeesPage.xaml

<UserControl x:Class="TestApp.Pages.EmployeesPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:fx="clr-namespace:Fx"
             Width="800" Height="450">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="300" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Column="0">
            <TextBlock Text="List Items" FontWeight="Bold" FontSize="18" />

            <!-- here is the main part -->
            <fx:ListView x:Name="EmployeesList" ItemDoubleClick="EmployeesList_ItemDoubleClick" ItemKeyDown="EmployeesList_ItemKeyDown" />
            <!-- main part ends here -->

        </StackPanel>
    </Grid>
</UserControl>

<强> /Pages/EmployeesPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace TestApp.Pages
{
    /// <summary>
    /// Interaction logic for EmployeesPage.xaml
    /// </summary>
    public partial class EmployeesPage : UserControl
    {
        public EmployeesPage()
        {
            InitializeComponent();

            // Fill the ListView with data...
            // EmployeesList.ItemsSource = SomeObservableCollectionOrDataSource
        }

        private void EmployeesList_ItemDoubleClick(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("an item was double clicked");

            ListViewItem item = sender as ListViewItem;
            // entityType obj = item.DataContext as entityType

            // you can begin editing here
        }

        private void EmployeesList_ItemKeyDown(object sender, KeyEventArgs e)
        {
            MessageBox.Show(e.Key.ToString() + " key was pressed on an item");

            ListViewItem item = sender as ListViewItem;
            // entityType obj = item.DataContext as entityType

            if (e.Key == Key.F2)
            {
                // begin editing here
            }
            else if (e.Key == Key.Enter)
            {
                // end editing here
            }
            else if (e.Key == Key.Escape)
            {
                // cancel editing here
            }
        }
    }
}

希望这至少对你有用..祝你好运。

答案 1 :(得分:0)

至于确保在进入编辑模式时选择文本,我之前使用了以下附加到我的另一个项目中的文本框的行为。它在VB中,但应该非常简单。

Public Class SelectAllOnFocusTextboxBehavior
    Inherits Behavior(Of TextBox)

    Protected Overrides Sub OnAttached()
        MyBase.OnAttached()
        AddHandler AssociatedObject.GotKeyboardFocus, AddressOf AssociatedObjectGotKeyboardFocus

    End Sub
    Protected Overrides Sub OnDetaching()
        MyBase.OnDetaching()
        RemoveHandler AssociatedObject.GotKeyboardFocus, AddressOf AssociatedObjectGotKeyboardFocus

    End Sub

    Private Sub AssociatedObjectGotKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
        AssociatedObject.SelectAll()
    End Sub

End Class