Taemyr
Taemyr

Reputation: 3437

How to make a datagrid see edits in a control?

I have a user control that I am using to populate a datagrid.

I would like the user to be able to add items by editing the empty row at the bottom. (This is why I am using a datagrid rather than an itemscontrol) However the datagrid does not realise that the last item is edited unless the user clicks the background of the control. I would like the new item to be added when the user makes changes on the properties that the control exposes.

XAML of the control:

<UserControl x:Class="ControlTest.MyControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:ControlTest"
         mc:Ignorable="d" 
         d:DesignHeight="50" d:DesignWidth="300"
         DataContext="{Binding Path=., Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
         >
<StackPanel Orientation="Vertical">
        <TextBox Text="{Binding Path=p1, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
           Width="300"
           Height="30"
           VerticalAlignment="Center"/>
    <ComboBox ItemsSource="{Binding Path=DropDownValues,
              RelativeSource={RelativeSource Mode=FindAncestor,
                AncestorType=local:MyControl}}"
              SelectedItem="{Binding Path=p2, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
              Height="30"/>
    </StackPanel>
</UserControl>

cs:

public partial class MyControl : UserControl
{
    private static readonly DependencyProperty DropDownValuesProperty =
        DependencyProperty.Register(
        "DropDownValues", 
        typeof(List<String>),
        typeof(MyControl),
        new FrameworkPropertyMetadata(new List<String>()
        ));
    public List<String> DropDownValues
    {
        get
        {
            return (List<String>)GetValue(DropDownValuesProperty);
        }
        set
        {
            SetValue(DropDownValuesProperty, value);
        }
    }

    public MyControl()
    {
        InitializeComponent();
    }        
}

DataGrid XAML

    <DataGrid 
        AutoGenerateColumns="False" 
        ItemsSource="{Binding objs, Mode=TwoWay}" 
        HeadersVisibility="None"
        Margin="0,0,0.4,0"
        CanUserAddRows="True"
        >
        <DataGrid.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </DataGrid.ItemsPanel>
        <DataGrid.Columns>
            <DataGridTemplateColumn Width="300">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="local:Measure">
                        <local:MyControl 
                            DataContext="{Binding ., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                            DropDownValues=
                        "{Binding DataContext.list, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                            Width="300"
                        />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

Can I make this work, and/or is there a better way to do this?

Upvotes: 3

Views: 151

Answers (2)

Vishal
Vishal

Reputation: 6378

I would like to suggest you a different way of doing that:

set CanUserAddRows=false on your DataGrid and then manually add rows to the ObservableCollection<Something> to which your DataGrid is bound to.

OR

If you are still interested in the approach that you follow:

In your xaml file:

<DataGrid x:Name="myDataGrid" CellEditEnding="DataGrid_CellEditEnding" .....>
    <!--Some Code-->
</DataGrid>

Then in the Code-Behind:

private void DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
    myDataGrid.CommitEdit();
}

If you don't understand anything then feel free to ask.

Update

If you are following the same approach:

In your DataGrid's Beginning edit event you can try:

private void DataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
    if ((selectedRow as DataGridRow).Item.ToString() != "{NewItemPlaceholder}")
    {
        //Here you can add the code to add new item. I don't know how but you should figure out a way
    }
}

Note: The code mentioned above is not tested.

I would also suggest you :

Not to use DataGrid. Instead use ListBox. Because, you are trying to add some data. At this time you never need sorting, searching and column-reordering fascilities. In such scenario, ListBox is useful as it is light-weight control than datagrid. I have a sample here: https://drive.google.com/open?id=0B5WyqSALui0bTXFGZWxQUWVRdkU

Upvotes: 2

Patrick Graham
Patrick Graham

Reputation: 992

Is the problem that the UI is not being notified of changes to the objs collection? What I would do is try setting up whatever view model that contains objs to make objs an observable collection. I would also ensure that whatever objs is an observable collection of implements INotifyPropertyChanged and that properties p1 and p2 both fire OnPorpertyChanged when they are set.

public ObservableCollection<YourObject> objs

and

public class YourObject : INotifyPropertyChanged
{
    protected void OnPropertyChanged(string Name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(Name));
        }
    }

    private string _p1;
    public string p1
    {
        get { return _p1; }
        set
        {
            if (_p1 != value)
            {
                _p1 = value;
                OnPropertyChanged("p1");
            }
        }
    }
    private string _p2;
    public string p2
    {
        get { return _p2; }
        set
        {
            if (_p2 != value)
            {
                _p2 = value;
                OnPropertyChanged("p2");
            }
        }
    }
}

Upvotes: 1

Related Questions