I have a rehosted designer in my application that is all working ok with my custom activities. When the user is designing their workflows, they will drag certain activities on to the designer surface just like normal. However, after the user selects certain values from a drop down box (not in the designer), I want to remove certain activities from the design surface so they can not be saved and executed.
I have tried so many different ways for doing this, using the WorkflowInspectionServices object to navigate the ModelItemTree, grabbing the parent Sequence activity and removing the custom ones from it's Activities collection but I just can't seem to make it work.
Has anyone out there actually managed to successfully remove an activity from a rehosted designer surface in code (not just right clicking it and choosing Delete!!).
To be clear...this is not when the workflow is being executed, but when it's being designed in a rehosted designer.
I'm going to bet you don't remove children from the ModelItem but the Activity tree that the ModelItem wraps. I.e., you do a "GetCurrentValue", cast the return to your Activity type, then remove the children that way. That won't work, as the ModelItem representation of the Activity tree will get out of sync. You'll have to remove children by getting the ModelItem for the property that holds the children, then clear that out.
For example, given the following activity
[Designer(typeof(NativeActivity1Designer))]
public sealed class NativeActivity1 : NativeActivity, IActivityTemplateFactory
{
public Activity Child { get; set; }
protected override void Execute(NativeActivityContext context) { }
Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new NativeActivity1
{
Child = new Sequence()
};
}
}
and the following designer (ActivityDesigner node removed for brevity)
<StackPanel>
<sap:WorkflowItemPresenter
MinHeight="100"
HintText="Drop it here"
Item="{Binding ModelItem.Child}" />
<Button
Content="Remove"
Click="Button_Click" />
</StackPanel>
you can use the code in Button_Click to remove the child from the workflow in the designer.
public partial class NativeActivity1Designer
{
public NativeActivity1Designer()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ModelProperty child = ModelItem.Properties["Child"];
child.SetValue(null);
}
}
Related
Ok so here is what i am trying to do: I have a page called 'SettingsPage'. Now, in this page i Have a tabPane with 3 tabs; users, buttons, and sales. Buttons and Users both have tables which need to be populated with data before they are viewed by the user. When I was first making the program I just used a 'initialize' method to populate the user table on the opening of 'SettingsPage'. However, now that I am beginning to try and populate the buttons table, I am encountering a memory error because now there is too much data to be loaded at once together.
So, I though a good solution to this would be to make an Event method that is called whenever a certain tab is opened. I am using SceneBuilder atm, and what seemed to be the equivalent of this was onSelectionChanged, however I can't seem to use this as you would use 'methodEg(ActionEvent event)...'. So my question is, how can ensure that a method will be called on the opening of a certain tab.
For example, when 'buttonTab' is clicked, the 'populateButtonTable' method is called.
The answer to this was this code:
And if anyone asks why I didn't use .isSelected()... I did, it for some reason didn't work, but what I have done below does, so I dunno...
'''
#FXML public void initialize(URL url, ResourceBundle rb){
tabs.getSelectionModel().selectedItemProperty().addListener(
new ChangeListener<Tab>() {
#Override
public void changed(ObservableValue<? extends Tab> ov, Tab t, Tab t1)
{
if (tabs.getSelectionModel().getSelectedItem() == ExampleTab)
{
//do whatever
}
}
}
);
Relatively new to Xamarin, hitting an issue with PushAsync and navigation I can't figure out.
I have a main navigation page, and then a "MyContentPage" that is responsible for rendering a dynamic list based on a supplied id. When the user clicks on a list item they go to a next (newed up) "MyContentPage" (same class) with a different id. Basically a recursive page hierarchy based on a local db.
Problem is that navigation seems to quickly get messed up in some way I can't work out. The pages get swapped around, or get lost. Navigating back to root, if I click back down again, it skips to a page that is further down etc.
So basically the one page apart from the main page (which has multiple navigationpages in tabs - though I only use one tab at this point) binds its controls to this function:
public async Task NavigateToContent(int contentId)
{
await ((Application.Current.MainPage) as TabbedPage)?.CurrentPage.Navigation.PushAsync(new MyContentPage(contentId));
}
The above is then used recursively. Ie. Similar controls bind to the same function until there are no further pages to click down to.
The MyContentPage constructor loads the model:
public MyContentPage(int id)
{
InitializeComponent();
_id = id;
BindingContext = viewModel = new ContentPageViewModel(id);
}
What is the issue here?
From what you mentioned in comments, the issue is caused by the navigation code called in the 'service' class. When you call the service method multiple times, it actually changes the current navigation stack in xamarin forms. Move the page navigation code from service class to viewmodel class.
Or try to put the page navigation source code into something like 'NavigationService' (one example is the one in https://learn.microsoft.com/en-us/xamarin/xamarin-forms/enterprise-application-patterns/ ) and inject this service into your view model class.
OK so this all turned out to be an issue with concurrency.
The original button click was like this:
private void Button_Clicked(object sender, EventArgs e)
{
Task.Run(async () => await (BindingContext as ContentPageViewModel).ExecuteNavCommand(sender));
}
But this resulted in a UI operation happening on a different task
The event handler can be declared as async
The correction is
private async void Button_Clicked(object sender, EventArgs e)
{
await viewModel.ExecuteNavCommand(sender);
}
Here's my situation. I have a javafx pane which I don't want to recreate each time I show it, because I want to keep all filled by user fields in between switching between views.
For that purpose I did this:
#FXML
private void initialize() {
createOrderPane = FxmlUtils.fxmlLoader(CREATE_ORDER_FXML);
}
public void setCenter(String fxmlPath) {
if(CREATE_ORDER_FXML.equals(fxmlPath)) {
borderPane.setCenter(createOrderPane);
}
else {
borderPane.setCenter(FxmlUtils.fxmlLoader(fxmlPath));
}
}
so in case user wants to see CREATE_ORDER_FXML it doesn't reload it, but uses already existing instance.
The problem is that some parts of the view should be reinitalized. For example database might change and I want to refresh some comboboxes which reads value from DB. How to achieve that?
Is there some onShow property? Or maybe I am able to get to the controller of createOrderPane object?
I have a window editor that holds nodes. I would like to open a custom inspector when one of these nodes is selected. The node class is a custom serializable class. Is this possible?.
It seems that custom inspectors can be created manually through the Editor.CreateEditor method but can't see how to let it appear docked like an usual inspector in the unity inspector window.
I would like to achieve the same behaviour that we have when we select a gameobject in sceneview that inmediately show properties for the object (Components, etc...) in the unity inspector.
Cheers
As I'm not sure what you're asking, here are multiple different solutions;
Selection
If you just want your nodes to become the focus of the hierarchy, then in your custom window's OnGUI method, use the code below;
[CustomEditor(typeof(NodeManager))]
public class NodeManager : EditorWindow
{
private static NodeManager window;
private Node[] m_nodes;
[MenuItem("Custom Windows/Node Inspector")]
public static void Init()
{
if(window == null)
window = GetWindow<NodeManager>("Node Manager", true, typeof(SceneView));
}
public void OnGUI()
{
m_nodes = GameObject.FindObjectsOfType<Node>();
foreach(Node node in m_nodes)
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(node.name);
if (GUILayout.Button("Select"))
Selection.objects = new Object[] { node.gameObject };
}
GUILayout.EndHorizontal();
}
}
}
This adds a Button for each object in your custom window view, that will then select that object in the hierarchy.
Auto-Docking
I originally found the second response to this question, which goes into the details of parameters of the GetWindow method, and with this you can clearly see how to dock the window (I've converted it from JS to C# below).
(I looked fairly extensively in UnityEditor and UnityEditorInternal namespaces but couldn't find the Hierarchy or the Inspector).
GetWindow<T>(string title, bool focus, params System.Type[] desiredDockNextTo)
Which we can write for this example as;
EditorWindow.GetWindow<NodeInspector>("Node Test", true, typeof(SceneView));
This example docks the windows next to the SceneView window. This functionality can be combined with a custom inspector's OnInspectorGUI method, to automatically launch the custom node window, if it's not already open.
[CustomEditor(typeof(Node))]
public class NodeInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
NodeManager.Init();
}
}
Sorry if this isn't what you are looking for, if it's not then please give more details and I will amend my answer to better suit the question.
Hope this helped.
Has a possibility, you can make a custom ScriptableObject and Custom Editor for It, and when open the node inspector, just find a instance of the scriptable object and use Selection.selectedObject = scriptableObject, and in the custom editor, make a static field called 'editor' to draw inside.
It will work.
Our group is working on a Custom Activity Designer around our Email activity. It's a pretty straight forward designer, allow the user to enter settings / creds, but instead of cluttering the activity designer with all the settable options, we thought about putting some settings in a dialog window. (Which opens when you click the button beside the server address box).
Some of our email activity properties are InArguments so we are trying to make use of the ExpressionTextBox to display these values without much luck. The main problem is we aren't sure how to properly set up the binding and the OwnerActivity on the ExpressionTextBox. In the Activity Designer's xaml this is simply done by setting Expression=ModelItem.Property using a converter for the InArgument and setting the OwnerActivity=ModelItem, like this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="{Binding ModelItem.ServerAddress, ConverterParameter=In, Converter={StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="{x:Type system:String}" OwnerActivity="{Binding ModelItem}" Margin="2" MaxLines="1" />
If anyone has any ideas on how we could accomplish this in a dialog, please advise.
Well, this is more a WPF\MVVM question than WF4, really.
When developing custom activities designers you just have to keep one thing in mind: any change made on designer\dialog should be reflected on ModelItem. Either through XAML binding expressions or through code on ModelItem.Properties property.
Now, when and how you do it, there are several answers to that but that's really an implementation detail and depends on how you want to do it.
Lets assume you're showing the dialog on button-beside-the-server-address-box click. And lets also assume you've access to dialog textboxes through their name. At that point, you've access to ModelItem so just set its properties as needed:
private void ButtonNextToServerAddressBox_OnClick(object sender, RoutedEventArgs e)
{
var dialog = new ServerAddressEditor();
var result = dialog.ShowDialog();
if (result ?? false)
{
ModelItem.Properties["Server"].SetValue(new InArgument<string>(dialog.ServerTextBox.Text));
ModelItem.Properties["Port"].SetValue(new InArgument<string>(dialog.PortTextBox.Text));
// ... set all other properties
}
}
Now, if you are using any other pattern, or you want pure MVVM, it can be a little more tricky because of how ModelItem works. But this is a totally fine approach.
I resolved this by creating a property in the dialog's ViewModel to hold the Activity Designer's ModelItem.
public ModelItem OwnerActivity {
get { return _OwnerActivity; }
set { _OwnerActivity = value; }
}
vm.OwnerActivity = this.DataContext.ModelItem;
I then set the Xaml for the Expression Text Box in my dialog to binding to this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="
{Binding Path=OwnerActivity.ServerAddress, ConverterParameter=In, Converter=
{StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="
{x:Type system:String}" OwnerActivity="{Binding OwnerActivity}" Margin="2"
MaxLines="1" />
Because I'm now binding directly to the ModelItem from the Activity Designer, any change made to the ModelItem property from the dialog is ALWAYS committed, even if you choose to Cancel from the dialog. To wire up the Ok/Cancel buttons so they work accordingly, I did the following in the dialog:
// declare a ModelEditingScope to make changes transactional
private ModelEditingScope _editScope;
// add this to the constructor of the dialog to begin transactional edits on the ModelItem
_editScope = editorViewModel.OwnerActivity.BeginEdit();
// ok & cancel button click event to commit or revert the changes.
private void OK_Click(object sender, RoutedEventArgs e)
{
_editScope.Complete();
this.DialogResult = DialogResult.OK;
this.Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_editScope.Revert();
this.DialogResult = DialogResult.Cancel;
this.Close()
}