2009-02-18 22 views
147

मेरे पास एक कॉम्बोबॉक्स है जो चयनित इटैम/चयनित वैल्यू को अपडेट नहीं करता है।एक कस्टम सूची में बाध्यकारी WPF कॉम्बोबॉक्स

ComboBox ItemsSource SelectedItem या SelectedValue ViewModel का एक और संपत्ति के लिए (अलग समय पर) एक ViewModel वर्ग है कि एक CollectionView के रूप में रास फोनबुक प्रविष्टियों का एक समूह को सूचीबद्ध करता है पर एक संपत्ति के लिए बाध्य है, तो मैं बाध्य किया है दोनों। मैंने डाटाबेस द्वारा सेट किए गए मान डीबग करने के लिए सेव कमांड में एक संदेशबॉक्स जोड़ा है, लेकिन SelectedItem/SelectedValue बाध्यकारी सेट नहीं किया जा रहा है।

public ConnectionViewModel 
{ 
    private readonly CollectionView _phonebookEntries; 
    private string _phonebookeEntry; 

    public CollectionView PhonebookEntries 
    { 
     get { return _phonebookEntries; } 
    } 

    public string PhonebookEntry 
    { 
     get { return _phonebookEntry; } 
     set 
     { 
      if (_phonebookEntry == value) return; 
      _phonebookEntry = value; 
      OnPropertyChanged("PhonebookEntry"); 
     } 
    } 
} 

_phonebookEntries संग्रह एक व्यापार वस्तु से निर्माता में initialised किया जा रहा है:

ViewModel वर्ग इस तरह दिखता है। ComboBox XAML इस तरह दिखता है:

<ComboBox ItemsSource="{Binding Path=PhonebookEntries}" 
    DisplayMemberPath="Name" 
    SelectedValuePath="Name" 
    SelectedValue="{Binding Path=PhonebookEntry}" /> 

मैं केवल ComboBox में दिखाया गया है वास्तविक स्ट्रिंग मान में दिलचस्पी है, न इस के रूप में वस्तु के किसी भी अन्य गुण मूल्य मैं जब मैं रास करने के लिए भर में पारित करने के लिए की जरूरत है वीपीएन कनेक्शन बनाना चाहते हैं, इसलिए DisplayMemberPath और SelectedValuePath दोनों ConnectionViewModel की नाम संपत्ति दोनों हैं। कॉम्बोबॉक्स DataTemplate में एक विंडो पर ItemsControl पर लागू है जिस पर डेटाकॉन्टेक्स्ट को व्यूमोडेल इंस्टेंस पर सेट किया गया है।

कॉम्बोबॉक्स सही वस्तुओं की सूची प्रदर्शित करता है, और मैं बिना किसी समस्या के यूआई में एक का चयन कर सकता हूं। हालांकि जब मैं कमांड से संदेश बॉक्स प्रदर्शित करता हूं, तो फोनबुक एंटर्री प्रॉपर्टी में अभी भी प्रारंभिक मान है, कॉम्बोबॉक्स से चयनित मूल्य नहीं। अन्य टेक्स्टबॉक्स उदाहरण संदेश अपडेट में ठीक और प्रदर्शित कर रहे हैं।

मुझे कॉम्बोबॉक्स डेटाबेस के साथ क्या याद आ रही है? मैंने बहुत सी खोज की है और ऐसा कुछ भी नहीं लगता है जो मैं गलत कर रहा हूं।


यह वह व्यवहार है जिसे मैं देख रहा हूं, हालांकि यह मेरे विशेष संदर्भ में किसी कारण से काम नहीं कर रहा है।

