Update SyncFusion Grid after backend data source has changed - syncfusion

I have a SyncFusion data grid tied to a backend SQL database. My crud actions are called through custom buttons that call a dialog box.
This works nicely except that the grid is not updated with the backend data after an add/edit/delete. I have tired refreshing the grid but that doesn't seem to work.
What do I need to do?
MyTemplates.razor
#page "/My_Templates"
#using WireDesk.Models
#inject IWireDeskService WireDeskService
<ReusableDialog #ref="dialog"></ReusableDialog>
<SfGrid #ref="Grid" DataSource="#Templates" TValue="Template" AllowSorting="true" Toolbar="ToolbarItems">
<GridEvents OnToolbarClick="OnClicked" TValue="Template"></GridEvents>
<GridColumns>
<GridColumn Field=#nameof(Template.Owner) HeaderText="Owner" ValidationRules="#(new ValidationRules { Required = true })" Width="120"></GridColumn>
<GridColumn Field=#nameof(Template.Users) HeaderText="Users" TextAlign="TextAlign.Left" Width="130"></GridColumn>
<GridColumn Field=#nameof(Template.Description) HeaderText="Description" TextAlign="TextAlign.Left" Width="130"></GridColumn>
<GridColumn Field=#nameof(Template.FundType) HeaderText="Fund Type" TextAlign="TextAlign.Left" Width="120"></GridColumn>
</GridColumns>
</SfGrid>
#code{
//Instantiate objects
SfGrid<Template> Grid { get; set; }
ReusableDialog dialog;
//Instantiate toolbar and toolbar items
private List<Object> ToolbarItems = new List<Object>()
{
new ItemModel() { Text = "Create New Template", TooltipText = "Add", PrefixIcon = "e-icons e-update", Id = "Add", },
new ItemModel() { Text = "Edit Template", TooltipText = "Edit", PrefixIcon = "e-icons e-update", Id = "Edit"}
};
//Instatiate records
public IEnumerable<Template> Templates { get; set; }
//Instantiate Records
protected override void OnInitialized()
{
Templates = WireDeskService.GetTemplates();
}
//Handle toolbar clicks
public async Task OnClicked(Syncfusion.Blazor.Navigations.ClickEventArgs Args)
{
//Create Record
if (Args.Item.Id == "Add")
{
Args.Cancel = true; //Prevent the default action
dialog.Title = "This is the Add Title";
dialog.Text = "This is the add text";
dialog.template = new Template();
dialog.OpenDialog();
WireDeskService.InsertTemplate(dialog.template);
//Grid.CallStateHasChanged(); Doesn't Work
//Templates = WireDeskService.GetTemplates(); Doesn't Work
}
//Edit Records
if (Args.Item.Id == "Edit")
{
Args.Cancel = true; //Prevent the default action
var selected = await Grid.GetSelectedRecordsAsync();
if (selected.Count > 0)
{
//Call Dialog Box Here
dialog.Title = "This is the Edited Title";
dialog.Text = "This is the edited text";
dialog.template = selected[0];
dialog.OpenDialog();
WireDeskService.UpdateTemplate(dialog.template.TemplateId, dialog.template);
Grid.CallStateHasChanged();
}
}
}
}
<style>
.e-altrow {
background-color: rgb(182 201 244);
}
</style>
WireDeskService.cs
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
namespace WireDesk.Models
{
public class WireDeskService : IWireDeskService
{
private WireDeskContext _context;
public WireDeskService(WireDeskContext context)
{
_context = context;
}
public void DeleteTemplate(long templateId)
{
try
{
Template ord = _context.Templates.Find(templateId);
_context.Templates.Remove(ord);
_context.SaveChanges();
}
catch
{
throw;
}
}
public IEnumerable<Template> GetTemplates()
{
try
{
return _context.Templates.ToList();
}
catch
{
throw;
}
}
public void InsertTemplate(Template template)
{
try
{
_context.Templates.Add(template);
_context.SaveChanges();
}
catch
{
throw;
}
}
public Template SingleTemplate(long id)
{
throw new NotImplementedException();
}
public void UpdateTemplate(long templateId, Template template) {
try
{
var local = _context.Set<Template>().Local.FirstOrDefault(entry => entry.TemplateId.Equals(template.TemplateId));
// check if local is not null
if (local != null)
{
// detach
_context.Entry(local).State = EntityState.Detached;
}
_context.Entry(template).State = EntityState.Modified;
_context.SaveChanges();
}
catch
{
throw;
}
}
void IWireDeskService.SingleTemplate(long templateId)
{
throw new NotImplementedException();
}
}
}

