我有TextBox
我绑定了一个字符串,如果我现在手动编辑文本我将能够通过TextBox.Undo()
撤消这些更改,但是如果我更改了字符串和TextBox的文本已更新,我无法撤消这些更改,TextBox.CanUndo
属性将始终为false
我想这可能与完全替换文本有关,而不是修改它。
关于如何让它发挥作用的任何想法?
答案 0 :(得分:6)
好的,开始发表评论并意识到这是一个答案:)
TextBox.Undo()旨在撤消用户与文本框的交互,而不是其绑定的属性中的值更改。文本框绑定的属性更改只会更新TextBox的值,这与用户通过焦点/键盘编辑的更改不同。如果您需要撤消对绑定属性的更改,则可能需要调查向应用程序添加撤消/重做堆栈。
答案 1 :(得分:6)
我遇到了同样的问题(需要在 Enter 上接受输入并在 Escape 上恢复为原始值)并且能够以这种方式处理它:
UpdateSourceTrigger
绑定的TextBox.Text
设置为Explicit
。处理TextBox的KeyDown
事件并将以下代码放在其中:
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项目的就地编辑。
答案 2 :(得分:5)
直接分配给TextBox:
textBox.SelectAll();
textBox.SelectedText = newText;
答案 3 :(得分:2)
我认为你必须考虑一些替代方法,比如
http://blog.notifychanged.com/2009/01/30/using-the-viewmodel-pattern-to-provide-undo-redo-in-wpf/
同样允许文本框处理undostack似乎有一些内存泄漏问题,请参阅此处
http://www.infosysblogs.com/microsoft/2008/03/wpf_textbox_memory_leak_issue_1.html
答案 4 :(得分:1)
TextBox会将更改应用于内部撤消堆栈,如果它们的应用方式似乎来自用户,如下所示:
Clipboard.SetText("NewTextHere");
TextBox.Paste();
这是一个糟糕的解决方法,因为它会杀死用户在剪贴板上的任何内容(在此处对其进行悲观的讨论:How do I backup and restore the system clipboard in C#?)但我认为尽管如此仍然值得发布。
答案 5 :(得分:0)
所以,我认为ViewModel Undo / Redo文章很好,但它与ViewModel模式一样多,因为它与如何编写自定义撤消/重做功能有关。另外,为了回应confusedGeek,我认为可能有一些示例可以撤消模型中的更改,而不仅仅是在您的单个控件中(例如,您有一个文本框和滑块都绑定到示例属性,您要撤消更改)无论采用哪种控制方式,我们都在谈论应用级别撤销而不是控制级别。
所以考虑到这一点,这里有一个简单的,如果不是有点像kludgey的例子,使用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)我没有将多个击键分组到一个撤销步骤中,这两个都是你想要的东西考虑一个真实的应用程序,但应该相对简单地实现自己。