项目重建后,使用自定义TypeConverter进行的控件无法正常运行

时间:2019-03-07 21:25:09

标签: c# visual-studio typeconverter

概述

我有一个C#项目ControlsLib,其中包含以下内容(稍后将介绍代码):

  1. 一个自定义C#控件Control_Audible,它播放.wav文件,该文件是在设计时从属性窗格中选择的。
  2. 一个SoundItem类作为支持播放哪个文件的类
  3. 作为SoundItemConverter的{​​{1}}类,用于在属性窗口中优雅地选择要播放的音频文件。

什么在起作用

首先打开我的解决方案并且未构建我的TypeConverter项目时,我的代码可以按预期工作。

当我将ControlsLib添加到表单时,我可以从可用的音频资源中选择一个音频文件:

Select audio file from available audio resources; "None" is NOT bold...

当选择了非默认的SoundItem时,设计器文件将生成我期望的代码:

Expected designer code generated from non-default SoundItem

什么不起作用

在构建/重建我的Control_Audible项目并将ControlsLib控件添加到表单之后,我的代码似乎无法按预期工作...

可用的音频资源文件将按预期方式填充到下拉列表中,但实际上不应该将指定为Control_Audible(“无”)的SoundItem设为粗体

Select audio file from available audio resources; DefaultValue "None" is BOLD!?

此外,每当我尝试保存表单时,都会收到以下错误消息:

Code generation ... failed ... Unable to convert ... to InstanceDescriptor

我一次能够为我的DefaultValue生成一些自动代码,但是即使那样,它也没有生成我期望的代码,现在看来根本不生成任何代码,而我的{{ 1}}功能搞砸了。

问题

如果我不构建自己的Control_Audible项目,这似乎很好用,但是我不能期望每次我想使它工作时都必须重新启动Visual Studio。我应该能够尽可能多地重建我的项目,并且仍能使我的控件正常工作。我会提到我正在使用 Visual Studio 2015

有人对为什么这种方式有任何想法吗?我花了很多时间,无法弄清楚为什么会发生这种情况!

代码

Control_Audible.cs

ConvertTo

AudibleSounds.Designer.cs

ControlsLib

AudibleSounds.resx

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.Globalization;
using System.Media;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;

namespace ControlsLib
{
   public class Control_Audible : UserControl
   {
      public Control_Audible() : base()
      {
         FileToPlay = SoundItem.None;
         InitializeComponent();
      }

      private void InitializeComponent()
      {
         this.SuspendLayout();
         // 
         // Control_Audible
         // 
         this.AutoScaleDimensions = new SizeF(6F, 13F);
         this.Name = "Control_Audible";
         this.ResumeLayout(false);
      }

      [Category("Sounds")]
      [DisplayName("SoundToPlay")]
      [Description("File to play")]
      [DefaultValue(typeof(SoundItem), "None")]
      //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
      public SoundItem FileToPlay
      {
         get;
         set;
      }
   }

   [TypeConverter(typeof(SoundItemConverter))]
   [Serializable]
   public struct SoundItem
   {
      public static readonly SoundItem None = new SoundItem("None");

      //[Browsable(false)]
      //[DefaultValue("None")]
      //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
      internal string Name
      {
         get;
         set;
      }

      // Todo: Create a valid flag
      public SoundItem(string Name) : this()
      {
         this.Name = Name;
      }

      public SoundPlayer GetSoundPlayer()
      {
         SoundPlayer retVal = null;

         if (!string.IsNullOrEmpty(Name))
         {
            retVal = new SoundPlayer(AudibleSounds.ResourceManager.GetStream(Name));
         }

         return retVal;
      }

      public override string ToString()
      {
         return Name;
      }

      public static bool operator ==(SoundItem left, SoundItem right)
      {
         return left.Name == right.Name;
      }

      public static bool operator !=(SoundItem left, SoundItem right)
      {
         return !(left == right);
      }

      public override bool Equals(object obj)
      {
         // override Equals and write code to actually
         // test for equality so that the SoundItem created
         // from the call to ConvertFrom will be equal to
         // the field you created

         if (!(obj is SoundItem))
         {
            return false;
         }

         SoundItem comp = (SoundItem)obj;

         // Note value types can't have derived classes, so we don't need 
         // to check the types of the objects here.  -- Microsoft, 2/21/2001
         return comp.Name == Name;
      }

      public override int GetHashCode()
      {
         return Name.GetHashCode();
      }
   }

   public class SoundItemConverter : TypeConverter
   {
      private Dictionary<string, object> _resourceValues = new Dictionary<string, object>()
      {
         {"None", SoundItem.None },
      };

      public SoundItemConverter()
      {
         // Initializes the standard values list with defaults.
         ResourceSet _resourceSet = AudibleSounds.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);

         if (_resourceSet == null)
         {
            throw new Exception("Missing Resource Audio files");
         }

         // Make a dictionary now for easier use
         foreach (DictionaryEntry entry in _resourceSet)
         {
            string resourceKey = entry.Key.ToString();
            //object resource = entry.Value;
            _resourceValues.Add(resourceKey,
                                new SoundItem(resourceKey));
         }
      }

      // Indicates this converter provides a list of standard values.
      public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
      {
         return true;
      }

      // Returns a StandardValuesCollection of standard value objects.
      public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
      {
         return new StandardValuesCollection( _resourceValues.Values);
      }

      // Returns true for a sourceType of string to indicate that 
      // conversions from string are supported. (The 
      // GetStandardValues method requires a string to native type 
      // conversion because the items in the drop-down list are 
      // translated to string.)
      public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
      {
         if (sourceType == typeof(string))
         {
            return true;
         }

         return base.CanConvertFrom(context, sourceType);
      }

