WPF DataGrid में एक ComboBoxColumn का बाइंडिंग आइटम स्रोत


82

मेरे पास दो सरल मॉडल कक्षाएं और एक ViewModel है ...

public class GridItem
{
    public string Name { get; set; }
    public int CompanyID { get; set; }
}

public class CompanyItem
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class ViewModel
{
    public ViewModel()
    {
        GridItems = new ObservableCollection<GridItem>() {
            new GridItem() { Name = "Jim", CompanyID = 1 } };

        CompanyItems = new ObservableCollection<CompanyItem>() {
            new CompanyItem() { ID = 1, Name = "Company 1" },
            new CompanyItem() { ID = 2, Name = "Company 2" } };
    }

    public ObservableCollection<GridItem> GridItems { get; set; }
    public ObservableCollection<CompanyItem> CompanyItems { get; set; }
}

... और एक साधारण खिड़की:

<Window x:Class="DataGridComboBoxColumnApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Name}" />
                <DataGridComboBoxColumn ItemsSource="{Binding CompanyItems}"
                                    DisplayMemberPath="Name"
                                    SelectedValuePath="ID"
                                    SelectedValueBinding="{Binding CompanyID}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

ViewModel को MainWindow के DataContextApp.xaml.cs में सेट किया गया है :

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        MainWindow window = new MainWindow();
        ViewModel viewModel = new ViewModel();

        window.DataContext = viewModel;
        window.Show();
    }
}

जैसा कि आप देख सकते हैं मैं ItemsSourceडेटाग्रिड को डेटा सेट पर भेज सकता हूंGridItems ViewModel संग्रह में । यह हिस्सा काम करता है, नाम "जिम" के साथ एकल ग्रिड लाइन प्रदर्शित की जाती है।

मैं भी ViewModel ItemsSourceके CompanyItemsसंग्रह के लिए हर पंक्ति में कॉम्बो बॉक्स सेट करना चाहता हूं । यह हिस्सा काम नहीं करता है: कॉम्बोबॉक्स खाली रहता है और डीबगर आउटपुट विंडो में मुझे एक त्रुटि संदेश दिखाई देता है:

System.Windows.Data Error: 2: टारगेट एलिमेंट के लिए गवर्निंग फ्रेमवर्क या फ्रेमवर्ककंटेनमेंट नहीं पा सकते हैं। बाइंडिंग एक्सप्रेशन: पाथ = कंपनीइम्स; DataItem = null; लक्ष्य तत्व 'डेटाग्रिडबॉम्ब बॉक्स-कॉलम' (हैशकोड = 28633162) है; टारगेट प्रॉपर्टी है 'आइटम्स सोर्स' (टाइप करें 'IEnumerable')

मेरा मानना ​​है कि WPF की CompanyItemsएक संपत्ति होने की उम्मीद हैGridItem मामला नहीं है, और यही कारण है कि बाध्यकारी विफल हो जाता है।

मैं पहले से ही एक के साथ काम करने की कोशिश की है RelativeSourceऔर AncestorTypeऐसा है:

<DataGridComboBoxColumn ItemsSource="{Binding CompanyItems, 
    RelativeSource={RelativeSource Mode=FindAncestor,
                                   AncestorType={x:Type Window}}}"
                        DisplayMemberPath="Name"
                        SelectedValuePath="ID"
                        SelectedValueBinding="{Binding CompanyID}" />

लेकिन यह मुझे डिबगर आउटपुट में एक और त्रुटि देता है:

System.Windows.Data त्रुटि: 4: संदर्भ के साथ बाइंडिंग के लिए स्रोत नहीं ढूँढ सकता बाइंडिंग एक्सप्रेशन: पाथ = कंपनीइम्स; DataItem = null; लक्ष्य तत्व 'DataGridComboBoxColumn' (HashCode = 1150788) है; टारगेट प्रॉपर्टी है 'आइटम्स सोर्स' (टाइप करें 'IEnumerable')

प्रश्न: मैं DataMridComboBoxColumn के आइटम्स को कंपनी के साथ कैसे जोड़ सकता हूं, यह ViewModel के संग्रह के लिए है? क्या यह सभी के लिए संभव है?

