Как я могу отменить изменения текста TextBox, вызванные привязкой? - PullRequest
4 голосов
/ 18 декабря 2010

У меня есть TextBox, к которому я привязал строку, если я теперь отредактирую текст вручную, я смогу отменить эти изменения с помощью TextBox.Undo(), если, однако, я изменю строку и текст TextBox обновится,не может отменить эти изменения, и свойство TextBox.CanUndo всегда будет false.Я полагаю, что это может быть связано с полной заменой текста, а не с его модификацией.

Любые идеи о том, как я могу заставить это работать?

Ответы [ 6 ]

7 голосов
/ 18 декабря 2010

ОК, начал оставлять комментарий и понял, что это был ответ:)

TextBox.Undo () предназначен для отмены взаимодействия пользователя с текстовым полем, а не изменения значения в свойстве, с которым он связан,Изменение свойства, к которому привязано текстовое поле, просто обновит значение TextBox, это изменение, отличное от редактирования пользователем с помощью фокуса / клавиатуры.Если вам нужно отменить изменения в связанных свойствах, вам, вероятно, нужно изучить возможность добавления стека Undo / Redo в ваше приложение.

6 голосов
/ 13 ноября 2016

Я столкнулся с той же проблемой (необходимо было принять ввод при Ввести и вернуться к исходному значению при Выход ) и смог справиться с этим следующим образом:

  1. Установите UpdateSourceTrigger вашей TextBox.Text привязки к Explicit.
  2. Обработайте KeyDown событие вашего TextBox и поместите туда следующий код:

    if (e.Key == Key.Enter || e.Key == Key.Escape)
    {
      BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
    
      if (e.Key == Key.Enter)
      {
        if (be != null) be.UpdateSource();
      }
      else if (e.Key == Key.Escape)
      {
        if (be != null) be.UpdateTarget(); //cancels newly supplied value and reverts to the original value
      }
    }
    

Мне показалось, что это решение очень элегантно, потому что его можно использовать и в DataTemplates. Например, в моем случае я использовал его, чтобы разрешить редактирование элементов ListBox на месте.

5 голосов
/ 01 июня 2011

Назначить непосредственно в TextBox:

textBox.SelectAll();
textBox.SelectedText = newText;
2 голосов
/ 18 декабря 2010

Я думаю, вы должны взглянуть на некоторые альтернативные подходы, такие как

http://blog.notifychanged.com/2009/01/30/using-the-viewmodel-pattern-to-provide-undo-redo-in-wpf/

Также допускается, что при разрешении текстового поля обрабатывать откат стека возникают проблемы с утечкой памяти.

http://www.infosysblogs.com/microsoft/2008/03/wpf_textbox_memory_leak_issue_1.html

1 голос
/ 04 января 2012

TextBox будет применять изменения к внутреннему стеку отмены, если они применяются таким образом, что они, похоже, исходят от пользователя, например:

        Clipboard.SetText("NewTextHere");
        TextBox.Paste();

Это ужасный обходной путь, так какэто убивает все, что пользователь имеет в буфере обмена (восстановление которого пессимистически обсуждается здесь: Как сделать резервную копию и восстановить системный буфер обмена в C #? ), но я подумал, что, тем не менее, стоит опубликовать.1006 *

0 голосов
/ 18 декабря 2010

Итак, я думаю, что статья ViewModel Undo / Redo хорошая, но она касается не только шаблона ViewModel, но и того, как написать пользовательскую функциональность Undo / Redo.Кроме того, в ответ на confusedGeek, я думаю, могут быть примеры, когда отмена изменений в вашей модели, а не только в ваших отдельных элементах управления, уместна (скажем, у вас есть текстовое поле и ползунок, оба привязаны к свойству примера, вы хотите отменить изменениенезависимо от того, какой элемент управления был создан, поэтому мы говорим об отмене уровня приложения вместо уровня управления).

Итак, учитывая, что это простой, если не несколько хитрый, пример выполнения именно того, что вы просите, используяCommandBinding и упрощенный стек отмены:

public partial class MainWindow : Window
{
    public static readonly DependencyProperty MyStringProperty =
        DependencyProperty.Register("MyString", typeof(String), typeof(MainWindow), new UIPropertyMetadata(""));

    // The undo stack
    Stack<String> previousStrings = new Stack<String>();
    String cur = ""; // The current textbox value
    Boolean ignore = false; // flag to ignore our own "undo" changes

    public String MyString
    {
        get { return (String)GetValue(MyStringProperty); }
        set { SetValue(MyStringProperty, value); }
    }

    public MainWindow()
    {
        InitializeComponent();
        this.LayoutRoot.DataContext = this;

        // Using the TextChanged event to add things to our undo stack
        // This is a kludge, we should probably observe changes to the model, not the UI
        this.Txt.TextChanged += new TextChangedEventHandler(Txt_TextChanged);

        // Magic for listening to Ctrl+Z
        CommandBinding cb = new CommandBinding();
        cb.Command = ApplicationCommands.Undo;
        cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        };

        cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e)
        {
            if (previousStrings.Count > 0)
            {
                ignore = true;
                this.Txt.Text = previousStrings.Pop();
                ignore = false;
            }

            e.Handled = true;
        };

        this.CommandBindings.Add(cb);
    }

    void Txt_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (!ignore)
        {
            previousStrings.Push(cur);
        }

        cur = this.Txt.Text;
    }

    private void SetStr_Click(object sender, RoutedEventArgs e)
    {
        this.MyString = "A Value";
    }
}

А вот XAML:

<Window x:Class="TestUndoBinding.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<StackPanel  Name="LayoutRoot">
    <TextBox Name="Txt" Text="{Binding Path=MyString, Mode=TwoWay}" />
    <Button Name="SetStr" Click="SetStr_Click">Set to "A Value"</Button>
</StackPanel>
</Window>

В этом примере поведение немного отличается от обычного поведения отмены TextBox, поскольку 1) яигнорируя выделение, и 2) я не группирую несколько нажатий клавиш в один шаг отмены, оба из которых вы бы хотели рассмотреть в реальном приложении, но должны быть относительно простыми для реализации самостоятельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...