Moving to new record on Collection - wpf

I'm just getting started with collections (ObservableCollections) and I've hit a wall that I assumed would be easy. I'm sure it is easy but I'm just not finding the answer.
I have a WPF screen with a DataGrid to the left and TextBoxes to the right of the screen. The DataGrid is bound to the ObservableCollection (Activities) and I can click up and down the DataGrid and see my TextBoxes refresh with the correct info. I can then alter the info in the TextBoxes and save it back to the DB. All works perfectly!
However, when it comes to Adding a record to the collection I'm lost as to the correct approach. I'm using the Add method as shown below, but how do I move to this newly created record so it can be edited? I've tried a dozen approaches but I've yet to find a correct approach. The TextBoxes just remain focussed on the last edited record. Any ideas?
Private Activities As ObservableCollection(Of ActivityRecord)
Private Sub AddMode()
Dim _ActivityRecord As New ActivityRecord(0, DateTime.Now, Nothing, "", gWorkerID, "")
Activities.Add(_ActivityRecord)
'Code to move to the newly created record should go here

In WPF, every ItemsControl derived control (list your DataGrid) secretly uses a derivative of CollectionView to facilitate the navigation between records/items - in effect it is the class which provides the currency mechanism that help other controls (like the textbox) determine which data bound item is current.
If you create a new object and add it to your ObservableCollection, you can use one of the CollectionView's MoveXXX methods to move to make that item current. You can (depending on what sort of CollectionView you get) also call the Add method on the CollectionView, and it will automatically call the Add method on your underlying ObservableCollection.
Either way, retriving a reference to the CollectionView is the secret. You can either use the CollectionView's static method (I forget it's name) to retrieve the view being used for your DataGrid, or (and this is my preferred method) you can explicitely create a ListCollectionView and bind your DataGrid to it, instead of the ObservableCollection.

Related

list[box|view|whatever] with an 'add new...' item at the end

I'm trying to create a wpf control consisting of a list with an element at the end to add a new item (kind of what some grids have). I've been googling around trying to find a similar component but I've found nothing.
I'm new to wpf and willing to write it from scratch if there is nothing similar.
Any ideas will be appreciated.
Thanks!
A DataGrid can do that (does so by default), if your item has no default constructor you can use a BindingList<T> and factory code via the AddingNew event, of course your collection needs to implement IList so items can be added in any case.
For other controls you can also bind them to collection views that support adding, in that case you need to style the NewItemPlaceholder (make it a Button with adding logic for example).

How to assign context and refresh it in Entity Framework?

I created a new entity object and bound it to controls in another window (edit window). After modifying and saving I assigned a new entity object into the one in the main window. The old entity object is bound into a datagrid, now I want the datagrid to display the data that I had modified and saved.
ObjectContext.Refresh Method (RefreshMode, Object) seems to be what I want but I don't know how to use it correctly.
In short :
I have a main window with datagrid displaying the whole data of the table. Users can pick one row and edit it in a edit window. After saving, the datagrid should display what has been modified.
Your best bet here is to use an ObservableCollection as your data source for the datagrid instead of the query.
And look at implementing INotifyPropertyChanged interface in your Customer class.
The ObservableCollection is initially populated by the database query. User changes are made to elements within the ObservableCollection and once complete you then just need to trigger transferring the changes to wherever you originally obtained your list of Customer objects
By doing this changes made both to the collection of Customers and to individual Customer objects (if present within the datagrid) will be automatically updated for you.
edit
I must admit that I'm a bit rushed to offer up any code at the moment, but here's a pretty good article that explains how to use ObservableCollections and classes that implement INotifyPropertyChanged. It also has code examples, which although in VB.NET should give you enough of an idea to get started.
In effect you separate your code into distinct layers UI (View), business logic (View Model) and data layer (Model where your entity framework resides).
You bnd your datagrid to the ObservableCollection type property in your Customers class and your edit csutomer window is bound to as instance of your Customer class.