      // If the type of the value to convert is string, parses the string 
      // to set the value of the property to.
      public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
      {
         // This is the method that is called when you specify a default value for your type.
         // Convert a string representation to SoundItem.
         string strValue = value as string;

         if (strValue != null)
         {
            string text = strValue.Trim();

            if (text.Length == 0)
            {
               return SoundItem.None;
            }
            else
            {
               return new SoundItem(text);
            }
         }

         return base.ConvertFrom(context, culture, value);
      }

      public override bool CanConvertTo(ITypeDescriptorContext context, Type destType)
      {
         if (destType == typeof(InstanceDescriptor))
         {
            return true;
         }

         return base.CanConvertTo(context, destType);
      }

      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
      {
         if (destType == null)
         {
            throw new ArgumentNullException("destinationType");
         }

         if (value is SoundItem)
         {
            SoundItem si = (SoundItem)value;

            if (destType == typeof(string))
            {
               TypeConverter stringConverter = TypeDescriptor.GetConverter(typeof(string));

               // Note: ConvertToString will raise exception if value cannot be converted.
               return stringConverter.ConvertToString(context, culture, si.Name);
            }
            else if (destType == typeof(InstanceDescriptor))
            {
               ConstructorInfo ctor = typeof(SoundItem).GetConstructor(new Type[] { typeof(string) });

               if (ctor != null)
               {
                  return new InstanceDescriptor(ctor, new object[] { si.Name });
               }
            }
         }

         return base.ConvertTo(context, culture, value, destType);
      }

      //
      // Summary:
      //     Creates an instance of this type given a set of property values for the object.
      //
      // Parameters:
      //   context:
      //     A type descriptor through which additional context can be provided.
      //
      //   propertyValues:
      //     A dictionary of new property values. The dictionary contains a series of name-value
      //     pairs, one for each property returned from SoundItemConverter.GetProperties(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Attribute[]).
      //
      // Returns:
      //     The newly created object, or null if the object could not be created. The default
      //     implementation returns null.
      public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
      {
         if (propertyValues == null)
         {
            throw new ArgumentNullException("propertyValues");
         }

         object name = propertyValues["Name"];

         if (name == null || !(name is string))
         {
            throw new ArgumentException();
         }

         return new SoundItem((string)name);
      }

      //
      // Summary:
      //     Determines if changing a value on this object should require a call to SoundItemConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
      //     to create a new value.
      //
      // Parameters:
      //   context:
      //     A System.ComponentModel.TypeDescriptor through which additional context can be
      //     provided.
      //
      // Returns:
      //     true if the SoundItemConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
      //     method should be called when a change is made to one or more properties of this
      //     object; otherwise, false.
      public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
      {
         return true;
      }

      //
      // Summary:
      //     Retrieves the set of properties for this type. By default, a type does not return
      //     any properties.
      //
      // Parameters:
      //   context:
      //     A type descriptor through which additional context can be provided.
      //
      //   value:
      //     The value of the object to get the properties for.
      //
      //   attributes:
      //     An array of System.Attribute objects that describe the properties.
      //
      // Returns:
      //     The set of properties that are exposed for this data type. If no properties are
      //     exposed, this method might return null. The default implementation always returns
      //     null.
      public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
      {
         PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(SoundItem), attributes);
         return props.Sort(new string[] { "Name" });
      }

      //
      // Summary:
      //     Determines if this object supports properties. By default, this is false.
      //
      // Parameters:
      //   context:
      //     A System.ComponentModel.TypeDescriptor through which additional context can be
      //     provided.
      //
      // Returns:
      //     true if SoundItemCoverter.GetProperties(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Attribute[])
      //     should be called to find the properties of this object; otherwise, false.
      public override bool GetPropertiesSupported(ITypeDescriptorContext context)
      {
         return true;
      }
   }
}

编辑

编辑#1

我还应该提到,通过将Visual Studio作为外部程序运行来调试解决方案时,我的代码可以按预期工作:

Control works as expected when debugging with external Visual Studio

但是在构建我的//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace ControlsLib { using System; /// <summary> /// A strongly-typed resource class, for looking up localized strings, etc. /// </summary> // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class AudibleSounds { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AudibleSounds() { } /// <summary> /// Returns the cached ResourceManager instance used by this class. /// </summary> [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ControlsLib.AudibleSounds", typeof(AudibleSounds).Assembly); resourceMan = temp; } return resourceMan; } } /// <summary> /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// </summary> [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// <summary> /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. /// </summary> public static System.IO.UnmanagedMemoryStream Horn { get { return ResourceManager.GetStream("Horn", resourceCulture); } } /// <summary> /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. /// </summary> public static System.IO.UnmanagedMemoryStream HornOff { get { return ResourceManager.GetStream("HornOff", resourceCulture); } } } } 项目之后,该代码无法正常工作,并且我将<?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 2.0 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">2.0</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> <value>[base64 mime encoded serialized .NET Framework object]</value> </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> <comment>This is a comment</comment> </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used for serialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" /> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string" /> <xsd:attribute name="type" type="xsd:string" /> <xsd:attribute name="mimetype" type="xsd:string" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> <xsd:attribute ref="xml:space" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <data name="HornOff" type="System.Resources.ResXFileRef, System.Windows.Forms"> <value>Resources\HornOff.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </data> <data name="Horn" type="System.Resources.ResXFileRef, System.Windows.Forms"> <value>Resources\Horn.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </data> </root> 控件添加到窗体中,而没有通过外部Visual Studio进行调试(就像大多数普通用户一样。)。 / p>

0 个答案:

没有答案