これは、私が同様の問題を乗り越えるために使用したものです-人々に役立つことを願っています(この一般的なロジックは、Avalonエディターに関連するさまざまな問題に適用できます)...
実際に起こることは、おそらくアバロンのせいです(などとの組み合わせでListItem
)。それはマウスの処理を台無しにし、私はフォーカスを推測しています(これはTextArea
for コマンドにありCanExecute
、動作する必要があります.
これmouse handling
が問題です-windows
context menu
キーを押すだけで、コマンドが有効になっている通常のメニューがポップアップ表示されます。Avalon エディターには複雑なマウス/キー処理があります (優れたエディターを作成するのは困難です) - キーボードではfocus
、TextArea で明示的に ' ' を実行します。実際
CanCutOrCopy
に. _
「キーボード」メニューの場合、最初にそこに入り、次にポップアップします。「マウス」の場合、ポップアップし、終了時に(そのメソッドに入ります)をチェックします。それはすべて間違っています!Editing/EditingCommandHandler.cs
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>
コード ビハインド - ビュー モデル:
(注: 名前はそのまま残しましたが、小道具には小文字を使用しない でください:)
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" }); } }
}
(注:Window.DataContext
あなたがしたように、ビューモデルに接続するだけです)
これをラップするには、2 つのカスタム クラスが必要です。
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;
}
}
ノート:
EditText
は単なる依存関係プロパティです-bind
テキスト(あなたのtext
)にできるようにするため-それはAvalonの欠点です。ここにあるのはただの楽しみですが、必要になるかもしれないので、残しました。
AvalonRelayCommand
アプリケーション ルーティング コマンドを再配線するために使用します。他のものについては、独自のコマンド実装を使用します。これらの 2 つのクラスがコアです。
TextEditor の代わりに使用する必要がありますAvalonTextEditor
- これはほんの小さなラッパーです - に接続するContextMenu
にはTextEditor
(他の問題は別として、メニュー項目がsuffering
不足visual tree
しているため、簡単にコントロールを取得することはできません)。そしてTextEditor
、CommandParameter
( a に設定されているContextMenu
)からへの参照を取得する必要があります。これは、いくつかの添付プロパティ (TextEditor をオーバーライドせずに) だけで行うこともできましたが、この方法の方がすっきりしているように見えます。
XAML 側では、いくつかの小さな変更を加えるだけで、ラッパー エディターを使用して、各コマンドに適切なパラメーターを指定 するMenuItem
スタイルを作成できます (別の方法で行うこともできますが、これはより優れていました)。injects
コマンド処理をhack
手動で呼び出すことによって、マウス処理の欠点をショートカットしているだけです。TextArea
それだけです。
楽しみ!