C# 在ListView';s ItemTemplate没有';行不通
我在我的项目中使用了AvaloneEdit控件。当我使用快捷键Ctrl+C或Ctrl+V时,关联的复制/粘贴命令可以正常工作。我决定在上下文菜单中使用这些命令以提高可用性,因为有些用户习惯于右键单击而不是快捷方式。我使用以下XAML代码进行控制:C# 在ListView';s ItemTemplate没有';行不通,c#,.net,wpf,binding,avalonedit,C#,.net,Wpf,Binding,Avalonedit,我在我的项目中使用了AvaloneEdit控件。当我使用快捷键Ctrl+C或Ctrl+V时,关联的复制/粘贴命令可以正常工作。我决定在上下文菜单中使用这些命令以提高可用性,因为有些用户习惯于右键单击而不是快捷方式。我使用以下XAML代码进行控制: <avalonedit:TextEditor.ContextMenu> <ContextMenu> <MenuItem Command="Undo" /> <Menu
<avalonedit:TextEditor.ContextMenu>
<ContextMenu>
<MenuItem Command="Undo" />
<MenuItem Command="Redo" />
<Separator/>
<MenuItem Command="Cut" />
<MenuItem Command="Copy" />
<MenuItem Command="Paste" />
</ContextMenu>
</avalonedit:TextEditor.ContextMenu>
(正如您在下面的评论中看到的)我意识到,当您将AvaloneEdit放置在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;
}
}
公共类MyViewModel:INotifyPropertyChanged
{
公共MyViewModel()
{
集合=新的ObservableCollection();
mc=新的myClass();
}
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged(字符串propName)
{
var h=已更改的财产;
如果(h!=null)
h(这是新的PropertyChangedEventArgs(propName));
}
公共ObservableCollection集合{get;set;}
公共myClass mc{get;set;}
}
公共类myClass
{
公共字符串文本{get;set;}
}
公共部分类主窗口:窗口
{
MyViewModel _viewModel=新的MyViewModel();
公共主窗口()
{
初始化组件();
this.DataContext=\u viewModel;
}
}
和主窗口的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>
如果您尝试此测试,您可以看到,如果在内容控件上使用DataTemplate,则上下文菜单中的命令绑定工作正常,但在ListViewItem中,它们被禁用。
还要注意的是,DataTemplate中的上下文菜单对于TextBox很有效,并且显示ListView本身并不会从本质上破坏命令链
如何修复关联菜单并连接到listView项目中的控制命令?这是我过去解决类似问题的方法-我希望它对人们有用(这种通用逻辑可以应用于大量与Avalon编辑器相关的问题)
实际发生的情况可能是Avalon的错误(与ListItem
等结合使用)。它搞乱了鼠标操作,我猜焦点(应该在TextArea
上,用于命令和CanExecute
工作
鼠标操作
是问题所在-就像您只需按窗口一样
上下文菜单
键弹出一个带有已启用命令的常规菜单。
Avalon编辑器具有复杂的鼠标/键处理(很难创建
好的编辑器)-在键盘上,它在
您还可以通过在
CanCutOrCopy
方法(Editing/EditingCommandHandler.cs
,下载
实际处理
应用程序命令。复制
。对于“键盘”菜单,它首先进入
在那里,然后弹出。对于“鼠标”一个,它弹出-然后继续
退出它检查CanExecute
(进入该方法)。这就是全部
错!
还有勘误表
您自己的命令没有问题,只是正常地公开您的命令,所有这些都应该可以工作
对于应用程序命令
(即路由命令
)它没有正确连接,Execute
,CanExecute
没有去它应该去的地方,即TextArea
。要纠正这一点,你需要将命令重新连接到你自己的包装中,并基本上调用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>
注意事项:
EditText
只是一个依赖属性-能够bind
一个文本(你的Text
)-这是Avalon的一个缺点。这里只是为了好玩,但你可能需要它,所以我把它放在里面了
使用AvalonRelayCommand
重新连接应用程序路由命令-对于其他内容,使用您自己的命令实现。这两个类是核心
您需要使用AvalonTextEditor
而不是TextEditor(它只是一个很小的包装器)来连接ContextMenu
和TextEditor
(除了其他问题之外,菜单项由于缺少可视化树而,并且您无法从中轻松获得任何控件)。我们需要从命令参数
(设置为上下文菜单
)中获取对文本编辑器
的引用。这可以通过一些附件属性来完成(不覆盖文本编辑器),但这样看起来更干净
在XAML方面——只是一些小的更改——使用包装器编辑器——您有一个MenuItem
样式,它为每个命令注入了正确的参数(您可以用其他方式,这更好)
这不是一个黑客行为
——我们只是在弥补
鼠标处理-通过手动调用TextArea
命令处理。
差不多就是这样
享受!我尝试了你的代码,效果很好!我使用了中的库并将你的代码放入xaml中。问题可能是你忽略的avalonedit:TextEditor
的其他属性。@MD.Unicorn:谢谢你的评论。那篇文章使用了
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" }); } }
}
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;
}
}