We have analyzed your query and we understand that you want to save the changes in your database when data is bound to Grid using DataSource property. We would like to inform you that when data is bound to Grid component using DataSource property, CRUD actions needs to handled using ActionEvents (OnActionComplete and OnActionBegin).
OnActionBegin event – Will be triggered when certain action gets initiated.
OnActionComplete event – Will be triggered when certain action gets completed.
We suggest you to achieve your requirement to save the changes in database using OnActionBegin event of Grid when RequestType is Save. While saving the records, irrespective of Add or Update action. OnActionBegin event will be triggered when RequestType as Save. In that event we can update the changes into database.
Since the Add and Edit actions share the same RequestType “Save”, we can differentiate the current action using Args.Action argument. Similarly we request you fetch the updated data from your database and bind to Grid in OnActionComplete event of Grid.
Refer the below code example.
<SfGrid #ref="Grid" DataSource="#GridData" Toolbar="#(new List<string> { "Add", "Edit", "Delete", "Cancel", "Update" })" AllowFiltering="true" AllowSorting="true" AllowPaging="true">
<GridEditSettings AllowAdding="true" AllowDeleting="true" AllowEditing="true"></GridEditSettings>
<GridEvents OnActionBegin="OnBegin" OnActionComplete="OnComplete" TValue="Order"></GridEvents>
<GridColumns>
<GridColumn Field=#nameof(Order.OrderID) HeaderText="Order ID" IsIdentity="true" IsPrimaryKey="true" TextAlign="TextAlign.Right" Width="120"></GridColumn>
<GridColumn Field=#nameof(Order.CustomerID) HeaderText="Customer Name" Width="150"></GridColumn>
<GridColumn Field=#nameof(Order.EmployeeID) HeaderText="Id" Width="150"></GridColumn>
</GridColumns>
</SfGrid>
#code{
SfGrid<Order> Grid { get; set; }
public IEnumerable<Order> GridData { get; set;}
protected override void OnInitialized()
{
GridData = OrderData.GetAllOrders().ToList();
}
public void OnComplete(ActionEventArgs<Order> Args)
{
if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Save || Args.RequestType == Syncfusion.Blazor.Grids.Action.Refresh)
{
GridData = OrderData.GetAllOrders().ToList(); // fetch updated data from service and bind to grid datasource property
}
}
public void OnBegin(ActionEventArgs<Order> Args)
{
if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Save) // update the changes in Actionbegine event
{
if (Args.Action == "Add")
{
//Args.Data contain the inserted record details
//insert the data into your database
OrderData.AddOrder(Args.Data);
}
else
{
//Args.Data contain the updated record details
//update the data into your database
OrderData.UpdateOrder(Args.Data);
}
} else if (Args.RequestType == Syncfusion.Blazor.Grids.Action.Delete)
{
OrderData.DeleteOrder(Args.Data.OrderID); // delete the record from your database
}
}
}
Refer our UG documentation for your reference
https://blazor.syncfusion.com/documentation/datagrid/events/#onactionbegin
https://blazor.syncfusion.com/documentation/datagrid/events/#onactioncomplete

Related

showing items of picker while consuming web service xamarin

