views:

20

answers:

3

Hi,

I am having a very strange issue. I have a datagrid which is binding to an observable collection of type Person The selected item is bind to an Object Person. I have 2 textboxes firstname and lastname. When ever user selects an item from grid, the textbox values gets populated. user can edit the values and click submit button, values gets updated.

Source to Target works correctly - i.e. able to display from viewModel When I update the values gets updated.

Lets say user selected an item with firstname john, lastname smith The problem is user edits the firstname to johnny and he doesn't click submit button instead he selects a different item from datagrid, so when I go back to the original selected item. In the grid the selected item is shown as John smith, but in the textbox the value is shown as Johnny smith.

How to solve this problem? Any help would be greatly appreciated.

A: 

If you want to reflect changes in datagrid - use INotifyPropertyChanged. If you don't want to automatically save changes of textbox, use

<TextBox x:Name="lastNameText" Text="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />

and call

BindingExpression be = lastNameText.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
vorrtex
A: 

To start, bind to the SelectedItem in the DataGrid to an item from the ObservableCollection. Then bind the TextBox controls to the same SelectedItem from DataGrid (in my case SelectedCustomer). Then update the SelectedCustomer by implementing INotifyPropertyChanged to keep the ObservableCollection insync with the SelectedCustomer. Finally you can include UpdateSourceTrigger=PropertyChanged in the TextBox controls to update the DataGrid while typing in the TextBox if needed.

I've included the code below (except ViewModelBase) to get you started.

Here is the XAML with a DataGrid and two TextBox controls:

<Window x:Class="DataGridTextBox.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
  Title="Main Window" Height="400" Width="800">
  <DockPanel>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <WpfToolkit:DataGrid  
            Grid.Column="0"
            SelectedItem="{Binding Path=SelectedCustomer, Mode=TwoWay}"
            ItemsSource="{Binding Path=Customers, Mode=OneWay}" >
        </WpfToolkit:DataGrid>
        <StackPanel Grid.Column="1">
            <TextBlock Text="First Name"/>
            <TextBox Text="{Binding Path=SelectedCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
            <TextBlock Text="Last Name"/>
            <TextBox Text="{Binding Path=SelectedCustomer.LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
    </Grid>
  </DockPanel>
</Window>

Here is a simple ViewModel:

public class MainViewModel : ViewModelBase
{

  public MainViewModel()
  {
     _customers = Customer.GetSampleCustomerList();
     _selectedCustomer = _customers[0];
  }

  private ObservableCollection<Customer> _customers = null;
  public ObservableCollection<Customer> Customers
  {
     get
     {
        return _customers;
     }
  }

  private Customer _selectedCustomer;
  public Customer SelectedCustomer
  {
     get
     {
        return _selectedCustomer;
     }
     set
     {
        _selectedCustomer = value;
        OnPropertyChanged("SelectedCustomer");
     }
  }
}

For sample code I just set the DataContext of the View to the ViewModel here:

public partial class App : Application
{
  private void OnStartup(object sender, StartupEventArgs e)
  {
     // Create the ViewModel and expose it using the View's DataContext
     Views.MainView view = new Views.MainView();
     view.DataContext = new ViewModels.MainViewModel();
     view.Show();
  }
}

Finally a simple Customer defintion:

public class Customer
{
  public String FirstName { get; set; }
  public String MiddleName { get; set; }
  public String LastName { get; set; }
  public String Address { get; set; }
  public Boolean IsNew { get; set; }

  // A null value for IsSubscribed can indicate 
  // "no preference" or "no response".
  public Boolean? IsSubscribed { get; set; }

  public Customer(String firstName, String lastName,
      String address, Boolean isNew, Boolean? isSubscribed)
  {
     this.FirstName = firstName;
     this.MiddleName = lastName;
     this.LastName = lastName;
     this.Address = address;
     this.IsNew = isNew;
     this.IsSubscribed = isSubscribed;
  }

  public static ObservableCollection<Customer> GetSampleCustomerList()
  {
     return new ObservableCollection<Customer>(new Customer[4] {
            new Customer("Jeff", "Zero", 
                "12 North Third Street, Apartment 45", 
                false, true), 
            new Customer("Joel", "One", 
                "34 West Fifth Street, Apartment 67", 
                false, false),
            new Customer("Jon", "Two", 
                "56 East Seventh Street, Apartment 89", 
                true, null),
            new Customer("Zamboni", "Three", 
                "78 South Ninth Street, Apartment 10", 
                true, true)
        });
  }
}
Zamboni
+1  A: 

Thank you All. The problem is solved using De-Activating event. so whenever a user clicks a new item in the grid, the old item De_Activating event will check whether the data has changed from the original if yes, it will show a warning message for user to either go for a new item or stay back and complete the editing. If the user wishes to stay back and complete the editing then the event is cancelled using e.Cancel = true; and the Active record remains with the old item. if the user goes ahead with the new selection, the old values are restored to the object.

I am sure there might be much better solution and I am definately willing to learn. Thanks a ton for your effort. I really appreciate it.

Naveen Mallela