WPF: Cannot set Name attribute value errror

When writing a custom user control you might at some point be tempted to use it as an item container. If you then try to name the nested control you will get the following error:

Cannot set Name attribute value 'myControl' on element 'SomeControl'. 'SomeControl' is under the scope of element 'ContentPanel', which already had a name registered when it was defined in another scope.

This is unfortunately a common problem caused solely by the XAML parser.

The easiest solution to overcome this is by constructing the control yourself. I did this by defining my XAML in a resource dictionary and then writing the lines required to load it at runtime. The only downside in this approach is that no designer preview is available.

Here are the 3 easy steps to write a simple container with a title on top:

1. Add a new class (ContentPanel.cs) and a Resource Dictionary (ContentPanel.xaml) to your WPF project.

2. Then write your XAML into the dictionary defining the style for your control:


<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyNamespace">
    <Style TargetType="{x:Type local:ContentPanel}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ContentPanel}">
                    <Grid Background="Transparent">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="18"/>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" Background="#FF6C79A2" 
CornerRadius="4 4 0 0" Padding="5 3 5 3">
                            <TextBlock VerticalAlignment="Center" Foreground="White" 
Text="{Binding Path=Title, RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type local:ContentPanel}}}"/>
                        </Border>
                        <ContentPresenter Grid.Row="1"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3. Finally edit the class so that it inherits from the UserControl class and add a initialize method that will be called by the constructor to load the XAML from the resource dictionary:

    public class ContentPanel: UserControl
    {
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(ContentPanel), 
new UIPropertyMetadata(""));
        public ContentPanel()
        {
            Initialize();
        }
        private void Initialize()
        {
            ResourceDictionary resources = new ResourceDictionary();
            resources.Source = new Uri("/MyAssemblyName;component/ContentPanel.xaml", 
UriKind.RelativeOrAbsolute);
            this.Resources = resources;
        }
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }
    }

Supporting WPF Binging Without the WindowsBase Library

WPF Bindings provide a powerful way to link your data with the user interface. But what can you do to keep your data classes independent from the WPF framework?

There are a couple of ways to support WPF binding. For none-UI classes the best option is to implement the INotifyPropertyChanged and INotifyCollectionChanged interfaces. Implementing them explicitly keeps the interface of your data classes clean and all the WPF functionality available only for those classes that support it.

private event PropertyChangedEventHandler PropertyChanged;


protected void OnPropertyChanged(string name)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(name));
}
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
    add { PropertyChanged += value; }
    remove { PropertyChanged -= value; }
}

Unfortunately in .NET 3.5 both the interfaces are a part of the WindowsBase WPF library, so it is not possible to support bindings without referencing to it. This also means all the assemblies that need to take advantage of your data classes need to reference the library.

It made me very happy to read in Jamie Rodriguez’s blog that in .NET 4.0 the notify interfaces have been type forwarded to System.dll! I could also confirm this using Visual Studio 2010 Beta 2. Hurray!

Ps. According to the Data Binding Performance article on MSDN, the notify interfaces provide a slightly better performance compared to using XXXChanged events.

Faster XML Serialization

A lot of run-time compilation is involved when the XmlSerializer is used. For better performance it is recommended to run sgen.exe to generate a serialization assembly to speed up the XML serialization. In a way, the idea behind SGen is the same as for NGen.

There is a Generate Serialization Assembly drop-down in the project settings in Visual Studio, but this covers only available Web service proxies. To have Visual Studio run SGen automatically for other types, you need to manually add the following lines to your project file:

<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)"  Outputs="$(OutputPath)$(_SGenDllName)">         
    <SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)"  References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)">
        <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
    </SGen>
</Target>

Controlling Design-Time Resizing

I needed a very simple extension of the standard combobox so I setup my own UserControl. As comboboxes have a fixes height I wanted to prevent the user from being able to resize vertically.

I remembered from the past that this was a trivial task, but could not remember how it was done. It took me one hour of googling before I found the solution, so I decided to spare everyone else from the trouble and post it here.

To control the selection rules of your control you need to first setup a custom designer:

    public class ComboboxControlDesigner : ControlDesigner
    {
        public override SelectionRules SelectionRules
        {
            get
            {
                return SelectionRules.LeftSizeable |
                    SelectionRules.RightSizeable |
                    SelectionRules.Visible |
                    SelectionRules.Moveable;
            }
        }
    }

Use the override to return any selection options you want to support.

