WPFのコンテキストメニューには、右のデータバインドさ項目に結合しません
-
20-08-2019 - |
質問
私は問題を抱えています。 私はメニューを初めて使用するときは、それは素晴らしい作品(タブ上で右クリック)が、私はタブを切り替えた場合、コマンドが初めて使用されたデータ・バインドインスタンスを使用します。
私は...期待どおりに動作するユーザーコントロールにコマンドにバインドされているボタンを配置した場合、
誰かが私がやっているものを私に教えてくださいすることができ、間違った??
この問題を公開テストプロジェクトは、次のとおりです。
App.xaml.csます:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
CompanyViewModel model = new CompanyViewModel();
Window1 window = new Window1();
window.DataContext = model;
window.Show();
}
}
Window1.xamlます:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="HeaderTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type vw:PersonViewModel}">
<vw:UserControl1/>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding Path=Persons}"
ItemTemplate="{StaticResource HeaderTemplate}"
IsSynchronizedWithCurrentItem="True" />
</Grid>
</Window>
UserControl1.xamlます:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
MinWidth="200">
<UserControl.ContextMenu>
<ContextMenu >
<MenuItem Header="Change" Command="{Binding Path=ChangeCommand}"/>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0">The name:</Label>
<TextBox Grid.Column="1" Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
CompanyViewModel.csます:
public class CompanyViewModel
{
public ObservableCollection<PersonViewModel> Persons { get; set; }
public CompanyViewModel()
{
Persons = new ObservableCollection<PersonViewModel>();
Persons.Add(new PersonViewModel(new Person { Name = "Kalle" }));
Persons.Add(new PersonViewModel(new Person { Name = "Nisse" }));
Persons.Add(new PersonViewModel(new Person { Name = "Jocke" }));
}
}
PersonViewModel.csます:
public class PersonViewModel : INotifyPropertyChanged
{
Person _person;
TestCommand _testCommand;
public PersonViewModel(Person person)
{
_person = person;
_testCommand = new TestCommand(this);
}
public ICommand ChangeCommand
{
get
{
return _testCommand;
}
}
public string Name
{
get
{
return _person.Name;
}
set
{
if (value == _person.Name)
return;
_person.Name = value;
OnPropertyChanged("Name");
}
}
void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
TestCommand.csます:
public class TestCommand : ICommand
{
PersonViewModel _person;
public event EventHandler CanExecuteChanged;
public TestCommand(PersonViewModel person)
{
_person = person;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_person.Name = "Changed by command";
}
}
Person.csます:
public class Person
{
public string Name { get; set; }
}
解決
ここで覚えておくべき重要なことは、をコンテキストメニューは、ビジュアルツリーの一部ではないです。の
そこで彼らは結合のために属しているコントロールと同じソースを継承しません。これに対処する方法は、それ自体のContextMenuの配置標的に結合することがあります。
<MenuItem Header="Change" Command="{Binding
Path=PlacementTarget.ChangeCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
/>
他のヒント
私はコンテキストメニュー項目にコマンドを結合することが判明しているクリーンな方法はCommandReferenceと呼ばれるクラスを使用することを含みます。あなたは WPF先物のでCodePlexに上MVVMツールキットでそれを見つけることができます。
XAMLは次のようになります。
<UserControl x:Class="View.MyView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ViewModel;assembly=MyViewModel"
xmlns:mvvm="clr-namespace:ViewModelHelper;assembly=ViewModelHelper"
<UserControl.Resources>
<mvvm:CommandReference x:Key="MyCustomCommandReference" Command="{Binding MyCustomCommand}" />
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Plate">
<MenuItem Header="Inspect Now" Command="{StaticResource MyCustomCommandReference}"
CommandParameter="{Binding}">
</MenuItem>
</MenuItem>
</ContextMenu>
</UserControl.Resources>
MyCustomCommandはViewModelににRelayCommandです。この例では、ビューモデルは、コードビハインドでビューのDataContextのに貼り付けた。
注:このXAMLは、作業プロジェクトからコピーされ、説明のために簡略化しました。タイプミスやその他の小さなエラーがある場合があります。
私は、リストボックスにあるのContextMenuで最近、同じ問題がありました。私は、任意のコードビハインドせずにコマンドにMVVMの道をバインドしようとしました。私は最終的にあきらめたと私は彼の助けのための友人に尋ねました。彼は少しねじれたが、簡潔な解決策を見つけました。 彼は、リストボックスのDataContextのにアクセスすることによって、ビューモデルにコマンドを見つけ、その後のContextMenuのDataContextのでListBoxコントロールを渡すとされます。これは私がこれまで見てきた最も簡単な解決策です。いいえカスタムコードなし、タグ、ただ純粋なXAMLとMVVMます。
私はのGithub の上完全に動作するサンプルを掲載しました。ここではXAMLの抜粋です。
<Window x:Class="WpfListContextMenu.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow" Height="350" Width="268">
<Grid>
<DockPanel>
<ListBox x:Name="listBox" DockPanel.Dock="Top" ItemsSource="{Binding Items}" DisplayMemberPath="Name"
SelectionMode="Extended">
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Selected" Command="{Binding Path=DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</DockPanel>
</Grid>
</Window>
私は別のソリューションを好みます。 コンテキストメニューローダーイベントを追加します。
<ContextMenu Loaded="ContextMenu_Loaded">
<MenuItem Header="Change" Command="{Binding Path=ChangeCommand}"/>
</ContextMenu>
イベント内のデータコンテキストを割り当てます。
private void ContextMenu_Loaded(object sender, RoutedEventArgs e)
{
(sender as ContextMenu).DataContext = this; //assignment can be replaced with desired data context
}
私は深いコントロールテンプレート内のコンテキストメニューからバインドするときに非常に便利Tagプロパティを使用して、この方法を見つけます:
ます。http://blog.jtango。ネット/結合ツー・メニュー項目・イン・-WPF-コンテキスト・メニューの
これは、コンテキストメニューがから開かれたことをコントロールに利用可能なのDataContextにバインドすることが可能になります。コンテキストメニューは、「PlacementTarget」を通じてクリックされたコントロールにアクセスすることができます。クリックされたコントロールのTagプロパティは、希望のDataContextにバインドされている場合、コンテキストメニューの中から「PlacementTarget.Tag」に結合すると、そののDataContextに直接ごパチンコされます。
私は、これはすでに古い記事ですけど、私はそれを行うためのさまざまな方法を探しているものを1のための別の解決策を追加したいと思います。
私は何かをやろうとしたため、私は、私の場合で動作するように同一の溶液を作ることができなかった。(ただそれに接続されているサブメニューとツールバーのような)は、マウスクリックでコンテキストメニューを開き、またにコマンドをバインドします私のモデル。私はイベントトリガーを使用していたので、PlacementTargetオブジェクトがnullだっます。
これは、私はそれがXAMLを使用してのみ動作させるために見つかったソリューションは、次のとおりです。
<!-- This is an example with a button, but could be other control -->
<Button>
<...>
<!-- This opens the context menu and binds the data context to it -->
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.DataContext">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="{Binding}"/>
</ObjectAnimationUsingKeyFrames>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="ContextMenu.IsOpen">
<DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
<!-- Here it goes the context menu -->
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Item 1" Command="{Binding MyCommand1}"/>
<MenuItem Header="Item 2" Command="{Binding MyCommand2}"/>
</ContextMenu>
</Button.ContextMenu>
</Button>