Update DataGrid on item change with Caliburn Micro - mvvm

I have a datagrid which is bound to a collection of items using Caliburn Micro. I would like the grid to update as soon as a user makes an edit on each row. I would think this would be simple (like ASP.NET simple) but I haven't found anything that seems to work.
Here is my ViewModel
public class JournalViewModel : Caliburn.PresentationFramework.PropertyChangedBase
{
private CrystalRptDataEntities ctx = new CrystalRptDataEntities();
private BindableCollection<EmployeeInfo> employees;
public JournalViewModel()
{
Load();
}
public void Load()
{
employees = new BindableCollection<EmployeeInfo>(ctx.EmployeeInfoes);
AllEmployees = employees;
}
public BindableCollection<EmployeeInfo> AllEmployees
{
get { return employees; }
set
{
employees = value;
NotifyOfPropertyChange(() => AllEmployees);
}
}
//....
}
Here is my view
<DataGrid x:Name="AllEmployees" AutoGenerateColumns="True" />

I found the solution to my own question - it took 3 things.
1) I had to add this method to my JournalViewModel class
public void SaveChanges()
{
ctx.SaveChanges();
}
2) Then I had to add these 2 references to my xaml file
xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org"
3) Then I had to attach an Event to my Datagrid like this:
<DataGrid x:Name="AllEmployees"
AutoGenerateColumns="True"
cal:Message.Attach="[Event CellEditEnding]=[Action SaveChanges()]">
That way every time I finished editing a cell, the ctx gets saved.

Related

Delete rows from Nattable

I want to implement a row deletion logic in a Nebula Nattable.
This is what I plan to do:
Add context menu to the Nattable which is described in http://blog.vogella.com/2015/02/03/nattable-context-menus-with-eclipse-menus/
Add an SWT Action to the menu which will implement the delete
my question is, which is the best way to accomplish this:
Should I delete the corresponding value from my data model and the table view is refreshed when I execute this.natview.refresh();?
OR
Should I get the rows from SelectionLayer and delete them (if so how do I do ?)?
OR
is there any default support for this function through IConfiguration?
In NatTable you would typically do the following:
Create a command for deleting a row
public class DeleteRowCommand extends AbstractRowCommand {
public DeleteRowCommand(ILayer layer, int rowPosition) {
super(layer, rowPosition);
}
protected DeleteRowCommand(DeleteRowCommand command) {
super(command);
}
#Override
public ILayerCommand cloneCommand() {
return new DeleteRowCommand(this);
}
}
Create a command handler for that command
public class DeleteRowCommandHandler<T> implements ILayerCommandHandler<DeleteRowCommand> {
private List<T> bodyData;
public DeleteRowCommandHandler(List<T> bodyData) {
this.bodyData = bodyData;
}
#Override
public Class<DeleteRowCommand> getCommandClass() {
return DeleteRowCommand.class;
}
#Override
public boolean doCommand(ILayer targetLayer, DeleteRowCommand command) {
//convert the transported position to the target layer
if (command.convertToTargetLayer(targetLayer)) {
//remove the element
this.bodyData.remove(command.getRowPosition());
//fire the event to refresh
targetLayer.fireLayerEvent(new RowDeleteEvent(targetLayer, command.getRowPosition()));
return true;
}
return false;
}
}
Register the command handler to the body DataLayer
bodyDataLayer.registerCommandHandler(
new DeleteRowCommandHandler<your type>(bodyDataProvider.getList()));
Add a menu item to your menu configuration that fires the command
new PopupMenuBuilder(natTable)
.withMenuItemProvider(new IMenuItemProvider() {
#Override
public void addMenuItem(NatTable natTable, Menu popupMenu) {
MenuItem deleteRow = new MenuItem(popupMenu, SWT.PUSH);
deleteRow.setText("Delete");
deleteRow.setEnabled(true);
deleteRow.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent event) {
int rowPosition = MenuItemProviders.getNatEventData(event).getRowPosition();
natTable.doCommand(new DeleteRowCommand(natTable, rowPosition));
}
});
}
})
.build();
Using this you don't need to call NatTable#refresh() because the command handler fires a RowDeleteEvent. I also don't suggest to call NatTable#refresh() in such a case, as it might change and refresh more than it should and would not update other states correctly, which is done correctly by firing the RowDeleteEvent.
Note that the shown example deletes the row for which the context menu is opened. If all selected rows should be deleted, you should create a command handler that knows the SelectionLayer and retrieve the selected rows as shown in the other answer.
In our application we do the following:
Get selected row objects:
SelectionLayer selectionLayer = body.getSelectionLayer();
int[] selectedRowPositions = selectionLayer.getFullySelectedRowPositions();
Vector<Your Model Objects> rowObjectsToRemove = new Vector<Your Model Objects>();
for (int rowPosition : selectedRowPositions) {
int rowIndex = selectionLayer.getRowIndexByPosition(rowPosition);
rowObjectsToRemove .add(listDataProvider.getRowObject(rowIndex));
}
Remove them from the data provider
call natTable.refresh()

