2009-06-18 18 views
59

क्या किसी और ने देखा है कि ElementName के साथ बाइंडिंग MenuItem ऑब्जेक्ट्स के लिए सही ढंग से हल नहीं करती हैं जो ContextMenu ऑब्जेक्ट्स में निहित हैं? इस नमूने की जाँच करें:ContextMenu में मेनूइटम से एलिमेंटनाम बाइंडिंग

<Window x:Class="EmptyWPF.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1" Height="300" Width="300" 
    x:Name="window"> 
    <Grid x:Name="grid" Background="Wheat"> 
     <Grid.ContextMenu> 
      <ContextMenu x:Name="menu"> 
       <MenuItem x:Name="menuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/> 
       <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/> 
       <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/> 
       <MenuItem Header="Menu Item" Tag="{Binding ElementName=menuItem}" Click="MenuItem_Click"/> 
      </ContextMenu> 
     </Grid.ContextMenu> 
     <Button Content="Menu" 
       HorizontalAlignment="Center" VerticalAlignment="Center" 
       Click="MenuItem_Click" Tag="{Binding ElementName=menu}"/> 
     <Menu HorizontalAlignment="Center" VerticalAlignment="Bottom"> 
      <MenuItem x:Name="anotherMenuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/> 
      <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/> 
      <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/> 
      <MenuItem Header="Menu Item" Tag="{Binding ElementName=anotherMenuItem}" Click="MenuItem_Click"/> 
     </Menu> 
    </Grid> 
</Window> 

बाइंडिंग के सभी ContextMenu के भीतर निहित बाइंडिंग के अलावा महान काम करते हैं। वे रनटाइम के दौरान आउटपुट विंडो में एक त्रुटि प्रिंट करते हैं।

किसी को भी किसी भी काम के बारे में पता है? यहाँ क्या चल रहा है?

+0

समस्या स्पष्ट रूप से नामकोपों ​​के साथ कुछ करने के लिए है ... –

+0

क्या ContextMenus डिफ़ॉल्ट रूप से अपना नामस्थान परिभाषित करता है? –

उत्तर

51

मुझे एक बहुत ही सरल समाधान मिला।

UserControl के लिए पीछे कोड में:

NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this)); 
+0

यह अब फ्रेमवर्क 4.0 में काम नहीं कर रहा है। –

+0

क्षमा करें, मैंने इसे 4.0 –

+5

पर आजमाया नहीं है, यह वास्तव में 4.0 में मेरे लिए काम करता है। – esylvestre

4

एक सा प्रयोग करने के बाद, मैं चारों ओर एक काम की खोज की:

शीर्ष स्तर Window/UserControlINameScope को लागू करने और शीर्ष स्तर पर नियंत्रण करने के लिए ContextMenu की NameScope सेट करें।

public class Window1 : Window, INameScope 
{ 
    public Window1() 
    { 
     InitializeComponent(); 
     NameScope.SetNameScope(contextMenu, this); 
    } 

    // Event handlers and etc... 

    // Implement INameScope similar to this: 
    #region INameScope Members 

    Dictionary<string, object> items = new Dictionary<string, object>(); 

    object INameScope.FindName(string name) 
    { 
     return items[name]; 
    } 

    void INameScope.RegisterName(string name, object scopedElement) 
    { 
     items.Add(name, scopedElement); 
    } 

    void INameScope.UnregisterName(string name) 
    { 
     items.Remove(name); 
    } 

    #endregion 
} 

यह Window के अंदर नामित आइटम ढूँढने के लिए संदर्भ मेनू अनुमति देता है। कोई अन्य विकल्प?

5

संदर्भ मेनू के खिलाफ बाध्य करने के लिए मुश्किल हैं। वे आपके नियंत्रण के दृश्य पेड़ के बाहर मौजूद हैं, इसलिए वे आपके तत्व का नाम नहीं ढूंढ सकते हैं।

अपने संदर्भ मेनू के डेटाकॉन्टेक्स्ट को अपने प्लेसमेंट लक्ष्य पर सेट करने का प्रयास करें। आपको सापेक्ष स्रोत का उपयोग करना होगा।

<ContextMenu 
    DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ... 
+0

प्लेसटेक्स्ट में डेटाकॉन्टेक्स्ट को सेट करना एलिमेंटनाम बाइंडिंग को प्रभावित करेगा? मुझे लगता है कि डेटाकॉन्टेक्स्ट केवल बाइंडिंग के लिए उपयोग किया जाता है जिसमें कोई स्रोत, सापेक्ष स्रोत, या ElementName प्रॉपर्टी सेट नहीं है। –

+0

एलीमेंटनाम संपत्ति सेट करना केवल तभी काम करेगा जब लेआउट मैनेजर दृश्य पेड़ को नेविगेट करके संबंधित तत्व पा सकता है। संदर्भ मेनू नियंत्रण के दृश्य पेड़ के अंदर मौजूद नहीं है जिसमें उन्हें जोड़ा जाता है। आपको संदर्भ मेनू का डेटाकॉन्टेक्स्ट सेट करना होगा ताकि लेआउट मैनेजर संबंधित तत्व को ढूंढने के लिए अपने प्लेसमेंट लक्ष्य के दृश्य पेड़ को नेविगेट कर सके। – Josh

+0