Items control data source's item property update, not reflecfted in the items control's itempanel template instance

I have a custom panel control that is intended to be used as an itemspaneltemplate in a items control.
The itemscontrol will be databound to a data source.
This datasource is a List, and each item in the list is a custom business object.
In the application, the user is able to update each of these business objects in the list, and that fires the notification on property changed as expected.
Now my problem is here:
When the user updates the object's properties in the data source (the itms in the List) that the items control is bound to, my custom panel control is not able to get that notification, so as a result the items control does not get updated with the updated items in its view.
I tried using an ObservableCollection instead of List - the problem is still the same.
I must be missing something fundamental here... please help with any pointers, answers or solution.
Change notification in a collection is a bit tricky. Say you have a collection of Products. you can implement change notification is three different places.
Change notification in the Product class (implementing INotifyPropertyChanged in class Product)
Change notification in the collection itself (i.e. using ObservableCollection)
Change notification in the class that holds the collection, that is, implementing INotifyPropertyChanged on the class that contains the collection. (usually this would be the ViewModel under MVVM)
Those tree ways are not the same, and each is for a different situation.
Let's say that the collection is ObservableCollection<Product> Products {get;set;}
if you want changes in the product to register (i.e., if you're doing something like Products[0].Name = "New Product"; then #1 is the right one.
If you want to do Products.Add(new Product(...)) then #2 is the right one.
If you want to do Products = new ObservableCollection<Product>() then #3 is the correct one. This is especially tricky since i'm not changing the collection, but creating a new one, so the ObservableCollection won't help - I'd need to implement INPC in the containing class.

How do I open a WPF window in AddNew mode before any data has been loaded?

I want to open a WPF4/EF4 form in AddNew mode so the user can start entering data in bound controls before any data has been selected from the database. I already have an "Add New Record" button but it only works with a populated DataContext (my CollectionViewSource). Here is the code so far:
private void btnAddNewRecord_Click(object sender, RoutedEventArgs e)
{
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesListCollectionView.AddNewItem(newEntity);
}
Background: This is a basic WPF app with bound controls. I started with an Entity Framework model that appears in my DataSources window. I dragged my LabSample entity from the DataSources window and let it create my CollectionViewSource (labSamplesViewSource) in the XAML's Windows.Resources section. The DataContext for all my controls is the labSamplesViewSource. I create a new LabEntities object called _labEntitiesContext as the window is instantiated. I use _labEntitiesContext to build my filtered ObjectQuery(of LabSample) and to SaveChanges, but I'm a little confused as to how this _labEntitiesContext is hooked up to my CollectionViewSource. If you could clarify this along with answering my question that would be helpful. Note: I'm not ready to use MVVM.
When the window loads I use this.FindResource to grab a reference to the CollectionViewSource in a class level variable named _labSamplesCollectionViewSource. I allow the user to enter search fields to populate the screen with data. My LoadData routine looks something like this:
System.Data.Objects.ObjectQuery<LabSample> labSamplesObjectQuery = this.GetLabSamplesFiltered_Query(_labEntitiesContext, sampleID_LIKE, xxx_LIKE, yyy_LIKE);
System.Data.Objects.ObjectResult<LabSample> labSamplesObjectResult = labSamplesObjectQuery.Execute(System.Data.Objects.MergeOption.AppendOnly);
_labSamplesCollectionViewSource.Source = new System.Collections.ObjectModel.ObservableCollection<LabSample>(labSamplesObjectResult);
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
The _labSamplesListCollectionView class level variable set above is used in my btnAddNewRecord_Click code. Before LoadData is called the _labSamplesListCollectionView is null causing my AddNew code to fail with "Object reference not set to an instance of an object".
How can I make this work? I'm wondering if I should be making use of _labSamplesListCollectionView.AddNew instead of my current technique but I couldn't get that work either. Your help will be greatly appreciated.
I am writing an app that does something similar. I am however using MVVM pattern, which allows me to do some neat things. In mine, I am working with Shipments. On the ShipmentsView, I can click an "Add New" button which fires off a bound command property which is housed in the associated ViewModel class. That command methods looks like the following: Note: Views in this context are not CollectionView but refer to MVVM View classes.
Dim NewShipment = New Shipment()
_Context.AddToShipments(NewShipment)
Dim ShipVM = New ShipmentViewModel(NewShipment)
ShipmentVMCollection.Add(ShipVM)
Dim NewShipmentView as ShipmentView(ShipVM)
My ShipmentView handles it's placement and visiblility, and my Shipment object has it's property values initialized so that it does not immediately present errors via it's validation handlers. This way the user can create a new shipment and if they get sidetracked they can save it and come back to it without having a bunch of mandatory fields.
When I use a CollectionViewSource, I populate it with an ObservableCollection of my entities, and then add the entities to that observable collection when I create them. ObservableCollection implements INotifyPropertyChanged and INotifyCollectionChanged events and notifies the UI when something happens, and it all works through the CollectionViewSource.
You might take a look at the MVVM pattern which is really good for moving data and keeping it in the proper scope, and there are some good MVVM frameworks out there that will help you make a nice application with MVVM.
MVVM may be a bit of overkill for your app if it is small. But if it gets over more than just a few Views it is going to get unwieldy and hard to move data back and forth and keep it current, and maintainable.
Wiki Article for MVVM - a pretty good place to start and get links
This is my Constructor for one of my ViewModels. I realize you don't want to implement in MVVM right now, but a code behind would be similar. In this instance, I am using a background worker to get my entity records, (the constructor call be for that and the View setting immediately afterward can be disregarded), then I link up my CVS, Populate it with my ObservableCollection, and set it's View to a field so I can filter on it later.
Public Sub New(ByRef MyView As NTMsView)
Me.New(ViewManagerService.CreateInstance, ViewModelUIService.CreateInstance)
NTMsBackgroundWorker.RunWorkerAsync()
_View = MyView
_NTMCollectionViewSource = _View.FindResource("NTMCollectionViewSource")
_NTMCollectionViewSource.Source = NTMs
_NTMCollectionView = _NTMCollectionViewSource.View
End Sub
This is an example of my AddRecord method. Then I instance a new object, add it to the appropriate collection in the Context, Save it, execute a stored procedure, then refresh the context since the stored procedure did a few things to the record. Then I add the object to my Observable.
Private Sub AddNTM()
'Create an NTM Object.
Dim NewNTM As New NTMShipment()
'Add it to the context
_context.AddToNTMShipments(NewNTM)
_context.SaveChanges()
_context.MakeNewSecurityID(NewNTM.NTMShipID)
_context.Refresh(RefreshMode.StoreWins, NewNTM)
'Wrap it in a ViewModel and Add it to the NTMs collection
NTMs.Add(New NTMViewModel(NewNTM))
End Sub
As for creating a new entity before your CollectionViewSource is created, a couple of questions. Is your edit forms datacontext related to the CVS? In my forms, the CVS is only used in conjunction with ItemsControls since it is displaying a collection of items. If your edit forms controls are dissociated from the CVS, you should not have much trouble populating them with a new entity and when it comes time to save, check to see if CVS is null and if so, create it then populate it.
If that is not a good answer, could you expand on how your application is structured?
Instead of opening the Window in AddNew mode I disable all data entry controls when the window loads or the when a search returns no records. When the "Add New Record" button is clicked I ALWAYS start over with a new data context that contains just one new entity. This means I have to prompt to save changes if any dirty (modified) records exist. The prompt allows the user to save changes, discard changes or continue editing (never entering AddNew mode). Here is the AddNew code:
MessageBoxResult response = PromptToSaveChanges(ReasonForPromptToSave.LoadingData);
if (response == MessageBoxResult.Cancel) return;
LabSample newEntity = _labEntitiesContext.LabSamples.CreateObject<LabSample>();
_labEntitiesContext.LabSamples.AddObject(newEntity);
_labSamplesCollectionViewSource.Source = new ObservableCollection<LabSample>();
_labSamplesListCollectionView = (ListCollectionView)_labSamplesCollectionViewSource.View;
_labSamplesListCollectionView.AddNewItem(newEntity);
_labSamplesListCollectionView.CommitNew();
_labSamplesListCollectionView.Refresh();
Here are my steps to put the window in AddNew mode:
1) Prompt to save changes.
2) Create a new entity and add it to my data context.
3) Create a new ObservableCollection of my entity type and assign it to the .Source of my CollectionViewSource. Note the _labSamplesCollectionViewSource is a reference to the XAML's CollectionViewSource that was auto-generated by dragging a table from the data sources window.
4) Assign the .View of the CollectionViewSource to a class level ListCollectionView variable.
5) Add the new entity to the ListCollectionView that was just created.
6) Call CommitNew and Refresh on the ListCollectionView
I have answered my own question here, but keep in mind that the answer is the result of trial and error and may not be ideal. Regarding my confusion as to how the _labEntitiesContext is hooked up to the CollectionViewSource I believe the answer is in the line that reads _labSamplesListCollectionView.AddNewItem(newEntity), but I'd like to see an explanation of how all of the objects reference each other.
My final comment/question is that I'm disappointed at how hard it is to find a standard reference application or document that teaches non-MVVM WPF/Entity Framework databinding in detail. Microsoft promotes drag-and-drop binding but leaves us without a reference on how to build a complete application. I'll move on to MVVM soon, meanwhile if anyone can direct me to a GREAT resource or feature complete application that is WPF, non-MVVM and Entity Framework I would greatly appreciate it.

How to remove items from an Observable Collection

I appologize for the novel but I wanted to explain as much as I have done thus far.
Within my current project I have an application that consumes a service that provides a collection as a <List>. Due to how I am using this data in the application I have had to convert this data to an observable collection. This was done so that as the data was selected and moved about the application UI updates would be refreshed using INotifyPropertyChanged and INotifyCollectionChanged.
Where I am having a challenge now is I have a listbox that is bound to the observable collection within the listbox I have a datatemplate that renders out the items of the collection. This data template contains a button which needs to allow the user to click the button for each item to remove them from the collection.
The use case for this is a listbox that stores selected name as chosen from a gridview. Once the user has selected names from the gridview they are stored ( within the observable collection as a queue) and rendered out in the UI in a listbox control which shows all selected names. I need to provide the user with the ability to remove these names in any order selected.
From what I have been reading there is no means to enumerate / index an observable collection. For situations such as this you should use List or an Array. However in order for the items to refresh in the list view they need to be in an Observable Collection.
From what I have read it appears that when the event is triggered I need to convert the observable collection to an Array and then evaluate the array to determine the index and then remove the record accordingly?
I think I may be off base on this as it seems like I am over engineering this problem? The above scenario does not seem correct is because I fell as if I am doing a lot of converting to and from the collections to just remove a record?
Does anyone know of an efficient means to remove records from a collection ( in any order selected) when the collection is rendered out as an items control within a listbox?
I’ve been successful in removing the last record added to the collection using RemoveAt() however I have not had any success in randomly removing records.
Afterthought: Part of this issue could be related to the fact that I have a button inserted within the datatemplate (control item) and as a result the item is not actually being selected before the event is fired on the button event?
Sorry for the rambling on this but I have had my head in this for hours and made minor progress. Any tips or ideas would be appreciated!
ObservableCollection<T> inherits from Collection<T> which implements IList<T>, so you can certainly index and enumerate it. It has a Remove method that takes the object to remove and removes the first occurrence in the collection and a RemoveAt method that takes an index and removes the item at that index.
Based on your afterthought, it sounds like you have a WPF ListBox with an ItemTemplate that creates a Button. ListBox will set the DataContext of each instantiated template to the item in the list being bound to, so you can get a reference to the item that created a Button from the DataContext property on the Button or by using a Binding.

Resources