【问题标题】:Conditional CanUserAddRows in a DataGrid in WPFWPF 中 DataGrid 中的条件 CanUserAddRows
【发布时间】:2014-04-04 19:05:22
【问题描述】:

假设我有一个像这样的组合框:

<ComboBox SelectedValue="{Binding DataContext.CanUserAddMultipleRows, 
                                  RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}},
                                 Converter={StaticResource yesNoToBooleanConverter}}">
    <ComboBoxItem>Yes</ComboBoxItem>
    <ComboBoxItem>No</ComboBoxItem>
</ComboBox>

这是转换器:

public class YesNoToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value == null || value == DependencyProperty.UnsetValue))
        {
            if ((bool)value == true)
            {
                return "Yes";
            }
            else
            {
                return "No";
            }
        }
        else
        {
            return "No";
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value == null || value == DependencyProperty.UnsetValue))
        {
            if (((ComboBoxItem)value).Content.ToString() == "Yes")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
}

现在我有一个 DataGrid :

<DataGrid Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" AutoGenerateColumns="False" 
          CanUserAddRows="{Binding DataContext.CanUserAddMultipleRows, 
                                   RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"
          ItemsSource="{Binding DataContext.MyObject,
                                RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Quantity" Binding="{Binding Quantity}"></DataGridTextColumn>
        <DataGridTextColumn Header="Rate" Binding="{Binding Rate}"></DataGridTextColumn>
        <DataGridTextColumn Header="Amount" Binding="{Binding Amount}"></DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

现在我想默认为用户提供 1 行,这样如果 CanUserAddRows = false,那么他们也应该能够向 DataGrid 添加 1 项。如果 CanUserAddRows = true,则用户可以拥有任意数量的行。

这件事可能很简单,但我是 DataGrid 的新手。所以,我问了这个问题。