अग्रिम में मदद के लिए धन्यवाद!

जवाबों:


123

Pls, जांचें कि क्या DataGridComboBoxColumn xaml नीचे आपके लिए काम करेगा:

<DataGridComboBoxColumn 
    SelectedValueBinding="{Binding CompanyID}" 
    DisplayMemberPath="Name" 
    SelectedValuePath="ID">

    <DataGridComboBoxColumn.ElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
        </Style>
    </DataGridComboBoxColumn.ElementStyle>
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="{x:Type ComboBox}">
            <Setter Property="ItemsSource" Value="{Binding Path=DataContext.CompanyItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
        </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

यहां आप अपनी समस्या का एक और समाधान पा सकते हैं: WPF DataGrid के साथ कॉम्बो बॉक्स का उपयोग करना


4
नरक, यह काम करता है !!! अगर मैं ही समझ सकता हूँ क्यों? और रेचल की सिफारिश के साथ मूल कोड क्यों नहीं? वैसे भी, बहुत बहुत धन्यवाद!
सलुमा

1
मुझे विश्वास है कि आप यहाँ स्पष्टीकरण पा सकते हैं: wpf.codeplex.com/workitem/8153?ProjectName=wpf (टिप्पणियां देखें)
serge_gubenko

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

1
Joemorrison.org/blog/2009/02/02/17/… लिंक के लिए +1 । इससे मेरी समस्या हल हो गई। बेकार है, ~ 5 घंटे और मुझे एहसास हुआ कि मेरे पास इस परियोजना में पहले से ही कुछ और था जो हम कर रहे थे :( इसकी हमेशा सीखने की प्रक्रिया है।
ट्रैविसड

मेरे लिए काम नहीं करता है। EditingElementStyle काम करने लगता है, लेकिन किसी कारण से एक बार जब मैं ElementStyle जोड़ देता हूं तो मुझे ComboBoxes मिलता है जो किसी भी चीज़ को पॉप्युलेट नहीं करता है (इसके बजाय DisplayMemberPath से मान), और जब मैं क्लिक करता हूं तो वह EditingElementStyle पर वापस नहीं आता।
विलियम

46

के बारे में MSDN पर प्रलेखन ItemsSourceकाDataGridComboBoxColumn कहना है कि केवल स्थिर संसाधनों कॉम्बो बक्से में आइटम के स्थिर कोड या इनलाइन संग्रह करने के लिए बाध्य किया जा सकता है ItemsSource:

ड्रॉप-डाउन सूची को पॉप्युलेट करने के लिए, पहले निम्न विकल्पों में से एक का उपयोग करके कॉम्बो बॉक्स के लिए ItemSource संपत्ति सेट करें:

  • एक स्थिर संसाधन। अधिक जानकारी के लिए, StaticResource मार्कअप एक्सटेंशन देखें।
  • एक एक्स: स्टेटिक कोड इकाई। अधिक जानकारी के लिए, x: स्टेटिक मार्कअप एक्सटेंशन देखें।
  • ComboBoxItem प्रकारों का इनलाइन संग्रह।

यदि मैं इसे सही तरीके से समझूं, तो DataContext की प्रॉपर्टी से बाइंडिंग संभव नहीं है।

और वास्तव में: जब मैं ViewModel में CompanyItemsएक स्थिर संपत्ति बना ...

public static ObservableCollection<CompanyItem> CompanyItems { get; set; }

... नामस्थान जोड़ें जहां ViewModel विंडो में स्थित है ...

xmlns:vm="clr-namespace:DataGridComboBoxColumnApp"

... और बाइंडिंग को बदल ...

<DataGridComboBoxColumn
    ItemsSource="{Binding Source={x:Static vm:ViewModel.CompanyItems}}" 
    DisplayMemberPath="Name"
    SelectedValuePath="ID"
    SelectedValueBinding="{Binding CompanyID}" />

... तो यह काम करता है। लेकिन एक स्थिर संपत्ति के रूप में आइटम स्रोत कभी-कभी ठीक हो सकता है, लेकिन यह हमेशा वह नहीं होता है जो मैं चाहता हूं।


1
मुझे उम्मीद है कि microsoft इस बग को ठीक कर देगा
juFo

38

सही समाधान लगता है:

<Window.Resources>
    <CollectionViewSource x:Key="ItemsCVS" Source="{Binding MyItems}" />
</Window.Resources>
<!-- ... -->
<DataGrid ItemsSource="{Binding MyRecords}">
    <DataGridComboBoxColumn Header="Column With Predefined Values"
                            ItemsSource="{Binding Source={StaticResource ItemsCVS}}"
                            SelectedValueBinding="{Binding MyItemId}"
                            SelectedValuePath="Id"
                            DisplayMemberPath="StatusCode" />
</DataGrid>

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

मैं इसे किसी और के लिए यहां छोड़ रहा हूं जो इस बंधन से जूझ रहा था, और सोचता था कि क्या कोई बेहतर तरीका था (जैसा कि यह पृष्ठ स्पष्ट रूप से अभी भी खोज परिणामों में आ रहा है, यही कारण है कि मैं यहां आ गया हूं)।


1
हालांकि यकीनन एक अच्छा जवाब है, यह शायद है निकाला ओपी के सवाल से। आपका MyItemsएक संकलन त्रुटि के लिए नेतृत्व करेंगे अगर ओपी के कोड के साथ प्रयोग किया
MickyD

22

मुझे लगता है कि यह प्रश्न एक वर्ष से अधिक पुराना है, लेकिन मैं बस इसी तरह की समस्या से निपटने में इसे पार कर गया और सोचा कि मैं एक और संभावित समाधान साझा करूंगा अगर यह भविष्य के यात्री (या खुद को, जब मैं इसे बाद में भूल जाऊं और खुद को ढूंढूं मेरी मेज पर निकटतम वस्तु की चीख और फेंक के बीच StackOverflow पर चारों ओर फ़्लॉप करना)।

मेरे मामले में मैं एक DataGridComboBoxColumn के बजाय एक DataGridTemplateColumn का उपयोग करके अपना इच्छित प्रभाव प्राप्त करने में सक्षम था, एक ला स्निपेट। [चेतावनी: मैं .NET 4.0 का उपयोग कर रहा हूं, और जो मैं पढ़ रहा हूं वह मुझे विश्वास दिलाता है कि डेटाग्रिड ने बहुत अधिक विकास किया है, इसलिए पूर्व संस्करण का उपयोग करते हुए YMMV]

<DataGridTemplateColumn Header="Identifier_TEMPLATED">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox IsEditable="False" 
                Text="{Binding ComponentIdentifier,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                ItemsSource="{Binding Path=ApplicableIdentifiers, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ComponentIdentifier}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

पहले दो जवाबों के साथ संघर्ष करने के बाद, मैंने यह कोशिश की और यह मेरे लिए भी काम आया। धन्यवाद।
17

7

RookieRick सही है, DataGridTemplateColumnइसके बजाय DataGridComboBoxColumnएक बहुत सरल XAML देता है।

इसके अलावा, CompanyItemसूची को सीधे पहुंच से बाहर रखने से GridItemआप छुटकारा पा सकते हैं RelativeSource

IMHO, यह आपको बहुत साफ समाधान देता है।

XAML:

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding GridItems}" >
    <DataGrid.Resources>
        <DataTemplate x:Key="CompanyDisplayTemplate" DataType="vm:GridItem">
            <TextBlock Text="{Binding Company}" />
        </DataTemplate>
        <DataTemplate x:Key="CompanyEditingTemplate" DataType="vm:GridItem">
            <ComboBox SelectedItem="{Binding Company}" ItemsSource="{Binding CompanyList}" />
        </DataTemplate>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn CellTemplate="{StaticResource CompanyDisplayTemplate}"
                                CellEditingTemplate="{StaticResource CompanyEditingTemplate}" />
    </DataGrid.Columns>
