4

I want to hide all written WPF UserControls from several Assemblies in my project inside the Visual Studio Toolbox. I know I can flag the UserControls with the [DesignTimeVisible(false)] attribute to hide each individual control.

Is there another solution to hide all of them with one attribute on the Assembly? I don't want to flag each new Control. Sadly the attribute is not inherited from its parent so I even cannot create a basecontrol with the DesignTimeVisible(false).

Any Ideas?

Mohnkuchenzentrale
  • 5,745
  • 4
  • 30
  • 41

2 Answers2

2

As the first preference, such controls can be internal if they should be used visible in the container project. You can make them public based on requirements. Also being internal, they can be used in friend assemblies as well.

Anyway to reduce the difficulty of turning on/off of DesignTimeVisible is what you are looking for, you can consider these options:

Option 1

As an option, you can decrease the difficulty of turning on/off the DesignTimeVisible attribute. You can decorate all classes just once with DesignTimeVisible but control its value from a central point.

To do so, create a class to hold setting:

public class MyGlobalSettings
{
    public const bool DesignTimeVisible = false;
}

Then decorate controls this way:

[DesignTimeVisible(MyGlobalSettings.DesignTimeVisible)]
public partial class UserControl1 : UserControl

Then to turn on/off showing controls in toolbox, it's enough to set DesignTimeVisible. This way, it's just a single point setting.

Option 2

An another option you can use a T4 Template to generate partial classes for your controls. In the file you can have a variable which will be used as value of DesignTimeVisible attribute. Then in the T4 template, decorate all partial classes with DesignTimeVisible having specified value. You can simply change the value in a single point.

Class names can be generated automatically using code, but in this example I used static class names:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#  var designTimeVisibleValue = "false"; #>
using System.Windows.Controls;
using System.ComponentModel;
namespace WpfCustomControlLibrary1
{
    [DesignTimeVisible(<#=designTimeVisibleValue#>)]
    public partial class UserControl1 : UserControl{}

    [DesignTimeVisible(<#=designTimeVisibleValue#>)]
    public partial class UserControl2 : UserControl{}
}

Note

Also as mentioned in comments, you can use tools like Fody, PostSharp, dIHook,... to modify assembly at build time. Using these libraries just for such requirement is too much. Such tools can have lots of benefits but using them just for such requirement is too much and not a good idea.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks for your effort Reza. The central handling of true/false is a good point but still there is that extra efford in decorating each control although I know I don't want a single one of them being displayed :( – Mohnkuchenzentrale Nov 04 '16 at 15:29
  • So you have 2 other solutions, **1)** Like the second option which I proposed in answer use a t4 template, but the t4 generates all partial classes automatically for you. **2)** Using a tool like Fody, PostSharp, dIHook,... to moify assembly at build time. I don't prefer using the latter just for such requirement. Such tools can have lots of benefits but using them just for such requirement is not a good idea. But let t4 option is a good fit here IMO. – Reza Aghaei Nov 05 '16 at 06:12
  • Surely using a t4 which do the task automatically for us would be a good solution, but if I were you I'd prefer the first option of the current answer, the characters which you need to type probably is less than the characters you wrote for this answer and comments ;) Anyway probably I'll change the t4 file to generate those class names automatically. – Reza Aghaei Nov 05 '16 at 06:15
  • 1
    By the way, why don't you make them `internal`? – Reza Aghaei Nov 05 '16 at 10:07
  • Good point. By default such a Control is public. My real world problem is, there are about 12 - 15 developers working on a solution and some of them are simply forgetting about hiding their Usercontrols. So everyone working on this solution has a lot of UserControls in their toolbox. I know it is a question of discipline and conventions but I thought there might be a more global solution for this. I guess we will build a code inspection solution that every usercontrol should be internal. – Mohnkuchenzentrale Nov 05 '16 at 12:20
  • Yes, if those controls should be used only in the same project (or friend assemblies) they should be internal. I'll add it as a note to the answer. – Reza Aghaei Nov 05 '16 at 12:22
  • By the way, let me know if you are interested in the automatic t4 solution? As I said, it's not my personal preference but it's applicable. – Reza Aghaei Nov 05 '16 at 12:24
  • Thanks for your efford. There doesn't seem to be a better approach than you suggested. – Mohnkuchenzentrale Nov 05 '16 at 12:24
  • I guess the t4 approach is a little too much. Guess we will be using custom VS ItemTemplates for UserControls in future anyway. Until then Roslyn will haunt the lazy developers by checking the 'internal' convention – Mohnkuchenzentrale Nov 05 '16 at 12:26
  • Yes, it's really better option to change your item templates and ask developers about making things internal by default. Thanks for the feed back :) – Reza Aghaei Nov 05 '16 at 12:27
1

Maybe I am late, but if:

  1. Your controls assembly is not register in the GAC
  2. Your controls assembly's project is not in the solution you are working in

then you can use WPF Designer Extensibility. Practically you can extend the visual studio WPF designer by using attributes.

The attribute that we need is ToolboxBrowsableAttribute. The more important feature of this framework is that it allows to define attributes for our controls in a separate assembly (called design assembly).

So let's suppose these are our custom controls:

namespace CustomControls
{
    public class CustomTextBox : TextBox
    {
    }

    public class CustomButton : Button
    {
    }

    public class CustomComboBox : ComboBox
    {
    }
}

The custom controls are in an assembly named CustomControls.dll; for adding the attribute we need to each control, a new assembly called CustomControls.Design.dll should be created. This new assembly has to reference:

  • PresentationCore
  • PresentationFramework
  • System.Xaml
  • WindowsBase
  • Microsoft.Windows.Design.Extensibility
  • Microsoft.Windows.Design.Interaction

In the AssemblyInfo add this line of code:

[assembly: ProvideMetadata(typeof(CustomControls.Design.Metadata))]

That attribute points out which class will provide attribute for the designer. So let's see the code of Metadata class:

namespace CustomControls.Design
{
    public class Metadata : IProvideAttributeTable
    {
        AttributeTable IProvideAttributeTable.AttributeTable
        {
            get
            {
                AttributeTableBuilder builder = new AttributeTableBuilder();
                Assembly assembly = Assembly.GetAssembly(typeof(CustomControls.CustomButton));

                foreach (Type objectType in assembly.GetTypes())
                {
                    if (objectType.IsPublic && typeof(FrameworkElement).IsAssignableFrom(objectType))
                    {
                        builder.AddCustomAttributes(objectType,
                            ToolboxBrowsableAttribute.No);
                    }
                }

                return builder.CreateTable();
            }
        }
    }
}

In this way I am adding the ToolboxBrowsable attribute to each public object of the CustomControl assembly which extends the FrameworkElement class, without the need of decorating each control one by one.

CustomControls.Design.dll must be in the same folder of CustomControls.dll.

You can find useful info here (even that code regards a slightly different argument).

Pay attetion: if condition 1 and condition 2 are not both met, this method does not work and probabily the Reza Aghaei's solution is more suitable.

I hope it can help.

Il Vic
  • 5,576
  • 4
  • 26
  • 37