उपरोक्त उदाहरण में DataContext जोड़ना समस्या को ठीक नहीं करता है। मुझे अभी भी आउटपुट विंडो में निम्न त्रुटि मिली है: "System.Windows.Data त्रुटि: 4: संदर्भ 'ElementName = window' के साथ बाध्यकारी के लिए स्रोत नहीं मिल रहा है। बाइंडिंग एक्सप्रेशन: (कोई पथ नहीं); DataItem = null; लक्ष्य तत्व 'MenuItem है '(नाम =' menuItem '); लक्ष्य संपत्ति' टैग 'है (प्रकार' ऑब्जेक्ट ') " –

19

यहाँ एक और XAML-केवल वैकल्पिक हल है। (यह भी मान लिया आप चाहते हैं क्या DataContext, जैसे अंदर है, तो आप MVVMing रहे यह)

विकल्प एक है, जहां ContextMenu की मूल तत्व एक DataTemplate में नहीं है:

Command="{Binding PlacementTarget.DataContext.MyCommand, 
     RelativeSource={RelativeSource AncestorType=ContextMenu}}" 

यह ओपी के प्रश्न के लिए काम करेगा। यदि आप डेटा टेम्पलेट के अंदर हैं तो यह काम नहीं करेगा। इन मामलों में, DataContext एक संग्रह में कई की अक्सर एक है, और ICommand आप के लिए है एक ही ViewModel भीतर संग्रह का एक भाई संपत्ति (DataContext विंडो की, कहते हैं) के लिए बाध्य करना चाहते हैं।

इन मामलों में, आप Tag का लाभ लेने के लिए अस्थायी रूप से माता पिता DataContext जो दोनों संग्रह और अपने ICommand शामिल धारण करने के लिए कर सकते हैं:

class ViewModel 
{ 
    public ObservableCollection<Derp> Derps { get;set;} 
    public ICommand DeleteDerp {get; set;} 
} 

और XAML

<!-- ItemsSource binds to Derps in the DataContext --> 
<StackPanel 
    Tag="{Binding DataContext, ElementName=root}"> 
    <StackPanel.ContextMenu> 
     <ContextMenu> 
      <MenuItem 
       Header="Derp"      
       Command="{Binding PlacementTarget.Tag.DeleteDerp, 
       RelativeSource={RelativeSource 
            AncestorType=ContextMenu}}" 
       CommandParameter="{Binding PlacementTarget.DataContext, 
       RelativeSource={RelativeSource AncestorType=ContextMenu}}"> 
      </MenuItem> 
+0

मुझे लगता है कि आप जिस प्रासंगिक बिंदु को यहां बना रहे हैं वह यह है कि आप टैग का उपयोग कर सकते हैं और दृश्य पेड़ में किसी अन्य स्थान पर डेटा प्राप्त करने के लिए सापेक्ष स्रोत बाइंडिंग। –

+0

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

1

में मुझे यकीन नहीं है कि माउस के लिए इवेंटहालर के अंदर कोड की एक पंक्ति से बचने के लिए जादू की चाल का सहारा क्यों लें, आप पहले से ही हैंडल पर क्लिक करें:

private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e) 
    { 
     // this would be your tag - whatever control can be put as string intot he tag 
     UIElement elm = Window.GetWindow(sender as MenuItem).FindName("whatever control") as UIElement; 
    } 
+0

ऐसा करने से यह बाध्य कमांड के अनुसार मेनू आइटम स्वचालित रूप से अक्षम होने की अनुमति नहीं देता है। इसलिए जब यह निष्पादन के लिए काम करता है, तो आपको लोड होने पर मेनू आइटम को अक्षम/सक्षम करने के लिए और कोड जोड़ना होगा। यह बुरा नहीं है, यह बहुत से लोगों के लिए WinFoms यूआई कोड स्पेगेटी की बुरी यादें वापस लाता है। – jpierson

16

जैसा कि दूसरों ने कहा है, 'ContextMenu' दृश्य पेड़ में निहित नहीं है और 'ElementName' बाध्यकारी काम नहीं करेगा। संदर्भ मेनू के 'नेमस्कोप' को सेट किए गए उत्तर द्वारा सुझाए गए अनुसार केवल तभी काम करता है जब संदर्भ मेनू को 'डेटा टेम्पलेट' में परिभाषित नहीं किया गया हो। मैंने इसे {x:Reference} Markup-Extension का उपयोग करके हल किया है जो 'एलिमेंटनाम' बाइंडिंग के समान है लेकिन दृश्य पेड़ को छोड़कर बाध्यकारी को अलग-अलग हल करता है। मैं इसे 'प्लेसमेंट टार्गेट' का उपयोग करने से कहीं ज्यादा पठनीय मानता हूं।

<Image Source="{Binding Image}">  
    <Image.ContextMenu> 
     <ContextMenu> 
      <MenuItem Header="Delete" 
         Command="{Binding Source={x:Reference Name=Root}, Path=DataContext.RemoveImage}" 
         CommandParameter="{Binding}" /> 
     </ContextMenu> 
    </Image.ContextMenu> 
</Image> 

MSDN-प्रलेखन

x:Reference is a construct defined in XAML 2009. In WPF, you can use XAML 2009 features, but only for XAML that is not WPF markup-compiled. Markup-compiled XAML and the BAML form of XAML do not currently support the XAML 2009 language keywords and features.

जो कुछ भी है कि इसका मतलब है ... मेरे लिए काम करता है, हालांकि के अनुसार: यहाँ एक उदाहरण है।

+4

दरअसल इसकी सबसे उपयोगी टिप्पणी यहां –

+1

यह वास्तव में सबसे अच्छा काम करता है, मैंने सभी उत्तरों की कोशिश की और यह WPF .Net 4.5.2 के लिए सही है। धन्यवाद @ मार्स –

संबंधित मुद्दे