I'm making a VS Extension to manage Github issues. Due to some peculiarities of CommunityToolkit.MVVM
and on the advice of @Sergio0694, I've had to separate my ViewModels, Services etc into a separate project in my solution. Because of this, I've also had to move my OptionsProvider (which manages VS>Tools>Options for the extension) into the additional library as ViewModels and Services will rely heavily on these options.
The main VSIX Project VisGit
holds the main ToolWindow as a view called MainView
. Viewmodels/services etc exist in a separate project, VisGit.Core
. This view is bound to an instance of MainViewModel
purely via xaml (nothing in code-behind):
<UserControl x:Class="VisGit.Views.MainView"
xmlns:viewmodels="clr-namespace:VisGit.Core.ViewModels;assembly=VisGit.Core"/>
<UserControl.DataContext>
<viewmodels:MainViewModel />
</UserControl.DataContext>
All is working - VisGit contains a reference to VisGit.Core. My OptionsProvider class is in VisGit.Core. I had to create this in VisGit
first and move it, as "Options Page (Community)" was not avaiable in VisGit.Core
as this is just a standard class library. The code:
namespace VisGit.Core.Services
{
public partial class OptionsProvider
{
// Register the options with this attribute on your package class:
// [ProvideOptionPage(typeof(OptionsProvider.UserSettingsOptions), "VisGit.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)]
[ComVisible(true)]
public class UserSettingsOptions : BaseOptionPage<UserSettings>
{ }
}
public class UserSettings : BaseOptionModel<UserSettings>
{
private string personalAccessToken = "{UNSET}";
[DisplayName("Personal Access Token")]
public string PersonalAccessToken
{
get => Encyption.DpapiToInsecureString(Encyption.DpapiDecryptString(personalAccessToken));
set => personalAccessToken = Encyption.DpapiEncryptString(Encyption.DpapiToSecureString(value));
}
}
}
I have referenced Community.VisualStudio.Toolkit.17
in VisGit.Core
. Now, because it is not in the VSIX project, I've amended the boilerplate directions to point to the right location for UserSettings in my VisGitPackage.cs
file in VisGit
:
namespace VisGit
{
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
[ProvideToolWindow(typeof(MainToolWindow.Pane), Style = VsDockStyle.Tabbed, Window = WindowGuids.SolutionExplorer)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(PackageGuids.VisGitString)]
[ProvideOptionPage(typeof(VisGit.Core.Services.OptionsProvider.UserSettingsOptions), "VisGit.Core.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)]
public sealed class VisGitPackage : ToolkitPackage
{
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
await this.RegisterCommandsAsync();
this.RegisterToolWindows();
}
}
}
VisGit.Core
compiles, but VisGit
doesn't. I get the baffling error:
Error CreatePkgDef : error : TypeLoadException: Could not load type 'VisGit.Core.Services.OptionsProvider+UserSettingsOptions' from assembly 'VisGit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
at System.Reflection.CustomAttribute._CreateCaObject(RuntimeModule pModule, IRuntimeMethodInfo pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
at System.Reflection.CustomAttribute.CreateCaObject(RuntimeModule module, IRuntimeMethodInfo ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeModule decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes, Boolean isDecoratedTargetSecurityTransparent)
at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
at Microsoft.VisualStudio.Tools.CreatePkgDef.ProcessAssembly(String fileName, Hive hive, PkgDefContext context, Boolean register, RegistrationMode mode) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 341
at Microsoft.VisualStudio.Tools.CreatePkgDef.DoCreatePkgDef(InputArguments inputArguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 202
at Microsoft.VisualStudio.Tools.CreatePkgDef.Main(String[] arguments) in D:\a\_work\1\s\src\product\vssdk\tools\CreatePkgDef\CreatePkgDef.cs:line 91 VisGit
I do note that the OptionsProvider
class is partial, but if I create a new OptionsProvider in the VSIX project, and try to "Go to Definition" on the class name, I can't find any other definition for this class (i.e. no other partial).
With present architecture, I can't instantiate the UserOptions in the VSIX project (VisGit) and pass them to any view models, as the ViewModels are instantiated in the View XAML. Besides, if there were a way to do this, it feels a bit hacky as the view model should be able to create its own instance to be fully separate from the view. Admittedly, I'm still a bit baffled by how to instantiate viewmodels properly.
Any advice appreciated.
Could not load type 'VisGit.Core.Services.OptionsProvider+UserSettingsOptions' from assembly 'VisGit.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null')
It seems that the compiler cannot parse the class 'VisGit.Core.Services.OptionsProvider+UserSettingsOptions'. After checking your directory structure, it looks like you service
folder are under namesapce VisGit.Core
. The name of the namespace must be a valid C# identifier name.
Please try to remove the point(".") in your namespace VisGit.Core
and rename it to VisGitCore
.
For example
[ProvideOptionPage(typeof(VisGitCore.Services.OptionsProvider.UserSettingsOptions), "VisGitCore.Services", "UserSettings", 0, 0, true, SupportsProfiles = true)]
After doing that, i am able to build VisGit
fine without errors.
I think that as Namespaces are delimited by using the .
operator but if Nampespace
's name includes dots, it will treat .
as a separator.
Hope it can help you.