Updating Eclipse JFace Treeviewer when model changes?

I am developing a RCP application with a TreeViewer. While there are good number of articles to explain how to add editing support to the Viewer (and how changes in view are updated in the model), I don't find much for updating the Treeview when the underlaying model changes. my question in short:
TreeView ----> Model updation ------ there are lots of examples
Model ----> Treeview updation ----- this is my question
Edit:
This is what I tried and it works. comments please
viewer.getTree().addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if(e.keyCode==SWT.F3){
System.out.println("F3 pressed... new element will be added");
TreeParent root = (TreeParent) viewer.getInput();
TreeParent activityRoot = (TreeParent) root.getChildren()[0];
activityRoot.addChild(new TreeObject("NEW_ACTIVITY"));
//viewer.update(root, null);
viewer.refresh();
}
}
});
The data model is provided by your content provider, TreeViewer does not provide any means of changing this data - you must do that it your own code. When you have changed to model you can use the following methods to tell the TreeViewer about the change:
If you have just changed what needs to be shown for a single item in the tree use
TreeViewer.update(object, null);
to get that item in the tree updated. There is also an array version of this to update multiple objects.
If you have added or removed objects in the tree use
TreeViewer.refresh();
to rebuild the whole tree or
TreeViewer.refresh(object);
to refresh the part of the tree start at object.
To tell the tree about adding and removing objects there are
TreeViewer.add(parent, object);
TreeViewer.remove(object);
there are also array variants of these.
To help the TreeViewer find the objects call
TreeViewer.setUseHashlookup(true);
(must be called before TreeViewer.setInput). Since this uses a hash table the objects should have sensible hashCode and equals methods. You can also use TreeViewer.setComparer to specify a different class to do the hash code and comparison.
Based on the comments in this thread,one of the eclipse corner articles on using TreeViewer and few experimenting I had created a working model.
Here are the steps:
Create a listener interface like the following
public interface TreeModelListener extends EventListener {
public void onDelete(TreeObject obj);
}
Let the tree Content provider to add listeners to each tree model item and implement this interface like below
public class TreeContentProvider implements IStructuredContentProvider,ITreeContentProvider,TreeModelListener {
TreeViewer tv;
public TreeContentProvider(TreeViewer tv){
this.tv=tv;
}
int cnt=0;
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
cnt ++;
System.out.println("inputChanged() called "+oldInput+" new: "+newInput);
if(newInput!=null){
((TreeParent)newInput).setListener(this);
TreeObject []items = ((TreeParent)newInput).getChildren();
for(TreeObject obj : items){
if(obj instanceof TreeParent){
((TreeParent) obj).setListener(this);
}
}
}
}
....
#Override
public void onDelete(TreeObject obj) {
System.out.println("Delete of "+obj+" handled by content handler ");
TreeParent parent = obj.getParent();
if(parent.getChildren().length<=1){
return;
}
parent.removeChild(obj);
this.tv.refresh();
}
}
Add a method to the TreeModel class as below . And obviously TreeParent class should have an ArrayList of listeners that is being used in #1 above
public void fireChildDelete(final TreeObject obj){
if(this.listener!=null){
new Runnable(){
#Override
public void run() {
System.out.println("New thread spawned with ID "+Thread.currentThread().getId());
listener.onDelete(obj);
}
}.run();
}
}
Finally add KeyListener to the TreeViewer Object to handle Delete key as below:
tv.getTree().addKeyListener(new KeyListener(){
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if(e.keyCode==SWT.F3){
System.out.println("F3 pressed... new element will be added");
TreeParent root = (TreeParent) tv.getInput();
TreeParent activityRoot = (TreeParent) root.getChildren()[0];
activityRoot.addChild(new TreeObject("NEW_ACTIVITY"));
//viewer.update(root, null);
tv.refresh();
}
if(e.keyCode==SWT.DEL){
System.out.println("DEL key pressed... element will be deleted "+((Tree)e.getSource()).getSelection().length);
if(((Tree)e.getSource()).getSelection().length>0){
final IStructuredSelection selection = (IStructuredSelection) tv
.getSelection();
System.out.println("DEL#2 key pressed... element will be deleted "+selection.getFirstElement().getClass());
TreeParent parent = ((TreeObject)selection.getFirstElement()).getParent();
parent.fireChildDelete((TreeObject) selection.getFirstElement());
//tv.remove(selection.getFirstElement());
//viewer.update(viewer.getInput(),null);
//tv.refresh();
}
}
}
});

