WPF ComboBox in Datagrid throws error if Binding i

2019-08-01 18:04发布

I have a DataGrid I want to edit. One column is a Combobox

<DataGrid ItemsSource="{Binding Persons, Mode=TwoWay}"
          SelectedItem="{Binding SelectedPerson, Mode=TwoWay}" >
     <DataGrid.Columns>
         <DataGridTemplateColumn Header="Company" >
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Companies}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Company.Name}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

The problem is if the Company is null then I get the error

Two-way binding requires Path or XPath

How can I get around that to allow the Company be set to null?

2条回答
The star\"
2楼-- · 2019-08-01 18:50

Text="{Binding Company.Name}"always requires Companyto be a valid object because the system wants to finally access the Name property. I still have some hope you can create a dummy Company object and set the Name to an empty string. Otherwise a converter helps:

<DataGrid ItemsSource="{Binding Persons, Mode=TwoWay}"
      SelectedItem="{Binding SelectedPerson, Mode=TwoWay}" >
    <DataGrid.Resources>
        <local:MyConverter x:Key="MyConverter"/>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Company" >
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Companies}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Company, Converter={StaticResource MyConverter}}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>


public class MyConverter : IValueConverter
{
    public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            return string.Empty;
        }

        Type myType = value.GetType ();
        PropertyInfo pinfo = myType.GetProperty ("Name");

        if (pinfo == null)
        {
            return string.Empty;
        }

        return (string)pinfo.GetValue(value);
    }

    public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

The converter uses reflexion to access the Name property. If the type of Companyis accessible for the converter (and the logic allows it) you can of course use is directly.

查看更多
看我几分像从前
3楼-- · 2019-08-01 18:54

The question already has a correct answer, so this is just a quick follow up. Let's assume a demo ViewModel:

public MyViewModel()
{
    CompanyType companyA = new CompanyType();
    companyA.Name = "Comp. A";
    CompanyType companyB = new CompanyType();
    companyB.Name = "Comp. B";
    Companies.Add(companyA);
    Companies.Add(companyB);
    PersonType person1 = new PersonType();
    person1.Id = 1;
    person1.Company = null;
    PersonType person2 = new PersonType();
    person2.Id = 2;
    person2.Company = companyB;
    persons.Add(person1);
    persons.Add(person2);
}

Now, what is not working in the ComboBox is that it is not changing the value of a Person's Company after a new value of a different Company is selected from the ComboBox: in the demo let's say we want assign both persons1 and 2 to companyA from the UI.

Furthermore CompanyB is not selected in the ComboBox of person2, if we don't bind the SelectedItem of the ComboBox to the Company property of the PersonType.

Here you find the fix to the Xaml of the Combobox

                <DataTemplate>
                    <ComboBox
                        DisplayMemberPath="Name"
                        SelectedValuePath="Name"
                        SelectedItem="{Binding Company}"
                        ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Companies}"/>
                </DataTemplate>

The classes of my demo

public class PersonType // don't need to notify below
{
    public int Id { get; set; }
    public CompanyType Company { get; set; }
}
public class CompanyType
{
    public string Name { get; set; }
}

Please notice also that, due to the binding of Combobox SelectedItem, person2 has its ComboBox item CompanyB selected in the beginning and now it is possible to change one's Company to CompanyA, for example.

查看更多
登录 后发表回答