</DataGrid>

देखें मॉडल:

public class GridItem
{
    public string Name { get; set; }
    public CompanyItem Company { get; set; }
    public IEnumerable<CompanyItem> CompanyList { get; set; }
}

public class CompanyItem
{
    public int ID { get; set; }
    public string Name { get; set; }

    public override string ToString() { return Name; }
}

public class ViewModel
{
    readonly ObservableCollection<CompanyItem> companies;

    public ViewModel()
    {
        companies = new ObservableCollection<CompanyItem>{
            new CompanyItem { ID = 1, Name = "Company 1" },
            new CompanyItem { ID = 2, Name = "Company 2" }
        };

        GridItems = new ObservableCollection<GridItem> {
            new GridItem { Name = "Jim", Company = companies[0], CompanyList = companies}
        };
    }

    public ObservableCollection<GridItem> GridItems { get; set; }
}

4

आपका ComboBox GridItem[x].CompanyItems, जो मौजूद नहीं है, को बांधने की कोशिश कर रहा है।

आपका RelativeBinding करीब है, हालांकि यह DataContext.CompanyItemsWindow.CompanyItems मौजूद नहीं है क्योंकि यह करने के लिए बाध्य करने की जरूरत है


जवाब के लिए धन्यवाद! मैंने कोशिश की है कि ( मेरे प्रश्न में अंतिम XAML स्निपेट CompanyItemsद्वारा प्रतिस्थापित DataContext.CompanyItems) लेकिन यह मुझे डिबगर आउटपुट में समान त्रुटि देता है।
सालामा

1
@ सुलामा मुझे यकीन नहीं है फिर, यह काम करना चाहिए। केवल एक ही चीज़ जो मुझे आपके द्वारा एक्सएएमएल के साथ दिखाई देती है वह है विधा = फाइंडऑनस्टर और मैं आमतौर पर इसे छोड़ देता हूं। क्या आपने अपनी मूल विंडो को एक नाम देने का प्रयास किया है और इसे RelativeSource का उपयोग करने के बजाय अपनी बाइंडिंग में नाम से संदर्भित किया है? {Binding ElementName=RootWindow, Path=DataContext.CompanyItems}
राहेल

दोनों चीजों की कोशिश की है (छोड़ दिया मोड = FindAncestor और एक नामित तत्व के लिए बंधन बदल दिया है), लेकिन यह काम नहीं करता है। अजीब बात है कि यह तरीका आपके लिए काम करता है। मैंने अपने आवेदन से समस्या को बहुत ही सरल संदर्भ में खींचने के लिए यह सरल परीक्षण एप्लिकेशन बनाया है। मुझे नहीं पता कि मैं क्या गलत कर सकता हूं, जिस कोड को आप सवाल में देख रहे हैं वह पूर्ण अनुप्रयोग है (VS2010 में WPF प्रोजेक्ट टेम्पलेट से बनाया गया है), इस कोड के आसपास कुछ भी नहीं है।
सुलामा

1

मैं जिस तरह से उपयोग करता हूं मैं उसी संपत्ति पर टेक्स्टब्लॉक और कॉम्बोबॉक्स को बांधता हूं और इस संपत्ति को InformPropertyChanged का समर्थन करना चाहिए।

मैं relativeresource का उपयोग माता-पिता के डेटा डेटोनेक्स्ट को बांधने के लिए करता था, जो बाध्यकारी में डेटाग्रिड स्तर तक जाने के लिए usercontrol है क्योंकि इस मामले में डेटाट्रेड उस वस्तु में खोज करेगा जो आपने datagrid.itemsource में उपयोग किया था

<DataGridTemplateColumn Header="your_columnName">
     <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
             <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit.Name, Mode=TwoWay}" />
           </DataTemplate>
     </DataGridTemplateColumn.CellTemplate>
     <DataGridTemplateColumn.CellEditingTemplate>
           <DataTemplate>
            <ComboBox DisplayMemberPath="Name"
                      IsEditable="True"
                      ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.UnitLookupCollection}"
                       SelectedItem="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SelectedUnit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                      SelectedValue="{Binding UnitId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                      SelectedValuePath="Id" />
            </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.