Then finally attach the designer to your UserControl using the Designer attribute:

[Designer(typeof(ComboboxControlDesigner))]

To control resizing during run-time and design-time you can also just try overriding the SetBounds method of the control:

        protected override void SetBoundsCore(int x, int y, 
            int width, int height, BoundsSpecified specified)
        {
            if (this.DesignMode)
                base.SetBoundsCore(x, y, 100, 21, specified);
            else
                base.SetBoundsCore(x, y, width, height, specified);
        }

AutoCAD and .NET

Since AutoCAD 2008 Autodesk ObjectARX® has provided managed wrapper classes that enable you to use .NET to write commands and extensions for AutoCAD. These managed classes enable you to throw away the old COM type libraries and adapt a new way of interacting with AutoCAD in a way much similiar to working with a database. Unfortunately there are quite little examples available to help get started.

The assemblies you need are acdbmgd.dll and acmgd.dll located under the AutoCAD installation directory and also included with the Autodesk ObjectARX SDK. When you reference to these, make sure you change the value of the “Copy local” property of the reference to false as the libraries are already loaded when AutoCAD calls your classes.

To create a new command or extension for AutoCAD you need to setup a class library project and create the necessary classes:

using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
 
[assembly: CommandClass(typeof(AutoCadDemo.MyCommands))]
[assembly: ExtensionApplication(typeof(AutoCadDemo.MyExtensionApplication))]
 
namespace AutoCadDemo
{
    public class MyCommands
    {
        [CommandMethod("MYCOMMAND")]
        public void MyCommand()
        {
            // Command implemenation
        }
    }
 
    public class MyExtensionApplication : IExtensionApplication
    {
        public void Initialize()
        {
            // Extension implemenation
        }
 
        public void Terminate()
        {
            // Extension disposer
        }
    }
}

As you can see, assembly attributes are used to tell AutoCAD what classes it should load. You also need to use attributes to define each command. To load your commands and extensions in AutoCAD you need to use the netload command. There is also a way to use the registry to define what assemblies should be loaded when AutoCAD starts.

The following example shows how to start a transaction, get input from the user and create a new object:

private void DrawCircle()
{
    // Uses editor to prompt user for center point and radius
    Editor editor = Application.DocumentManager.MdiActiveDocument.Editor;
 
    PromptPointResult pointResult = editor.GetPoint("Select center point:");
    if (pointResult.Status != PromptStatus.OK)
        return;
 
    PromptDistanceOptions radiusOptions = new PromptDistanceOptions("Radius:");
    radiusOptions.BasePoint = pointResult.Value;
    radiusOptions.UseBasePoint = true;
 
    PromptDoubleResult radiusResult = editor.GetDistance(radiusOptions);
    if (radiusResult.Status != PromptStatus.OK)
        return;
 
    // Starts transaction to enable editing of drawing
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
    {
        try
        {
            // Creates new object representing a circle
            Circle circle = new Circle(pointResult.Value, Vector3d.ZAxis, 
                radiusResult.Value);
            BlockTableRecord activeSpace = (BlockTableRecord)transaction.GetObject(
                database.CurrentSpaceId, OpenMode.ForWrite);
 
            // Adds object to active space
            activeSpace.AppendEntity(circle);
 
            // Adds newly created object to transaction
            transaction.AddNewlyCreatedDBObject(circle, true);
 
            // Commits changes
            transaction.Commit();
        }
        catch (System.Exception)
        {
            // Rollbacks changes on error
            transaction.Abort();
        }
    }
}

Things work a bit differently compared to the old COM interface used in the VBA macros and most Autodesk examples. Each object has a object id that is used to call the object. Database object has properties that provide the ids of most of the main collections e.g. blocks table.

As the drawing file can be seen as a database also access to it can be shared. This means you need to define the minimum access level (open mode) you need to complete your actions. The active transaction enables you to rollback any changes you have made in case something goes wrong.

The wrapper classes also enable you to store data into the drawing in such a way not accessable by the user through the AutoCAD user interface. For this you can either use the database specific Named Object Dictionary (NOD) or the Extenion dictionary of every object.