i want to show the items of picker from consuming Restful webservice, but i have an error !
<Picker x:Name="natures" ItemsSource="{Binding Naturee}" SelectedItem="ItemNature"
ItemDisplayBinding="{Binding Name}"
Title="choisir votre nature de dépense"
SelectedIndexChanged="natures_SelectedIndexChanged"/>
my modal PickerModelNature
public class PickerModelNature
{
public class NatureD
{
public string Label;
}
public class ResponseDataN
{
public RootModel Data;
}
public class RootModel : INotifyPropertyChanged
{
List<NatureD> natureList;
[JsonProperty("natureList")]
public List<NatureD> NatureList
{
get { return natureList; }
set
{
if (natureList != value)
{
natureList = value;
OnPropertyChanged();
}
}
}
NatureD itemNature;
public NatureD ItemNature
{
get { return itemNature; }
set
{
if (itemNature != value)
{
itemNature = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and the xaml.cs
public partial class CreerDepense : ContentPage
{
public CreerDepense()
{
InitializeComponent();
this.BindingContext = new RootModel();
GetExpenseNature();
} private async void GetExpenseNature()
{
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://192.168.1.6:3000/api/adepApi/GetExpensesNatureList");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync("http://192.168.1.6:3000/api/adepApi/GetExpensesNatureList");
var content = await response.Content.ReadAsStringAsync();
ResponseDataN EL = JsonConvert.DeserializeObject<ResponseDataN>(content);
// var Items = JsonConvert.DeserializeObject<List<NatureD>>(content);
//listexpense.ItemsSource = Items;
natures.ItemsSource = EL.Data.NatureList;
}
the error is:
Java.Lang.NullPointerException: 'Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference'
what should i do ?

microsoft bot framework typing indicator form flow(Form Builder)

I need to add typing indicator activity inside the form flow, I have used the following code but it only works out side of form flow, once the user enter the form builder the typing indicator does not appear.
Activity replytyping1 = activity.CreateReply();
replytyping1.Type = ActivityTypes.Typing;
replytyping1.Text = null;
ConnectorClient connector2 = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector2.Conversations.ReplyToActivityAsync(replytyping1);
I am using the following code inside dialog to call the form builder:
var myform = new FormDialog<TrainingForm>(new TrainingForm(), TrainingForm.MYBuildForm, FormOptions.PromptInStart, null);
context.Call<TrainingForm>(myform, AfterChildDialog);
my form builder code:
public enum MoreHelp { Yes, No };
public enum Helpfull { Yes, No };
[Serializable]
public class TrainingForm
{
public string More = string.Empty;
public string usefull = string.Empty;
[Prompt("Is there anything else I can help you with today? {||}")]
[Template(TemplateUsage.NotUnderstood, "What does \"{0}\" mean?", ChoiceStyle = ChoiceStyleOptions.Auto)]
public MoreHelp? needMoreHelp { get; set; }
[Prompt("Was this helpful? {||}")]
[Template(TemplateUsage.NotUnderstood, "What does \"{0}\" mean?", ChoiceStyle = ChoiceStyleOptions.Auto)]
public Helpfull? WasHelpful { get; set; }
public static IForm<TrainingForm> MYBuildForm()
{
return new FormBuilder<TrainingForm>()
.Field(new FieldReflector<TrainingForm>(nameof(needMoreHelp))
.SetActive(state => true)
.SetNext(SetNext2).SetIsNullable(false))
.Field(new FieldReflector<TrainingForm>(nameof(WasHelpful))
.SetActive(state => state.More.Contains("No"))
.SetNext(SetNext).SetIsNullable(false)).OnCompletion(async (context, state) =>
{
if (state.usefull == "No")
{
await context.PostAsync("Sorry I could not help you");
}
else if (state.usefull == "Yes")
{
await context.PostAsync("Glad I could help");
}
if(state.More == "Yes")
{
await context.PostAsync("Ok! How can I help?");
}
context.Done<object>(new object());
})
.Build();
}
If you are attempting to send the typing activity from the dialog that loaded the FormFlow dialog, it will not work because the code in the parent dialog does not execute every time the FormFlow dialog is loaded.
However, you can modify the MessagesController and inspect the dialog stack. If the FormFlow dialog is the last dialog on the stack, then send typing:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity) {
if (activity.Type == ActivityTypes.Message)
{
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(default(CancellationToken));
var stack = scope.Resolve<IDialogTask>();
if (stack.Frames != null && stack.Frames.Count > 0)
{
var lastFrame = stack.Frames[stack.Frames.Count - 1];
var frameValue = lastFrame.Target.GetType().GetFields()[0].GetValue(lastFrame.Target);
if(frameValue is FormDialog<TrainingForm>)
{
var typingReply = activity.CreateReply();
typingReply.Type = ActivityTypes.Typing;
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
await connector.Conversations.ReplyToActivityAsync(typingReply);
}
}
}
await Conversation.SendAsync(activity, () => FormDialog.FromForm(TrainingForm.MYBuildForm));
}
else
{
this.HandleSystemMessage(activity);
}
return Request.CreateResponse(HttpStatusCode.OK);
}

Can you Drag and Drop a JButton copy?

Found a neat sample that really helped with illustrating how DnD works when it drags a values from a list and places it on the panel.
This sample grabs a copy of the list value.
I have since modified the sample to add a JButton. I can DnD this onto the panel but it moves it instead of making a copy.
Is there something specific as to why the JButton was moved instead of copied?
What change is required to have the button copied instead of moved?
I even tried pressing the CTRL key as I dragged the button but it still moved it instead of copying.
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.io.IOException;
import javax.swing.*;
public class TestDnD {
public static void main(String[] args) {
new TestDnD();
}
public TestDnD() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList list;
public TestPane() {
setLayout(new BorderLayout());
list = new JList();
DefaultListModel model = new DefaultListModel();
model.addElement(new User("Shaun"));
model.addElement(new User("Andy"));
model.addElement(new User("Luke"));
model.addElement(new User("Han"));
model.addElement(new User("Liea"));
model.addElement(new User("Yoda"));
list.setModel(model);
add(new JScrollPane(list), BorderLayout.WEST);
//Without this call, the application does NOT recognize a drag is happening on the LIST.
DragGestureRecognizer dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
list,
DnDConstants.ACTION_COPY_OR_MOVE,
new DragGestureHandler(list)); ///DragGestureHandler - is defined below
///and really just implements DragGestureListener
///and the implemented method defines what is being transferred.
JPanel panel = new JPanel(new GridBagLayout());
add(panel);
//This registers the Target (PANEL) where the Drop is to occur.
DropTarget dt = new DropTarget(
panel,
DnDConstants.ACTION_COPY_OR_MOVE,
new DropTargetHandler(panel), ////DropTargetHandler - is defined below
true); ///and really just implements DropTargetListener
setupButtonTest();
}
private void setupButtonTest()
{
JButton myButton = new JButton("Drag Drop Me");
add(myButton, BorderLayout.NORTH);
DragGestureRecognizer dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
myButton,
DnDConstants.ACTION_COPY, // ACTION_COPY_OR_MOVE,
new DragGestureHandler(myButton)); ///DragGestureHandler - is defined below
///and really just implements DragGestureListener
///and the implemented method defines what is being transferred.
}
}
public static class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return name;
}
}
////This Class handles the actual item or data being transferred (dragged).
public static class UserTransferable implements Transferable {
public static final DataFlavor JIMS_DATA_FLAVOR = new DataFlavor(User.class, "User");
private User user;
private JButton jbutton;
public UserTransferable(User user) {
this.user = user;
}
public UserTransferable(JButton user) {
this.jbutton = user;
}
#Override
public DataFlavor[] getTransferDataFlavors() {
//Executed as soon as the User Object is dragged.
System.out.println("UserTransferable : getTransferDataFlavors()");
return new DataFlavor[]{JIMS_DATA_FLAVOR};
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
//This is what is executed once the item is dragged into a JComponent that can accept it.
System.out.println("UserTransferable : isDataFlavorSupported()");
return JIMS_DATA_FLAVOR.equals(flavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
//Once a Drop is done then this method provides the data to actually drop.
System.out.println("UserTransferable : getTransferData()");
Object value = null;
if (JIMS_DATA_FLAVOR.equals(flavor)) {
if (user != null)
value = user;
else if (jbutton != null)
value = jbutton;
} else {
throw new UnsupportedFlavorException(flavor);
}
return value;
}
}
protected class DragGestureHandler implements DragGestureListener {
private JList list;
private JButton button;
public DragGestureHandler(JList list) {
this.list = list;
}
public DragGestureHandler(JButton list) {
this.button = list;
}
#Override
public void dragGestureRecognized(DragGestureEvent dge) {
//This executes once the dragging starts.
System.out.println("DragGestureHandler : dragGesturRecognized()");
if (dge.getComponent() instanceof JList)
{
Object selectedValue = list.getSelectedValue();
if (selectedValue instanceof User) {
User user = (User) selectedValue;
Transferable t = new UserTransferable(user); ////This is where you define what is being transferred.
DragSource ds = dge.getDragSource();
ds.startDrag(
dge,
null,
t,
new DragSourceHandler());
}
}
else if (dge.getComponent() instanceof JButton)
{
Object selectedValue = dge.getComponent();
if (selectedValue instanceof JButton) {
JButton jb = button;
Transferable t = new UserTransferable(jb); ////This is where you define what is being transferred.
DragSource ds = dge.getDragSource();
ds.startDrag(
dge,
null,
t,
new DragSourceHandler());
}
}
}
}
protected class DragSourceHandler implements DragSourceListener {
public void dragEnter(DragSourceDragEvent dsde) {
//This means you have entered a possible Target.
System.out.println("DragSourceHandler : DragEnter()");
}
public void dragOver(DragSourceDragEvent dsde) {
//Continually executes while the DRAG is hovering over an potential TARGET.
System.out.println("DragSourceHandler : DragOver()");
}
public void dropActionChanged(DragSourceDragEvent dsde) {
}
public void dragExit(DragSourceEvent dse) {
//Executes once the potential target has been exited.
System.out.println("DragSourceHandler : DragExit()");
}
public void dragDropEnd(DragSourceDropEvent dsde) {
//Once the mouse button is lifted to do the drop.
//Executes against any potential drop.
System.out.println("DragSourceHandler : dragDropEnd()");
}
}
protected class DropTargetHandler implements DropTargetListener {
////THESE ARE EXECUTED ONLY WHEN THE MOUSE AND DRAGGED ITEM IS OVER THE TARGET.
private JPanel panel;
public DropTargetHandler(JPanel panel) {
this.panel = panel;
}
public void dragEnter(DropTargetDragEvent dtde) {
System.out.println("DropTargetHandler : dragEnter()");
if (dtde.getTransferable().isDataFlavorSupported(UserTransferable.JIMS_DATA_FLAVOR)) {
//This shows the outline within the TARGET to indicate it will accept the DROP.
System.out.println(" Accept...");
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
} else {
//If an item is not registered to accept a certain drop this is executed.
System.out.println(" DropTargetHandler : DragEnter() - Else");
dtde.rejectDrag();
}
}
public void dragOver(DropTargetDragEvent dtde) {
//Active while the item is being Dragged over the Target
System.out.println("DropTargetHandler : dragOver()");
}
public void dropActionChanged(DropTargetDragEvent dtde) {
System.out.println("DropTargetHandler : dropActionChanged()");
}
public void dragExit(DropTargetEvent dte) {
//Once the dragged item is taken out of the Target area.
System.out.println("DropTargetHandler : dragExit()");
}
public void drop(DropTargetDropEvent dtde) {
//Once the mouse button is released to do the Drop then this is executed.
System.out.println("DropTargetHandler : drop()");
if (dtde.getTransferable().isDataFlavorSupported(UserTransferable.JIMS_DATA_FLAVOR)) {
Transferable t = dtde.getTransferable();
if (t.isDataFlavorSupported(UserTransferable.JIMS_DATA_FLAVOR)) {
try {
Object transferData = t.getTransferData(UserTransferable.JIMS_DATA_FLAVOR);
if (transferData instanceof User) {
User user = (User) transferData;
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
panel.add(new JLabel(user.getName()));
panel.revalidate();
panel.repaint();
}
else if (transferData instanceof JButton) {
JButton jb = (JButton) transferData;
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
panel.add(jb);
panel.revalidate();
panel.repaint();
}
else {
dtde.rejectDrop();
}
} catch (UnsupportedFlavorException ex) {
ex.printStackTrace();
dtde.rejectDrop();
} catch (IOException ex) {
ex.printStackTrace();
dtde.rejectDrop();
}
} else {
dtde.rejectDrop();
}
}
}
}
}

JPA in a JavaFX Context

So far I used JPA in a managed container but currently I'm working on a desktop application with JavaFX and JPA and I have to manage the transactions. I have an application that has multiple javafx windows:
First window: It shows some projects in a list (entry)
When you click on an entry it shows some details of that project (details view)
In the details view you can add some information about that project (details form)
First of all I create a project that will be listed in the entry view. Then I enter the details view of that project. In the details view I add a new entry by filling out the third view. I click enter and the details view gets updated with that information. Then I leave the details view and I have a kind of summary in the entry page about the whole project. This all works fine. Then I enter the details view again and edit some stuff in the form (step 3). When I click ok the details view gets updated. Then I leave the details view to return to the entry page but the summary is not updated.
So my problem actually is that the entry page is not updated when the details are updated (the very first time when I create the details the entry page is updated correctly)
Some code...
The entry page [1] opens the details page like this:
protected void showActivities(Project project, Window window) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ProjectActivities.fxml"));
Stage stage = ModalDialogHelper.createNewModal("Project Activity Log", loader, window);
ProjectActivityController pac = loader.getController();
project = projectDataService.detach(project);
pac.setProject(project);
pac.onPostLoad();
stage.showAndWait();
projectDataService.merge(project);
onReload();
} catch (Exception e) {
e.printStackTrace();
}
}
The details page (ProjectActivities.fxml [2]) opens a the form [3]e in a new window like this (triggered by a button on the window):
#FXML
private void openActivityEditor(ActionEvent event) {
createActivityEditor(((Node) event.getSource()).getScene().getWindow(), null);
}
protected void createActivityEditor(Window window, Activity activity) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ActivityLogForm.fxml"));
Stage stage = ModalDialogHelper.createNewModal("Create A New Activity...", loader, window);
ActivityLogController controller = loader.getController();
controller.setProject(project);
controller.setActivity((activity != null) ? activityDataService.detach(activity) : activity);
controller.onPostLoad();
stage.showAndWait();
if (activity != null) {
activity = activityDataService.merge(activity);
}
onReload();
} catch (IOException e) {
e.printStackTrace();
}
}
I submit the form [3] (ActivityLogForm.fxml) with this piece of code:
#FXML
private void saveActivity(ActionEvent event) {
if (activity == null) {
activity = new Activity();
activity.setProject(project);
project.getActivities().add(activity);
} else {
activityDataService.merge(activity);
}
activity.setBegin(getBegin());
activity.setStop(getStop());
if (activity.getLog() == null) {
Log log = new Log();
activity.setLog(log);
}
if (details != null) {
activity.getLog().setDetails(details.getText());
}
if (activity.getId() > 0) {
activity = activityDataService.merge(activity);
} else {
activityDataService.persist(activity);
}
activityDataService.detach(activity);
ModalDialogHelper.close(((Node) event.getSource()).getScene().getWindow());
}
Some more infos...
The entry view [1] shows a list of projects inside a VBox. When I click on a project a new window will be opened (details view [2]) and the clicked project will be passed to the details view controller to that view. In the details view one can open a new form (window [3]) to enter the details. By committing the form the details get updated as wanted in the details view [2]. Then I exit the details view and the main view [1] is updated as expected. Then I go back into the details of the very same project and want to change some stuff in the details form [3] (it's the same form as for creating new details as before). I submit the changes and the details view [2] gets updated correctly but then I exit the details view and get back to the main view [1]. But now in this view I don't see the changes I just made but they are written to the datastore.
Update
I forgot to say that i'm using EclipseLink and here's my generic dataservice:
public abstract class AbstractDataService<T> {
protected EntityManager entityManager;
protected int pageSize;
protected Class<T> type;
public AbstractDataService(Class<T> clazz) {
this.type = clazz;
init();
}
protected void init() {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("ttUnit");
entityManager = factory.createEntityManager();
setPageSize(Integer.parseInt(Resources.getString("settings.pagination.default"), 10));
}
public T find(Object o) {
return entityManager.find(type, o);
}
public List<T> findAll() {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(type);
Root<T> entities = query.from(type);
return entityManager.createQuery(query.select(entities)).getResultList();
}
public List<T> getPage(int page) {
if (isPageOutOfBounds(page)) {
page = correctPage(page);
}
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(type);
Root<T> entities = query.from(type);
return entityManager.createQuery(query.select(entities)).setFirstResult(getFirstResult(page))
.setMaxResults(pageSize).getResultList();
}
private boolean isPageOutOfBounds(int page) {
int pageCount = getPages();
return page < 0 || (pageCount > 0 && page > pageCount);
}
private int correctPage(int page) {
int pageCount = getPages();
if (page < 0) {
page = 1;
}
if (pageCount > 0) {
while (page > pageCount) {
page--;
}
} else {
// reset to the first page
page = 1;
}
return page;
}
public int getPages() {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Expression<Long> entities = builder.count(query.from(type));
long count = entityManager.createQuery(query.select(entities)).getSingleResult();
int pageCount = (int) Math.ceil((double) count / (double) pageSize);
return pageCount;
}
public int getFirstResult(int page) {
return (page - 1) * pageSize;
}
public T merge(T obj) {
try {
entityManager.getTransaction().begin();
return entityManager.merge(obj);
} finally {
entityManager.getTransaction().commit();
}
}
public void persist(T obj) {
try {
entityManager.getTransaction().begin();
entityManager.persist(obj);
} finally {
entityManager.getTransaction().commit();
}
}
public void remove(T obj) {
try {
entityManager.getTransaction().begin();
entityManager.remove(obj);
} finally {
entityManager.getTransaction().commit();
}
}
public void flush() {
try {
entityManager.getTransaction().begin();
entityManager.flush();
} finally {
entityManager.getTransaction().commit();
}
}
public T detach(T obj) {
entityManager.detach(obj);
return obj;
}
public void clear() {
entityManager.clear();
}
/**
* #return the pageSize
*/
public int getPageSize() {
return pageSize;
}
/**
* #param pageSize
* the pageSize to set
*/
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
}
Here's the data model that I'm using:
Project
| 1 (- activities (Cascade: ALL))
|
| 0..n
Activity
- project (Cascade: MERGE, DETACH)
I've found the solution for it. The problem was that when I created a new activity, the activity passed to the ActivityLogController in createActivityEditor was null. Which means that I only merge the entity when it has been edited but I did not merge the project if a new activity is created:
protected void createActivityEditor(Window window, Activity activity) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ActivityLogForm.fxml"));
Stage stage = ModalDialogHelper.createNewModal("Create A New Activity...", loader, window);
ActivityLogController controller = loader.getController();
controller.setProject(project);
controller.setActivity((activity != null) ? activityDataService.detach(activity) : activity);
controller.onPostLoad();
stage.showAndWait();
if (activity != null) {
// re-attach the existing activity
activity = activityDataService.merge(activity);
} else {
// the activity did not yet exist but the project needs to be
// merged with the new activity
project = projectDataService.merge(project);
}
onReload();
} catch (IOException e) {
e.printStackTrace();
}
}
Like that the project is synchronized with the underlying database and is aware of that new activity. So further changes will be merged because the activity is not null and the project property in the Activity Entity is marked as cascade = MERGE.
Maybe it helps someone with similar issues and thanks for your help

