dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.35k stars 962 forks source link

Add unimplemented types for deprecated UI controls #3783

Open msneijders opened 4 years ago

msneijders commented 4 years ago

Is your feature request related to a problem? Please describe.

I am migrating a large Winforms .NET Framework 4.7.1 solution to .NET 5.0 (preview 8). My biggest pain are the deprecated UI Controls like ContextMenu and related types like 'MenuMerge'. Not because of my own code, I don't use them anymore. But we use quite some 3rd party libraries who still have references to these deprecated types. Libraries that are not so much in development anymore.

For example a library supports both ContextMenu and ContextMenuStrip. I don't use ContextMenu, so this is always a null reference, but because the type ContextMenu cannot be loaded from System.Windows.Forms.dll using .NET5 all these 3rd party libraries are broken in runtime.

Describe the solution you'd like and alternatives you've considered

If instead of completely removing these deprecated types, it was replaced with "empty" placeholder types, maybe throwing a 'DeprecatedException' when a method of it is called, just so old libraries can load it in runtime, then my solution conversion to .NET5 would be so much easier.

Will this feature affect UI controls?

No?

weltkante commented 4 years ago

I don't think this will work, I'm not 100% certain but I think loading types is lazy, so it already will only load them at the point they are used. However if they are used then it typically will be in the form/control constructor (in the generated designer code part), so throwing an exception will mean you have that exception being thrown in your third party form/control constructor. That probably is not helpful and just changes the error you are getting?

Libraries that are not so much in development anymore.

I suspect if these are WinForms based libraries you'll have a hard time to get this working properly in .NET Core, there are subtile changes like the default font being different which mean that it often needs code adjustments to work well on .NET Core.

It would have been nice to be able to just run Desktop Framework WinForms code on .NET Core but the decision was made against that and I don't think it can be changed at this point.

msneijders commented 4 years ago

In general you are right and this will not work -- but all my 3rd party libraries are using the "new" controls that have replaced the deprecated ones -- it is just that they kept supporting the deprecated ones with code like "if (this.ContextMenuStrip != null) { ..use new control...} else if (this.ContextMenu != null) { ..keep it backwards compatible...}. Now if ContextMenu just worked and returned null as I do not use it, it would have worked in runtime -- now it gives "cannot find/load type ContextMenu.

Making it hard to upgrade existing winforms applications to .NET5 is a mistake in my opinion. (it all worked nicely in .NET Core 3.0, why change that?)

weltkante commented 4 years ago

these controls already were removed in .NET Core 3.1 (the LTS version of 3.0 - .NET Core 3.0 is already out of support)

msneijders commented 4 years ago

Exactly, that is why I cannot use .NET Core 3.0, not shipping something that is out of support to my customers.

teo-tsirpanis commented 4 years ago

@msneijders have you tried creating dummy classes for these deprecated types in your assembly, and type-forwarding them to the Windows Forms assembly?

msneijders commented 4 years ago

@teo-tsirpanis I did not try this yet, but I will soon, thank you for this suggestion.

msneijders commented 4 years ago

Type-forwarding does not seem like a workaround -- I think forwarding needs to be done from the System.Windows.Forms assembly.

teo-tsirpanis commented 4 years ago

Hmm, doesn't it work the other way around? There's an attribute named TypeForwardedFrom which is applied to the declared type, in contrast with the TypeForwardedToAttribute which is applied to the receiving assembly.

It is written like this: [TypeForwardedFrom("System.Windows.Forms")].

weltkante commented 4 years ago

you also need to have facade assemblies to compile against, the compiler doesn't understand type forwarding, its only an instruction for the runtime. If it is to be effectful for the runtime it has to be put into the place the type was originally. Since the type originally wasn't in the OPs assemblies it won't be of any use to put those attributes there, unless he recompiles the libraries in question.

teo-tsirpanis commented 4 years ago

@msneijders said he doesn't himself use these legacy controls in application code. He uses an old control library wich supports both the legacy and the new controls, but the legacy control code path will never be executed.

We don't care about the compiler recognizing them. All we want is a runtime trick to not crash the loader because of a couple of classes were not found.

weltkante commented 4 years ago

But if the library isn't compiled against his assembly the runtime won't bother to look at type forwards in his assembly. Just explaining, feel free to try anyways. Type forwards are a very specific solution to a very specific problem, making them unsuitable as workaround unless you can recompile 3rd party libraries (either WinForms or the library needing the workaround).

Anyways, I don't want to derail the discussion, which was about something else, so I'm collapsing this part. You may do the same with your responses to me, if you want to.

JensNordenbro commented 3 years ago

We also experienced this. Added https://github.com/dotnet/runtime/discussions/41088 , but ... ...basically we have accepted that moving to net5+ means replacing some 3rd party altogether.