How can I observe the changed state of model items in an ObservableList?

I have an ObservableList of model items. The model item is enabled for property binding (the setter fires a property changed event). The list is the content provider to a TableViewer which allows cell editing. I also intend to add a way of adding new rows (model items) via the TableViewer so the number of items in the list may vary with time.
So far, so good.
As this is all within an eclipse editor, I would like to know when the model gets changed. I just need one changed event from any changed model item in order to set the editor 'dirty'. I guess I could attach some kind of listener to each individual list item object but I wonder if there is a clever way to do it.
I think that I might have a solution. The following class is an inline Text editor. Changes to the model bean (all instances) are picked up using the listener added in doCreateElementObservable. My eclipse editor just needs to add its' own change listener to be kept informed.
public class InlineEditingSupport extends ObservableValueEditingSupport
{
private CellEditor cellEditor;
private String property;
private DataBindingContext dbc;
IChangeListener changeListener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
for (ITableEditorChangeListener listener : listenersChange)
{
listener.changed();
}
}
};
public InlineEditingSupport(ColumnViewer viewer, DataBindingContext dbc, String property)
{
super(viewer, dbc);
cellEditor = new TextCellEditor((Composite) viewer.getControl());
this.property = property;
this.dbc = dbc;
}
protected CellEditor getCellEditor(Object element)
{
return cellEditor;
}
#Override
protected IObservableValue doCreateCellEditorObservable(CellEditor cellEditor)
{
return SWTObservables.observeText(cellEditor.getControl(), SWT.Modify);
}
#Override
protected IObservableValue doCreateElementObservable(Object element, ViewerCell cell)
{
IObservableValue value = BeansObservables.observeValue(element, property);
value.addChangeListener(changeListener); // ADD THIS LINE TO GET CHANGE EVENTS
return value;
}
private List<ITableEditorChangeListener> listenersChange = new ArrayList<ITableEditorChangeListener>();
public void addChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
listenersChange.add(listener);
}
public void removeChangeListener(ITableEditorChangeListener listener)
{
listenersChange.remove(listener);
}
}

EF: Partial class - acces to the adapter?