UCMA Generic QuestionAnswer activity

I am working on a UCMA 3.0 workflow application and am attempting to generate queries into our client management system allowing end users to obtain data about specific clients via voice or instant message. I was wondering anyone knows how to create a generic questionanswer activity using UCMA that allows generic input. I know that I can set up expected inputs and grammars, but with the bi-capitalization options, and the likelihood that an end user would know the exact client name (or client number for that matter), I would prefer to allow the user to enter part of the name and then search the database for a list of names that might meet the criteria. Does anyone know of a way, and have sample code that might allow me to do this if it is possible?
I had the same problem and had to write a custom activity to capture generic input from a user. This needs some work to make it production ready. Note the classy catching of system.exception in multiple places, as well as throwing an exception if input isn't received within the timeout period instead of reprompting the user. Also no regex on the user input...
The InstanceDependencyProperty is something else that was frustrating about Workflow. Without using InstanceDependencyProperties you won't be able to retrieve any results after the activity is completed.
Hope this helps!
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Workflow.Activities;
using Microsoft.Rtc.Workflow.Common;
namespace ActivityLibrary
{
public partial class CaptureIMInput : Activity, IInstanceDependencyContainer
{
#region Private member variables
private CallProvider _callProvider;
private Timer _maximumTimer;
private string _imText;
private bool messageReceived = false;
private bool _maximumTimerElapsed = false;
#endregion
public CaptureIMInput()
{
InitializeComponent();
_instanceDependencyProperties = new Dictionary<string, object>();
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
try
{
this._callProvider = Utilities.GetCallProviderFromParent<InstantMessagingCall>(this);
InstantMessagingCall call = (InstantMessagingCall)this._callProvider.Call;
try
{
if (call.State == CallState.Established)
{
call.Flow.EndSendInstantMessage(call.Flow.BeginSendInstantMessage(MainPrompt, null, null));
_maximumTimer = new Timer(new TimerCallback(OnMaximumTimerFired), null, MaximumPrompt, new TimeSpan(0, 0, 0, 0, -1));
call.Flow.MessageReceived += this.InstantMessagingFlow_MessageReceived;
while (!messageReceived)
{
if (_maximumTimerElapsed)
throw new TimeoutException("User input was not detected within alloted time");
}
if (!string.IsNullOrEmpty(_imText))
{
IMText = _imText;
{
this.Stop();
return ActivityExecutionStatus.Closed;
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
catch (Exception ex)
{
throw ex;
}
return ActivityExecutionStatus.Closed;
}
protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
{
this.Stop(); //Clean up timer
return ActivityExecutionStatus.Canceling;
}
private void Stop()
{
if (_maximumTimer != null)
{
_maximumTimer.Dispose();
_maximumTimer = null;
}
}
private void InstantMessagingFlow_MessageReceived(object sender, InstantMessageReceivedEventArgs e)
{
//Can't set dependencyproperties directly from sub-thread
_imText = e.TextBody;
//Mark bool so main while loop exits
messageReceived = true;
}
//Callback to
private void OnMaximumTimerFired(object state)
{
_maximumTimerElapsed = true;
}
#region InstanceDependency dictionary
private Dictionary<string, object> _instanceDependencyProperties;
[Browsable(false)]
public Dictionary<string, object> InstanceDependencyProperties
{
get { return _instanceDependencyProperties; }
}
#endregion
#region Maximum Prompt Timeout
[NonSerialized]
private static readonly InstanceDependencyProperty MaximumPromptProperty = InstanceDependencyProperty.Register("MaximumPrompt", typeof(TimeSpan), typeof(CaptureIMInput), new TimeSpan(0, 0, 30));
[TypeConverter(typeof(TimeSpanConverter))]
public TimeSpan MaximumPrompt
{
get
{
if (base.DesignMode)
return (TimeSpan)InstanceDependencyHelper.GetValue<CaptureIMInput>(this, MaximumPromptProperty);
else
return (TimeSpan)InstanceDependencyHelper.GetValue<CaptureIMInput>(this, this.WorkflowInstanceId, MaximumPromptProperty);
}
set
{
if (base.DesignMode)
InstanceDependencyHelper.SetValue<CaptureIMInput>(this, MaximumPromptProperty, value);
else
InstanceDependencyHelper.SetValue<CaptureIMInput>(this, this.WorkflowInstanceId, MaximumPromptProperty, value);
}
}
#endregion
#region MainPrompt
public static DependencyProperty MainPromptProperty =
DependencyProperty.Register("MainPrompt", typeof(string), typeof(CaptureIMInput));
[ValidationOption(ValidationOption.Required)]
public string MainPrompt
{
get
{
return (string)base.GetValue(MainPromptProperty);
}
set
{
base.SetValue(MainPromptProperty, value);
}
}
#endregion
#region Instant Message Text
public static InstanceDependencyProperty IMTextProperty =
InstanceDependencyProperty.Register("IMText",
typeof(string),
typeof(CaptureIMInput));
[Description("InstantMessaging Text from user")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string IMText
{
get
{
if (base.DesignMode) return ((string)InstanceDependencyHelper.GetValue<CaptureIMInput>(this, CaptureIMInput.IMTextProperty));
else return ((string)(InstanceDependencyHelper.GetValue<CaptureIMInput>(this, this.WorkflowInstanceId, CaptureIMInput.IMTextProperty)));
}
set
{
if (base.DesignMode) InstanceDependencyHelper.SetValue<CaptureIMInput>(this, CaptureIMInput.IMTextProperty, value);
else InstanceDependencyHelper.SetValue<CaptureIMInput>(this, this.WorkflowInstanceId, CaptureIMInput.IMTextProperty, value);
}
}
#endregion
}
}