I have an external file that is a csharp file. The code searches through the public folders in Outlook calendar and return a calendar data based on root folder name and calendar name. The code includes for example:
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using Microsoft.SqlServer.Dts.Runtime;
using System.Net;
using Microsoft.Exchange.WebServices;
using Microsoft.Exchange.WebServices.Data;
using System.Linq;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data.Common;
#endregion
/// <summary>
/// This is the class to which to add your code. Do not change the name, attributes, or parent
/// of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
/// <summary>
/// This method is called once, before rows begin to be processed in the data flow.
///
/// You can remove this method if you don't need to do anything here.
/// </summary>
public override void PreExecute()
{
base.PreExecute();
/*
* Add your code here
*/
}
/// <summary>
/// This method is called after all the rows have passed through this component.
///
/// You can delete this method if you don't need to do anything here.
/// </summary>
public override void PostExecute()
{
base.PostExecute();
/*
* Add your code here
*/
}
byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public override void CreateNewOutputRows()
{
bool pbCancel = false;
Uri OutlookWebAccessUri = new Uri(#"https://outlook.office365.com/EWS/Exchange.asmx");
try
{
#region create service binding
// Create the service binding.
ExchangeService esb = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
esb.Credentials = new WebCredentials("username", "password");// exchangeAccessAccount;
esb.Url = OutlookWebAccessUri;
#endregion
#region create CalendarView
// this week --SET YOUR VIEW ON WHAT TIMELINE YOU WANT TO RETRIEVE YOUR CALENDAR DATA --
//if no calendar data - start here
DateTime startDate = new DateTime(Variables.StartYear, Variables.StartMonth, 1);
DateTime endDate = new DateTime(Variables.StartYear, Variables.StartMonth, DateTime.DaysInMonth(Variables.StartYear, Variables.StartMonth));
//there is no calendar data, get all data
startDate = new DateTime(Variables.StartYear, Variables.StartMonth, 1);
endDate = new DateTime(Variables.StartYear, Variables.StartMonth, DateTime.DaysInMonth(Variables.StartYear, Variables.StartMonth));
//find the root folder
Folder rootfolder = Folder.Bind(esb, WellKnownFolderName.PublicFoldersRoot, new PropertySet());
FolderView view = new FolderView(100);
SearchFilter search = new SearchFilter.ContainsSubstring(FolderSchema.DisplayName, Variables.RootFolder); //Enter your rootfolder here that you are looking for in the public folder domain
FindFoldersResults myFolderResults = rootfolder.FindFolders(search, view);
foreach (Folder myFolder in myFolderResults.Folders)
{
foreach (CalendarFolder PublicItem in myFolder.FindFolders(view))
{
string myname = PublicItem.DisplayName;
if (myname == Variables.CalendarName) //Enter your calendar name here within the sub folder
{
CalendarFolder PublicCalendar = CalendarFolder.Bind(esb, PublicItem.Id, new PropertySet());
CalendarView cPublicView = new CalendarView(startDate, endDate, 512);
cPublicView.PropertySet = new PropertySet(AppointmentSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End, AppointmentSchema.Id);
FindItemsResults<Appointment> findPublicItemResponse = PublicCalendar.FindAppointments(cPublicView);
if (findPublicItemResponse == null)
{
return;
}
//get additional properties for each item returned by view, do this as view cannot return a lot of useful stuff like attendees
ServiceResponseCollection<ServiceResponse> addPublicProperties =
esb.LoadPropertiesForItems(from Item item in findPublicItemResponse select item,
new PropertySet(
BasePropertySet.IdOnly,
AppointmentSchema.Resources,
AppointmentSchema.RequiredAttendees,
AppointmentSchema.OptionalAttendees,
AppointmentSchema.Subject,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.IsCancelled,
AppointmentSchema.Location,
AppointmentSchema.Body,
AppointmentSchema.Categories
));
List<Appointment> additionalProperties = new List<Appointment>(addPublicProperties.Count);
if (addPublicProperties != null)
{
foreach (ServiceResponse currentResponce in addPublicProperties)
{
additionalProperties.Add(((Appointment)((GetItemResponse)currentResponce).Item));
}
}
Appointment currentAppointmentAddProps = null;
foreach (Appointment currentAppointment in findPublicItemResponse)
{
#region find additional properties for current Appointment
currentAppointmentAddProps = additionalProperties.Find(delegate (Appointment arg)
{ return arg.Id == currentAppointment.Id; });
#endregion
OutputRecordSetBuffer.AddRow();
if (currentAppointmentAddProps.RequiredAttendees.Count > 0)
{
foreach (var attendee in currentAppointmentAddProps.RequiredAttendees) //what to do if no attendee?
{
OutputRecordSetBuffer.Attendee = attendee.Name;
OutputRecordSetBuffer.ActualStartDate = currentAppointmentAddProps.Start;
OutputRecordSetBuffer.ActualEndDate = currentAppointmentAddProps.End;
OutputRecordSetBuffer.Subject = currentAppointment.Subject;
OutputRecordSetBuffer.Location = currentAppointment.Location;
OutputRecordSetBuffer.Detail.AddBlobData(GetBytes(currentAppointment.Body.Text));
OutputRecordSetBuffer.DetailType = currentAppointment.Body.BodyType.ToString();
OutputRecordSetBuffer.CalendarName = Variables.CalendarName;
//Currently no way to return category color
}
}
else
{
OutputRecordSetBuffer.ActualStartDate = currentAppointmentAddProps.Start;
OutputRecordSetBuffer.ActualEndDate = currentAppointmentAddProps.End;
OutputRecordSetBuffer.Subject = currentAppointment.Subject;
OutputRecordSetBuffer.Location = currentAppointment.Location;
if (currentAppointment.Body.Text != null)
{
OutputRecordSetBuffer.Detail.AddBlobData(GetBytes(currentAppointment.Body.Text));
}
OutputRecordSetBuffer.DetailType = currentAppointment.Body.BodyType.ToString();
OutputRecordSetBuffer.CalendarName = Variables.CalendarName;
}
}
}
}
}
#endregion
}
catch (Exception e)
{
ComponentMetaData.FireError(1, "Get empty calendar data for insert has error", e.Message, "", 0, out pbCancel);
}
}
}
My biml xml gets broken due to the (I think) types in the list. The ServiceResponseCollection and List. Biml reads the tags <> as part of biml and then don't read the rest of the file as C#. How do I reference a C# file as I'd like to keep things neat and not having to post a whole lot of code into the Scriptprojects but keep it as a separate file as below.
<ScriptProjects>
<ScriptComponentProject Name="GetData">
<AssemblyReferences>
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSPipelineWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.DTSRuntimeWrap" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.PipelineHost" />
<AssemblyReference AssemblyPath="Microsoft.SqlServer.TxScript" />
<AssemblyReference AssemblyPath="System.dll" />
<AssemblyReference AssemblyPath="System.AddIn.dll" />
<AssemblyReference AssemblyPath="System.Data.dll" />
<AssemblyReference AssemblyPath="System.Xml.dll" />
</AssemblyReferences>
<ReadOnlyVariables>
<Variable VariableName="CalendarName" DataType="String" Namespace="User" ></Variable>
<Variable VariableName="RootFolder" DataType="String" Namespace="User" ></Variable>
<Variable VariableName="StartMonth" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="StartYear" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="EndMonth" DataType="Int32" Namespace="User" ></Variable>
<Variable VariableName="EndYear" DataType="Int32" Namespace="User" ></Variable>
</ReadOnlyVariables>
<OutputBuffers>
<OutputBuffer Name="CalendarOutput" IsSynchronous="false">
<Columns>
<Column Name="Detail" DataType="String" ></Column>
<Column Name="Status" DataType="String" Length="50" ></Column>
<Column Name="ActualStartDate" DataType="Date" ></Column>
<Column Name="Attendee" DataType="String" Length="50" ></Column>
<Column Name="Location" DataType="String" Length="500"></Column>
<Column Name="Subject" DataType="String" Length="255" ></Column>
<Column Name="ActualEndDate" DataType="Date" ></Column>
<Column Name="Category" DataType="String" Length="50"></Column>
<Column Name="DetailType" DataType="String" Length="50"></Column>
<Column Name="CalendarName" DataType="String" Length="50"></Column>
</Columns>
</OutputBuffer>
</OutputBuffers>
<Files >
<File Path="Properties\AssemblyInfo.cs">
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("GetData")]
[assembly: AssemblyDescription("Get calendar data from Exchange")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HCC DataServices")]
[assembly: AssemblyProduct("GetData")]
[assembly: AssemblyCopyright("Copyright # 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyVersion("1.0.*")]
</File>
<ExternalFile Path="main.cs" ExternalPath="ExchangeCalendarScript.cs"/>
</Files>
</ScriptComponentProject>
</ScriptProjects>
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Packages>
<Package Name="<#=PackageName#>" ProtectionLevel="EncryptSensitiveWithUserKey">
<Tasks>
<Dataflow Name="Get Calendar data">
<Transformations>
<ScriptComponentSource Name="Get calendar data">
<ScriptComponentProjectReference ScriptComponentProjectName="GetData"></ScriptComponentProjectReference>
</ScriptComponentSource>
</Transformations>
</Dataflow>
</Tasks>
</Package>
</Packages>
</Biml>
How do I fix this?
Related
There are 2 elements binding data in my test app, one is Button, another is Listview. But Listview cannnot be refreshed automatically. But if I disable ButtonText = $"It took {totalSecs} seconds" in ViewModel, Listview can be refreshed automatically. Any ideas? TIA.
Here is the Xaml file.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:test"
x:Class="test.MainPage">
<ContentPage.BindingContext>
<local:PurchasesInvoiceMasterViewModel />
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Start">
<Button
x:Name="PurchasesInvoiceList"
Text="{Binding ButtonText}"
Command="{Binding AddListCommand}"
SemanticProperties.Hint="Get Purchases Invoice List"
HorizontalOptions="Center" />
<ListView x:Name="PurchasesInvoiceMasterObjectView" ItemsSource="{Binding PurchasesInvoiceMasterObject}" HeightRequest="500">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding purchases_invoice_id}"
Detail="{Binding purchases_invoice_date }" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ScrollView>
Here is ViewModel.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml.Linq;
namespace test
{
class PurchasesInvoiceMasterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddListCommand { get; set; }
private string _buttonText = "Purchases Invoice List";
public string ButtonText
{
get => _buttonText;
set
{
if (_buttonText != value)
{
_buttonText = value;
OnPropertyChanged("");
}
}
}
private ObservableCollection<PurchasesInvoiceMaster> _purchasesInvoiceMasterObject = new();
public ObservableCollection<PurchasesInvoiceMaster> PurchasesInvoiceMasterObject
{
get => _purchasesInvoiceMasterObject;
set
{
if (_purchasesInvoiceMasterObject != value)
{
_purchasesInvoiceMasterObject = value;
OnPropertyChanged("");
}
}
}
public PurchasesInvoiceMasterViewModel()
{
AddListCommand = new Command(() =>
{
double statrtSecs = DateTime.Now.TimeOfDay.TotalSeconds;
string purchasesInvoiceMaster = "test.PurchasesInvoiceMaster.xml";
using (Stream streamPurchasesInvoiceMaster = this.GetType().Assembly.
GetManifestResourceStream(purchasesInvoiceMaster))
{
using (var readerPurchasesInvoiceMaster = new System.IO.StreamReader(streamPurchasesInvoiceMaster))
{
var xmlpurchasesInvoiceMaster = XDocument.Load(readerPurchasesInvoiceMaster);
foreach (XElement xmmd in xmlpurchasesInvoiceMaster.Descendants("curtemp01"))
{
_purchasesInvoiceMasterObject.Add(new PurchasesInvoiceMaster
{
purchases_invoice_id = xmmd.Element("purchases_invoice_id").Value,
purchases_invoice_date = xmmd.Element("purchases_invoice_date").Value,
});
}
}
}
double endSecs = DateTime.Now.TimeOfDay.TotalSeconds;
float totalSecs = (float)(endSecs - statrtSecs);
ButtonText = $"It took {totalSecs} seconds";
});
}
public void OnPropertyChanged([CallerMemberName] string name = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
I'm trying to create a compound view component in Xamarin Forms called FormElement which is composed of two labels and an Entry:
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:custom="clr-namespace:Mynamespace;assembly=Mynamespace"
x:Class="Mynamespace.Components.FormEntry">
<StackLayout Orientation="Horizontal">
<Label x:Name="formRequiredStar"
IsVisible="{Binding IsRequired}"
Text="*" TextColor="Red"
FontSize="15"
FontAttributes="Bold"
Margin="-12,0,0,0"
HorizontalOptions="Start" />
<Label x:Name="formLabel"
HorizontalOptions="Start"
Text="{Binding LabelText}"
TextColor="{Binding LabelTextColor}"
FontSize="{Binding LabelTextFontSize}"
FontAttributes="{Binding LabelTextFontStyle}" />
</StackLayout>
<Frame BorderColor="Black"
CornerRadius="7"
Padding="5,0"
Margin="0,-3,0,0"
HasShadow="false">
<Entry x:Name="mainEntry"
Keyboard="{Binding KeybdType}"
Placeholder="{Binding EntryPlaceHolder}"
TextColor="Black"
FontSize="Default"
HeightRequest="{Binding EntryHeight}" />
</Frame>
</StackLayout>
Next, I want to switch focus from the Entry to a "next" element when the user taps the DONE button, so I do:
namespace Mynamespace.Components
{
public partial class FormEntry : StackLayout
{
public VisualElement NextFocus
{
get { return (VisualElement)GetValue(NextFocusProperty); }
set { SetValue(NextFocusProperty, value); }
}
public static readonly BindableProperty NextFocusProperty =
BindableProperty.Create(nameof(NextFocus),
typeof(VisualElement),
typeof(FormEntry),
null,
Xamarin.Forms.BindingMode.OneWay);
public FormEntry()
{
InitializeComponent();
BindingContext = this;
mainEntry.Completed += (s, e) =>
{
if (NextFocus != null)
{
NextFocus.Focus();
}
};
}
}
}
Next, in order for a FormEntry to be the target of NextFocus, I tried adding
this.Focused += (s,e) => { mainEntry.Focus(); };
to the constructor, but the handler is never called, and I also tried overriding
public new void Focus() {
mainEntry.Focus();
}
but this method is never called. Layout classes are descended from VisualElement so they should inherit Focused. Is there something about Layout objects that I'm missing? I could understand that Layout objects aren't usually the target of focus, but the event handler is supposedly there so I ought to be able to use it.
Here's an example of how I utilize the FormEntry on a login screen:
<!-- Email -->
<controls:FormEntry x:Name="usernameEntry"
Margin="25,40,25,0"
IsRequired="true"
EntryHeight="40"
KeybdType="Email"
NextFocus="{x:Reference passwordEntry}"
LabelText="{il8n:Translate Emailorusername}"
EntryPlaceHolder="{il8n:Translate EnterUsername}">
</controls:FormEntry>
<!-- Password -->
<controls:FormEntry x:Name="passwordEntry"
Margin="25,0,25,0"
IsRequired="true"
EntryHeight="40"
LabelText="{il8n:Translate Password}"
EntryPlaceHolder="{il8n:Translate EnterPassword}" />
I think you have get the nextfocus element, you can get mainEntry from nextfocus, like this:
public FormEntry ()
{
InitializeComponent ();
BindingContext = this;
mainEntry.Completed += (s, e) =>
{
if (NextFocus != null)
{
FormEntry formentry = (FormEntry)NextFocus;
Entry entry = formentry.mainEntry;
entry.Focus();
}
};
}
Then you can find you will get focus.
I'm developing a UWP app and I'm facing a problem. The app uses the MVVM pattern with Template10. I have created a similar solution that recreates the problem that I'm facing. In that solution, a list of orders are displayed, the user chooses an order and then click the "Edit" button. Then a second page is displayed and pre-loaded with the previous selected order, in this second page the user can edit the order. The problem is in the second page, the data bound to comboboxes doesn't show. Maybe the problem is related to this question. In my case, the SelectedValue is set before the ItemsSource. After debugging, I have reached these lines of code in OrderEditionPage.g.cs:
private void Update_ViewModel(global::ComboApp.ViewModels.OrderEditionPageViewModel obj, int phase)
{
this.bindingsTracking.UpdateChildListeners_ViewModel(obj);
if (obj != null)
{
if ((phase & (NOT_PHASED | DATA_CHANGED | (1 << 0))) != 0)
{
this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
}
if ((phase & (NOT_PHASED | (1 << 0))) != 0)
{
this.Update_ViewModel_BusinessAssociates(obj.BusinessAssociates, phase);
this.Update_ViewModel_TransactionTypes(obj.TransactionTypes, phase);
this.Update_ViewModel_OrderTypes(obj.OrderTypes, phase);
this.Update_ViewModel_ShowSelectedOrder(obj.ShowSelectedOrder, phase);
}
}
}
If I could achieve this line of code be executed at last, my problem would be solved: this.Update_ViewModel_SelectedOrder(obj.SelectedOrder, phase);
How could I achieve this? How does Visual Studio determine the order of this lines?
OrderEditionPage.xaml
<Page
x:Class="ComboApp.Views.OrderEditionPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:myconverters="using:ComboApp.Converters"
xmlns:t10converters="using:Template10.Converters"
mc:Ignorable="d">
<Page.Resources>
<t10converters:ChangeTypeConverter x:Key="TypeConverter" />
<myconverters:DateTimeConverter x:Key="DateTimeConverter" />
</Page.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel
Padding="15, 5"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox
Header="Order #"
Margin="5"
Width="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.ExternalId, Mode=TwoWay}" />
<ComboBox
Header="Business Associate"
Margin="5"
MinWidth="300"
SelectedValuePath="BusinessAssociateId"
DisplayMemberPath="Name1"
ItemsSource="{x:Bind ViewModel.BusinessAssociates}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.BusinessAssociateId, Mode=TwoWay, Converter={StaticResource TypeConverter}}" />
<DatePicker
Header="Delivery Date"
Margin="5"
MinWidth="0"
Width="200"
Date="{x:Bind ViewModel.SelectedOrder.DeliveryDate, Mode=TwoWay, Converter={StaticResource DateTimeConverter}}" />
<ComboBox
Header="Transaction"
MinWidth="200"
Margin="5"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.TransactionTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.TransactionType, Mode=TwoWay}" />
<TextBox
Header="Priority"
Margin="5"
MaxWidth="150"
HorizontalAlignment="Left"
Text="{x:Bind ViewModel.SelectedOrder.Priority}" />
<ComboBox
Header="Type"
Margin="5"
MinWidth="200"
SelectedValuePath="Value"
DisplayMemberPath="Display"
ItemsSource="{x:Bind ViewModel.OrderTypes}"
SelectedValue="{x:Bind ViewModel.SelectedOrder.OrderType, Mode=TwoWay}" />
<TextBox
Header="Information"
Margin="5"
Height="100"
AcceptsReturn="True"
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{x:Bind ViewModel.SelectedOrder.Information, Mode=TwoWay}" />
<Button
Margin="5"
Content="Show"
Width="100"
HorizontalAlignment="Right"
Command="{x:Bind ViewModel.ShowSelectedOrder}" />
</StackPanel>
</ScrollViewer>
</Page>
OrderEditionPage.xaml.cs
using ComboApp.ViewModels;
using Windows.UI.Xaml.Controls;
namespace ComboApp.Views
{
public sealed partial class OrderEditionPage : Page
{
public OrderEditionPageViewModel ViewModel => DataContext as OrderEditionPageViewModel;
public OrderEditionPage()
{
this.InitializeComponent();
}
}
}
OrderEditionPageViewModel.cs
using ComboApp.Models;
using ComboApp.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Template10.Mvvm;
using Template10.Utils;
using Windows.UI.Xaml.Navigation;
namespace ComboApp.ViewModels
{
public class OrderEditionPageViewModel
: ViewModelBase
{
private IBusinessAssociateService businessAssociateService;
private Order selectedOrder;
public Order SelectedOrder
{
get { return selectedOrder; }
set { Set(ref selectedOrder, value); }
}
public ObservableCollection<object> TransactionTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<object> OrderTypes { get; set; } = new ObservableCollection<object>();
public ObservableCollection<BusinessAssociate> BusinessAssociates { get; set; } = new ObservableCollection<BusinessAssociate>();
public OrderEditionPageViewModel(IBusinessAssociateService businessAssociateService)
{
this.businessAssociateService = businessAssociateService;
TransactionTypes.Add(new { Value = "I", Display = "Incoming" });
TransactionTypes.Add(new { Value = "O", Display = "Outgoing" });
TransactionTypes.Add(new { Value = "T", Display = "Transfer" });
OrderTypes.Add(new { Value = "M", Display = "Manual" });
OrderTypes.Add(new { Value = "A", Display = "Automatic" });
OrderTypes.Add(new { Value = "S", Display = "Semi-automatic" });
}
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> state)
{
// Loading buiness associates
var response = await businessAssociateService.GetNextPageAsync();
if (response.IsSuccessful)
{
BusinessAssociates.AddRange(response.Result.Items);
}
SelectedOrder = (Order)parameter;
await base.OnNavigatedToAsync(parameter, mode, state);
}
private DelegateCommand showSelectedOrder;
public DelegateCommand ShowSelectedOrder => showSelectedOrder ?? (showSelectedOrder = new DelegateCommand(async () =>
{
await Views.MessageBox.ShowAsync(JsonConvert.SerializeObject(SelectedOrder, Formatting.Indented));
}));
}
}
It is a known issue of x:Bind when the SelectedValue of a ComboBox is sometimes set before its ItemsSource, you can read more about it here.
As a workaround you can use Bindings instead of x:Bind, but make sure that ItemsSource binding is placed before SelectedValue binding in XAML.
Alternatively you can try calling Bindings.Update() in the Page_Loaded event of your second page.
In my document management process it is often necessary to provide some additional documents (e.g. list of comments, list of differences, some screenshot, etc).
These additional documents would be convenient to add in the Activiti forms. I would like to be able to add documents at the initiation stage of a business process and at the stage of the revise.
For this, I added an aspect with associations in the workflow-model.xml (relevant part):
...
<type name="mswf:activitiRevise">
...
<mandatory-aspects>
...
<aspect>mswf:attachments</aspect>
</mandatory-aspects>
</type>
...
<aspect name="mswf:attachments">
<associations>
<association name="mswf:package">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
</associations>
</aspect>
...
etc
In share-config-custom.xml I have the following (relevant part):
...
<config evaluator="task-type" condition="bpm:startTask">
<forms>
<form id="workflow-details">
<field-visibility>
...
<show id="mswf:package" />
</field-visibility>
<appearance>
...
<set id="attachments" appearance="title" label-id="Additional docs" />
<field id="mswf:package" set="attachments" />
</appearance>
</form>
<form>
<field-visibility>
<show id="mswf:package" />
...
</field-visibility>
<appearance>
<set id="attachments" appearance="title" label-id="Additional docs" />
<field id="mswf:package" set="attachments" />
...
</appearance>
</form>
</forms>
</config>
...
etc
Now I have an additional field where I can choose the appropriate documents.
It works - I added some documents and can see them at all of the stages of the document management process.
The problem occurs when I try to change a set of initially selected files. For example, when I try to add a new one. If I add a new one (or remove) - changes are not saved and in the next task, I see the same documents that were selected at the beginning.
To get control of this behavior, I developed WebScript, in which I try to manage properties. I call the WebScript from Share in the getAddedItems() method:
/**
* Returns items that have been added to the current value
*
* #method getAddedItems
* #return {array}
*/
getAddedItems: function ObjectFinder_getAddedItems() {
var addedItems = [],
currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));
for (var item in this.selectedItems) {
if (this.selectedItems.hasOwnProperty(item)) {
if (!(item in currentItems)) {
addedItems.push(item);
}
}
}
...
// here the call to the WebScript
return addedItems;
},
Part of my Java-backed WebScript:
...
public class WorkflowAttachmentsManipulator extends DeclarativeWebScript {
private static final String WORKFLOW_MODEL_URI = "...";
private WorkflowService workflowService;
#Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
Map<String, Object> model = new HashMap<>();
String taskId = req.getParameter("taskId");
String addedItem = req.getParameter("addedItem");
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
Map<QName, Serializable> taskProperties = workflowTask.getProperties();
...
taskProperties.replace(
QName.createQName(WORKFLOW_MODEL_URI, "package"), oldValue, addedItem);
workflowService.updateTask(taskId, taskProperties, null, null);
...
I'm trying to replace the selected files with some arbitrary and the replace(...) method returns true. In alfrescotomcat-stdout.2017-09-06.log I also see that the property has been replaced:
Before calling the WebScript (two files in the package):
key: {WORKFLOW_MODEL_URI_HERE}package
value: [workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a,
workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769]
After calling the WebScript (one file in the package):
key: {WORKFLOW_MODEL_URI_HERE}package
value: workspace://SpacesStore/1a0b110f-1e09-4ca2-b367-fe25e4964a4e
After updating the form at the current task I see my new file.
But the value is not saved (lost) after revise / review and in the next task I see the same files. Let's say, the task ID for the current user was activiti$204587, then it became equals activiti$204647...
I added some debugging code to the BPMN diagram and found that the contents of mswf_package did not change after the calling the WebScript.
In 'Submit', main config:
for(var i = 0; i < mswf_package.size(); i++) {
logger.log(mswf_package.get(i).nodeRef);
}
Output:
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769
In 'Review Task', listeners of the create and complete events:
for(var i = 0; i < mswf_package.size(); i++) {
logger.log(mswf_package.get(i).nodeRef);
}
Output:
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769
How to add additional attachments in the workflow? Is it possible?
A set of strings with NodeRefs can be passed to the following WebScript, for example:
public class WorkflowAttachmentsManipulator extends DeclarativeWebScript {
private static final String WORKFLOW_MODEL_URI = "...";
private WorkflowService workflowService;
#Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
Map<String, Object> model = new HashMap<>();
String taskId = req.getParameter("taskId");
String addedItems = req.getParameter("addedItems");
String oldValue = "";
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
Map<QName, Serializable> taskProperties = workflowTask.getProperties();
Iterator taskIterator = taskProperties.entrySet().iterator();
while(taskIterator.hasNext()) {
Map.Entry taskPair = (Map.Entry)taskIterator.next();
Object key = taskPair.getKey();
if(key != null &&
key.toString().equalsIgnoreCase("{" + WORKFLOW_MODEL_URI + "}package")) {
if(taskPair.getValue() != null) {
oldValue = taskPair.getValue().toString();
if(!oldValue.equals("[]")) {
oldValue = oldValue.replaceAll("[\\[\\]]", "");
addedItems = "[" + oldValue + "," + addedItems + "]";
} else {
if(addedItems.indexOf(",") > 0) {
addedItems = "[" + addedItems + "]";
}
}
}
taskProperties.replace(
QName.createQName(WORKFLOW_MODEL_URI, "package"),
oldValue,
addedItems);
workflowService.updateTask(workflowTask.getId(),
taskProperties, null, null);
break;
}
}
...
}
public WorkflowService getWorkflowService() {
return workflowService;
}
public void setWorkflowService(WorkflowService workflowService) {
this.workflowService = workflowService;
}
}
This code overrides attachments for the certain task.
Additional files need to differentiate from those that are involved in the document management process. It can be done, for example, as follows:
/**
* Returns items that have been added to the current value
*
* #method getAddedItems
* #return {array}
*/
getAddedItems: function ObjectFinder_getAddedItems() {
var addedItems = [],
currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));
var attachments = [];
for (var item in this.selectedItems) {
if (this.selectedItems.hasOwnProperty(item)) {
if (!(item in currentItems)) {
// modified for differentiation
if (this.options.displayMode == "items") {
attachments.push(item);
} else {
addedItems.push(item);
}
}
}
}
...
// call to the WebScript with attachments
// modified for merge
return addedItems.concat(attachments);
},
For saving the overridden attachments in the process variable it is necessary to define the listener of the complete event.
Moreover, it is possible to "pass" files by the chain from task to task (with changes) by using this technique:
Listener of the complete event:
public class TaskCompleteListener implements TaskListener {
#Override
public void notify(DelegateTask delegateTask) {
DelegateExecution execution = delegateTask.getExecution();
execution.setVariable("mswf_package", delegateTask.getVariable("mswf_package"));
}
}
Listener of the create event:
public class TaskCreateListener implements TaskListener {
#Override
public void notify(DelegateTask delegateTask) {
DelegateExecution execution = delegateTask.getExecution();
delegateTask.setVariable("mswf_package", execution.getVariable("mswf_package"));
}
}
This solved my issue.
How can I set DataTemplate to a ContentControl in a PageResource? I would like to display a UserControl in my ContentControl and I want use the ContentControl like a navigation region. So it can change the UserControls what are displayed in it.
I have a Shell.xaml:
<Page
x:Class="MyProject.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyProject"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:View="using:MyProject.View"
xmlns:ViewModel="using:MyProject.ViewModel">
<Page.DataContext>
<ViewModel:ShellViewModel />
</Page.DataContext>
<Page.Resources>
<DataTemplate>
<View:MyUserControlViewModel1 />
</DataTemplate>
<DataTemplate>
<View:MyUserControlViewModel2 />
</DataTemplate>
</Page.Resources>
<StackPanel>
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentControl Content="{Binding CurrentPageViewModel}">
</ContentControl>
</StackPanel>
</Page>
My Shell's view model is:
namespace MyProject.ShellViewModel
{
class ShellViewModel : ObservableObject
{
#region Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
#endregion
#region Properties / Commands
public List<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
{
_pageViewModels = new List<IPageViewModel>();
}
return _pageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get { return _currentPageViewModel; }
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
#endregion
#region Methods
public ShellViewModel()
{
PageViewModels.Add(new MyUserControlViewModel1());
PageViewModels.Add(new MyUserControlViewModel2());
CurrentPageViewModel = PageViewModels[0];
}
#endregion
}
}
I tried set Page.Resource like below from this link: Window vs Page vs UserControl for WPF navigation?
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomeViewModel}">
<local:HomeView /> <!-- This is a UserControl -->
</DataTemplate>
<DataTemplate DataType="{x:Type local:ProductsViewModel}">
<local:ProductsView /> <!-- This is a UserControl -->
</DataTemplate>
</Window.Resources>
But these use another namespaces and it doesn't work for me because my app is a Windows 10 Universal App and there is no DataType attribute for the DataTemplate for example.
I am trying to make my application using MVVM pattern (if it was not obiously from the code snippets).
You can do it by creating a class derived from DataTemplateSelector.
In this class you can override SelectTemplateCore method, that will return the DataTemplate you need based on the data type. To make all this more auto-magical, you can set the key of each template to match the name of the class and then search for the resource with that name retrieved using GetType().Name.
To be able to provide specific implementations on different levels, you can walk up the tree using VisualTreeHelper.GetParent() until the matching resource is found and use Application.Current.Resources[ typeName ] as fallback.
To use your custom template selector, just set it to ContentTemplateSelector property of the ContentControl.
Example
Here is the sample implementation of an AutoDataTemplateSelector
public class AutoDataTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore( object item ) => GetTemplateForItem( item, null );
protected override DataTemplate SelectTemplateCore( object item, DependencyObject container ) => GetTemplateForItem( item, container );
private DataTemplate GetTemplateForItem( object item, DependencyObject container )
{
if ( item != null )
{
var viewModelTypeName = item.GetType().Name;
var dataTemplateInTree = FindResourceKeyUpTree( viewModelTypeName, container );
//return or default to Application resource
return dataTemplateInTree ?? ( DataTemplate )Application.Current.Resources[ viewModelTypeName ];
}
return null;
}
/// <summary>
/// Tries to find the resources up the tree
/// </summary>
/// <param name="resourceKey">Key to find</param>
/// <param name="container">Current container</param>
/// <returns></returns>
private DataTemplate FindResourceKeyUpTree( string resourceKey, DependencyObject container )
{
var frameworkElement = container as FrameworkElement;
if ( frameworkElement != null )
{
if ( frameworkElement.Resources.ContainsKey( resourceKey ) )
{
return frameworkElement.Resources[ resourceKey ] as DataTemplate;
}
else
{
return FindResourceKeyUpTree( resourceKey, VisualTreeHelper.GetParent( frameworkElement ) );
}
}
return null;
}
}
You can now instantiate it as a resource and create resources for each type of ViewModel you use
<Application.Resources>
<local:AutoDataTemplateSelector x:Key="AutoDataTemplateSelector" />
<!-- sample viewmodel data templates -->
<DataTemplate x:Key="RedViewModel">
<Rectangle Width="100" Height="100" Fill="Red" />
</DataTemplate>
<DataTemplate x:Key="BlueViewModel">
<Rectangle Width="100" Height="100" Fill="Blue" />
</DataTemplate>
</Application.Resources>
And now you can use it with the ContentControl as follows:
<ContentControl ContentTemplateSelector="{StaticResource AutoDataTemplateSelector}"
Content="{x:Bind CurrentViewModel, Mode=OneWay}" />
I have put the sample solution on GitHub