let's say that I have a table TabA
namespace MyProject.Models.Database //<-- the same namespace as the EF's dbmx file
{
public partial class TabA
{
public void Foo()
{
//
}
}
}
Inside the Foo method, I need to perform some operations on the other table which isn't asosiated with the TabA In the other words, I need to access to the Entity Framework adapter inside that method. Is it possible ?
Edit
the answer is here https://stackoverflow.com/a/11135157/106616
If I understand the problem correctly, I assume you have your reasons for wanting to work on another entity from the TabA entity. If this is true, I can see two ways of doing this.
A) If you want your changes to be applied at the same time as other potential changes to the TabA Entity, then you can always pass in the context as a parameter:
namespace MyProject.Models.Database //<-- the same namespace as the EF's dbmx file
{
public partial class TabA
{
public void Foo(YourDbContext context)
{
var otherTableQuery = from x in context.SecondTable
where ...
select x;
foreach (var item in otherTableQuery)
{
item.Column1 = "A certain value";
}
}
}
}
Your calling method might look like:
public void DoChangesToTabA()
{
using ( YourDbContext context = new YourDbContext())
{
var tabAquery = from x in context.TabA
where ...
select x;
foreach( var item in tabAQuery)
{
item.LastModified = DateTime.Now;
if(???)
{
}
}
}
}
Now your changes will be applied the next time you call context.SaveChanges() from the calling method.

MVVM using Page Navigation On Windows Phone 7

The Navigation framework in Windows Phone 7 is a cut down version of what is in Silverlight. You can only navigate to a Uri and not pass in a view. Since the NavigationService is tied to the View, how do people get this to fit into MVVM. For example:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container, IView view)
{
this.container = container;
this.view = view;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView { get { return this.view; } }
public void GoToNextPage()
{
// What do I put here.
}
}
public class View : PhoneApplicationPage, IView
{
...
public void SetModel(IViewModel model) { ... }
}
I am using the Unity IOC container. I have to resolve my view model first and then use the View property to get hold of the view and then show it. However using the NavigationService, I have to pass in a view Uri. There is no way for me to create the view model first. Is there a way to get around this.
Instead of passing the view through the constructor. You could construct the view first via the NavigationService and pass it into the view-model. Like so:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container)
{
this.container = container;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView
{
get { return this.view; }
set { this.view = value; this.view.SetModel(this); }
}
public void GoToNextPage()
{
// What do I put here.
}
}
PhoneApplicationFrame frame = Application.Current.RootVisual;
bool success = frame.Navigate(new Uri("View Uri"));
if (success)
{
// I'm not sure if the frame's Content property will give you the current view.
IView view = (IView)frame.Content;
IViewModel viewModel = this.unityContainer.Resolve<IViewModel>();
viewModel.View = view;
}
If you are using Mvvm Light you could try:
Windows Phone 7 — Navigation between pages using MVVM Light Messaging
(See similar post: Silverlight Navigation using Mvvm-light(oobe)+MEF?)
My opinion is that the view-model should be created and registered at application startup. By placing it inside the root DataContext all pages will automatically get a reference to it without any code-behind or IoC tricks.
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
RootFrame.DataContext = m_ViewModel;
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
m_ViewModel.Activated(PhoneApplicationService.Current.State);
RootFrame.DataContext = m_ViewModel;
}
If you are using MVVM architecture,then you can pass navigationPage after registering using Messenger. Create a model class (say NavigateToPageMessage) with a string(say PageName) variable. You want to pass string from homepage.xaml to newpage.xaml,then in Homepage viewmodel just send the message like this under the command you binded (say HomeNavigationCommand)
private void HomeNavigationCommandHandler()
{
Messenger.Default.Send(new NavigateToPageMessage {PageName = "newpage"});
}
In the newpage Viewmodel,you should register the messenger like this,
Messenger.Default.Register<NavigateToPageMessage>(this, (action) => ReceiveMessage(action));
private object ReceiveMessage(NavigateToPageMessage action)
{
var page = string.Format("/Views/{0}.xaml", action.PageName);
NavigationService.Navigate(new System.Uri(page,System.UriKind.Relative));
return null;
}
//Assuming your views are in View Folder