2010-11-27 24 views
5

मैं listboxes की एक ListBox है कि आपका कोई एप्लिकेशन है। मैं इनरलिस्ट बॉक्स को पारस्परिक रूप से अनन्य बनाना चाहता हूं। मेरे ViewModel एक संग्रह Foos जिसमें कोई वर्णन, एक IsSelected संपत्ति और एक संग्रह बार्स जो एक नाम और IsSelected संपत्ति है है है।WPF परस्पर अनन्य listboxes

public class MyViewModel : INotifyPropertyChanged 
{ 
    public ObservableCollection<Foo> Foos { /* code removed for brevity */ } 
} 

public class Foo : INotifyPropertyChanged 
{ 
    public string Description { /* code removed for brevity */ } 
    public ObservableCollection<Bar> Bars { /* code removed for brevity */ } 
    public bool IsSelected { /* code removed for brevity */ } 
} 

public class Bar : INotifyPropertyChanged 
{ 
    public string Name { /* code removed for brevity */ } 
    public bool IsSelected { /* code removed for brevity */ } 
} 

नीचे मेरी MainWindow जिसका DataContext MyViewModel पर सेट किया जाता का एक हिस्सा है। यह ListBox के ItemsSource संपत्ति ItemsSource={Binding Path=Foos} का उपयोग कर ही है और इस ListBox के लिए टेम्पलेट में एक आंतरिक ListBox जो ItemsSource="{Binding Path=Bars}" का उपयोग कर के लिए बाध्य किया जाता है। ए, बी, और सी फूओस विवरण हैं। उनमें शामिल वस्तुओं बार के नाम हैं।

|--------------------------| 
| A |--------------------| | 
| | Item 1    | | 
| | Item 2    | | 
| | Item 3    | | 
| |--------------------| | 
|       | 
| B |--------------------| | 
| | Item X    | | 
| | Item Y    | | 
| | Item Z    | | 
| |--------------------| | 
|       | 
| C |--------------------| | 
| | Item l    | | 
| | Item m    | | 
| |--------------------| | 
|--------------------------| 

मुझे इसे बनाने की ज़रूरत है ताकि उपयोगकर्ता किसी भी बार्स में से केवल एक आइटम का चयन कर सके। इसलिए, यदि उपयोगकर्ता Foo A से आइटम 1 का चयन करता है तो Foo B से आइटम एक्स का चयन करता है तो आइटम 1 को अचयनित किया जाना चाहिए।

मैं भी खिड़की पर कहीं और किसी पाठ बॉक्स नियंत्रण के लिए चयनित आइटम के लिए बाध्य करने की जरूरत है, लेकिन 1 बात एक समय मुझे लगता है।

कोड और चयन में इसे बदलना घटनाएं एक विकल्प नहीं है। मैं केवल XAML का उपयोग करके इसे रखना पसंद करूंगा।

अग्रिम धन्यवाद।

अद्यतन
Moonshield की सलाह के बाद मैं इस के साथ आ गया है, लेकिन यह अभी भी पूरी तरह से काम नहीं कर रहा।

public class MyViewModel 
{ 
    private Bar _selectedBar; 

    public ObservableCollection<Foo> Foos { /* code removed for brevity */ } 
    public Bar SelectedBar 
    { 
      get { return _selectedBar; } 
      set 
      { 
       _selectedBar = null; 
       NotifyPropertyChanged("SelectedBar"); 

       _selectedBar = value; 
       NotifyPropertyChanged("SelectedBar"); 
      } 
    }  
}
<ListBox x:Name="lbFoos" ItemsSource="{Binding Path=Foos}" SelectedItem="{Binding Path=SelectedBar}"> 
    <ListBox.ItemContainerStyle> 
     <Style TargetType="{x:Type ListBoxItem}"> 
      <Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
         <StackPanel> 
          <TextBlock Text="Foo: " /> 
          <TextBlock Text="{Binding Path=Description}" /> 
          <ListBox ItemsSource="{Binding Path=Bars}" SelectedItem="{Binding Path=SelectedItem RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBox}}}"> 
           <ListBox.ItemContainerStyle> 
            <Style TargetType="ListBoxItem"> 
             <Setter Property="Template"> 
              <Setter.Value> 
               <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
                <TextBlock Text="{Binding Path=Name}" /> 
               </ControlTemplate> 
              </Setter.Value> 
             </Setter> 
             <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" /> 
            </Style> 
           </ListBox.ItemContainerStyle> 
          </ListBox> 
         </StackPanel> 
        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </ListBox.ItemContainerStyle> 
