c# - 托管 UserControl 设计器的 ToolStripControlHost 不会发生序列化

标签 c# .net visual-studio winforms serialization

我目前正在开发一个应用程序,我想在其中的上下文菜单中显示一个 UserControl。我能够(使用 ToolStripControlHost 在某种程度上实现了这一点)。显示在(NumericUpDownToolStripItem 代码):下面是对象的代码(用 VC++.net 2.0 编写)。注意:关于这个有半相似的 SO 问题,但似乎没有一个是在处理序列化用户控件,只是用户控件中的标准对象。

对象后面显示的是实际用户控件的代码,它是一个带有标签的用户控件和一个数字上下控件。

问题:当我为我的应用程序加载设计器时,我可以很好地添加我的 NumericUpDownToolStripItem,但是,当我打开并使用公开的属性编辑我的用户控件时,这些数据都没有序列化到 InitializeComponent 方法中我的 NumericUpDownToolStripItem 对象。这样做的效果是我的控件在运行时加载了所有默认值。每次我重新加载我的表单时,修改都会丢失。

我尝试使用位于 On Msdn 的 TypeConverter 教程, 但它没有正常工作。一切都编译得很好,除了我的对象在设计网格中完全变灰(只是访问器属性,而不是整个菜单)。我注意到的另一个问题是这个方法不是专门为 UserControls 设计的,它可能有几个不同的可修改属性,并且不可能对每个属性都有重载。

那么,我有以下问题:

  1. 我正在做的事情是否实用,或者我的结构是否偏离规范。我敢肯定属性中有很多冗余。
  2. 序列化包含在另一个 UserControl\toolstriphost“父”中的用户控件“子”的正确方法是什么。 “child”中的任何属性都是简单值(字符串、小数等)。
  3. 当未实现 TypeConverter 类时,每次我更改一个属性(例如标签文本)时,对象的绘制都会被顶起并表现得很奇怪,直到我重新引导上下文\菜单或表单。有没有合适的方式通知设计师因为我做了改动而重新粉刷? (我使用了 invalidate,它充其量是狡猾的)。

提前致谢。我将继续研究这个问题并不断更新问题。