मेरे पास एक MainWindowViewModel है जिसमें कनेक्शन ViewModels का CollectionView है। MainWindowView.xaml फ़ाइल कोड-बैक में, मैंने DataContext को MainWindowViewModel पर सेट किया है। MainWindowView.xaml में ItemsControl कनेक्शन कनेक्शन दृश्य संग्रह के लिए बाध्य है। मेरे पास एक डेटा टेम्पलेट है जिसमें कॉम्बोबॉक्स और साथ ही कुछ अन्य टेक्स्टबॉक्स भी हैं। टेक्स्टबॉक्स सीधे Text="{Binding Path=ConnectionName}" का उपयोग कर कनेक्शन व्यू मॉडेल के गुणों से बंधे हैं।

public class ConnectionViewModel : ViewModelBase 
{ 
    public string Name { get; set; } 
    public string Password { get; set; } 
} 

public class MainWindowViewModel : ViewModelBase 
{ 
    // List<ConnectionViewModel>... 
    public CollectionView Connections { get; set; } 
} 

XAML कोड-पीछे:

public partial class Window1 
{ 
    public Window1() 
    { 
     InitializeComponent(); 
     DataContext = new MainWindowViewModel(); 
    } 
} 

फिर XAML:

<DataTemplate x:Key="listTemplate"> 
    <Grid> 
     <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" 
      DisplayMemberPath="Name" 
      SelectedValuePath="Name" 
      SelectedValue="{Binding Path=PhonebookEntry}" /> 
     <TextBox Text="{Binding Path=Password}" /> 
    </Grid> 
</DataTemplate> 

<ItemsControl ItemsSource="{Binding Path=Connections}" 
    ItemTemplate="{StaticResource listTemplate}" /> 

बक्सें सही ढंग से सब बाँध, और उन्हें और कोई परेशानी नहीं के साथ ViewModel के बीच डेटा ले जाता है। यह केवल कॉम्बोबॉक्स है जो काम नहीं कर रहा है।

आप फोनबुक एंटर्री क्लास के बारे में अपनी धारणा में सही हैं।

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


यहां एक परीक्षण कार्यान्वयन है जो ऊपर दिए गए उदाहरण के आधार पर समस्या का प्रदर्शन करता है।

XAML:

<Window x:Class="WpfApplication7.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"> 
    <Window.Resources> 
     <DataTemplate x:Key="itemTemplate"> 
      <StackPanel Orientation="Horizontal"> 
       <TextBox Text="{Binding Path=Name}" Width="50" /> 
       <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" 
        DisplayMemberPath="Name" 
        SelectedValuePath="Name" 
        SelectedValue="{Binding Path=PhonebookEntry}" 
        Width="200"/> 
      </StackPanel> 
     </DataTemplate> 
    </Window.Resources> 
    <Grid> 
     <ItemsControl ItemsSource="{Binding Path=Connections}" 
      ItemTemplate="{StaticResource itemTemplate}" /> 
    </Grid> 
</Window> 

कोड-पीछे:

namespace WpfApplication7 
{ 
    /// <summary> 
    /// Interaction logic for Window1.xaml 
    /// </summary> 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
      DataContext = new MainWindowViewModel(); 
     } 
    } 

    public class PhoneBookEntry 
    { 
     public string Name { get; set; } 
     public PhoneBookEntry(string name) 
     { 
      Name = name; 
     } 
    } 

    public class ConnectionViewModel : INotifyPropertyChanged 
    { 

     private string _name; 

     public ConnectionViewModel(string name) 
     { 
      _name = name; 
      IList<PhoneBookEntry> list = new List<PhoneBookEntry> 
              { 
               new PhoneBookEntry("test"), 
               new PhoneBookEntry("test2") 
              }; 
      _phonebookEntries = new CollectionView(list); 
     } 
     private readonly CollectionView _phonebookEntries; 
     private string _phonebookEntry; 

     public CollectionView PhonebookEntries 
     { 
      get { return _phonebookEntries; } 
     } 

     public string PhonebookEntry 
     { 
      get { return _phonebookEntry; } 
      set 
      { 
       if (_phonebookEntry == value) return; 
       _phonebookEntry = value; 
       OnPropertyChanged("PhonebookEntry"); 
      } 
     } 

     public string Name 
     { 
      get { return _name; } 
      set 
      { 
       if (_name == value) return; 
       _name = value; 
       OnPropertyChanged("Name"); 
      } 
     } 
     private void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     public event PropertyChangedEventHandler PropertyChanged; 
    } 

    public class MainWindowViewModel 
    { 
     private readonly CollectionView _connections; 

     public MainWindowViewModel() 
     { 
      IList<ConnectionViewModel> connections = new List<ConnectionViewModel> 
                  { 
                   new ConnectionViewModel("First"), 
                   new ConnectionViewModel("Second"), 
                   new ConnectionViewModel("Third") 
                  }; 
      _connections = new CollectionView(connections); 
     } 

     public CollectionView Connections 
     { 
      get { return _connections; } 
     } 
    } 
} 

