Closed jnm2 closed 6 years ago
I hate to complain because of the amazing results you've already had, but six lines feels a bit more verbose than it needs to be. Is something like this possible? (Is SubType
needed in the .csproj itself or can that info be cached after examining the contents? Could the nesting be automatic as well?)
<Compile Update="Form1.cs" SubType="Form" />
<Compile Update="Form1.designer.cs" SubType="Form" DependentUpon="Form1.cs" />
More differences:
Form1.cs
is not the Form icon.Form1
.Form1.resx
shows up and Form1.designer.cs
nests under that. Usually they'd nest as siblings with the parent Form1.cs
.Correction: This is not an absolute showstopper, just very tedious to work around.
Form1.designer.cs
gets replaced by the codegen for Form1.resx
. This loses the partial
modifier so the code does not compile, and moves InitializeComponent
and all the field declarations into Form1.cs
which is less than ideal.
Every time, I have to manually remove the internal
modifier and add the partial
modifier to Form1.designer.cs
and delete the constructor.
I don't have the option to not use the resx. It gets created just by me setting a form property in the designer and saving.
I'm pretty sure that the fact that the form is a partial class with the RESX code will be a major problem at some point- for example, if I don't want a parameterless constructor.I tried deleting the .resx and the resx-generated designer file and now the designer will not open. Two errors:
NotImplementedException, call stack:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at Microsoft.VisualStudio.Design.Serialization.LicenseService.GetLicenseDocData(FileAccess access)
at Microsoft.VisualStudio.Design.Serialization.LicenseService.FillLicensedTypes()
at Microsoft.VisualStudio.Design.Serialization.LicenseService.get_LicensedTypes()
at Microsoft.VisualStudio.Design.Serialization.LicenseService.OnComponentAdded(Object sender, ComponentEventArgs e)
at System.ComponentModel.Design.ComponentEventHandler.Invoke(Object sender, ComponentEventArgs e)
at System.ComponentModel.Design.DesignerHost.AddToContainerPostProcess(IComponent component, String name, IContainer containerToAddTo)
at System.ComponentModel.Design.DesignerHost.PerformAdd(IComponent component, String name)
at System.ComponentModel.Design.DesignerHost.System.ComponentModel.Design.IDesignerHost.CreateComponent(Type componentType, String name)
at System.ComponentModel.Design.Serialization.DesignerSerializationManager.CreateInstance(Type type, ICollection arguments, String name, Boolean addToContainer)
at System.ComponentModel.Design.Serialization.DesignerSerializationManager.System.ComponentModel.Design.Serialization.IDesignerSerializationManager.CreateInstance(Type type, ICollection arguments, String name, Boolean addToContainer)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeInstance(IDesignerSerializationManager manager, Type type, Object[] parameters, String name, Boolean addToContainer)
at System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer.DeserializeInstance(IDesignerSerializationManager manager, Type type, Object[] parameters, String name, Boolean addToContainer)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeExpression(IDesignerSerializationManager manager, String name, CodeExpression expression)
at System.ComponentModel.Design.Serialization.CodeDomSerializer.DeserializeStatementToInstance(IDesignerSerializationManager manager, CodeStatement statement)
at System.ComponentModel.Design.Serialization.CodeDomSerializer.Deserialize(IDesignerSerializationManager manager, Object codeObject)
at System.Windows.Forms.Design.ControlCodeDomSerializer.Deserialize(IDesignerSerializationManager manager, Object codeObject)
at System.ComponentModel.Design.Serialization.TypeCodeDomSerializer.DeserializeName(IDesignerSerializationManager manager, String name, CodeStatementCollection statements)
and "The variable 'buttonEdit1' is either undeclared or was never assigned." which takes me to the code, wherein buttonEdit1 is most certainly both declared and assigned.
The point of this exercise was to be able to see if the new .csproj offers deliverance from the madness that is licenses.licx
, but I wasn't able to get that far. I'm guessing the NotImplementedException above, plus the message boxes I was getting earlier while moving controls around with the same NotImplementedException message, are indicative of a license system failure.
Here's what I'm hoping will eventually be the case:
I was able to confirm that a call to LicenseManager.Validate
in a custom control constructor causes the NotImplementedException.
That brings this issue to a total of seven issues. Should I start issues for these or are they closely related enough for your purposes?
@jnm2 Hey thanks for trying out these scenarios.
As you noticed, we don't support WinForms yet. For the time being we're focused on .NET Core and only build and deploy for .NET Framework. The designers, etc do not/will not work until post VS 2017 RTM.
I think it's helpful to get issues filed on these and let's get them tagged so that we can understand what it takes to bring up WinForms (it's a going to be peeling of the onion).
The sub type issue is already being tracked here: https://github.com/dotnet/roslyn-project-system/issues/997.
The nesting issue is being tracked here: https://github.com/dotnet/roslyn-project-system/issues/159.
The other issues we are not tracking - would you like to file them or do you want me to?
As you noticed, we don't support WinForms yet. For the time being we're focused on .NET Core and only build and deploy for .NET Framework. The designers, etc do not/will not work until post VS 2017 RTM.
Understood. My goal is to make these things visible so they happen sooner rather than later, because until there's a strong UI library for Core I'll be using WinForms for a while.
The other issues we are not tracking - would you like to file them or do you want me to?
Doesn't matter to me. They will probably be of more value if you file them since you know what's under the hood.
When it will be done? It's important to me because it is the last thing remaining on my way to new csproj format :)
This is also bugging me.
I'm trying to move entirely into new csproj land, and while most of my stuff will work as is, I have one(!) WinForms app that is holding me back.
Is there anyway, even if undocumented, to make new csproj deal with WinForms projects (while keeping the designer functional)?
Yes, @onovotny, I've been meaning to ask if you have inside information to swoop in and save the day again?
@damageboy Did you find a solution for this yet? I am currently trying to figure out the exact same issue. I am glad I got it working to conditionally reference System.Windows.Forms whenever it is building for net461... That works but my Designer doesnt show up.
@johmarjac Have you tried using the MSBuild.Sdk.Extras? It adds the proper nesting support.
@onovotny Does MSBuild.Sdk.Extras
cause the designers to be usable again?
I can't say 100% for WinForms (since I haven't actually tried), but it does work for WPF with designers. There are currently some workarounds needed in your csproj for WPF due to the 2-pass compile steps. You can see it in action with NuGet Package Explorer: https://github.com/NuGetPackageExplorer/NuGetPackageExplorer/blob/master/PackageExplorer/NuGetPackageExplorer.csproj
Just tried MSBuild.Sdk.Extras
in a brand new project. It doesn't have any effect on winforms designers—still broken in the same ways.
@davkean For the Windows Forms designers themselves, that's not something your team is responsible for, is it? I should probably use the VS feedback tool for that?
@onovotny For me it does not even nest Form1.cs and Form1.Designer.cs together allthough I installed (and restored) the MSBuild.Sdk.Extras Package. Here my .csproj
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.2.1" />
</ItemGroup>
<ItemGroup>
<Reference Condition="'$(TargetFramework)' == 'net461'" Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Update="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Update="Form1.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Form1.resx</DependentUpon>
</Compile>
<Compile Update="Form1.Designer.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Form1.Designer.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Form1.Designer.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Form1.Designer.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Form1.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Form1.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>
And that's how it looks in Solution Explorer
Yeah, Forms look like they need something else -- plus it also cannot automatically add the StronglyTypedResourcesGenerator (Extras has a way to disable that), since Forms' resx works differently.
Same issue here - although I find that just adding <SubType>Form</SubType>
and every time deleting the generated resx and Designer.cs, does the trick in making things more or less usable. This is with the very latest 15.5.7 version. I'd say, just comment the code generating those 2 files all the time (in VS) and we're more or less there ;)
I hear what you're saying, but I want the .designer.cs for sure so the autogenerated fields and InitializeComponent stay out of sight. I don't want to be the one to submit the PR removing dozens of .designer.cs and .resx files. 😁
@jnm2 @onovotny @johmarjac
I have successfully created the worst msbuild hack of all time...
I basically have two projects for a X
winforms project
One X.Designer
project which I just use for opening up the designer and editing the crap out of my soon to be dead winforms god willing (If MS support this horrid crap in the future)
The Other, X
, basically uses msbuild goo to "import" every piece of code/structure from the other project automatically, and that's the one I actually use to build the final executable with dotnet cli tools
I'm attaching the X.Designer.csproj
, X.csproj
, here and the Resurrect-WinForms.targets
file it imports:
X.Designer
(the only important thing here is that it builds into the killme
folder which we need to know in advance about so we can exclude it inside Resurrect-WinForms.targets
)
:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="15.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<ProjectGuid>{8F13900E-A99B-4278-9231-5ADB631698FB}</ProjectGuid>
<ApplicationIcon>X.ico</ApplicationIcon>
<AssemblyName>X</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
<LangVersion>latest</LangVersion>
<OutputType>WinExe</OutputType>
<RootNamespace>X</RootNamespace>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\</SolutionDir>
<Prefer32Bit>true</Prefer32Bit>
<DebugType>portable</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<NoWarn>1701</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Label="MultilingualAppToolkit">
<MultilingualAppToolkitVersion>4.0</MultilingualAppToolkitVersion>
<MultilingualFallbackLanguage>en-US</MultilingualFallbackLanguage>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>killme\Release\</OutputPath>
<DefineConstants>TRACE;</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<OutputPath>killme\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;</DefineConstants>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>
<!-- Too much msbuild crap, as created by the old project system -->
</Project>
X.csproj
:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net471</TargetFramework>
<ApplicationIcon>..\..\designer\X.Designer\X.ico</ApplicationIcon>
<OutputType>winexe</OutputType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<Import Project="../Resurrect-WinForms.targets" />
<ItemGroup>
<PackageReference Include="DockPanelSuite" Version="3.0.3" />
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.3" />
<PackageReference Include="LibGit2Sharp" Version="0.25.0-preview-0073" />
<PackageReference Include="NLog" Version="4.5.0-rc04" />
<PackageReference Include="Npgsql" Version="3.2.6" />
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="1.1.2" />
</ItemGroup>
</Project>
And here's Resurrect-WinForms.targets
:
<Project>
<PropertyGroup>
<WinFormsPath>..\..\designer\$(AssemblyName).Designer</WinFormsPath>
<WinFormsExcludes>$(WinFormsPath)\killme\**;$(WinFormsPath)\obj\**</WinFormsExcludes>
</PropertyGroup>
<!-- This is a really really special construct we use in our build:
The idea is that we have X.Designer which is a old-csproj projet we HAVE to continue using
to preserve the WinForms designer functionality because life sucks https://github.com/dotnet/project-system/issues/1272
But once the "designing" is done, we don't really have to do anything else with the old format, so we have THIS .csproj
which does magic and re-includes the files so that we can compile them with "standard" dotnet
-->
<ItemGroup>
<Compile
Include="$(WinFormsPath)\**\*$(DefaultLanguageSourceExtension)"
Exclude="$(DefaultItemExcludes);$(WinFormsExcludes)"
Link="%(RecursiveDir)%(FileName)%(Extension)" />
<EmbeddedResource
Include="$(WinFormsPath)\**\*.resx"
Exclude="$(DefaultItemExcludes);$(WinFormsExcludes)"
Link="%(RecursiveDir)%(FileName)%(Extension)" />
<None
Include="$(WinFormsPath)\**\*"
Exclude="$(DefaultItemExcludes);$(WinFormsExcludes)"
Link="%(RecursiveDir)%(FileName)%(Extension)" />
<None Remove="$(WinFormsPath)\**\*$(DefaultLanguageSourceExtension)" />
<None Remove="$(WinFormsPath)\**\*.resx" />
<!-- Causes weird VS2017 15.5 b0rk
<Compile
Update="$(WinFormsPath)/**/*.Designer$(DefaultLanguageSourceExtension)"
DependentUpon="$([System.String]::Copy('%(Filename)').Replace('.Designer', ''))$(DefaultLanguageSourceExtension)"
SubType="Code" /> -->
<EmbeddedResource
Update="$(WinFormsPath)\**\*.resx"
DependentUpon="%(Filename)$(DefaultLanguageSourceExtension)"
SubType="Designer" />
<EmbeddedResource
Update="$(WinFormsPath)\**\*.zh-CN.resx"
DependentUpon="$([System.String]::Copy('%(Filename)').Replace('.zh-CN', ''))$(DefaultLanguageSourceExtension)"
SubType="Designer" />
<EmbeddedResource Remove="$(WinFormsPath)\Properties\Resources.resx" />
<EmbeddedResource Remove="$(WinFormsPath)\Properties\Resources.zh-CN.resx" />
<EmbeddedResource Include="$(WinFormsPath)\Properties\Resources.resx">
<Link>Properties\Resources.resx</Link>
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="$(WinFormsPath)\Properties\Resources.zh-CN.resx">
<Link>Properties\Resources.zh-CN.resx</Link>
</EmbeddedResource>
<Compile Remove="$(WinFormsPath)\Properties\Resources.Designer.cs" />
<Compile Include="$(WinFormsPath)\Properties\Resources.Designer.cs">
<Link>Properties\Resources.Designer.cs</Link>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>
I take no responsibility for anything with this horrid hack I've copy pasted above. May the good lord have mercy on your soul.
For me, when I add new form "test.cs" the csproj will contain:
<Compile Update="test.cs">
<SubType>Form</SubType>
</Compile>
in order to nest the designer, manually edit the csproj by adding right below it, two new nodes in the same ItemGroup:
<Compile Update="test.Designer.cs">
<SubType>Form</SubType>
<DependentUpon>test.cs</DependentUpon>
</Compile>
<EmbeddedResource Update="test.resx">
<DependentUpon>test.cs</DependentUpon>
</EmbeddedResource>
now, Shift+F7 works, designer works. And I did not include MSBuildSdkExtras
But what happens when you change something through the designer?
@jnm2 We're bringing up WinForms now - let's use new issues to track each individual issue; bugs we're aware of are being tracked by the WinForms label: https://github.com/dotnet/project-system/labels/Feature-WinForms.
The nesting itself is being tracked by https://github.com/dotnet/project-system/issues/159, and we've implemented for 16.0 using rules of legacy but without DependentUpon. We'll respect DependentUpon if its present, otherwise, we'll just auto-nest based on existing rules. I'm going to dupe this bug against it.
The point of this exercise was to be able to see if the new .csproj offers deliverance from the madness that is
licenses.licx
, but I wasn't able to get that far. I'm guessing the NotImplementedException above, plus the message boxes I was getting earlier while moving controls around with the same NotImplementedException message, are indicative of a license system failure.Here's what I'm hoping will eventually be the case:
- I can migrate all built-in csproj types to the new project system. WinForms, WPF, anything. This was the main justification for the decision to unify everything under .csproj and I'm still looking forward to the list of benefits across the board.
- No more listing .cs files and the resulting merge conflicts and careful source control diffing on each checkin!
- No more licenses.licx appearing in the .csproj! It's fine existing locally (even though it's truly pointless and should be cached outside of the source code), but the file itself is filtered from source control because of the inevitable thrashing that happens and the inevitable version errors on both dev machines and the build server. The last remaining piece of the puzzle is to keep it from appearing in the .csproj at all, because it's hard to notice and catch that before accidentally checking in the .csproj and having the build server choke on the missing file.
I'm facing the same issue with Active Report, do we have any idea to bring back the designer?
@jnm2
Correction: This is not an absolute showstopper, just very tedious to work around.
- Every time the form is edited in the designer,
Form1.designer.cs
gets replaced by the codegen forForm1.resx
. This loses thepartial
modifier so the code does not compile, and movesInitializeComponent
and all the field declarations intoForm1.cs
which is less than ideal. Every time, I have to manually remove theinternal
modifier and add thepartial
modifier toForm1.designer.cs
and delete the constructor. I don't have the option to not use the resx. It gets created just by me setting a form property in the designer and saving. I'm pretty sure that the fact that the form is a partial class with the RESX code will be a major problem at some point- for example, if I don't want a parameterless constructor.I tried deleting the .resx and the resx-generated designer file and now the designer will not open. Two errors:
NotImplementedException, call stack:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) at Microsoft.VisualStudio.Design.Serialization.LicenseService.GetLicenseDocData(FileAccess access) at Microsoft.VisualStudio.Design.Serialization.LicenseService.FillLicensedTypes() at Microsoft.VisualStudio.Design.Serialization.LicenseService.get_LicensedTypes() at Microsoft.VisualStudio.Design.Serialization.LicenseService.OnComponentAdded(Object sender, ComponentEventArgs e) at System.ComponentModel.Design.ComponentEventHandler.Invoke(Object sender, ComponentEventArgs e) at System.ComponentModel.Design.DesignerHost.AddToContainerPostProcess(IComponent component, String name, IContainer containerToAddTo) at System.ComponentModel.Design.DesignerHost.PerformAdd(IComponent component, String name) at System.ComponentModel.Design.DesignerHost.System.ComponentModel.Design.IDesignerHost.CreateComponent(Type componentType, String name) at System.ComponentModel.Design.Serialization.DesignerSerializationManager.CreateInstance(Type type, ICollection arguments, String name, Boolean addToContainer) at System.ComponentModel.Design.Serialization.DesignerSerializationManager.System.ComponentModel.Design.Serialization.IDesignerSerializationManager.CreateInstance(Type type, ICollection arguments, String name, Boolean addToContainer) at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeInstance(IDesignerSerializationManager manager, Type type, Object[] parameters, String name, Boolean addToContainer) at System.ComponentModel.Design.Serialization.ComponentCodeDomSerializer.DeserializeInstance(IDesignerSerializationManager manager, Type type, Object[] parameters, String name, Boolean addToContainer) at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeExpression(IDesignerSerializationManager manager, String name, CodeExpression expression) at System.ComponentModel.Design.Serialization.CodeDomSerializer.DeserializeStatementToInstance(IDesignerSerializationManager manager, CodeStatement statement) at System.ComponentModel.Design.Serialization.CodeDomSerializer.Deserialize(IDesignerSerializationManager manager, Object codeObject) at System.Windows.Forms.Design.ControlCodeDomSerializer.Deserialize(IDesignerSerializationManager manager, Object codeObject) at System.ComponentModel.Design.Serialization.TypeCodeDomSerializer.DeserializeName(IDesignerSerializationManager manager, String name, CodeStatementCollection statements)
and "The variable 'buttonEdit1' is either undeclared or was never assigned." which takes me to the code, wherein buttonEdit1 is most certainly both declared and assigned.
I just ran into this exact issue when trying to update the project file of a winforms project to the SDK style project (note, I'm only updating the project file, the target framework is still .NET Framework 4.7.2). Forms/UserControls that use vanilla WinForms controls open fine in the Designer, but if I used a licensed control, it fails with the exception mentioned above.
Is there any fix/workaround for this? Or any ETA on when (if ever) it will get fixed?
@petroemil It's on the roadmap to have a preview late this year. Workarounds are here: https://github.com/dotnet/winforms/blob/master/Documentation/winforms-designer.md This is for .NET Core but I'd expect it to work for .NET Framework as well. The workaround of having a second csproj would be a bit redundant because that's the csproj you already have to start with. :)
Visual Studio 2019 is steadily improving in this area too: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes#netsdk-tools
I found out (not why but) when it happens. It happens as soon as the relative path to your .cs and .resx files has a white space. As soon as there is any white space, the .designer.resx will not move to .resx and stay at .cs.
How to:
I was not able to tell if this issue was already tracked.
Start with this .csproj (15.0.26014.0):
Select Windows Form in Add New Item and see Form1.cs and Form1.designer.cs nodes are siblings in Solution Explorer, and this is the .csproj: