新行上的WPF C#datagrid组合框不会更新数据源(更新!)

时间:2015-10-03 18:35:23

标签: c# wpf mvvm combobox datagrid

这是一个展示我遇到麻烦的行为的例子。我有一个datagrid绑定到viewmodel中的可观察记录集合。在datagrid中,我有一个DataGridTemplateColumn,它包含一个组合框,该组合框是从viewmodel中的列表中填充的。数据网格还包含文本列。窗口底部有一些文本框,用于显示记录内容​​。

<Window x:Class="Customer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Customer"
    Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <local:SelectedRowConverter x:Key="selectedRowConverter"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="8*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
                  ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow,
                    Converter={StaticResource selectedRowConverter}, Mode=TwoWay}"
                  CanUserAddRows="True" Grid.Row="0" SelectionChanged="dgCustomers_SelectionChanged">
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="Auto" Header="Country">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cmbCountry" ItemsSource="{Binding DataContext.countries,
                                RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                      DisplayMemberPath="name" SelectedValuePath="name" Margin="5"
                                      SelectedItem="{Binding DataContext.SelectedCountry,
                                RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay,
                                UpdateSourceTrigger=PropertyChanged}" SelectionChanged="cmbCountry_SelectionChanged" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Name" Binding="{Binding name}" Width="1*"/>
                <DataGridTextColumn Header="Phone" Binding="{Binding phone}" Width="1*"/>
            </DataGrid.Columns>
        </DataGrid>

        <Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
                <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            <Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            <BulletDecorator  Grid.Column="0">
                <BulletDecorator.Bullet>
                    <Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
            </BulletDecorator>
            <BulletDecorator Grid.Column="1">
                <BulletDecorator.Bullet>
                    <Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
            </BulletDecorator>
            <BulletDecorator Grid.Column="2">
                <BulletDecorator.Bullet>
                    <Label  Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
                </BulletDecorator.Bullet>
                <TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
            </BulletDecorator>
        </Grid>
    </Grid>
</Window>

最初没有记录,因此datagrid为空,只显示包含组合框的一行。如果用户首先将数据输入到文本列中,则将记录添加到集合中,并且可以将组合框值添加到记录中。但是,如果用户首先选择组合框值,则在选择另一列时该值将消失。如果首先选择组合框数据,如何将组合框数据添加到记录中?

代码隐藏:

public partial class MainWindow : Window
{
    public GridModel gridModel { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        gridModel = new GridModel();
        //dgCustomers.DataContext = gridModel;
        this.DataContext = gridModel;
    }

    private void cmbCountry_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ComboBox c = sender as ComboBox;
        Debug.Print("ComboBox selection changed, index is " + c.SelectedIndex + ", selected item is " + c.SelectedItem);
    }
}

记录类:

public class Record : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private string _phone;
    public string phone
    {
        get { return _phone; }
        set
        {
            _phone = value;
            OnPropertyChanged("phone");
        }
    }

    private int _countryCode;
    public int countryCode
    {
        get { return _countryCode; }
        set
        {
            _countryCode = value;
            OnPropertyChanged("countryCode");
        }
    }
}

国家/地区类:

public class Country : ViewModelBase
{
    private string _name;
    public string name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("name");
        }
    }

    private int _id;
    public int id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("id");
        }
    }

    private int _code;
    public int code
    {
        get { return _code; }
        set
        {
            _code = value;
            OnPropertyChanged("code");
        }
    }

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

GridModel:

public class GridModel : ViewModelBase
{
    public ObservableCollection<Record> customers { get; set; }
    public List<Country> countries { get; set; }
    public GridModel()
    {
        customers = new ObservableCollection<Record>();
        countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
        new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
    }

    private Country _selectedCountry;
    public Country SelectedCountry
    {
        get
        {
            return _selectedCountry;
        }
        set
        {
            _selectedCountry = value;
            _selectedRow.countryCode = _selectedCountry.code;
            OnPropertyChanged("SelectedRow");
        }
    }

    private Record _selectedRow;
    public Record SelectedRow
    {
        get
        {
            return _selectedRow;
        }
        set
        {
            _selectedRow = value;
            Debug.Print("Datagrid selection changed"); 
            OnPropertyChanged("SelectedRow");
        }
    }
}

转换器:

class Converters
{
}

public class SelectedRowConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is Record)
            return value;
        return new Customer.Record();
    }
}

ViewModelBase:

public class ViewModelBase : INotifyPropertyChanged
{
    public ViewModelBase()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

1 个答案:

答案 0 :(得分:2)

您看到的行为符合预期。其背后的原因是ComboBox ItemsSource以及SelectedItem都绑定到Properties Window的{​​{1}} }其他列绑定到DataContext DataGrid。因此,当您修改下拉列表以外的列时,数据将添加到observable集合中。

您可以做的是从您需要自己添加记录的下拉列表中选择一个值后(可能通过调用ItemsSource属性中的函数)

修改

根据您的代码,我制作了一个工作模型,尽可能少地对现有代码进行更改。我无法使用转换器,因为我没有类SelectedCountry

的详细信息

的Xaml

Customer

您的GridModel类

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="8*"/>
        <RowDefinition Height="3*"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0">
        <Button HorizontalAlignment="Right" Content="Add User" Margin="0,2,2,2" Command="{Binding AddUserCommand}"/>
        <DataGrid x:Name="dgCustomers"
          AutoGenerateColumns="False"
          ItemsSource="{Binding customers}"
          SelectedItem="{Binding SelectedRow}"
          SelectionUnit="FullRow"
          CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn Width="Auto" Header="Country">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox Focusable="False" 
                                      ItemsSource="{Binding DataContext.countries, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                      DisplayMemberPath="name"
                                      SelectedValuePath="code"
                                      SelectedValue="{Binding countryCode, UpdateSourceTrigger=PropertyChanged}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn Header="Name" Binding="{Binding name, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
                <DataGridTextColumn Header="Phone" Binding="{Binding phone, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
            </DataGrid.Columns>
        </DataGrid>
    </StackPanel>
    <Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
        <BulletDecorator  Grid.Column="0">
            <BulletDecorator.Bullet>
                <Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="1">
            <BulletDecorator.Bullet>
                <Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
        </BulletDecorator>
        <BulletDecorator Grid.Column="2">
            <BulletDecorator.Bullet>
                <Label  Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
            </BulletDecorator.Bullet>
            <TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
        </BulletDecorator>
    </Grid>
</Grid>

我使用了包含public class GridModel : ViewModelBase { public ObservableCollection<Record> customers { get; set; } public ObservableCollection<Country> countries { get; private set; } public GridModel() { customers = new ObservableCollection<Record> { }; AddUserCommand = new RelayCommand(AddNewUser); countries = new ObservableCollection<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 }, new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 } }; } private void AddNewUser() { customers.Add(new Record()); } public ICommand AddUserCommand { get; set; } private Record _selectedRow; public Record SelectedRow { get { return _selectedRow; } set { _selectedRow = value; Debug.Print("Datagrid selection changed"); OnPropertyChanged("SelectedRow"); } } } 的{​​{3}}工具包。您还可以定义自己的RelayCommand实现,并使用它而不是工具包

编辑2

修正了我引入的错误,如果数据来自数据库,则会阻止组合框显示国家/地区。改进的代码不需要任何转换器