2017-01-04 8 views
0

3つのタブがあり、それぞれに異なる種類のファイルのリストボックスがあります。右クリックで3つのリストボックスにバインドされたWPF ContextMenu

リストボックス内の項目を右クリックすると、項目ヘッダーとして「新規作成、編集、削除」を含むContextMenuが必要になります。

私は、各リストボックスのためのContextMenuを持つことができると思いますし、各ヘッダーのための別々の方法を持っている、など:

   <ListBox.ContextMenu> 
        <ContextMenu x:Name="NewEditDeleteAdvCalcFileContextMenu"> 
         <MenuItem Name="NewAdv" Header="New" Click="NewAdv_Click" /> 
         <MenuItem Name="EditAdv" Header="Edit" Click="EditAdv_Click"/> 
         <MenuItem Name="DeleteAdv" Header="Delete" Click="DeleteAdv_Click"/> 
        </ContextMenu> 
       </ListBox.ContextMenu> 

しかし、実際に、私はより良い方法があると願っています。

は私が ContextMenu as Static Resource

を示し、この記事を見て、これは私がやりたい何かのようです。コマンドを使用することが提案されているのと同じスレッドで : ContextMenu with Commands

と私はそれを必要とするのでそれで、私は、私がクリックされたListBoxItemの種類を取得することができます願っています。新しいファイルタイプBは新しいファイルタイプCとは違って扱われなければなりませんが、私は巨大なcontextmenusとNew/Edit/Deleteメソッドを望んでいません。

だから、現在、私は私のXAMLファイルで、この高を持っている。そして、

<UserControl.Resources> 
    <ContextMenu x:Key="NewEditDeleteContextMenu"> 
     <MenuItem Header="New" 
        Command="{Binding Path=NewFileCommand}" 
        CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"/> 
     <MenuItem Header="Edit" 
        Command="{Binding Path=EditFileCommand}" 
        CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"/> 
     <MenuItem Header="Delete" 
        Command="{Binding Path=DeleteFileCommand}" 
        CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"/> 
    </ContextMenu> 
</UserControl.Resources> 

とのTabItemでのリストボックス:どうすればよい

<ListBox Name="CalcFilesListBox" 
        Margin="20" ItemsSource="{Binding CalcFilesList}" 
        PreviewMouseRightButtonUp="ListBox_PreviewMouseRightButtonUp" 
        ContextMenu="{StaticResource NewEditDeleteContextMenu}"> 
       <ListBox.ItemTemplate> 
        <DataTemplate> 
         <StackPanel Orientation="Horizontal"> 
          <TextBlock Text="{Binding Path=Name}" /> 
         </StackPanel> 
        </DataTemplate> 
       </ListBox.ItemTemplate> 
       <ListBox.ItemContainerStyle> 
        <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}"> 
         <EventSetter Event="MouseDoubleClick" Handler="CalcFileListBox_MouseDoubleClick"/> 
        </Style> 
       </ListBox.ItemContainerStyle> 
      </ListBox> 

質問#1

ListBoxItemの右クリックでコンテキストメニューが表示されますが、これは静的リソースですか? があるので、私のxaml.csに私はこれでした:

private void ListBox_PreviewMouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 
     // SelectItemOnRightClick(e); 
     NewEditDeleteContextMenu.PlacementTarget = sender as UIElement; 
     NewEditDeleteContextMenu.IsOpen = true; 

    } 

をしかし、今、私はエラーと言ってい:

名「NewEditDeleteContextMenu」は現在のコンテキストに存在しません。

もともと私のようなリストボックスの一部として、コンテキストメニューを持っていたので:

<ListBox.ContextMenu> 
... 

しかし、これまで私は、各リストボックスのために別々のContextMenuを意味するだろう見ることができたとして。

質問#2

はのは、ユーザーコントロールに示したContextMenuの新アイテムヘッダ(のためのNewFileCommandを言わせて、コマンドを使用する正しい方法です。次の操作を実行するコードのリソースブロック):私のViewModelで

:ViewModelにのコンストラクタで

public RelayCommand<string> NewFileCommand { get; private set; } 

、その後:基本的に

public CalcViewModel() 
    { 
     NewFileCommand = new RelayCommand<object>(NewFile); 
    } 

public void NewFile(object sender) 
    { 
     //Determine the type of file, based on the ListBoxItem's DataContext. 
That is, supposing the ListBoxItem is the object being passed as the sender. 
    } 

、私は1つのContextMenuが異なるListBoxコントロールにバインドしたいですこれは右クリックでポップアップするはずです。たとえば、ContextMenuでNew項目が選択された場合、ListBoxにバインドされているファイルの種類を判別したいと思います。 例:リストボックス1は、ファイルタイプBのコレクションにバインドされています。リストボックス2は、ファイルタイプCのコレクションにバインドされています。リストボックス2のアイテムを右クリックして新規を選択すると、 C.

質問#3

これは非常に複雑なビューではありません。私はMVVMフレームワークを使用していませんでした。なぜなら、これまで私がそれを学ぶには時間がかかるとは考えていませんでしたが、このシナリオを考えれば、ListBoxItemsをダブルクリックするより単純なケースですコードブロックの1つに表示される場合は、フレームワークの使用をお勧めしますか?

答えて

0

