2012-05-04 4 views
31

हम ListBox के SelectedItem को प्रोग्रामेटिक रूप से सेट करना चाहते हैं और उस आइटम को तब ध्यान केंद्रित करना चाहते हैं ताकि तीर कुंजियां उस चयनित आइटम से संबंधित हों। काफी सरल लगता है।आप एक WPF ListBox में चयनित इटैम पर प्रोग्रामेटिक रूप से फ़ोकस कैसे सेट करते हैं जो पहले से ही फोकस कर रहा है?

समस्या लेकिन यदि ListBox पहले से ही जब SelectedItem प्रोग्राम के रूप में स्थापित करने, जबकि इसे ठीक से ListBoxItem पर IsSelected संपत्ति को अद्यतन करता है कुंजीपटल ध्यान केंद्रित किया जाता है, यह नहीं सेट पर, कीबोर्ड इसे करने के लिए, और इस प्रकार, तीर कुंजी सूची में पहले केंद्रित आइटम के सापेक्ष स्थानांतरित करें, न कि नव-चयनित आइटम की अपेक्षा करें।

यह उपयोगकर्ता के लिए बहुत भ्रमित है क्योंकि यह कुंजीपटल का उपयोग करते समय चयन को चारों ओर कूदने के लिए दिखाई देता है क्योंकि यह प्रोग्रामेटिक चयन होने से पहले कहां जाता है।

नोट: जैसा कि मैंने कहा, यह केवल तब होता है जब आप SelectedItem संपत्ति को ListBox पर सेट करते हैं, जिसमें पहले से ही कीबोर्ड फोकस है। यदि यह नहीं करता है (या यदि यह करता है लेकिन आप छोड़ते हैं, तो वापस आते हैं), जब कीबोर्ड फ़ोकस ListBox पर वापस आ जाता है, तो सही आइटम के पास अब अपेक्षित कीबोर्ड फ़ोकस होगा।

यहां कुछ समस्या नमूना है जो इस समस्या को दिखा रहा है। इसे प्रदर्शित करने के लिए, कोड चलाएं, सूची में 'सात' का चयन करने के लिए माउस का उपयोग करें (इस प्रकार ListBox पर फ़ोकस डालें), फिर 'टेस्ट' बटन पर क्लिक करें। अंत में, फोकस आयत प्रकट करने के लिए अपने कीबोर्ड पर 'Alt' कुंजी टैप करें। आप देखेंगे कि यह वास्तव में 'सात' पर भी है और यदि आप ऊपर और नीचे तीरों का उपयोग करते हैं, तो वे उस पंक्ति के सापेक्ष हैं, उपयोगकर्ता के रूप में 'चार' की अपेक्षा नहीं होगी।

ध्यान दें कि मेरे पास Focusable बटन पर false पर सेट है क्योंकि इसे दबाते समय फोकस के सूची बॉक्स को लूटने के लिए नहीं है। अगर मेरे पास यह नहीं था, तो ListBox जब आप बटन पर क्लिक करते हैं तो फोकस खो देंगे, और इस प्रकार, जब फोकसबॉक्स पर फ़ोकस लौटाया जाता है, तो यह सही आइटम पर होगा।

XAML फाइल:

<Window x:Class="Test.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="525" Height="350" WindowStartupLocation="CenterScreen" 
    Title="MainWindow" x:Name="Root"> 

    <DockPanel> 

     <Button Content="Test" 
      DockPanel.Dock="Bottom" 
      HorizontalAlignment="Left" 
      Focusable="False" 
      Click="Button_Click" /> 

     <ListBox x:Name="MainListBox" /> 

    </DockPanel> 

</Window> 

कोड-पीछे:

using System.Collections.ObjectModel; 
using System.Windows; 

namespace Test 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      MainListBox.ItemsSource = new string[]{ 
       "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight" 
      }; 

     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      MainListBox.SelectedItem = MainListBox.Items[3]; 
     } 

    } 

} 

नोट: कुछ IsSynchronizedWithCurrentItem उपयोग करने के लिए सुझाव दिया है, लेकिन यह है कि संपत्ति जुड़े का Current संपत्ति के साथ ListBox की SelectedItem सिंक्रनाइज़ करता है राय। यह फोकस से संबंधित नहीं है क्योंकि यह समस्या अभी भी मौजूद है।

