【问题标题】:ToolStripControlHost hosting a UserControl designer Serializing won't occur托管 UserControl 设计器的 ToolStripControlHost 不会发生序列化
【发布时间】:2010-11-25 00:55:07
【问题描述】:

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

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

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

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

所以,我有以下问题:

  1. 我正在做的事情是实用的,还是我的结构偏离了规范。我敢肯定属性中有很多冗余。
  2. 序列化另一个 UserControl\toolstriphost 'parent' 中包含的 usercontrol 'child' 的正确方法是什么。 '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
};

【问题讨论】:

  • 抱歉线程复活,但这似乎是我能找到的唯一一篇处理这个确切问题的帖子。我想知道你做了什么来让你的控制主机设计器可见 - 无论我做什么,我似乎都无法让 Visual Studio 显示它。我已经应用了 MSDN 声称应该启用设计器支持的所有内容,但什么也没有。一些指针将不胜感激。
  • 我不记得我做了什么,但我知道这个控件已经发布并且是“在产品中”。我将尝试追踪代码并重新发布控件本身以查看是否有任何区别。
  • 在下面查看我的新帖子,我重新发布了我当前的工作设置
  • 非常感谢,这应该很有帮助。这一切都源于表单设计者的恼人习惯,即如果出现问题,要么静静地不做任何事情,要么完全崩溃,而不是告诉你问题出在哪里。
  • 有效!谢谢一堆。花了一些时间来处理 C++ 语法,我不确定您为什么使用 FlowLayoutPanel,而且我看不到 CustomCodeDomSerializer 的用途,但它现在出现在设计器中。感谢您的详尽示例。

标签: c# .net visual-studio winforms serialization


【解决方案1】:

好吧,经过大量搜索,我找到了答案。我的方法很好,除了一个主要问题:我根本不需要类型转换器。我的问题是需要自定义 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);
   }   
};

【讨论】:

    【解决方案2】:

    我最近的“有效”解决方案:

    ////////////////////////////////////////////////////////////////////////////////////////////////////
    ///   <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();
          void set(String ^val);
       }
    
       public: [Category("Custom Information"), Description(L"NumericUpDown Value"), 
                DefaultValue(1), Browsable(true), Localizable(true), NotifyParentProperty(true)]
       property Decimal UpDownValue
       {
          Decimal get();
          void set(Decimal val);
       }
    
       public: [Category("Custom Information"), Description(L"NumericUpDown Maximum"), 
                DefaultValue(100), Browsable(true), Localizable(true), NotifyParentProperty(true)]
       property Decimal UpDownMaximum
       {
          Decimal get();
          void set(Decimal val);
       }
    
       public: [Category("Custom Information"), Description(L"NumericUpDown Minimum"), 
                DefaultValue(0), Browsable(true), Localizable(true), NotifyParentProperty(true)]
       property Decimal UpDownMinimum
       {
          Decimal get();
          void set(Decimal val);
       }
    
       private: bool SupressEvents;
       public: Void UpDownValueSet_NoEvent(int Val);
       private: Void numericUpDown_ValueChanged(Object ^sender, EventArgs ^e);
       public: LabeledNumericUpDown(void);
       private: System::Windows::Forms::NumericUpDown^  numericUpDown;
       private: System::Windows::Forms::Label^  label;
       private: System::Windows::Forms::TableLayoutPanel^  tableLayoutPanel1;
       private: System::ComponentModel::Container ^components;
       #pragma region Windows Form Designer generated code
       void InitializeComponent(void);
    };
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>   CustomCodeDomSerializer
    /// This is a specialized usercontrol designed to incapsulate another usercontrol (in this case a  
    /// NumericUpDownToolStripItem.  In order to use this class, you must copy this entire class and 
    /// create a new object.  (You can do this right underneath your usercontrol in the same file 
    /// if you wish.  You must specifiy the type of your object every place its mentioned.
    ///   
    /// To Note:  The toolbox bitmap is what the icon will look like.  You can specify any old control.
    /// It is possible to use a custom icon, but I can't figure out how.
    ///</summary>
    /// 
    /// <value> The tool strip control host. </value>
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    
    [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);
       }   
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 2022-01-21
      • 2012-07-30
      • 2020-10-14
      相关资源
      最近更新 更多