2009-03-19 20 views
17

मुझे एक पृष्ठ पर मौजूद उपयोगकर्ता नियंत्रण पर संदर्भ मेनू में कमांड को बाध्य करते समय समस्या है। पहली बार जब मैं मेनू का उपयोग करता हूं (टैब पर राइट-क्लिक करें) यह बहुत अच्छा काम करता है, लेकिन यदि मैं टैब स्विच करता हूं तो कमांड डाटाबेस इंस्टेंस का उपयोग करेगा जो पहली बार उपयोग किया गया था।डब्ल्यूपीएफ संदर्भ मेनू सही डेटाबेस आइटम से बंधे नहीं है

अगर मैं एक बटन है कि usercontrol यह उम्मीद के रूप में काम करता है में आदेश के लिए बाध्य है ...

कोई मुझे बता सकते हैं मैं गलत क्या कर रही हूं डाल ??

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> 

यह एक परीक्षण परियोजना है कि समस्या को उजागर करता है

कॉम्पा nyViewModel.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; } 
} 

उत्तर

22

यहाँ याद करने के लिए महत्वपूर्ण बात यह है कि मुझे संदर्भित करता है nus दृश्य पेड़ का हिस्सा नहीं हैं।

इसलिए वे उसी स्रोत का वारिस नहीं करते हैं जो वे बाध्यकारी के नियंत्रण के रूप में करते हैं। इसके साथ निपटने का तरीका ContextMenu के प्लेसमेंट लक्ष्य से जुड़ना है।

<MenuItem Header="Change" Command="{Binding 
    Path=PlacementTarget.ChangeCommand, 
    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" 
/> 
+0

हाय कैमरून:

यह समाधान मैं इसे केवल XAML का उपयोग कर काम करने के लिए मिल गया है। क्या आपको लगता है कि यहां आपकी तकनीक किसी भी तरह की समस्या से संबंधित है जो मैंने यहां वर्णित की है: http://stackoverflow.com/questions/833607/wpf-why-do-contextmenu-items-work-for-listbox-but-not- itemscontrol ... मैं एक आदेश के लिए बाध्यकारी नहीं हूँ, लेकिन मुझे संदेह है कि यह एक संबंधित समस्या है। –

+2

मुझे इस जवाब से आश्वस्त नहीं है। कमांड बाइंडिंग मेनू आइटम के लिए काम करते हैं (यह जानता है कि इसे व्यू मॉडल को बांधना है) ... समस्या यह है कि मेन्यूटेम स्विच नहीं करते हैं जब स्विचिंग टैब के कारण डेटाकॉन्टेक्स्ट बदल जाता है। यदि उनके कारण दृश्य वृक्ष का हिस्सा नहीं है, तो यह पहली बार कैसे काम करता है? – Schneider

+0

@ स्क्नीडर: मैंने यह नहीं कहा कि मेनू में बाइंडिंग काम नहीं करती है, सिर्फ इसलिए कि वे अपने माता-पिता से अपने डेटाैकेंटेक्स्ट का वारिस नहीं करते हैं जैसे आप उम्मीद करेंगे। मैं कहूंगा कि डब्ल्यूपीएफ बाध्यकारी इंजन संदर्भ को सेट कर रहा है जब मेनू पहले खोला गया है और फिर टैब बदलते समय इसे अपडेट नहीं कर रहा है। –

8

संदर्भ मेनू आइटम्स में आदेशों को बाध्य करने के लिए सबसे साफ तरीका मुझे कमांडरेंस नामक कक्षा का उपयोग करना शामिल है। आप इसे WPF Futures पर कोडप्लेक्स पर एमवीवीएम टूलकिट में पा सकते हैं।

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 है। इस उदाहरण में, व्यूमोडेल को कोड के पीछे दृश्य के डेटाकॉन्टेक्स्ट से जोड़ा गया था।

