My Golden .csproj File for Custom Activities for NET461 AND NET6

Hey Peeps!

If you’ve ever tried creating your own activities, settings, wizards, or workflow rules, you may have noticed that UiPath’s documentation on it is not that helpful. The sample projects only target 1 particular runtime (ie. Legacy, Windows, Cross-Platform), and the UiPath.Workflow stuff they talk about still results in a bunch of issues when trying to create more complex Designers for activities targetting NET6.

I bring to you, the .csproj file that enabled me to target both Windows - Legacy, and Windows projects from Visual Studio 2022. Behold:

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <!-- Assembly/Project Settings -->
  <PropertyGroup>
    <TargetFrameworks>net461;net6.0-windows7.0</TargetFrameworks>
    <UseWPF>true</UseWPF>
    <AssemblyName>[ProjectName]</AssemblyName>
    <RootNamespace>[ProjectName]</RootNamespace>
    <Version>1.0.0</Version>
  </PropertyGroup>
  
  <!-- Package Settings -->
  <PropertyGroup>
    <PackageId>[ProjectName]</PackageId>
  </PropertyGroup>

  <!-- Package Version -->
  <PropertyGroup Condition="'$(Configuration)' == 'Debug' AND '$(DesignTimeBuild)' != 'true'">
    <PackageVersion>$([System.DateTime]::Now.ToString(yyyy.MM.dd-1HHmmss))</PackageVersion>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <PackageVersion>$([System.DateTime]::Now.ToString(yyyy.MM.dd))</PackageVersion>
  </PropertyGroup>
  
  <!-- NET461 ONLY Properties -->
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
  </PropertyGroup>

  <!-- Package Target -->
  <Target Name="CopyProjectReferencesToPackage" DependsOnTargets="BuildOnlySettings;ResolveReferences">
    <ItemGroup>
      <!--Filter out unnecessary files-->
      <_ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths-&gt;WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')-&gt;WithMetadataValue('PrivateAssets', 'All'))" />
    </ItemGroup>

    <!--Print batches for debug purposes-->
    <Message Text="Batch for .nupkg: ReferenceCopyLocalPaths = @(_ReferenceCopyLocalPaths), ReferenceCopyLocalPaths.DestinationSubDirectory = %(_ReferenceCopyLocalPaths.DestinationSubDirectory) Filename = %(_ReferenceCopyLocalPaths.Filename) Extension = %(_ReferenceCopyLocalPaths.Extension)" Importance="High" Condition="'@(_ReferenceCopyLocalPaths)' != ''" />

    <ItemGroup>
      <!--Add file to package with consideration of sub folder. If empty, the root folder is chosen.-->
      <BuildOutputInPackage Include="@(_ReferenceCopyLocalPaths)" TargetPath="%(_ReferenceCopyLocalPaths.DestinationSubDirectory)" />
    </ItemGroup>
  </Target>


  <!-- Dependencies-->
  <!-- NET 461 -->
  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <Reference Include="PresentationCore" />
    <Reference Include="PresentationFramework" />
    <Reference Include="System" />
    <Reference Include="System.IO" />
    <Reference Include="System.Activities" />
    <Reference Include="System.Activities.Core.Presentation" />
    <Reference Include="System.Activities.Presentation" />
    <Reference Include="System.ComponentModel.Composition" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xaml" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="WindowsBase" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\net461\UiPath.Studio.Activities.Api.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\net461\UiPath.Api.Base.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\net461\UiPath.Activities.Api.Base.dll" />
  </ItemGroup>
  
  <!-- NET 6 -->
  <PropertyGroup>
    <AppDataLocalDir>$([System.Environment]::GetFolderPath(SpecialFolder.LocalApplicationData))</AppDataLocalDir>
  </PropertyGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'net6.0-windows7.0' ">
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\System.Activities.Core.Presentation.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\System.Activities.Presentation.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\System.Activities.Metadata.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\System.Activities.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\UiPath.Workflow.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\UiPath.Studio.Activities.Api.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\UiPath.Api.Base.dll" />
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\UiPath.Activities.Api.Base.dll" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
  </ItemGroup>
  <ItemGroup>

  </ItemGroup>
</Project>

The main things to keep in mind:

  • We leverage the assemblies provided to us with Studio’s installation to load into Visual Studio
  • Within activity .xaml.cs files or others, depending on what types you require for it, you may need to include something like
#if NET6_0_OR_GREATER
extern alias SA;
using SA::System.Activities.Expressions;
#else
using System.Activities.Expressions;
#endif

In combination with updating your NET6 dependies with something like:

 <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\System.Activities.dll">
      <Aliases>SA</Aliases>
    </Reference>
    <Reference Include="$(AppDataLocalDir)\Programs\UiPath\Studio\UiPath.Workflow.dll" />

This is because for some reason, the UiPath.Workflow dll doesn’t actually contain the same System.Activities namespace that the System.Activities dll does. I don’t know why, and it may come back to bite me, but this seems to work for me :smile:

Thoughts?

1 Like