आपको लगता है कि उदाहरण चलाते हैं, तो आप व्यवहार के बारे में मैं बात कर रहा हूँ मिल जाएगा। जब आप इसे संपादित करते हैं तो टेक्स्टबॉक्स अपने बाध्यकारी जुर्माना को अद्यतन करता है, लेकिन कॉम्बोबॉक्स नहीं करता है। वास्तव में एकमात्र चीज जो मैंने किया है, बहुत ही भ्रमित करने वाला एक माता-पिता ViewModel पेश करता है।

मैं वर्तमान में इस धारणा के तहत श्रम कर रहा हूं कि डेटाकॉन्टेक्स्ट के बच्चे से जुड़ी एक वस्तु में उस बच्चे को डेटाकॉन्टेक्स्ट है। मुझे कोई दस्तावेज नहीं मिल रहा है जो इसे एक तरफ या दूसरे को साफ़ करता है।

Ie,

खिड़की -> DataContext = MainWindowViewModel
..Items -> DataContext.PhonebookEntries
को बाउंड .... मद -> DataContext = PhonebookEntry (परोक्ष जुड़े)

मैं डॉन यह नहीं पता कि क्या मेरी धारणा किसी भी बेहतर तरीके से बताती है?


मेरी धारणा की पुष्टि करने के लिए,

<TextBox Text="{Binding Mode=OneWay}" Width="50" /> 

होने के लिए पाठ बॉक्स के बंधन को बदलने और इस टेक्स्ट बॉक्स बाध्यकारी रूट (जो मैं DataContext की तुलना कर रहा हूँ) ConnectionViewModel उदाहरण है दिखाएगा।

उत्तर

156

आपने डिस्प्ले मेम्बरपैथ और चयनित वैल्यूपैथ को "नाम" पर सेट किया है, इसलिए मुझे लगता है कि आपके पास सार्वजनिक संपत्ति नाम के साथ एक क्लास फोनबुक एंटर है।

क्या आपने डेटा कनेक्शन को अपने कनेक्शन ViewModel ऑब्जेक्ट पर सेट किया है?

मैंने आपको कोड कॉपी किया और कुछ मामूली संशोधन किए, और ऐसा लगता है कि यह ठीक काम करता है। मैं व्यूमोडेल फोनबुकइन्टी प्रॉपर्टी और कंबोबॉक्स में चयनित आइटम को चयनित आइटम सेट कर सकता हूं, और मैं चयनित आइटम को कंबोबॉक्स में बदल सकता हूं और दृश्य मॉडल फोनबुकइन्ट्री प्रॉपर्टी सही ढंग से सेट हो सकती है। यहाँ

<Window x:Class="WpfApplication6.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"> 
<Grid> 

    <StackPanel> 
     <Button Click="Button_Click">asdf</Button> 
     <ComboBox ItemsSource="{Binding Path=PhonebookEntries}" 
        DisplayMemberPath="Name" 
        SelectedValuePath="Name" 
        SelectedValue="{Binding Path=PhonebookEntry}" /> 
    </StackPanel> 
</Grid> 
</Window> 