【问题讨论】:

    标签: c# wpf xaml datagrid


    【解决方案1】:

    在我的示例中,有一个 bool 类型的 MayUserAddRows 属性。如果MayUserAddRows == true那么用户可以添加尽可能多的记录,但如果MayUserAddRows == false那么他将只能填写一条记录。

    还有CanUserAddRows属性,直接绑定DataGrid.CanUserAddRows的属性。

    ViewModel 中的属性通过 NotificationObject 实现 INotifyPropertyChanged 接口。他有一个事件PropertyChangedEventHandler(propertyName) 通知通知属性。关键逻辑在这里:

    private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName.Equals("MayUserAddRows")) 
        {
            // The MayUserAddRows property is changed
            if (MyViewModel.MayUserAddRows == true) 
            {
                // Allow to add a lot of records
                MyViewModel.CanUserAddRows = true;
            }
    
            if (MyViewModel.MayUserAddRows == false)
            {
                // Prohibit the addition 
                MyViewModel.CanUserAddRows = false;
    
                // And add the empty row
                AddEmptyRow(MyViewModel.MyCollection);
            }                
        }
    }
    

    下面是一个完整的例子:

    XAML

    <Window x:Class="ConditionalCanUserAddRows.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:this="clr-namespace:ConditionalCanUserAddRows"
            WindowStartupLocation="CenterScreen"
            Title="MainWindow" Height="300" Width="325">
    
        <Grid>
            <CheckBox Content="{Binding Path=IsChecked,
                                        RelativeSource={RelativeSource Mode=Self}}"
                      ContentStringFormat="May user add rows - {0}"
                      HorizontalAlignment="Left"
                      VerticalAlignment="Top"
                      IsChecked="{Binding Path=MayUserAddRows}" />
    
            <Button Content="Clear" 
                    VerticalAlignment="Top"
                    HorizontalAlignment="Right"
                    Click="Clear_Click" />
    
            <DataGrid Name="SimpleDataGrid"
                      Width="200"
                      Height="200"
                      CanUserResizeColumns="False"                  
                      AutoGenerateColumns="False" 
                      RowHeaderWidth="0" 
                      CanUserAddRows="{Binding Path=CanUserAddRows, Mode=TwoWay}"
                      ItemsSource="{Binding Path=MyCollection}">
    
                <DataGrid.Columns>
                    <DataGridTextColumn Width="1.5*"
                                        Header="Name"
                                        Binding="{Binding Path=Name}" />
    
                    <DataGridTextColumn Header="Age"                                    
                                        Width="1.5*" 
                                        FontSize="14" 
                                        Binding="{Binding Path=Age}" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>
    

    Code-behind

    public partial class MainWindow : Window
    {   
        ViewModel MyViewModel = new ViewModel();
    
        public MainWindow()
        {
            InitializeComponent();
    
            this.DataContext = MyViewModel;
    
            MyViewModel.MyCollection = new ObservableCollection<Person>();
    
            MyViewModel.MyCollection.Add(new Person()
            {
                Age = 22,
                Name = "Nick",
            });
    
            MyViewModel.MyCollection.Add(new Person()
            {
                Age = 11,
                Name = "Sam",
            });
    
            MyViewModel.MyCollection.Add(new Person()
            {
                Name = "Kate",
                Age = 15,
            });
    
            AddEmptyRow(MyViewModel.MyCollection);
    
            MyViewModel.PropertyChanged += new PropertyChangedEventHandler(MyViewModel_PropertyChanged);
        }
    
        private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("MayUserAddRows"))
            {
                if (MyViewModel.MayUserAddRows == true) 
                {
                    MyViewModel.CanUserAddRows = true;
                }
    
                if (MyViewModel.MayUserAddRows == false)
                {
                    MyViewModel.CanUserAddRows = false;
                    AddEmptyRow(MyViewModel.MyCollection);
                }                
            }
        }
    
        #region AddEmptyRow
    
        private void AddEmptyRow(ObservableCollection<Person> collection) 
        {
            collection.Add(new Person()
            {
                Name = "",
                Age = 0,
            });
        }
    
        #endregion
    
        #region Clear
    
        private void Clear_Click(object sender, RoutedEventArgs e)
        {
            MyViewModel.MyCollection.Clear();
        }
    
        #endregion
    }
    
    #region ViewModel
    
    public class ViewModel : NotificationObject
    {
        #region MyCollection
    
        public ObservableCollection<Person> MyCollection
        {
            get;
            set;
        }
    
        #endregion
    
        #region CanUserAddRows
    
        private bool _canUserAddRows = false;
    
        public bool CanUserAddRows
        {
            get
            {
                return _canUserAddRows;
            }
    
            set
            {
                _canUserAddRows = value;
                NotifyPropertyChanged("CanUserAddRows");
            }
        }
    
        #endregion
    
        #region MayUserAddRows
    
        private bool _mayUserAddRows = false;
    
        public bool MayUserAddRows
        {
            get
            {
                return _mayUserAddRows;
            }
    
            set
            {
                _mayUserAddRows = value;
                NotifyPropertyChanged("MayUserAddRows");
            }
        }
    
        #endregion
    }
    
    #endregion
    
    #region Model
    
    public class Person
    {
        public string Name
        {
            get;
            set;
        }
    
        public int Age
        {
            get;
            set;
        }
    }
    
    #endregion
    
    #region NotificationObject
    
    public class NotificationObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    #endregion
    

    Output

    MayUserAddRows="False"

    MayUserAddRows="True"

    在Visual Studio 2010下编译的项目,这个例子完全符合MVVM风格。

    此项目可用here

    【讨论】:

    • 很好的答案!谢谢。我想再添加 3 个功能。 1)用户点击清除按钮。然后他检查了复选框,当时添加了 2 行。 1 具有默认值,另一个为空。那时我不希望用户有一行具有默认值。 2)当用户取消选中复选框时,我想清除所有行以及年龄列的单元格应该包含所有年龄的总和。例如在您回答的最后一张图片中,如果我取消选中该复选框,那么应该只有 1 行,并且年龄应该包含 1491(因为 22+11+15+666+777 = 1491)。
    • 3) 我还想要一个只有两个值的新 DataGridComboBoxColumn。即 1) 继续 2) 列表结束。如果用户选择继续,那么他应该能够在当前行中添加姓名和年龄等详细信息,并且应该能够在填充当前行的详细信息后添加新行。否则,不应允许他在当前行中填写更多详细信息,例如姓名和年龄,并且不应在该行之后创建新行。
    • @Vishal:这就是我必须实现的全部?对不起,但我只做了有问题的,其他你可以自己做的。此外,您最初必须在问题中写下它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    • 2012-12-05
    相关资源
    最近更新 更多