हमारे काम के आसपास अस्थायी रूप से कहीं और फोकस सेट करने के लिए है, तो चयनित आइटम निर्धारित करते हैं, तो ध्यान केंद्रित वापस ListBox करने के लिए सेट है, लेकिन यह हमारे ViewModelListBox खुद के बारे में पता करने के लिए हो रही हम में से undesireable प्रभाव पड़ता है, फिर इस पर निर्भर करता है कि इसमें फोकस है या नहीं, (यानी आप बस 'कहीं और फोकस नहीं करना चाहते हैं, फिर यहां वापस आएं, अगर' यहां 'पर ध्यान केंद्रित नहीं किया गया है, तो आप इसे पहले चुरा लेंगे कहीं और से।) इसके अलावा, आप इसे घोषणात्मक बाइंडिंग के माध्यम से आसानी से संभाल नहीं सकते हैं। यह कहने की जरूरत नहीं है कि यह बदसूरत है।

फिर फिर, 'बदसूरत' जहाजों, तो वहां है।

उत्तर

47

यह कोड की कुछ पंक्तियां हैं। यदि आप इसे कोड-बैक में नहीं चाहते थे, तो मुझे यकीन है कि इसे एक संलग्न व्यवहार में पैक किया जा सकता है।

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    MainListBox.SelectedItem = MainListBox.Items[3]; 
    MainListBox.UpdateLayout(); // Pre-generates item containers 

    var listBoxItem = (ListBoxItem) MainListBox 
     .ItemContainerGenerator 
     .ContainerFromItem(MainListBox.SelectedItem); 

    listBoxItem.Focus(); 
} 
+2

ContainerFromItem नल वापस लौटाएगा यदि उसके लिए कोई कंटेनर अभी तक उत्पन्न नहीं हुआ है, जो वर्चुअलाइज्ड सूची का मामला है और आइटम ऑफ-स्क्रीन है। इसके अलावा यदि आप बाध्यकारी से मान सेट करने का प्रयास करते हैं तो यह टूट जाता है क्योंकि आपके पास ListBox तक पहुंच नहीं है, न ही आपको चाहिए। (नीचे जारी है ...) – MarqueIV

+0

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

+0

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

2

शायद एक संलग्न व्यवहार के साथ?

public static DependencyProperty FocusWhenSelectedProperty = DependencyProperty.RegisterAttached(
      "FocusWhenSelected", 
      typeof(bool), 
      typeof(FocusWhenSelectedBehavior), 
      new PropertyMetadata(false, new PropertyChangedCallback(OnFocusWhenSelectedChanged))); 

private static void OnFocusWhenSelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
    { 
     var i = (ListBoxItem)obj; 
     if ((bool)args.NewValue) 
      i.Selected += i_Selected; 
     else 
      i.Selected -= i_Selected; 
    } 

static void i_Selected(object sender, RoutedEventArgs e) 
{ 
    ((ListBoxItem)sender).Focus(); 
} 

की तरह और XAML

 <Style TargetType="ListBoxItem"> 
      <Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/> 
     </Style> 
+1

यह है जहाँ मैं मूल रूप से जा रहा था था, लेकिन इस मुद्दे को उन वस्तुओं है डॉन यदि आपके पास वर्चुअलाइज्ड सूची है, तो अभी तक अस्तित्व में नहीं है, ताकि संलग्न व्यवहार अभी तक प्रतिक्रिया देने के लिए वायर्ड नहीं किया गया हो। आपको अभी भी किसी भी तरह से ScrollIntoView विधि करना है। दूसरा, यदि आप सख्ती से चयन कर रहे हैं, तो बहु-चयन मोड में चीजें थोड़ा पागल हो सकती हैं, लेकिन फिर, वांछित व्यवहार माना जा सकता है। वर्चुअलाइजेशन (जो डिफ़ॉल्ट रूप से चालू है) असली हत्यारा है। – MarqueIV

0

अपने XAML में कुछ तो आप इस की कोशिश की और काम नहीं किया?

<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=YourCollectionView}" SelectedItem="{Binding SelectedItem}"></ListBox> 

और SelectedItem संपत्ति:

private YourObject _SelectedItem; 
    public YourObject SelectedItem 
    { 
     get 
     { 
      return _SelectedItem; 
     } 
     set 
     { 
      if (_SelectedItem == value) 
       return; 

      _SelectedItem = value; 

      OnPropertyChanged("SelectedItem"); 
     } 
    } 