public void AddItemToNod(string key, string value)
{
    // Starts transaction
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
    Transaction transaction = database.TransactionManager.StartTransaction();
 
    try
    {
        // Retrieves the NOD table and inserts a new record
        DBDictionary nod = (DBDictionary)transaction.GetObject(
            database.NamedObjectsDictionaryId, OpenMode.ForWrite);
 
        ResultBuffer buffer = new ResultBuffer();
        buffer.Add(new TypedValue((int)DxfCode.Text, value));
 
        Xrecord record = new Xrecord();
        record.Data = buffer;
 
        nod.SetAt(key, record);
 
        // Adds newly created record to transaction
        transaction.AddNewlyCreatedDBObject(record, true);
 
        // Commits changes
        transaction.Commit();
    }
    catch (System.Exception)
    {
        transaction.Abort();
    }
}

By using XML serialization you can basically store any object data into the drawing.

Also the classic way of storing data under the file properties (summary info) is possible:

public void AddItemToSummaryInfo(string key, string value)
{
    Database database = Application.DocumentManager.MdiActiveDocument.Database;
 
    // Uses builder to edit the summary info (file properties) of the drawing
    DatabaseSummaryInfoBuilder infoBuilder = 
        new DatabaseSummaryInfoBuilder(database.SummaryInfo);
 
    infoBuilder.CustomPropertyTable.Add(key, value);
 
    database.SummaryInfo = infoBuilder.ToDatabaseSummaryInfo();
}

You should check out Kean Walmsleys blog “Through the Interface” for good tips on developing extensions for AutoCAD using .NET.

Setup Project: Custom Actions

Sometimes you need to add some custom actions to your setup project to complete the installation by running tools like regasm, ngen or caspol. The last time I was forced to do this was when my Inventor Add-in (COM based) did not get registered properly. The problem was that the Register=vsdrpCOM setting of the project output did not get the assembly codebase set. I needed to get my setup to include the codebase in the assembly registration to get my add-in installed.

The first step was that I added a new class library project to my solution and added there a new class called RegasmInstaller.cs:

[RunInstaller(true)]
public partial class RegasmInstaller : Installer
{
    public RegasmInstaller()
    {
        InitializeComponent();
    }

    public override void Install(IDictionary stateSaver)
    {
        base.Install(stateSaver);

        Regasm(true);
    }

    public override void Rollback(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    public override void Uninstall(IDictionary savedState)
    {
        base.Rollback(savedState);

        Regasm(false);
    }

    private void Regasm(bool register)
    {
        string file = base.Context.Parameters["Assembly"];
        if (string.IsNullOrEmpty(file))
            throw new InstallException("Assembly not defined");
        if (!File.Exists(file))
            return;

        RegistrationServices regsrv = new RegistrationServices();
        Assembly assembly = Assembly.LoadFrom(file);

        if (register)
        {
            regsrv.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
        }
        else
        {
            try
            {
                regsrv.UnregisterAssembly(assembly);
            }
            catch
            {
                //Exceptions are ignored: even if the unregistering failes
                //it should notprevent the user from uninstalling
            }
        }
    }
}

The basic idea in writing a installer class is to override the methods you need. Override the install method to perform whatever actions you need and override the rollback and uninstall methods to undo these changes. You usually only need to override the commit method if you need to cleanup any temporary files. Don’t forget to start every override by calling the base function first!

The CustomActionData property of each custom action can be used to pass parameters to the installer class. In the installer class these parameter values can be read from the Context.Parameters dictionary. In my installer I used the property to define the assembly I wanted to get registered.

To get my installer class to actually register an assembly I had two options:  to use the Process class to call regasm.exe or to use the RegistrationServices class. My first version used the regasm.exe, but I later changed it to use the service class, both options are fine.

The second step was to add the project output of the installer class library to the setup project and then add the actions I needed.  For each action I selected the project output containing my installer class.

Setup Actions

Finally I set the CustomActionData property of each action to define the path to the target assembly.

CustomActionData Property

Test Run Deployment Issue

I replaced one of my test files with an image from the internet. I was a bit surprised when I got the following error when trying to run a test that was working just minutes before:

Failed to queue test run ‘MYCOMPUTER 2008-12-08 15:11:35’: Test Run deployment issue: The location of the file or directory ‘Tests\Material\TestPic.jpg’ is not trusted.

 

I then googled around and found the following thread on the MSDN forums: http://social.msdn.microsoft.com/Forums/en-US/vststest/thread/1a9c13b3-e8b9-4619-9159-3edbfc67b8a9/

 

What you need to do is unblock the file. Go to your deployment files folder and open the file properties:

 

Blocked file

 

 

Notice the security note on the bottom. Click on the Unblock button and also uncheck the readonly checkbox.

 

After pressing OK Visual Studio will again accept the file.