नोट: यह एक्सएएमएल एक कामकाजी परियोजना से कॉपी किया गया था और चित्रण के लिए सरलीकृत किया गया था। टाइपो या अन्य मामूली त्रुटियां हो सकती हैं।

+1

क्या आपने रिलेकॉमैंड के साथ एक CanExecute प्रतिनिधि, साइबरमोंक के साथ यह कोशिश की है? मैंने पाया है कि कमांडरेंस को कैनएक्सक्यूट पर पैरामीटर में शून्य हो जाता है, हालांकि निष्पादन विधि सही मान पास हो जाती है। यह मुझे अभी इसका उपयोग करने से रोक रहा है। –

+0

ठीक है, यह काम कर सकता है लेकिन क्या कोई यह समझा सकता है कि इसकी आवश्यकता क्यों है? ContextMenus पर बाइंडिंग केवल एक बार क्यों चलती है? – Schneider

+0

मैं इस काम को सत्यापित कर सकता हूं ... स्पष्टीकरण स्वागत है :) – Schneider

0

मैं इस विधि टैग संपत्ति बहुत उपयोगी का उपयोग करते समय गहरे एक नियंत्रण टेम्पलेट के अंदर एक संदर्भ मेनू से बाध्यकारी पाया नियंत्रण मेनू के लिए उपलब्ध किसी भी डेटाकॉन्टेक्स्ट को संदर्भ मेनू से खोला गया था। संदर्भ मेनू "प्लेसमेंट लक्ष्य" के माध्यम से क्लिक किए गए नियंत्रण तक पहुंच सकता है। यदि क्लिक किए गए नियंत्रण की टैग प्रॉपर्टी वांछित डेटाकॉन्टेक्स्ट से जुड़ी हुई है, तो संदर्भ मेनू के अंदर से "प्लेसमेंट टाउनटैग" को बाध्यकारी आपको सीधे उस डेटाकॉन्टेक्स्ट पर स्लिंगशॉट करेगा।

1

मैं एक और समाधान पसंद करता हूं। संदर्भ मेनू लोडर घटना जोड़ें।

<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 
} 
4

मुझे हाल ही में एक सूची बॉक्स में स्थित कॉन्टेक्स्टमेनू के साथ एक ही समस्या थी। मैंने किसी भी कोड के पीछे एमवीवीएम तरीके को कमांड करने की कोशिश की। मैंने अंततः छोड़ दिया और मैंने एक दोस्त से उसकी मदद के लिए कहा। उन्होंने थोड़ा मोड़ लेकिन संक्षिप्त समाधान पाया। वह ContextMenu के DataContext में ListBox को पास कर रहा है और फिर ListBox के DataContext तक पहुंचकर दृश्य मॉडल में कमांड ढूंढें। यह अब तक का सबसे आसान समाधान है जिसे मैंने देखा है। कोई कस्टम कोड नहीं, कोई टैग नहीं, केवल शुद्ध एक्सएएमएल और एमवीवीएम।

मैंने Github पर एक पूरी तरह से काम कर रहे नमूना पोस्ट किया। एक्सएएमएल का एक अंश यहां दिया गया है।

<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> 
0

मैं जानता हूँ कि यह पहले से ही एक पुराने पोस्ट है, लेकिन मैं उन एक है जो यह करने के लिए अलग अलग तरीकों की तलाश कर रहे हैं के लिए एक और समाधान जोड़ना चाहते हैं।

मैं अपने मामले में काम करने के लिए एक ही समाधान नहीं कर सका, क्योंकि मैं कुछ और करने की कोशिश कर रहा था: माउस क्लिक के साथ संदर्भ मेनू खोलें (बस एक उपमेनू के साथ टूलबार की तरह) और कमांड बाध्य करें मेरे मॉडल के लिए। चूंकि मैं एक इवेंट ट्रिगर का उपयोग कर रहा था, प्लेसमेंट लक्ष्य ऑब्जेक्ट शून्य था।

<!-- 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> 
संबंधित मुद्दे