WPF DataGrid automatically updates in-memory data? - wpf

I'm using WPF and MVVM pattern to develop a desktop application. Maybe I'm not clear about how a DataGrid control would work, but if I modify an item (text, checkbox, etc.), the modification persists even if I don't make any permanent database update (using Entity Framework). For example, I may switch to view different data, and when I come back to view the grid with modified data (but without saving to db), the change is there. Somehow the in-memory data has been changed by the DataGrid control and is not refreshed or synced with database.
In other words, the data in the DataGrid remained modified until I stop and re-run it from visual studio.
UPDATED:
Another way to ask this question would be: What actually happens when I update, say, an item of a DataGrid? If it is bound to a ViewModel's property P in two-way mode then I suppose P will be updated. But even if I refresh its value (setting the P to null then calling the data access methods again), the modified data are still there.
Does anybody have any idea of what happened?
Thanks!
UPDATED 2:
Here is the xaml code which binds a DataGrid to a property named UserList in the ViewModel.
<DataGrid
x:Name="UserList"
ItemsSource="{Binding UserList, Mode=TwoWay}"
AutoGenerateColumns="False"
AllowDrop="True"
RowBackground="Orange"
AlternatingRowBackground="#FFC4B0B0">
<!-- define columns to view -->
<DataGrid.Columns>
...
</DataGrid.Columns>
</DataGrid>
Here is the code running in the ViewModel. The method InitialiseData() is called in the constructor of the VM and before I want to do something else with persistent data, so I supposed is always refreshed.
private void InitialiseData()
{
// Retrieves user list from the business layer's response
Response userList = _userBL.GetUserList();
if (userList is FailResponse)
{
MessageBox.Show(userList.Message);
return;
}
else
{
UserList = null;
UserList = (IEnumerable<User>)((SuccessResponse)userList).Data;
}
** UPDATED 3 **:
private IEnumerable<User> _userList;
public IEnumerable<User> UserList
{
get
{
return _userList;
}
set
{
_userList = value;
NotifyOfPropertyChange(() => UserList);
}
}

If you switch back, you are switching to in-memory collection which was updated by DataGrid before. Or do you load data from dtb again?
EDIT:
Ok, now as you have posted the code, I know where is the problem.
DataGrid is not refreshed as I thought. Make sure, you will raise NotifyProperyChanged on the property UserList. Then it will work. See ObservableCollection class as well.

Related

SelectedItem is updating when ListCollectionView changes

I am using WPF, MVVM, and entity framework.
I am working with a data entry application, and I am trying to enable cancel changes to work in my app. Where when changes are cancelled, all values reset to their original value. I think I have everything on the EF side setup correctly. Basically I just set all entities to unchanged if they are in the modified list.
My problem is when I come back to the ViewModel, and I am trying to re-setup all of the fields and derived properties. The biggest annoyance has been the collections. We have multiple combo box controls that we bind to a ListCollectionView and then I have an additional property in the view model that represents the SelectedItem. When I am resetting up the collections I was just allowing the process to re-initiate all the properties including the collections. When I change the collection, it tries to also change the selected property. The problem with this is if it changes the selected property the backing entity gets updated with the new values (as if the user selected an item), and I technically can't get the value back.
I was actually having a reverse problem when I was saving. After the save the form would go into its not edit mode and the value would be set to the old value. Re-opening the form in edit would load the correct value. To fix this I added to the form IsSynchronizedWithCurrentItem=true. But now I am having the problem reverse problem where the value goes back to old value during edit.
// View Code
<ComboBox Grid.Row="1"
Grid.Column="2"
ItemTemplate="{StaticResource TransformerTypeDisplayDataTemplate}"
ItemsSource="{Binding Path=TransformerTypeCollection}"
SelectedItem="{Binding Path=SelectedTransformerType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsSynchronizedWithCurrentItem="True"
Style="{StaticResource AssetViewStateAwareComboBox}" Margin="0,0,0,2" VerticalAlignment="Bottom" />
//ViewModel Properties
private ListCollectionView<TransformerType> _transformerTypeCollection;
public ListCollectionView<TransformerType> TransformerTypeCollection
{
get { return _transformerTypeCollection; }
set { _transformerTypeCollection = value; RaisePropertyChanged("TransformerTypeCollection"); }
}
private TransformerType _selectedTransformerType;
public TransformerType SelectedTransformerType
{
get
{
return _selectedTransformerType;
}
set
{
_selectedTransformerType = value;
if (IsInEditMode)
{
BackingEntity.TransformerTypeID = _selectedTransformerType.ID;
BackingEntity.TransformerType = _selectedTransformerType;
}
RaisePropertyChanged("SelectedTransformerType");
}
}
// Setting the collection will trigger the set method for SelectedTransformerType
TransformerTypeCollection = TaskCoordinator.TransformerTypes.GetView();
My current work around for this problem is I keep around a state variable that says the collections have already been populated. And it skips the resetting the collections on the re-setup of the view model.

How to get DataGrid.SelectedIndex from another ".cs" file?(MVVM)

Earlier when I wanted to edit a row in a DataGrid then I just set the cursor on the row of a DataGtid and wrote such code in a method of a codebehind form(xxx.xaml.cs):
int k = XXXDataGrid.SelectedIndex;
and then I could retrieve data from a row of a DataGrid.
Now I try to use MVVM pattern of WPF and I have read that all my CRUD operations should pe written in Models. OKAY. I tried to take "DataGrid.SelectedIndex" but it is not possible without creating a handler in codebehind file.
I would like to know how can I take "DataGrid.SelectedIndex" and data of a row of a DataGrid from other classes situated in "Models" of MVVM.
When are value types stored in stack?
I have read a lot of books of C# and always when I read about values and references types then to my mind comes a question: When are value types stored in stack? Cause programmer cannot initializes any value type out from class.
Your ViewModel will have Properties that are populated Model objects. Here you will assign them so the View will be able to display data.
public ParentModel
{
get { return parentModel; }
private set
{
if (parentModel != value)
{
parentModel = value;
RaisePropertyChanged("ParentModel");
}
}
}
public int SelectedItemIndex
{
get { return selectedItemIndex; }
set
{
if (selectedItemIndex != value)
{
selectedItemIndex = value;
RaisePropertyChanged("SelectedItemIndex");
}
}
}
The View will contain the object to display data, be it DataGrid, ComboBox etc. You can use ItemTemplates to customize how the data is displayed, but the key is to bind your data to the control. This can allow data to flow in either the direction mode of only ViewModel to View (OneWay), or View to ViewModel (OneWayToSource), or both (TwoWay). So as the user changes the selected index, the data will flow back to the ViewModel as it is set for TwoWay below. The ItemsSource here is set as OneWay so only the ViewModel can change that for the View.
<DataGrid ItemsSource="{Binding Path=ParentModel.Items, Mode=OneWay}"
SelectedIndex="{Binding Path=SelectedItemIndex, Mode=TwoWay}" />
If ViewModels need to interact with BusinessLogic, just pass the Data. If a seperate ViewModel needs the information, you'll need to use the concept available in your framework, eg. EventAggregator (Prism), to pass data around since they won't have knowledge of each other.
Core concept of MVVM is the binding of models and WPF controls' properties like this. I think you'll want to read up more on it to fully utilize the power and design. Some very helpful questions/answers can be found on StackOverflow, MSDN has several good tutorials, etc.

WPF Toolkit datagrid /doesn’t refresh data

H I use SQL CE and LINQ. I bind property typeof Table on ItemSource of Datagrid control from WPF Toolkit.
Something like this.
public Table<TestNick> MySource
{
get { return _tab; }
set
{
_tab = value;
NotifyPropertyChanged("MySource");
}
}
<Controls:DataGrid Name="Dg"
ItemsSource="{Binding Path=MySource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="0"/>
I retrieve data from DB with LINQ:
const string connStr = #"Spiri_SQL_CE_DB.sdf";
_dc = new Spiri_SQL_CE_DB(connStr);
MySource = _dc.TestNick;
If I add a breakpoint on last line I see all values from tables TestNick, but it doesn’t load this data in DataGrid.
What is bad?
EDITED:
I check the ItemSource of DataGrid control in code behind, the item source is correct but I see in DataGrid (view) "old data".
So binding must be correct, problem is that DataGrid control doesn’t refresh data.
Make sure datagrid autogeneratecolumns is true
While running check the output window if there are any binding issues
Another trick is to put a button on the view and write a code behind function on click of that button to debug whats the datagrid itemsource, if its empty try to invoke viewModel/Model's getDatagridData function and then see if it loads, in case it loads that means your NotifyPropertyChanged is not yet functional

WPF databinding to System.Data.Linq.Table<T>, how to update the interface?

I have this combo bound to a linq table.
Is there a lightweight way to update the UI (combo values that are displayed to the user) when I insert a new record in the linq table?
Basically from what I understand I should have used an ObservableCollection, but I don't want to copy the data back & forth from the linq table to that collection, I only want to have data in the linq table.
Is that possible?
EDIT
OK Here is what i have done (and still it doesn't work):
private ObservableCollection<Categories> m_Categories;
private ObservableCollection<Categories> Categories
{
get
{
return m_Categories;
}
}
in the XAML i have:
<ComboBox Name="cmbCategory"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
/>
So, pretty simple.
//if i have a new category, i want to update the combo's content
if (frmEditCategory.ShowDialog() == true)
{
//get the new category and add it to the ObservableCollection
LibraryDataStore.Instance.Categories.ToList().ForEach(p =>
{
if (!m_Categories.Contains(p))
{
m_Categories.Add(p);
}
});
//update the target? is this correct?!
BindingExpression be = cmbCategory.GetBindingExpression(ComboBox.ItemsSourceProperty);
if (be != null)
be.UpdateTarget();
}
Checked with the debugger, m_Categories contains the new category, but it doesn't show up in the combo.
Also, do you know any good tutorial or blog post about combo binding?...
Thank you in advance
What you are asking for is not possible. You need a mediator of some sort, and you have already identified the correct one- ObservableCollection. You have to move the Linq data somewhere if you want your user interface to be notified when it changes. You should have one ObservableCollection, which you add your Linq data to, and then you should bind the combo-box to the ObservableCollection rather than directly to the table. That is the simplest, most light-weight solution you can create and still receive automatic change notifications.

How to get an ItemsSource to refresh its bind?

I've got a view which shows a listbox that is bound to GetAll():
<DockPanel>
<ListBox ItemsSource="{Binding GetAll}"
ItemTemplate="{StaticResource allCustomersDataTemplate}"
Style="{StaticResource allCustomersListBox}">
</ListBox>
</DockPanel>
GetAll() is an ObservableCollection property in my ViewModel:
public ObservableCollection<Customer> GetAll
{
get
{
return Customer.GetAll();
}
}
which in turn calls a GetAll() model method which reads an XML file to fill the ObservableCollection.:
public static ObservableCollection<Customer> GetAll()
{
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
XDocument xmlDoc = XDocument.Load(Customer.GetXmlFilePathAndFileName());
var customerObjects = from customer in xmlDoc.Descendants("customer")
select new Customer
{
Id = (int)customer.Element("id"),
FirstName = customer.Element("firstName").Value,
LastName = customer.Element("lastName").Value,
Age = (int)customer.Element("age")
};
foreach (var customerObject in customerObjects)
{
Customer customer = new Customer();
customer.Id = customerObject.Id;
customer.FirstName = customerObject.FirstName;
customer.LastName = customerObject.LastName;
customer.Age = customerObject.Age;
customers.Add(customer);
}
return customers;
}
This all works fine EXCEPT when the user goes to another view, edits the XML file and comes back to this view where the old data is still showing.
How can I tell this view to "refresh its bindings" so that it shows the actual data.
It feels like I am going about WPF here with too much of an HTML/HTTP metaphor, I sense there is a more natural way to get ObservableCollection to update itself, hence its name, but this is the only way I can get the user to be able to edit data in a WPF application at the moment. So help on any level is appreciated here.
An ItemsControl requests its binding once and caches the reference thereafter.
If the content of the collection object are modified, and it implements INotifyCollectionChanged (as ObservableCollection does), it will pick up any added or removed object.
Now, if you want the binding to supply a new collection object to the ListBox, you can have your view-model implement INotifyPropertyChanged and raise PropertyChanged, passing in GetAll as the property name.
This will have the effect of warning the binding that the property value has changed (there is a new ObservableCollection ready to be picked up), which it will supply to the ListBox, which will re-generate its items.
So as long as you effect changes from your app, working on the ObservableCollection returned by GetAll, you can add and remove and the list will stay in synch. When you want to pick up external modifications (you might have a refresh button somewhere, or a natural point where it makes sense to reload the whole file), you can have your view-model raise the PropertyChanged event, which will automatically call the property getter, which will call the static method, which will return a fresh new collection.
Nitpicker note: why do you give method names to properties?
Below line work same as when we remove to add object in collection:
CollectionViewSource.GetDefaultView(CustomObservableCollection).Refresh();
Keep a reference to your ObservableCollection and the XML file's last-modified time as of the time you loaded it. Whenever the window gets focus, check the timestamp on the disk file. If it's changed, clear and re-populate the ObservableCollection. The GUI is automatically listening for change events from the ObservableCollection and will re-populate automatically when you modify the collection's contents.

Resources