NumericUpDownToolStripItem Class:
    [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability::All)]
    public ref class NumericUpDownToolStripItem : public ToolStripControlHost
    {
       public: 
       [DesignerSerializationVisibility(DesignerSerializationVisibility::Content | 
          DesignerSerializationVisibility::Visible)]
       property LabeledNumericUpDown ^LabeledNumericUpDownControl
       {
         LabeledNumericUpDown ^get() { return (LabeledNumericUpDown^)this->Control; }
       }

       public: NumericUpDownToolStripItem(void) : 
          ToolStripControlHost(gcnew LabeledNumericUpDown()) {}

       protected: void OnSubscribeControlEvents(Control ^control) new  { //irrelevant to question }
       protected: void OnUnsubscribeControlEvents(Control ^control) new { //irrelevant to question }       
    };

public ref class LabeledNumericUpDown : public UserControl
{
   public: [ DesignerSerializationVisibility(DesignerSerializationVisibility::Content | 
    DesignerSerializationVisibility::Visible)]
   property String ^DisplayText {
      String ^get() {
         return this->label->Text;
      }
      void set(String ^val) {
         if(this->label->Text != val)
         {
            this->label->Text = val;
            this->Invalidate();
         }
      }
   }

//constructor
//destructor
//initiailecomponent
};

最佳答案

好吧,经过大量搜索,我找到了答案。我的方法很好,除了一个主要问题:我根本不需要类型转换器。我的问题是需要自定义 CodeDomConverter。下面显示的是我的解决方案。

    generic<typename T>
    ref class CustomCodeDomSerializer : CodeDomSerializer
    {
    public: virtual Object ^Deserialize(IDesignerSerializationManager ^manager, Object ^codeObject) override
       {
          // This is how we associate the component with the serializer.
          CodeDomSerializer ^baseClassSerializer = (CodeDomSerializer^)manager->
             GetSerializer(T::typeid->BaseType, CodeDomSerializer::typeid);

           //This is the simplest case, in which the class just calls the base class
           //   to do the work. 
          return baseClassSerializer->Deserialize(manager, codeObject);
       }

       public: virtual Object ^Serialize(IDesignerSerializationManager ^manager, Object ^value) override
       {
           //Associate the component with the serializer in the same manner as with
           //   Deserialize 
          CodeDomSerializer ^baseClassSerializer = (CodeDomSerializer^)manager->
             GetSerializer(T::typeid->BaseType, CodeDomSerializer::typeid);

          Object ^codeObject = baseClassSerializer->Serialize(manager, value);

           //Anything could be in the codeObject.  This sample operates on a
           //   CodeStatementCollection. 
          if (dynamic_cast<CodeStatementCollection^>(codeObject))
          {
             CodeStatementCollection ^statements = (CodeStatementCollection^)codeObject;

             // The code statement collection is valid, so add a comment.
             String ^commentText = "This comment was added to this Object by a custom serializer.";
             CodeCommentStatement ^comment = gcnew CodeCommentStatement(commentText);
             statements->Insert(0, comment);
          }
          return codeObject;
       }

};




////////////////////////////////////////////////////////////////////////////////////////////////////
///   <summary>   
///   This Usercontrol is a simple label coupled with a numericupdown.  The class following
///   it will wrap this item in toolstrip container so that it can be part of a contextmenu
///   </summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
[DesignerSerializer(CustomCodeDomSerializer<LabeledNumericUpDown^>::typeid, CodeDomSerializer::typeid)]
public ref class LabeledNumericUpDown : UserControl
{
   public: event EventHandler ^NumericUpDownValueChanged;

   public: [Category("Custom Information"), Description(L"Text to display"), 
            DefaultValue(L"Default Text"), Browsable(true), Localizable(true), NotifyParentProperty(true)]
   property String ^DisplayText
   {
      String ^get()
      {
         return this->label->Text;
      }
      void set(String ^val)
      {
         this->label->Text = val;
         if(this->DesignMode || 
            LicenseManager::UsageMode == LicenseUsageMode::Designtime) 
            this->Invalidate();

      }
   }
  //designer stuff not important
}




[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability::All),
 ToolboxBitmap(::NumericUpDown::typeid)]
public ref class NumericUpDownToolStripItem : ToolStripControlHost
{
   //replace this type
   private: LabeledNumericUpDown ^_Control;

   public: [Category("Object Host"), Description(L"Hosted usercontrol object"), 
    DisplayName("UserControl Object"), Browsable(true), NotifyParentProperty(true),
    DesignerSerializationVisibility(DesignerSerializationVisibility::Content)]
    //replace this properties type
   property LabeledNumericUpDown ^UserControlObject
   {
     //replace this properties return type
     LabeledNumericUpDown ^get() { return this->_Control; }
   } 

   public: NumericUpDownToolStripItem(void) : 
      System::Windows::Forms::ToolStripControlHost(gcnew FlowLayoutPanel())
    { 
      //replace this constructor type
      _Control = gcnew LabeledNumericUpDown();

      //don't touch this
      FlowLayoutPanel ^thePanel = (FlowLayoutPanel ^)this->Control;
      thePanel->BackColor = Color::Transparent;
      thePanel->Controls->Add(_Control);
   }   
};

关于c# - 托管 UserControl 设计器的 ToolStripControlHost 不会发生序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1369039/

相关文章:

.net - 将Dispose/Close方法编写为异步方法不是一个好主意吗?

c - "make clean"的任务配置在 Visual Studio 2017 中不起作用

angular - 找不到模块@angular/core & Experimental Decorators vs2019

c# - 如何ReportProgress多个值?

c# - 无法连接到 Docker 容器中托管的 WCF 服务

c# - gridview中的自定义分页

c# - Path.Combine 背后究竟发生了什么

c# - Visual Studio 探查器不显示类名

c# - 使用 CompositionScopeDefinition 在 MEF 中定义范围

c++ - 使用 TBB 时找不到 msvcp80d.dll