あなたは正しい方向に進むつもりです。コードはちょっと更新する必要があります。まず、右クリックハンドラは必要ありません。コントロールにContextMenuが設定されている場合は、右クリックするとContextMenuが呼び出されます。 ContextMenuStaticResourceとして複数のコントロールに添付すると、ContextMenuが最初に設定した後にContextMenuがそのDataContextを更新しないバグがあるため、問題が少し発生します。つまり、最初にリストボックス#2のメニューを呼び出すと、そのリストボックスに選択した項目が表示されますが、リストボックス#3でその項目を呼び出すと、選択した項目がリストボックス#2に表示されます。しかし、これを回避する方法があります。

まずは、コンテキストメニューを見てみましょう、どのようにそれは、リストボックスにバインドされています:

<ContextMenu x:Key="contextMenu" DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> 
    <MenuItem Header="New" Command="{Binding DataContext.NewFileCommand}" CommandParameter="{Binding}"/> 
    <MenuItem Header="Delete" Command="{Binding DataContext.DeleteFileCommand}" CommandParameter="{Binding SelectedItem}"/> 
</ContextMenu> 

... 

<ListBox Margin="10" ItemsSource="{Binding Files1}" ContextMenu="{StaticResource contextMenu}"/> 

PlacementTargetContextMenuが接続されているコントロールです。明示的にメニューのデータコンテキストをPlacementTargetにバインドすると、呼び出されるたびに正しいListBoxが指し示されます。リスト項目を扱う「編集」や「削除」などのコマンドは簡単です:CommandParameterCommandTargetではなく)をListBoxSelectedItemにバインドするだけです。編集または削除したい項目は、コマンドのパラメータとして与えられます。

RelayCommandを使用して以来、私はGalaSoftのMVVMフレームワークを使用していると仮定しています。その場合は、ここであなたの「削除」コマンドがどのように見えるかです:あなたはアイテムが選択されているかどうか、新しいアイテムを作成できるようにしたいので

public RelayCommand<object> DeleteFileCommand { get; } = new RelayCommand<object>(DeleteFile_Executed, DeleteFile_CanExecute); 

private static bool DeleteFile_CanExecute(object file) 
{ 
    return file != null; 
} 

private static void DeleteFile_Executed(object file) 
{ 
    var filetype = file.GetType(); 
    System.Diagnostics.Debug.WriteLine(string.Format("Deleting file {0} of type {1}", file, file.GetType())); 

    // if(filetype == typeof(FileTypeA)) DeleteFileTypeA(file as FileTypeA); 
    // else if(filetype == typeof(FileTypeB)) DeleteFileTypeB(file as FileTypeB); 
    // etc... 
} 

「新規」コマンドは、ビットトリッカーになります。そこで、CommandParameterListBoxにバインドします。残念ながら、ListBoxに含まれるアイテムの種類を取得する良い方法はありません。それは、複数のタイプのアイテムを含むことも、アイテムを全く持たないこともあります。 x:Nameを与えて、コマンドハンドラの名前を調べることができますが、私が選択するのは、このListBoxのアイテムのタイプをTagというパラメータとしてListBoxとして扱うことです。Tagはあなたが好きな目的のために余分なデータのビットを使用することができますされています

<ListBox Margin="10" ItemsSource="{Binding Files1}" ContextMenu="{StaticResource contextMenu}" Tag="{x:Type local:FileTypeA}"/> 

は、今、私たちはこのように私たちの「新しい」コマンドハンドラを定義することができます。

private static bool NewFile_CanExecute(ListBox listbox) { return true; } 

private static void NewFile_Executed(ListBox listbox) 
{ 
    var filetype = listbox.Tag as Type; 

    System.Diagnostics.Debug.WriteLine(string.Format("Creating new file of type {0}", filetype)); 

    // if(filetype == typeof(FileTypeA)) CreateNewFileTypeA(); 
    // else if(filetype == typeof(FileTypeB)) CreateNewFileTypeB(); 
    // etc... 
} 

を、このシナリオが正当かどうかについてはMVVMかどうかにかかわらず、実際にファイルを作成、編集、削除するコードとともにViewModelに3つのファイルリストを置くことができ、WindowのコマンドでViewModelのコードを呼び出すことができます。私は通常、シナリオがより複雑になるまでは、しません。

+0

今週初めにありがとうございましたが、私はまだいくつかの変更とコードのリファクタリングを行っています。私はすべてをテストしたらこれを答えとしてマークします。私はまた、タブの1つにツリービューを持っていますが、おそらくリストボックスを最初に取得する方が簡単だと思いました。私はGalaSoftのMVVMフレームワークを使用しませんが、私はあなたの答えを自分のものにするよう努めています。だから、私はさらに明確化を求めるかもしれませんが、ありがとう、あなたは巨大な助けになっています! – Igavshne

+0

さて、コンテキストメニューをツリービューで共有することは可能ですか?私は実際に2つのリストボックスとツリービューを持っています。私はリストコマンドを使用してファイルタイプを送信するNewコマンドの問題を予見しています。コマンドにリストボックスの代わりにオブジェクトを送信すると、Tagプロパティが失われます。それ以外の場合は、私が推測するツリービュー用の別のコンテキストメニューを追加することができます。 – Igavshne

+1

できると思います。あなたはそれを試しましたか?ツリービューには1つのタイプの項目しか含まれていないと仮定すると、CanExecuteメソッドとExecuteメソッドでListBoxではなくスーパークラスのFrameworkElementを使用するだけで、Tagメソッドを使用できます。 – josh2112

関連する問題