</ListBox> 

उत्तर

8

यह करने के लिए सबसे आसान तरीका है शायद अपनी MyViewModel वर्ग के लिए एक SelectedBar संपत्ति जोड़ सकते हैं और है कि करने के लिए listboxes के SelectedItem संपत्ति बाध्य करने के लिए है। यह केवल एक आइटम को एक बार में चुना जा सकता है, और आपको बाद में अपने टेक्स्टबॉक्स को बाध्य करने के लिए कुछ प्रदान करता है।

फिर आप प्रत्येक बार की IsSelected संपत्ति को अद्यतन करने के लिए प्रत्येक ListBoxItem (ItemContainerStyle के माध्यम से) की IsSelected प्रॉपर्टी पर बाध्यकारी (OneWayToSource) सेट कर सकते हैं। फू वस्तुओं की IsSelected संपत्ति को अद्यतन करने के अगर यह रिक्त है की जाँच करने के लिए एक valueconverter साथ लिस्टबॉक्स के SelectedItem करने के लिए एक बाध्यकारी निर्धारित किया है।

संपादित करें:

SelectedItem संपत्ति (दान का ठीक लागू करने): बाइंडिंग के साथ

protected Bar selectedItem; 
public Bar SelectedItem{ 
    get 
    { 
     return selectedItem; 
    } 
    set 
    { 
     selectedItem = null; 
     NotifyPropertyChanged("SelectedItem"); 

     selectedItem = value; 
     NotifyPropertyChanged("SelectedItem"); 
    } 

ListBoxItem (ListBoxItem DataContext संभालने बार viewmodel है):

<ListBoxItem IsSelected="{Binding Path=IsSelected, Mode=OneWayToSource}" /> 

संपादित करें - अपने कोड को ठीक करता है :

मैं अपने कोड काम करने के लिए कामयाब रहे। दो मुद्दों मैंने पाया:

  1. कारण आइटम है कि आप चाहते हैं फिर से टेम्प्लेट की ListBoxItems बार वस्तुओं के साथ आबादी, इसलिए कोई प्रकाश डाला शैली जब एक आइटम का चयन किया गया था चयन करने के लिए प्रकट नहीं हो रहे थे - यह तय इसके बजाय ItemTemplate सेट करके, जो पूरे टेम्पलेट को ओवरराइड करने के बजाय आइटम की सामग्री टेम्पलेट करता है।

  2. इसके बजाय माता पिता की SelectedItem सूचकांक करने के लिए नेस्टेड listboxes में से एक की SelectedItem बाध्यकारी और फिर बाध्यकारी कि viewmodel करने के लिए, मैं viewmodel, जो कई-चयन समस्या का समाधान हो करने के लिए सीधे बाध्य करने के लिए बाध्यकारी बदल की

    <ListBox x:Name="lbFoos" ItemsSource="{Binding Path=Foos}"> <!--Removed SelectedItem binding.--> 
        <ListBox.ItemContainerStyle> 
         <Style TargetType="{x:Type ListBoxItem}"> 
          <Setter Property="Template"> 
           <Setter.Value> 
            <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
             <StackPanel> 
              <TextBlock Text="Foo: " /> 
              <TextBlock Text="{Binding Path=Description}" /> 
              <ListBox ItemsSource="{Binding Path=Bars}" SelectionChanged="ListBox_SelectionChanged" SelectedItem="{Binding Path=DataContext.SelectedBar, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type ListBox}}}"><!--Changed binding to bind directly to ViewModel--> 
               <ListBox.ItemTemplate><!--Set ItemTemplated rather than ControlTemplate--> 
                <DataTemplate> 
                 <TextBlock Text="{Binding Path=Name}" /> 
                </DataTemplate> 
               </ListBox.ItemTemplate> 
               <ListBox.ItemContainerStyle> 
                <Style TargetType="ListBoxItem"> 
                 <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" /> 
                </Style> 
               </ListBox.ItemContainerStyle> 
              </ListBox> 
             </StackPanel> 
            </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </ListBox.ItemContainerStyle> 
    </ListBox> 
    
+2

एक चेतावनी जो मैंने चलाया है वह यह है कि यदि सूची बॉक्स की चयनित इटैम संपत्ति किसी संपत्ति से जुड़ी हुई है और संपत्ति उस मूल्य पर ले जाती है जो सूची में नहीं है, तो यह चयनित आइटम को दृष्टिहीन रूप से अचयनित नहीं करेगा। मैंने चयनित ऑब्जेक्ट व्यू मॉडेल प्रॉपर्टी के सेटटर को संशोधित करके इस पर हैकिंग समाप्त कर दी ताकि वह अद्यतनों के बीच 'शून्य' की बदली गई संपत्ति को सूचित करे। यह सही ढंग से सूची बक्से को अचयनित करने का कारण बनता है। –

+0

@Moonshield यह अच्छा लगता है। क्या आप मुझे OneWayToSource बाध्यकारी करने के तरीके के बारे में एक छोटा XAML स्निपेट दे सकते हैं? – Adam

+0

@Dan - उस पर अच्छा पकड़, और एक दिलचस्प समाधान। – Moonshield

1

आप केवल एक आइटम चाहते हैं तो होने एक IsSelected संपत्ति व्यर्थ है किसी भी समय का चयन किया जाना। इसके बजाय आपके पास एक कंटेनर प्रॉपर्टी होनी चाहिए जिसमें वर्तमान में चयनित आइटम है (मूनशिल्ड के सुझाव के अनुसार)। इस मॉडल का तात्पर्य है कि केवल एक ही चुना जा सकता है, जबकि आपके मौजूदा मॉडल का तात्पर्य है कि कई का चयन किया जा सकता है। आखिरकार, फू के व्यक्तिगत उदाहरणों को शायद यह जानने की आवश्यकता नहीं है कि उन्हें वैसे भी चुना गया है।

0

चयनित इटैम को बाध्य करने के बजाय, चयनित वैल्यू को बाध्य करने का प्रयास करें। मेरे पास दो DataGrids के साथ एक समान स्थिति है जो आइटम्ससोर्स को मेरे व्यू मॉडेल में दो अलग-अलग ICollectionView गुणों से जोड़ती है। ये ICollectionView गुण एक ही ऑब्जर्जेबल कोलेक्शन को उनके मूल स्रोत के रूप में उपयोग करते हैं, और माइट टाइप की संपत्ति का उपयोग करके फ़िल्टर के माध्यम से पारस्परिक रूप से अनन्य होते हैं। (Ie एक ही संपत्ति के एक मूल्य पर अन्य फ़िल्टर और अन्य आईसीओलेक्शन व्यू फ़िल्टर एक ही संपत्ति के अलग-अलग मूल्य पर फ़िल्टर करते हैं।)

मेरे पास मेरे व्यूमोडेल में एक संपत्ति है जिसे मिस्ट टाइप टाइप किया गया है जिसे MyType टाइप किया गया है जो प्रत्येक की चयनित वैल्यू संपत्ति से जुड़ा हुआ है डेटा ग्रिड। जब किसी आइटम को डेटाग्रिड से चुना जाता है, तो पहले से चयनित आइटम को अचयनित किया जाता है।

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