Nas*_*adi 4 .net c# wpf binding avalonedit
我在我的项目中使用了AvalonEdit控件.当我使用Ctrl + C或Ctrl + V等快捷键时,相关的复制/粘贴命令可以正常工作.我决定在上下文菜单中使用这些命令以获得更多可用性,因为有些用户习惯于右键单击而不是快捷方式.我使用以下XAML代码进行控制:
<avalonedit:TextEditor.ContextMenu>
<ContextMenu>
<MenuItem Command="Undo" />
<MenuItem Command="Redo" />
<Separator/>
<MenuItem Command="Cut" />
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
</ContextMenu>
</avalonedit:TextEditor.ContextMenu>
Run Code Online (Sandbox Code Playgroud)
但是当我运行程序时,这些命令总是在上下文菜单中显示为禁用,如下所示:

当我第一次遇到这个问题时,我发布了一个不同的问题但是在MD.Unicorn的帮助下(正如你在下面的评论中看到的)我意识到当你将AvalonEdit放在ListBox或ListView命令的ItemTemplate中时不起作用.
在MD.unicorn的帮助下,我创建了以下测试代码来重现结果:
ViewModel类和数据模板的简单类
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
collection = new ObservableCollection<myClass>();
mc = new myClass();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
var h = PropertyChanged;
if (h != null)
h(this, new PropertyChangedEventArgs(propName));
}
public ObservableCollection<myClass> collection { get; set; }
public myClass mc { get; set; }
}
public class myClass
{
public string text { get; set; }
}
public partial class MainWindow : Window
{
MyViewModel _viewModel = new MyViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = _viewModel;
}
}
Run Code Online (Sandbox Code Playgroud)
和MainWindow的XAML代码
<Window.Resources>
<DataTemplate DataType="{x:Type local:myClass}">
<StackPanel>
<avalonedit:TextEditor x:Name="xmlMessage"
SyntaxHighlighting="XML" ShowLineNumbers="True" >
<avalonedit:TextEditor.ContextMenu>
<ContextMenu>
<MenuItem Command="Undo" />
<MenuItem Command="Redo" />
<Separator/>
<MenuItem Command="Cut" />
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
</ContextMenu>
</avalonedit:TextEditor.ContextMenu>
</avalonedit:TextEditor>
<TextBox Text="test" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<ListView ItemsSource="{Binding collection}" />
<ContentControl Content="{Binding mc}" />
</DockPanel>
Run Code Online (Sandbox Code Playgroud)
如果您尝试此测试,您可以看到如果在内容控件上使用DataTemplate,它在上下文菜单中的命令绑定工作正常,但在ListViewItem中它们被禁用.另请注意,DataTemplate中的上下文菜单适用于TextBox,并显示ListView本身并不会破坏命令链.
如何修复上下文菜单并连接到listView项目中的控制命令?
这就是我过去遇到的类似问题 - 我希望它对人们有用(这个通用逻辑可以应用于各种各样的Avalon编辑器相关问题)......
实际发生的可能是Avalon的错(与之结合ListItem等).它弄乱了鼠标操作,我猜测焦点(应该在TextArea命令和CanExecute工作上.
该
mouse handling是问题-因为如果你只需要按下windows context menu按键就弹出启用命令的普通菜单.Avalon编辑器有一个复杂的鼠标/键处理(很难做出一个好的编辑器) - 而在键盘上它focus在TextArea上做了一个明确的' '.您还可以通过在CanCutOrCopy方法上设置断点 (Editing/EditingCommandHandler.cs,下载Avalon源)来实际处理问题ApplicationCommands.Copy.对于"键盘"菜单,它首先进入,然后弹出.对于'鼠标',它会弹出 - 然后在退出时检查CanExecute(输入该方法).那都错了!
和勘误......
您自己的命令没有问题,只需正常公开您的命令,一切都应该有效.
对于ApplicationCommands(即RoutedCommand)它没有正确接线 - 并且Execute,CanExecute不要去它应该的地方,即TextArea.要纠正你需要rewire将命令放入你自己的包装器 - 并且基本上调用TextArea处理 - 这只是几行代码,但这是一个必要的步骤(我不认为有一个更'美丽'的解决方案,没有修复Avalon代码 - 这可能是一种痛苦,从未在我脑海中浮现.
(所有都是基于你的例子 - 填写我遗漏的空白)你的XAML:
<Window.Resources>
<DataTemplate DataType="{x:Type my:myClass}">
<StackPanel>
<my:AvalonTextEditor x:Name="xmlMessage" SyntaxHighlighting="XML" ShowLineNumbers="True" EditText="{Binding text}" >
<my:AvalonTextEditor.ContextMenu>
<ContextMenu x:Name="mymenu1">
<ContextMenu.Resources>
<Style TargetType="MenuItem">
<Setter Property="CommandParameter" Value="{Binding Path=., RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</Style>
</ContextMenu.Resources>
<MenuItem Header="My Copy" Command="{Binding CopyCommand}" />
<MenuItem Header="My Paste" Command="{Binding PasteCommand}" />
<MenuItem Header="My Cut" Command="{Binding CutCommand}" />
<MenuItem Header="My Undo" Command="{Binding UndoCommand}" />
<MenuItem Header="My Redo" Command="{Binding RedoCommand}" />
<Separator />
<MenuItem Command="Undo" />
<MenuItem Command="Redo" />
<Separator/>
<MenuItem Command="Cut" />
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
</ContextMenu>
</my:AvalonTextEditor.ContextMenu>
</my:AvalonTextEditor>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<DockPanel>
<ListView ItemsSource="{Binding collection}" />
<ContentControl Content="{Binding mc}" />
</DockPanel>
</StackPanel>
Run Code Online (Sandbox Code Playgroud)
后面的代码 - 视图模型:(
注意:我按照你的命名离开了命名 - 但请不要使用小型大写的道具:)
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MyViewModel()
{
collection = new ObservableCollection<myClass>(new[]
{
new myClass{ text = "some more test - some more test - some more test - some more test - some more test - some more test - some more test - some more test - some more test - " },
new myClass{ text = "test me test me = test me test me = test me test me = test me test me = test me test me = test me test me = " },
new myClass{ text = "test again - test again - test again - test again - test again - " },
new myClass{ text = "test again - test again - " },
new myClass{ text = "test again - " },
new myClass{ text = "test" },
});
mc = new myClass();
}
public ObservableCollection<myClass> collection { get; set; }
public myClass mc { get; set; }
}
public class myClass
{
public string text { get; set; }
AvalonRelayCommand _copyCommand;
public AvalonRelayCommand CopyCommand
{ get { return _copyCommand ?? (_copyCommand = new AvalonRelayCommand(ApplicationCommands.Copy) { Text = "My Copy" }); } }
AvalonRelayCommand _pasteCommand;
public AvalonRelayCommand PasteCommand
{ get { return _pasteCommand ?? (_pasteCommand = new AvalonRelayCommand(ApplicationCommands.Paste) { Text = "My Paste" }); } }
AvalonRelayCommand _cutCommand;
public AvalonRelayCommand CutCommand
{ get { return _cutCommand ?? (_cutCommand = new AvalonRelayCommand(ApplicationCommands.Cut) { Text = "My Cut" }); } }
AvalonRelayCommand _undoCommand;
public AvalonRelayCommand UndoCommand
{ get { return _undoCommand ?? (_undoCommand = new AvalonRelayCommand(ApplicationCommands.Undo) { Text = "My Undo" }); } }
AvalonRelayCommand _redoCommand;
public AvalonRelayCommand RedoCommand
{ get { return _redoCommand ?? (_redoCommand = new AvalonRelayCommand(ApplicationCommands.Redo) { Text = "My Redo" }); } }
}
Run Code Online (Sandbox Code Playgroud)
(注意:只需连接Window.DataContext到视图模型,就像你所做的那样)
这需要两个自定义类来包装.
public class AvalonTextEditor : TextEditor
{
#region EditText Dependency Property
public static readonly DependencyProperty EditTextProperty =
DependencyProperty.Register(
"EditText",
typeof(string),
typeof(AvalonTextEditor),
new UIPropertyMetadata(string.Empty, EditTextPropertyChanged));
private static void EditTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
AvalonTextEditor editor = (AvalonTextEditor)sender;
editor.Text = (string)e.NewValue;
}
public string EditText
{
get { return (string)GetValue(EditTextProperty); }
set { SetValue(EditTextProperty, value); }
}
#endregion
#region TextEditor Property
public static TextEditor GetTextEditor(ContextMenu menu) { return (TextEditor)menu.GetValue(TextEditorProperty); }
public static void SetTextEditor(ContextMenu menu, TextEditor value) { menu.SetValue(TextEditorProperty, value); }
public static readonly DependencyProperty TextEditorProperty =
DependencyProperty.RegisterAttached("TextEditor", typeof(TextEditor), typeof(AvalonTextEditor), new UIPropertyMetadata(null, OnTextEditorChanged));
static void OnTextEditorChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
ContextMenu menu = depObj as ContextMenu;
if (menu == null || e.NewValue is DependencyObject == false)
return;
TextEditor editor = (TextEditor)e.NewValue;
NameScope.SetNameScope(menu, NameScope.GetNameScope(editor));
}
#endregion
public AvalonTextEditor()
{
this.Loaded += new RoutedEventHandler(AvalonTextEditor_Loaded);
}
void AvalonTextEditor_Loaded(object sender, RoutedEventArgs e)
{
this.ContextMenu.SetValue(AvalonTextEditor.TextEditorProperty, this);
}
}
public class AvalonRelayCommand : ICommand
{
readonly RoutedCommand _routedCommand;
public string Text { get; set; }
public AvalonRelayCommand(RoutedCommand routedCommand) { _routedCommand = routedCommand; }
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
public bool CanExecute(object parameter) { return _routedCommand.CanExecute(parameter, GetTextArea(GetEditor(parameter))); }
public void Execute(object parameter) { _routedCommand.Execute(parameter, GetTextArea(GetEditor(parameter))); }
private AvalonTextEditor GetEditor(object param)
{
var contextMenu = param as ContextMenu;
if (contextMenu == null) return null;
var editor = contextMenu.GetValue(AvalonTextEditor.TextEditorProperty) as AvalonTextEditor;
return editor;
}
private static TextArea GetTextArea(AvalonTextEditor editor)
{
return editor == null ? null : editor.TextArea;
}
}
Run Code Online (Sandbox Code Playgroud)
笔记:
EditText只是一个依赖属性 - 能够bind文本(你的text) - 这是Avalon的缺点.这里只是为了好玩,但你可能需要它,所以我把它留在了.
使用AvalonRelayCommand重新布线应用路由命令-用于其他的东西用自己的命令执行.这两个类是核心.
您需要使用AvalonTextEditor而不是TextEditor - 这只是一个小包装 - 来ContextMenu与TextEditor(除了其他问题,菜单项suffering来自缺乏visual tree- 并且您无法轻易获得任何控件).我们需要TextEditor从CommandParameter(它设置为a ContextMenu)得到一个ref .这可以通过一些附件属性(没有覆盖TextEditor)完成,但这样看起来更干净.
在XAML方面 - 只是一些小的改动 - 使用包装器编辑器 - 你有一个MenuItem样式,injects每个命令的正确参数(你可以做其他方式,这是更好的).
它不是
hack- 我们只是通过手动调用TextArea命令处理来缩短鼠标处理的缺点.这就是它.
请享用!
| 归档时间: |
|
| 查看次数: |
2756 次 |
| 最近记录: |