It is frustrating however that net core 3.0 had the types ...

merriemcgaw commented 7 months ago

@LeafShi1 let's get this on your team's radar, so that we can help customers do their migrations. @Tanya-Solyanik FYI, per discussion with @JeremyKuhne

Tanya-Solyanik commented 7 months ago

@LeafShi1 @Epica3055 @SimonZhao888 @ricardobossan, here are the steps to start with -

  1. generate public API surface for NETFX 481 System.Windows.Forms.dd and for NET9 SystemWindows.Froms.dll and find public or protected APIs that we had removed, not only the classes but also class members. We have a list of removed controls here - https://learn.microsoft.com/dotnet/core/compatibility/winforms#removed-controls but it might be incomplete.
  2. create a repro application based on the description in this issue, a .NET9 exe that references a NETFX481 dll that uses deleted controls.
  3. add empty control and public members declarations to NET9, copying them from release/3.0 branch. All methods would throw a PlatformNotSupportedException
  4. all properties should be hidden from intellisense, property browser and serializers. This is achieved by setting browsable attributes to false/hide to restored members in the existing classes -
paul1956 commented 7 months ago

@Tanya-Solyanik I just want someone to make sure that I will still be able to use the NuGet package winforms-datavisualization from https://github.com/kirsan31/winforms-datavisualization which replaced the .NET Framework datavisualization control in applications that were moved to .Net Core.

Have all the obsolete controls been open sourced?

Tanya-Solyanik commented 7 months ago

@paul1956 - DataVisualization is out of scope of this item. This item is only about public APIs that were removed from System.Windows.Forms.dll

JensNordenbro commented 7 months ago

We have done the migration by reimplementing missing functionality. Winforms gave us the worst issues so solving this is Nice. but the re is still the life cycle issue of LTS being too short on .NET. Even If we developers love the pace of. NET, customers cant keep up. There is a need for an "eternal" Net 10 (just like Net48) for some customers to swap out Net48 and just ENJOY Windows Update security patching.

merriemcgaw commented 7 months ago

@JensNordenbro I totally understand what you're saying here! Note that you can enable .NET updates in Microsoft Update so that servicing updates are automatically installed. We don't have an automatic upgrade between major versions, however, and you're right about the 3-year LTS cycle. We do this to help facilitate our customers keeping updated with both new functionality and the latest perf and security updates.

/cc: @jamshedd

JensNordenbro commented 7 months ago

Yes @merriemcgaw, I know!

Maybe it is enough, however if you want to grab the late runners I think introducing a new tier of LTS of 5 years or something is needed.

Polyfilling api:s might not be the real problem. - It helps though.