और पीछे मेरी कोड है::

यहाँ मेरी XAML है

namespace WpfApplication6 
{ 
/// <summary> 
/// Interaction logic for Window1.xaml 
/// </summary> 
public partial class Window1 : Window 
{ 
    public Window1() 
    { 
     InitializeComponent(); 
     ConnectionViewModel vm = new ConnectionViewModel(); 
     DataContext = vm; 
    } 
    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     ((ConnectionViewModel)DataContext).PhonebookEntry = "test"; 
    } 
} 
public class PhoneBookEntry 
{ 
    public string Name { get; set; } 
    public PhoneBookEntry(string name) 
    { 
     Name = name; 
    } 
    public override string ToString() 
    { 
     return Name; 
    } 
} 
public class ConnectionViewModel : INotifyPropertyChanged 
{ 
    public ConnectionViewModel() 
    { 
     IList<PhoneBookEntry> list = new List<PhoneBookEntry>(); 
     list.Add(new PhoneBookEntry("test")); 
     list.Add(new PhoneBookEntry("test2")); 
     _phonebookEntries = new CollectionView(list); 
    } 
    private readonly CollectionView _phonebookEntries; 
    private string _phonebookEntry; 

    public CollectionView PhonebookEntries 
    { 
     get { return _phonebookEntries; } 
    } 

    public string PhonebookEntry 
    { 
     get { return _phonebookEntry; } 
     set 
     { 
      if (_phonebookEntry == value) return; 
      _phonebookEntry = value; 
      OnPropertyChanged("PhonebookEntry"); 
     } 
    } 
    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 
} 

संपादित करें: Geoffs दूसरे उदाहरण काम करने के लिए, जो मेरे लिए थोड़ा अजीब लगता है प्रतीत नहीं होता ।यदि मैं ConnectionViewModel पर PhonebookEntries प्रॉपर्टी को टाइपऑनली कोलेक्शन के प्रकार में बदलने के लिए, combobox पर चयनित वैल्यू प्रॉपर्टी के दोवे बाध्यकारी ठीक काम करता है।

शायद संग्रह दृश्य के साथ कोई समस्या है? मैंने आउटपुट कंसोल को चेतावनी दी: "System.Windows.Data चेतावनी: 50: संग्रह दृश्य का उपयोग सीधे पूरी तरह से समर्थित नहीं है। मूलभूत विशेषताएं काम करती हैं, हालांकि कुछ अक्षमता के साथ, लेकिन उन्नत सुविधाओं को ज्ञात बग का सामना करना पड़ सकता है। व्युत्पन्न का उपयोग करने पर विचार करें इन समस्याओं से बचने के लिए कक्षा। "

EDIT2 (नेट 4.5): DropDownList की सामग्री, ToString() और DisplayMemberPath की नहीं के आधार पर किया जा सकता है, जबकि DisplayMemberPath केवल चयनित और प्रदर्शित आइटम के लिए सदस्य निर्दिष्ट करता है।

+1

मैं पुष्टि कर सकता हूं कि संग्रह को बदलना, जिस पर 'आइटम स्रोत' संपत्ति बाध्य है, केवल पढ़ने के लिए संग्रह ही इसे काम करता है। मेरे मामले में मुझे इसे 'ऑब्जर्जेबल कोलेक्शन' से 'ReadOnlyObservableCollection' में बदलना पड़ा। नट। यह .NET 3.5 है - यह सुनिश्चित नहीं है कि यह 4.0 – ChrisWue

+1

में तय किया गया है, मैंने उस संदेश को भी नोटिस किया था, लेकिन मुझे लगता है कि * कवर किया गया था * मूल डेटा बाध्यकारी होगा। मुझे नहीं लगता। :) अब मैं गुणों को उजागर कर रहा हूं जैसे कि IList' <'T'> 'और आपके द्वारा उल्लिखित तरीके से _list.AsReadOnly() का उपयोग करके संपत्ति गेटर में। यह काम कर रहा है क्योंकि मैं उम्मीद करता था कि मूल विधि होगी। साथ ही, यह मेरे दिमाग को पार कर गया कि जब आइटमसोर्स बाध्यकारी ठीक काम कर रहा था, तो मैं कॉम्बोबॉक्स में चयनित आइटम तक पहुंचने के लिए व्यूमोडेल में वर्तमान प्रॉपर्टी का उपयोग कर सकता था। फिर भी, यह कॉम्बोबॉक्स चयनितवैल/चयनित इटैम संपत्ति को बाध्यकारी के रूप में प्राकृतिक महसूस नहीं करता है। –

22

मेरे पास पहली बार एक समान समस्या प्रतीत होती थी, लेकिन यह एनएचबीरनेट/डब्ल्यूपीएफ संगतता मुद्दे के कारण हुई। समस्या ऑब्जेक्ट समानता के लिए डब्ल्यूपीएफ जांच के तरीके के कारण हुई थी। मैं चयनित सामग्री और चयनित वैल्यूपैथ गुणों में ऑब्जेक्ट आईडी प्रॉपर्टी का उपयोग कर काम करने के लिए अपनी सामग्री प्राप्त करने में सक्षम था।

<ComboBox Name="CategoryList" 
DisplayMemberPath="CategoryName" 
SelectedItem="{Binding Path=CategoryParent}" 
SelectedValue="{Binding Path=CategoryParent.ID}" 
SelectedValuePath="ID"> 

जानकारी के लिए चेस्टर से निम्नलिखित लिंक देखें: The WPF ComboBox - SelectedItem, SelectedValue, and SelectedValuePath with NHibernate

60

ComboBox

List<ComboData> ListData = new List<ComboData>(); 
ListData.Add(new ComboData { Id = "1", Value = "One" }); 
ListData.Add(new ComboData { Id = "2", Value = "Two" }); 
ListData.Add(new ComboData { Id = "3", Value = "Three" }); 
ListData.Add(new ComboData { Id = "4", Value = "Four" }); 
ListData.Add(new ComboData { Id = "5", Value = "Five" }); 

cbotest.ItemsSource = ListData; 
cbotest.DisplayMemberPath = "Value"; 
cbotest.SelectedValuePath = "Id"; 

cbotest.SelectedValue = "2"; 

ComboData लग रहा है करने के लिए डेटा के लिए बाध्य करने के लिए की तरह:

public class ComboData 
{ 
    public int Id { get; set; } 
    public string Value { get; set; } 
} 
+0

यह समाधान मेरे लिए काम नहीं करता है। आइटमसोर्स ठीक काम करता है, लेकिन पथ प्रोपर्टीज कॉम्बोडाटा मानों पर सही ढंग से रीडायरेक्ट नहीं कर रहे हैं। – Coneone

+3

'आईडी' और' वैल्यू' होना चाहिए * गुण *, वर्ग फ़ील्ड नहीं, जैसे: 'पब्लिक क्लास कॉम्बोडाटा { सार्वजनिक int आईडी {get; सेट; } सार्वजनिक स्ट्रिंग मान {प्राप्त करें; सेट; } } ' – Edgar

+0

आप लड़के ... भयानक। –

1

मैं एक ऐसी ही था मुद्दा जहां चयनित इटिम कभी अपडेट नहीं हुआ।

मेरी समस्या यह थी कि चयनित आइटम सूची में मौजूद आइटम के समान उदाहरण नहीं था। तो मुझे बस अपने MyCustomObject में बराबर() विधि को ओवरराइड करना था और कॉम्बोबॉक्स को यह बताने के लिए उन दो उदाहरणों की आईडी की तुलना करना था कि यह वही वस्तु है।

public override bool Equals(object obj) 
    { 
     return this.Id == (obj as MyCustomObject).Id; 
    } 
संबंधित मुद्दे