How can I use data binding on a details form to add a new entity to my data source? I have created an Object data source, and dragged it onto my form as 'details', which automatically adds a bindingSource and bindingNavigator control to my form, in addition to data bound detail fields. I have the following code which all executes with no exceptions, but no new Branch record gets added when I click 'Add New' and then 'Save'.
private void BranchEditForm_Load(object sender, EventArgs e)
{
branchBindingSource.DataSource = _loansEntities.Branches;
}
private void branchBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
_loansEntities.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
}
I can't seem to find any Entity Framework data binding examples that don't just default to grids.
Adding a new record generated through a details form can be done by calling the the relevant add method in the model before calling the SaveChanges method to commit the record to data source.
For the above, this code might solve the problem, presuming there is a method called AddToBranches which takes a Branch object as an argument.
_loanEntities.AddToBranches((Branch)branchBindingSource.Current));
_loansEntities.SaveChanges(SaveOptions.AcceptAllChangesAfterSave);
Related
I have binded ObservableCollection to the itemsource of the datagrid,
Now for each and every operation performed on the datagrid, i am storing the changes in the undo-redo stack, we have two toolbar buttons for undo and redo, so we are supporting undo-redo on click of these buttons, now we have a requirement, where we want to provide a new button which should Undo All grid changes in one click, it should clear the undo stack.
At present, i am making the call to the business logic to get the original data and reloading the datagrid, as the original data which i have sent to the grid is modified and the changes are accepted.
I am trying to see if i can get the original state of the data at the initial load using undo-redo stack with out multiple refreshes in the datagrid [ user should not feel multiple refreshes hapenning]?
I can maintain a copy of the data before loading the data to the datagrid, but want to know if i can achieve this by the undo-redo stack or by any functions of observablecollections?
...
private myDBDataContext dc = new myDBDataContext(); //context DB
public MainWindow()
{
InitializeComponent();
//binding the datagrid (WPF) with context db and table
if (dc.DatabaseExists())
{
dataGrid.ItemsSource = dc.myTables;
}
}
private void Refres_Click(object sender, RoutedEventArgs e)
{
//call the refresh with "OverwriteCurrentValues" (this is what you need)
dc.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, dc.myTables);
}
Most WPF/EF tutorials only cover databinding in one window. However, in reality data gets displayed across many windows. You often display a record in the first window and dig deeper in related details in the next windows.
So, this also is the case in my scenario. Here you can see my data structure and the ui. Actually I am not dealing with Customers and Invoices, but the structure is the same. (My concrete questions are at the very end.)
In the InvoicesWindow I can select an Invoice and press "Show Invoice". That opens a CustomerWindow displaying Customer details and his invoices. The right invoice is pre-selected. To each Invoice displayed in the CustomerWindow I can add Items or edit them. This is done in a seperated window called "ItemWindow". Editing the DataGrids is not an option. They are set to ReadOnly.
Here is the code of the wpf-window classes (I only have done displaying data yet, not saving):
Invoices Window:
public partial class InvoicesWindow : Window
{
private MyEntities context = new MyEntities();
public InvoicesWindow ()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CollectionViewSource invoicesViewSource = (CollectionViewSource)FindResource("invoicesViewSource");
invoicesViewSource.Source = context.Invoices;
}
private void ShowInvoice_Click(object sender, RoutedEventArgs e)
{
Invoice selectedInvoice = (Invoice)InvoicesDataGrid.SelectedItem;
var customerWindow = new CustomerWindow(selectedInvoice);
customerWindow.ShowDialog();
}
}
Customer Window:
public partial class CustomerWindow : Window
{
private MyEntities context = new MyEntities();
private Invoice selectedInvoice;
public CustomerWindow()
{
InitializeComponent();
}
public CustomerWindow (Invoice selectedInvoice)
{
InitializeComponent();
this.selectedInvoice = selectedInvoice;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Set the data
CollectionViewSource customerViewSource = (CollectionViewSource)FindResource("customerViewSource ");
customerViewSource.Source = context.Customers.Where(p => p.id == selectedInvoice.Customer.id);
//Select the right invoice
CollectionViewSource customerInvoicesViewSource = (CollectionViewSource)FindResource("customerInvoicesViewSource ");
customerInvoicesViewSource.Items.MoveCurrentTo(((ObjectSet<Invoice>)customerInvoicesViewSource.Source).Where(p => p.id == selectedInvoice.id).SingleOrDefault());
}
private void EditItem_Click(object sender, RoutedEventArgs e)
{
Item selectedItem = (Item)ItemsDataGrid.SelectedItem;
var itemWindow = new ItemWindow((IQueryable<Customer>)(customerViewSource.Source),selectedInvoice,selectedItem);
itemWindow.ShowDialog();
}
}
Item window:
public partial class ItemWindow : Window
{
private Invoice _selectedInvoice;
private Invoice _selectedItem;
private IQueryable<Customer> _customers;
public ItemWindo()
{
InitializeComponent();
}
public ItemWindow(IQueryable<Customer> customers, Invoice selectedInvoice, Item selectedItem)
{
InitializeComponent();
this._customers = customers;
this._selectedInvoice = selectedInvoice;
this._selectedItem = selectedItem;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Set the data
CollectionViewSource customerViewSource = (CollectionViewSource)FindResource("customerViewSource");
invoicesViewSource.Source = _customers;
//Select the right invoice
CollectionViewSource customerInvoicesViewSource = (CollectionViewSource)FindResource("customerInvoicesViewSource ");
customerInvoicesViewSource.Items.MoveCurrentTo(_selectedInvoice);
//Select the right item
CollectionViewSource customerInvoicesItemsViewSource = (CollectionViewSource)FindResource("customerInvoicesItems");
customerInvoicesItems.Items.MoveCurrentTo(_selectedItem);
}
}
I wrote the code out of my mind. So, maybe some casts are missing and some methods are mis-spelled. I hope I got the right type with "ObjectSet", it also could be "ObjectCollection" or something like that.
The XAML was created widely with assistance of VS2010 like in this video: http://msdn.microsoft.com/de-de/data/ff806174.aspx
So, finally my questions ;)
Is the design of binding I applied correct?
In CustomerWindow I create a new context.
Between CustomerWindow and ItemWindow I just pass the data of the same context and select the current item manually.
In CustomerWindow I use a ObjectSet (or ObjectCollection, I'm not sure about the type anymore) with a single entry as Source for the customersCollectionViewSource. This works fine. However, there is no need for a collection, because I only edit a single Customer. I did not manage to set a single Customer as Source. I didn't know how to adjust the view source which was generated by VS2010.
I haven't done saving yet. But I think I am going to run into problems due to my design between CustomerWindow and ItemWindow. Maybe you can give me some advice here.
When the "Apply"-Button in ItemWindow gets pressed, the Item data should be updated in DB. But not the Customer- and Invoices-related data in the CustomerWindow underneath.
The DataGrid of Items in CustomerWindow should get updated, when closing the ItemWindow. But not the rest of the fields in the CustomerWindow, since here could have been data changed before opening the ItemWindow.
The only solution for me to overcome that "synchronisation problem": The User is forced to press "Apply" in the CustomerWindow before he can press the "New Item" or "Edit Item", if there have been any changes. (Kinda like the "window resolution control" of windows 7 when working with two monitors) But this is not too user friendly.
A cleaner design would be to use the MVVM design pattern.
Inject the view model into the window's context and bind the view model to either a collection of entities or a single entity, bind in the xaml to properties in the view model(s) and use commands implemented in the view model for actions e.g. add new, delete.
The windows shouldn't be aware of the context.
If you have a list view model + window and a details window (preferably with a view model), then the list view model should pass the selected item to the details view model (or window) as the context.
If the windows are not open at the same time or do not have related objects, then their views models should not share a database context, otherwise, in order for the changes to be reflected easily between the windows, they will have to share the database context.
I am very new to WPF so forgive me if the question doesn't make sense. Is there an event that is fired before data context change? I want to commit the pending data changes before the data context is switched away.
There is no DataContextChanging event, but the DataContextChanged event provides the old value of the DataContext:
private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
object oldDataContext = e.OldValue;
...
}
There is no such event, if you want to make sure data is saved or that the user can choose to abort edits you should look into navigational architectures where screens are changed in a managed way.
I am using Dataform to show a object in my Silverlight application. It is a simple Input page which where you can input value and the SAVE button at the bottom submits the information to the database.
My object contains 7 data fields, out of which I need to show only 5 of them. The other two data fields are added in the database directly.
My silverlight application communicates to the database via WCF services.
So, the question is can I filter data fields on the dataform?
If you are Auto-generating the DataForm, you can use
[Display(AutoGenerateField=false)]
public string SomeProperty {get;set;}
This attribute was previously called Bindable in the SL3 beta, and has since changed in the RTM release. More info here
Following is the snippet from xaml file
dataFormToolkit:DataForm
x:Name="dataForm"
CommitButtonContent="Save"
CancelButtonContent="Cancel"
AutoEdit="True"
AutoGenerateFields="False"
Following is the snippet from xaml.cs file
public CreateProduct()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(CreateProduct_Loaded);
}
private void CreateProduct_Loaded(object sender, RoutedEventArgs e)
{
ServiceReference.Product model = new ServiceReference.Product();
dataForm.CurrentItem = model;
}
I have a object that implements the IEditableObject interface exposed on a viewmodel bound to a Silverlight page.
How/Where do I call the BeginEdit, CancelEdit and EndEdit methods? How can I constrain only objects implementing this interface to my page?
I am NOT using DataGrid or DataForm controls. I am using Label, TextBox and DescriptionViewer controls to display the data for editing.
I know this is an old thread (but for the sake of future use...)
I do it this way:
whenever the current item (for instance of a CollectionViewSource) changes this is done:
void View_CurrentChanged(object sender, EventArgs e)
{
if (culturesView.Source != null)
{
((IEditableObject)SelectedRecord).BeginEdit();
RaisePropertyChanged("SelectedRecord");
}
}
Whenever i want to save (the current item) i do this:
private void Save()
{
((IEditableObject)SelectedRecord).EndEdit();
//do the actual saving to the dbms here ....
}
Whenever i want to cancel (current changes) i do this:
private void Cancel()
{
((IEditableObject)SelectedRecord).CancelEdit();
//allthough we have canceled the editing we have to re-enable the edit mode (because
//the user may want to edit the selected record again)
((IEditableObject)SelectedRecord).BeginEdit();
}
Hope it helps someone in the future!