I have a simple datagrid listing addresses and a child window where the user can edit/add new. On the main form with the datagrid i have a button to "Insert New Address" which should load the child window with an empty Address object. However it will not let me add a record. Am i doing something wrong? my current code is as follows:
Dim address As New Address
Dim frmAddressObj As New frmAddress
If frmAddressObj.AddressDomainDataSource.DataView.CanAdd = False Then
frmAddressObj.AddressDomainDataSource.Load()
End If
frmAddressObj.AddressDomainDataSource.DataView.Add(address)
Address is the address object. frmAddress is the child window form. AddressDomainDataSource is the same datasource i use in the datagrid as i use in the child. CanAdd is always false and i got told to try loading before adding but this does not appear to have helped. When it reaches the Add method it returns an Exception of 'Add' is not supported by this ICollectionView. Any help would be appreciated. Thanks
The DataView field should be thought of as a read-only collection. The simplest general usage of the DomainDataSource with a DataGrid goes something like this:
(myDataSource.DomainContext as myDomainContext).my_entitys.Remove(dgOrders.SelectedItem as order);
(myDataSource.DomainContext as myDomainContext).SubmitChanges();
It is similar for insert, you just use
my_entitys.Add(myNewEntityInstance);
instead of
my_entitys.Remove(entityToRemove);
And for updates you simply call
(myDataSource.DomainContext as myDomainContext).SubmitChanges();
You must also have the insert method in your domain service. So make sure you have a method that looks like:
Public Sub InsertAddress(address As Address)
End Sub
or in C#
public void InsertAddress(Address address)
I had the same problem recently and in my case, the DomainDataSource wasn't loaded (or even bound to its context yet) because it resided in a TabItem that wasn't selected.
Make sure you have your DomainDataSource properly loaded in the visual tree, that solved the issue in my case.
Related
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).
I have a winform that needs to be loaded to update its controls' values or properties, before it is to be shown.
I found a stackoverflow question asking the same thing, but it's answer doesn't really help me. Load a form without showing it
Any sample code will be appreciated. Thank you,
Only you need create a new instance of the form and set the values of the controls.
check this code
Var
AForm : ChildForm;
begin
AForm:= new ChildForm;
AForm.textBox1.Text:='Foo'; //this control can be accessed here because the Modifiers property was set to public.
AForm.Show;
end;
Btw remember if you want modify or access the controls of another form you must set the property Modifiers of the control to access to public.
Create the form like this:
form := new MyForm();
Assuming you have implemented a method on MyForm to update the values, call it:
form.Update();//may need to pass parameters here
Show the form in the usual way:
form.ShowDialog();
From MSDN:
Form.Load
Occurs before a form is displayed for the first time.
So you can do all updates to the controls that are necessary before you show the form in this event handler.
But actually it is probably better to use databinding on the controls, so that they automatically reflect the current values you want them to show and you don't have to write any glue code bringing data on controls (and reading from them).
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.
I populate a ListBox control with my own objects redefining ToString(). The objects are displayed correctly when I just add those objects using listBox1.Add(myObject). However, if I later change something in this object, no changes are displayed in the listbox. Debugging reveals that an object inside listBox1.Items is indeed changed, but it is not reflected on a screen.
Interestingly enough, if I reassign a particular listbox item to itself (sounds a bit weird, doesn't it?), like:
listBox1.Items[0] = listBox1.Items[0]
this line will display a correct value on screen.
What is going on here? Does it have anything to do with threading?
Since you're using ToString of the object to provide the text of the list box item, the ListBox has no idea that the value has changed. What you should do instead is have the object implement INotifyPropertyChanged then expose a public property such as Name or Text and return what you normally would have returned from ToString().
Then set the DisplayMember of the ListBox to the name of the new property.
Make sure you are correctly raising the PropertyChanged event in the object and the ListBox should be able to automatically pick up the changes.
Edit: Adrian's edit reminded me that I do believe you'll need to use a BindingList as your data source in order for the property change notifications to be picked up. A quick scan in Reflector looks like ListBox on its own will not pick up the property changes mentioned above. But INotifyPropertyChanged + BindingList should.
The ToString() value of each item is cached when the listbox is first displayed. If an item in the listbox's Items collection then changes, the listbox does not notice and still uses the cached ToString() values for display. To force the listbox to update, either call RefreshItems() to refresh all items, or call RefreshItem(int) specifying the index of the item to refresh.
From the MSDN docs for RefreshItems():
Refreshes all ListBox items and retrieves new strings for them.
EDIT: It turns out that both of these methods are protected, so cannot be called externally. In trying to find a solution, I came across this SO question that this question is basically a duplicate of.
Have you tried calling Refresh() on the ListBox? I think the problem is that the ListBox does not know your object changed. The reason reassigning the the item works is because the ListBox will repaint itself when the collection changes.
you could invalidate the control, forcing a re-paint... perhaps..
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.