Vishal
Vishal

Reputation: 6368

Conditional CanUserAddRows in a DataGrid in WPF

Suppose I have a ComboBox like :

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

Here is the Converter :

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;
        }
    }
}

Now I have a 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>

Now I want to provide 1 row by default to the users, so that if CanUserAddRows = false, then also they should be able to add 1 item to DataGrid. If CanUserAddRows = true, then user can have any number of rows he wants.

This thing might be simple but I am new to DataGrid. So, i asked this question.

Upvotes: 2

Views: 1479

Answers (1)

Anatoliy Nikolaev
Anatoliy Nikolaev

Reputation: 22702

In my example, there is a MayUserAddRows property of bool type. If MayUserAddRows == true then User can add as many records, but if MayUserAddRows == false then he will be able to fill only one record.

Also has CanUserAddRows property, which directly Binding with the property of DataGrid.CanUserAddRows.

Properties that are in the ViewModel implement the INotifyPropertyChanged interface via NotificationObject. He has an event PropertyChangedEventHandler(propertyName) which informs on notification properties. The key logic is here:

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);
        }                
    }
}

Below is a full example:

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"

enter image description here

MayUserAddRows="True"

enter image description here

Project compiled under the Visual Studio 2010, this example is fully fits the MVVM style.

This project available here

Upvotes: 2

Related Questions