SimonZhao888 commented 7 months ago
  1. generate public API surface for NETFX 481 System.Windows.Forms.dd and for NET9 SystemWindows.Froms.dll and find public or protected APIs that we had removed
  1. System.Windows.Forms.StatusBar override System.Windows.Forms.StatusBar.BackColor.get -> System.Drawing.Color override System.Windows.Forms.StatusBar.BackColor.set -> void override System.Windows.Forms.StatusBar.BackgroundImage.get -> System.Drawing.Image override System.Windows.Forms.StatusBar.BackgroundImage.set -> void override System.Windows.Forms.StatusBar.BackgroundImageLayout.get -> System.Windows.Forms.ImageLayout override System.Windows.Forms.StatusBar.BackgroundImageLayout.set -> void override System.Windows.Forms.StatusBar.CreateHandle() -> void override System.Windows.Forms.StatusBar.CreateParams.get -> System.Windows.Forms.CreateParams override System.Windows.Forms.StatusBar.DefaultImeMode.get -> System.Windows.Forms.ImeMode override System.Windows.Forms.StatusBar.DefaultSize.get -> System.Drawing.Size override System.Windows.Forms.StatusBar.Dispose(bool disposing) -> void override System.Windows.Forms.StatusBar.Dock.get -> System.Windows.Forms.DockStyle override System.Windows.Forms.StatusBar.Dock.set -> void override System.Windows.Forms.StatusBar.DoubleBuffered.get -> bool override System.Windows.Forms.StatusBar.DoubleBuffered.set -> void override System.Windows.Forms.StatusBar.Font.get -> System.Drawing.Font override System.Windows.Forms.StatusBar.Font.set -> void override System.Windows.Forms.StatusBar.ForeColor.get -> System.Drawing.Color override System.Windows.Forms.StatusBar.ForeColor.set -> void override System.Windows.Forms.StatusBar.OnHandleCreated(System.EventArgs e) -> void override System.Windows.Forms.StatusBar.OnHandleDestroyed(System.EventArgs e) -> void override System.Windows.Forms.StatusBar.OnLayout(System.Windows.Forms.LayoutEventArgs levent) -> void override System.Windows.Forms.StatusBar.OnMouseDown(System.Windows.Forms.MouseEventArgs e) -> void override System.Windows.Forms.StatusBar.OnResize(System.EventArgs e) -> void override System.Windows.Forms.StatusBar.Text.get -> string override System.Windows.Forms.StatusBar.Text.set -> void override System.Windows.Forms.StatusBar.ToString() -> string override System.Windows.Forms.StatusBar.WndProc(ref System.Windows.Forms.Message m) -> void override System.Windows.Forms.StatusBarPanel.Dispose(bool disposing) -> void override System.Windows.Forms.StatusBarPanel.ToString() -> string System.Windows.Forms.StatusBar.BackColorChanged -> System.EventHandler System.Windows.Forms.StatusBar.BackgroundImageChanged -> System.EventHandler System.Windows.Forms.StatusBar.BackgroundImageLayoutChanged -> System.EventHandler System.Windows.Forms.StatusBar.DrawItem -> System.Windows.Forms.StatusBarDrawItemEventHandler System.Windows.Forms.StatusBar.ForeColorChanged -> System.EventHandler System.Windows.Forms.StatusBar.ImeMode.get -> System.Windows.Forms.ImeMode System.Windows.Forms.StatusBar.ImeMode.set -> void System.Windows.Forms.StatusBar.ImeModeChanged -> System.EventHandler System.Windows.Forms.StatusBar.Paint -> System.Windows.Forms.PaintEventHandler System.Windows.Forms.StatusBar.PanelClick -> System.Windows.Forms.StatusBarPanelClickEventHandler System.Windows.Forms.StatusBar.Panels.get -> System.Windows.Forms.StatusBar.StatusBarPanelCollection System.Windows.Forms.StatusBar.ShowPanels.get -> bool System.Windows.Forms.StatusBar.ShowPanels.set -> void System.Windows.Forms.StatusBar.SizingGrip.get -> bool System.Windows.Forms.StatusBar.SizingGrip.set -> void System.Windows.Forms.StatusBar.StatusBar() -> void System.Windows.Forms.StatusBar.TabStop.get -> bool System.Windows.Forms.StatusBar.TabStop.set -> void virtual System.Windows.Forms.StatusBar.OnDrawItem(System.Windows.Forms.StatusBarDrawItemEventArgs sbdievent) -> void virtual System.Windows.Forms.StatusBar.OnPanelClick(System.Windows.Forms.StatusBarPanelClickEventArgs e) -> void

  2. System.Windows.Forms.StatusBar.StatusBarPanelCollection System.Windows.Forms.StatusBar.StatusBarPanelCollection.Contains(System.Windows.Forms.StatusBarPanel panel) -> bool System.Windows.Forms.StatusBar.StatusBarPanelCollection.Count.get -> int System.Windows.Forms.StatusBar.StatusBarPanelCollection.GetEnumerator() -> System.Collections.IEnumerator System.Windows.Forms.StatusBar.StatusBarPanelCollection.IndexOf(System.Windows.Forms.StatusBarPanel panel) -> int System.Windows.Forms.StatusBar.StatusBarPanelCollection.IsReadOnly.get -> bool System.Windows.Forms.StatusBar.StatusBarPanelCollection.StatusBarPanelCollection(System.Windows.Forms.StatusBar owner) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.Add(string text) -> System.Windows.Forms.StatusBarPanel virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.Add(System.Windows.Forms.StatusBarPanel value) -> int virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.AddRange(System.Windows.Forms.StatusBarPanel[] panels) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.Clear() -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.ContainsKey(string key) -> bool virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.IndexOfKey(string key) -> int virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.Insert(int index, System.Windows.Forms.StatusBarPanel value) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.Remove(System.Windows.Forms.StatusBarPanel value) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.RemoveAt(int index) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.RemoveByKey(string key) -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.this[int index].get -> System.Windows.Forms.StatusBarPanel virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.this[int index].set -> void virtual System.Windows.Forms.StatusBar.StatusBarPanelCollection.this[string key].get -> System.Windows.Forms.StatusBarPanel

  3. System.Windows.Forms.StatusBarDrawItemEventArgs System.Windows.Forms.StatusBarDrawItemEventArgs.Panel.get -> System.Windows.Forms.StatusBarPanel System.Windows.Forms.StatusBarDrawItemEventArgs.StatusBarDrawItemEventArgs(System.Drawing.Graphics g, System.Drawing.Font font, System.Drawing.Rectangle r, int itemId, System.Windows.Forms.DrawItemState itemState, System.Windows.Forms.StatusBarPanel panel, System.Drawing.Color foreColor, System.Drawing.Color backColor) -> void System.Windows.Forms.StatusBarDrawItemEventArgs.StatusBarDrawItemEventArgs(System.Drawing.Graphics g, System.Drawing.Font font, System.Drawing.Rectangle r, int itemId, System.Windows.Forms.DrawItemState itemState, System.Windows.Forms.StatusBarPanel panel) -> void

  4. System.Windows.Forms.StatusBarPanel System.Windows.Forms.StatusBarPanel.Alignment.get -> System.Windows.Forms.HorizontalAlignment System.Windows.Forms.StatusBarPanel.Alignment.set -> void System.Windows.Forms.StatusBarPanel.AutoSize.get -> System.Windows.Forms.StatusBarPanelAutoSize System.Windows.Forms.StatusBarPanel.AutoSize.set -> void System.Windows.Forms.StatusBarPanel.BeginInit() -> void System.Windows.Forms.StatusBarPanel.BorderStyle.get -> System.Windows.Forms.StatusBarPanelBorderStyle System.Windows.Forms.StatusBarPanel.BorderStyle.set -> void System.Windows.Forms.StatusBarPanel.EndInit() -> void System.Windows.Forms.StatusBarPanel.Icon.get -> System.Drawing.Icon System.Windows.Forms.StatusBarPanel.Icon.set -> void System.Windows.Forms.StatusBarPanel.MinWidth.get -> int System.Windows.Forms.StatusBarPanel.MinWidth.set -> void System.Windows.Forms.StatusBarPanel.Name.get -> string System.Windows.Forms.StatusBarPanel.Name.set -> void System.Windows.Forms.StatusBarPanel.Parent.get -> System.Windows.Forms.StatusBar System.Windows.Forms.StatusBarPanel.StatusBarPanel() -> void System.Windows.Forms.StatusBarPanel.Style.get -> System.Windows.Forms.StatusBarPanelStyle System.Windows.Forms.StatusBarPanel.Style.set -> void System.Windows.Forms.StatusBarPanel.Tag.get -> object System.Windows.Forms.StatusBarPanel.Tag.set -> void System.Windows.Forms.StatusBarPanel.Text.get -> string System.Windows.Forms.StatusBarPanel.Text.set -> void System.Windows.Forms.StatusBarPanel.ToolTipText.get -> string System.Windows.Forms.StatusBarPanel.ToolTipText.set -> void System.Windows.Forms.StatusBarPanel.Width.get -> int System.Windows.Forms.StatusBarPanel.Width.set -> void

  5. System.Windows.Forms.StatusBarPanelAutoSize System.Windows.Forms.StatusBarPanelAutoSize.Contents = 3 -> System.Windows.Forms.StatusBarPanelAutoSize System.Windows.Forms.StatusBarPanelAutoSize.None = 1 -> System.Windows.Forms.StatusBarPanelAutoSize System.Windows.Forms.StatusBarPanelAutoSize.Spring = 2 -> System.Windows.Forms.StatusBarPanelAutoSize

  6. System.Windows.Forms.StatusBarPanelBorderStyle System.Windows.Forms.StatusBarPanelBorderStyle.None = 1 -> System.Windows.Forms.StatusBarPanelBorderStyle System.Windows.Forms.StatusBarPanelBorderStyle.Raised = 2 -> System.Windows.Forms.StatusBarPanelBorderStyle System.Windows.Forms.StatusBarPanelBorderStyle.Sunken = 3 -> System.Windows.Forms.StatusBarPanelBorderStyle

  7. System.Windows.Forms.StatusBarPanelClickEventArgs System.Windows.Forms.StatusBarPanelClickEventArgs.StatusBarPanel.get -> System.Windows.Forms.StatusBarPanel System.Windows.Forms.StatusBarPanelClickEventArgs.StatusBarPanelClickEventArgs(System.Windows.Forms.StatusBarPanel statusBarPanel, System.Windows.Forms.MouseButtons button, int clicks, int x, int y) -> void System.Windows.Forms.StatusBarPanelClickEventHandler

  8. System.Windows.Forms.StatusBarPanelStyle System.Windows.Forms.StatusBarPanelStyle.OwnerDraw = 2 -> System.Windows.Forms.StatusBarPanelStyle System.Windows.Forms.StatusBarPanelStyle.Text = 1 -> System.Windows.Forms.StatusBarPanelStyle

  9. System.Windows.Forms.IDataGridEditingService System.Windows.Forms.IDataGridEditingService.BeginEdit(System.Windows.Forms.DataGridColumnStyle gridColumn, int rowNumber) -> bool System.Windows.Forms.IDataGridEditingService.EndEdit(System.Windows.Forms.DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort) -> bool

weltkante commented 7 months ago

I don't think thats complete? I remember the discussion of removing the old menu APIs. So unless I'm misunderstanding what you're trying to do, shouldn't those appear on the list too?

RussKie commented 7 months ago

You don't need to go far.