अब अपने कोड में आप कर सकते हैं:

SelectedItem = theItemYouWant; 

मेरे लिए इस दृष्टिकोण हमेशा काम करता है।

+0

मैं आपको उस पर फोन करने जा रहा हूं। या तुमने कोशिश की? अपने सूची बॉक्स को 100 आइटम के साथ लोड करें। माउस के साथ 50 वें आइटम पर क्लिक करें। फिर अपने फॉर्म पर एक बटन जोड़ें जो प्रोग्रामिक रूप से 'चयनित इटैम' को 10 वें आइटम पर सेट करता है। चयन बदलता है, लेकिन ए) कि चयनित आइटम दृश्य में स्क्रॉल नहीं करता है, क्योंकि बी) यह केंद्रित नहीं है। आप देख सकते हैं कि 'Alt' कुंजी टैप करके और आप अभी भी उस आइटम को देख पाएंगे जिस पर आपने क्लिक किया है, अभी भी कीबोर्ड फोकस है। – MarqueIV

+0

वैसे ... बस सुनिश्चित करने के लिए, आपको माउस या कीबोर्ड का उपयोग करके पहले दूसरी पंक्ति का शारीरिक रूप से चयन करना होगा। – MarqueIV

+0

@MarkquelV मैं शायद आप जो चाहते हैं उस पर उलझन में हूं। मैंने सोचा कि आप चयनित पंक्ति प्रोग्रामैटिक सेट करना चाहते हैं। चयनित पंक्ति केंद्रित पंक्ति है। वहां जाने के लिए आपको ScrollIntoView का उपयोग करना होगा। और हाँ, मेरा दृष्टिकोण एकाधिक चयन के लिए काम नहीं करता है। – Dummy01

0

प्रथम) आप ListBox.Items.IndexOf साथ लिस्टबॉक्स में चुने गए आइटम खोजने होंगे()।
दूसरा) अब ListBox.SelectedItems.Add() के साथ आइटम जोड़ें।

यह मेरा कोड है:

DataRow[] drWidgetItem = dtItemPrice.Select(widgetItemsFilter); 
lbxWidgetItem.SelectedItems.Clear(); foreach(DataRow drvItem in 
drWidgetItem) 
lbxWidgetItem.SelectedItems.Add(lbxWidgetItem.Items[dtItemPrice.Rows.IndexOf(drvItem)]); 

आप ListBox में किसी आइटम का चयन करने के लिए आप इस तरह से उपयोग कर सकते हैं करना चाहते हैं:
ListBox.SelectedItem = (आपका ListBoxItem);

आप कुछ आइटम ListBox में आप इस तरह से उपयोग करना चाहिए का चयन करना चाहते हैं:
ListBox.SelectedItems.Add (आपका ListBoxItem);

+0

मैं 100% निश्चित नहीं हूं, लेकिन मुझे लगता है कि आपने मेरे प्रश्न को गलत समझा होगा। चयन मुद्दा नहीं है। फोकस है। आपका कोड आइटम का चयन करता है, लेकिन यह फोकस सेट नहीं करता है, जो मैं हल करने की कोशिश कर रहा हूं। – MarqueIV

+0

क्षमा करें! इसलिए, मुझे लगता है कि यदि आप अपना आइटम फोकस करना चाहते हैं तो आप अपना नियंत्रण केंद्रित करते हैं। (मेरी बुरी अंग्रेजी के लिए खेद है) –

+0

लेकिन अगर नियंत्रण पहले से ही केंद्रित है, तो आप इसे फिर से सेट नहीं कर सकते हैं। आपको पहले कुछ और ध्यान देना होगा। – MarqueIV

0

आप की जरूरत है केवल ListBox.SelectedItem का उपयोग और उसके बाद का उपयोग ListBox.ScrollIntoView (listBox.SelectedItem)

उदाहरण कोड:

 private void textBox2_TextChanged(object sender, TextChangedEventArgs e) 
    { 

     var comparision = StringComparison.InvariantCultureIgnoreCase; 
     string myString = textBox2.Text; 
     List<dynamic> index = listBox.Items.SourceCollection.OfType<dynamic>().Where(x=>x.Nombre.StartsWith(myString,comparision)).ToList(); 
     if (index.Count > 0) { 
     listBox.SelectedItem= index.First(); 


      listBox.ScrollIntoView(listBox.SelectedItem); 


     } 

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