I've just created a solution and added a 'Component Class' to it.
All I need is to add a menu to Component Class when it is in the componentbar of a win-form, like the ImageList component of .NET.
Can anyone help me?
I assume you are referring to the little tiny arrow that appears on the ImageList component when you select it and you see a list of options. That requires a custom ComponentDesigner.
Make references to:
System.Components.Design
System.Design
System.Windows.Forms.Design
Here is a simple little component example:
[Designer(typeof(TestComponentDesigner))]
public class TestComponent : Component {
public class TestComponentDesigner : ComponentDesigner {
private DesignerVerbCollection verbs = new DesignerVerbCollection();
public override void Initialize(IComponent component) {
base.Initialize(component);
verbs.Add(new DesignerVerb("Say Hello", new EventHandler(SayHello)));
}
public override DesignerVerbCollection Verbs {
get {
return verbs;
}
}
private void SayHello(object sender, EventArgs e) {
MessageBox.Show("Hello");
}
}
}
Results:
For more information, see Writing Custom Designers for .NET Components
Related
UPDATE: I tried working out by making every class in Code public, but it doesn't seem to accept my class hierarchy.
This is my first post so please bear with me. Besides that, i'm a pretty big noob, so do excuse me if something dumb comes along
I'm currently doing a school project and I have a fully fleshed out .NET Framework project in Visual Studio. Now I have to visualize it with a WPF app. So I made a new WPF project in the same source.
Let's respectively call them Code and Visualization.
I've given Visualization a project reference to Code and put using Code; at the top of the XAML.cs
I made a button in Visualization and I want it's OnClick event to use
Code.Start();
What somehow seems to sort of work is making every class in Code public, but I don't remember that being a good practice, but do correct me if i'm wrong!
I've put multiple hours into finding a solution with none to be really found before. Seeing as nobody seems to have posted this question before I must be missing something really simple.
Cheers!
namespace Code
{
class Program
{
public void Start()
{
/// Do something
}
}
}
using Code;
namespace Visualization
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Start_Click(object sender, RoutedEventArgs e)
{
// Use the Start() function from Code
}
}
}
To use classes and their functions and properties from another project, you have to declare them as public.
namespace Code
{
public class Program
{
public void Start()
{
// Do something
}
}
}
Then go to the other project, right click > Add > Reference > select your project containing the code above (assuming you're using Visual Studio IDE). After, you can access the public functions and properties:
using Code;
namespace Visualization
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Start_Click(object sender, RoutedEventArgs e)
{
Program p = new Program();
p.Start();
}
}
}
If you are worried about security, then ensure that public functions and properties you deem are safe to expose to other projects. For example, what we just did to use the Start function, any other project or some 3rd party program could do also. The only difference is that a reference would be made to the project's .dll produced instead of the project itself.
A basic rule of thumb (at least for me) is that if there are anything I don't want to expose, then don't make them public and have a public function that can be called to perform different actions. This way I can limit what actions and information can be performed or accessed:
//within some project
namespace Code
{
public class Program
{
// can't be access from another project directly
private string _privateText { get;set; }
// can be accessed directly
public string PublicText { get;set; }
public void Start()
{
// Do something
}
public string getPrivateText()
{
// here you can limit what actions are done and what information to return
return _privateText;
}
}
}
You can then do the following:
// within another project
using Code;
namespace Visualization
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Start_Click(object sender, RoutedEventArgs e)
{
Program p = new Program();
string s1 = p.getPrivateText();
string s2 = p.PublicText;
p.Start();
}
}
}
Hope this helps!
I am working on a simple GUI framework, and I faced a problem calling protected virtual methods.
Here is the IKeyboardInputListenerService interface of service I use to receive keyboard events and the Control class that represents a base for all my GUI controls. The internal method is the one being refactored.
public interface IKeyboardInputListenerService
{
event EventHandler<KeyboardEventArgs> KeyPressed;
}
public abstract class Control
{
public IKeyboardInputListenerService KeyboardInputListenerService { get; }
protected Control(IKeyboardInputListenerService keyboardInputListenerService) =>
KeyboardInputListenerService = keyboardInputListenerService;
public event EventHandler<KeyboardEventArgs> KeyPressed;
/* protected */ internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{
if (enabled && visible && focused && !args.Suppressed)
{
KeyPressed?.Invoke(sender, args);
args.Suppressed = true;
}
}
public void Activate() =>
KeyboardInputListenerService.KeyPressed += new EventHandler<KeyboardEventArgs>(OnKeyPressed);
}
I also created a ContainerControl class that is supposed to contain child controls(like the Panel or GroupBox in Windows Forms) and that overrides the virtual method:
public abstract class ContainerControl : Control
{
private readonly ObservableCollection<Control> controls;
protected ContainerControl(IKeyboardInputListenerService keyboardInputListenerService)
: base(keyboardInputListenerService) =>
controls = new ObservableCollection<Control>();
/* protected */ internal override void OnKeyPressed(object sender, KeyboardEventArgs args)
{
foreach (Control control in controls)
control.OnKeyPressed(sender, args);
base.OnKeyPressed(sender, args);
}
}
Problem is, I cannot decide which modifier to use for methods such as OnKeyPressed. I wanted to make them protected, but it causes a compiler error:
Error CS1540 Cannot access protected member 'Control.OnKeyPressed(object, KeyboardEventArgs)' via a qualifier of type 'Control'; the qualifier must be of type 'ContainerControl' (or derived from it)
I can make them public, but I do not really think it is a good idea, because there is no reason for it except for resolving the problem caused by a cross-hierarchy call. I made them internal, but there is also a drawback: if anyone will want to create a user control, they will not be able to receive events, so the control will be useless.
The question is how to get access from a derived class to virtual methods of the base class without making the methods public-accessed.
Use protected internal
protected internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{ ... }
The documentation says:
protected internal The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly.
I'm working on an application that supports various plugins (well, that's planned at least). And I'd love to have it beautiful.
So I want to let the plugin developer send a big control (like a panel or other containers) to my host application and have the user setup their settings for the plugin in the application.
That would take the plugin-developer's effort to somehow implement a settings-panel that runs by in an own window.
Thing is, I'm not sure how to do that.
I can pass variables to my host application but as soon as I try to add the control to my container panel, I get a RemoteException, telling me that the field 'parent' on type 'System.Windows.Forms.Control' can't be found.
I tried to add the plugin-control that way:
panel.Controls.Add(pluginControl);
If I try it the other way around:
pluginControl.Parent = panel;
I get a SerializationException because the class System.Windows.Forms.Control isn't marked Serializable.
Maybe some person ran into the same thing and can help me.
Let me know if you need more information!
Edit: Have a look on my current implementation: https://dl.dropboxusercontent.com/u/62845853/Random%20crap/NotModified_SamplePluginSystem.zip
I tried something which you can adorn to your needs:
First i created a PluginBase class and the proper EventArgs in a ClassLibrary:
public abstract class PluginBase
{
public abstract void Initialize();
protected void showControl(UserControl control)
{
ShowControl(this, new ControlToBeShownEventArgs() { TheControl = control });
}
public event EventHandler<ControlToBeShownEventArgs> ShowControl = delegate { };
}
public class ControlToBeShownEventArgs : EventArgs
{
public UserControl TheControl { get; set; }
}
This library is referenced by every Plugin and by the host application.
The Plugin is in turn also a Class Library (build path set to the one of the host)
inside i made a plugin inheriting this base type:
class SomePlugin : PluginBase
{
public override void Initialize()
{
showControl(new UserControl1());
}
}
The UserControl1 is the Control to be shown.
Done that, I next added the following code to the main window of the host:
List<PluginBase> plugins = new List<PluginBase>();
private void Form1_Load(object sender, EventArgs e) //Hook in the event too
{
DirectoryInfo dir = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory;
foreach (var item in dir.GetFiles())
{
if (item.Name.Contains("Plugin") && item.Name.EndsWith(".dll"))
{
Assembly ass = Assembly.LoadFile(item.FullName);
foreach (Type type in ass.GetTypes().Where(t => t.BaseType.Name == "PluginBase"))
{
PluginBase pibase = (PluginBase)Activator.CreateInstance(type,false);
plugins.Add(pibase);
}
}
}
foreach (var item in plugins)
{
item.ShowControl += item_ShowControl;
item.Initialize();
}
}
void item_ShowControl(object sender, ControlToBeShownEventArgs e)
{
this.Controls.Add(e.TheControl);
}
I have a class what I would like to close into a component. I try to make it work, based on the following code,
The issue is, that the properties are editable and viewable in the property browser & the Test Event is viewable but it cannot be filled form the property browser, just from the code.
How can I solve this anomaly?
namespace TestComponents
{
public partial class Test: Component
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public SubClass SubClass { get; set; }
public Test()
{
InitializeComponent();
SubClass = new SubClass();
}
}
public delegate void TestEventHandler(Object sender, TestEventArgs e);
public class TestEventArgs: EventArgs
{
public Boolean Test { get; set; }
public TestEventArgs(Boolean ATest): base()
{
Test = ATest;
}
}
[TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public class SubClass
{
public Boolean TestProperty { get; set; }
public event TestEventHandler TestEvent;
protected virtual void OnTestEvent(TestEventArgs e)
{
if (TestEvent != null)
TestEvent(this, e);
}
}
}
Problem solved. If the subclass inherited from Component, the Visual Studio can manage "subclass events" well.
Suspect the property editor doesn't know how it should handle input related to the complex TestEventArgs class, so you can't edit in in the Visual Studio property editor.
You could have a look at writing a Custom UI Type Editor and then specify the custom editor by using the EditorAttribute:
[EditorAttribute(typeof(YourCustomEditor),typeof(System.Drawing.Design.UITypeEditor))]
I have been trying to work out how to call a method in a different class. Both classes are created dynamically at run-time. Most of the issues I have seen here relate to inheritance, which is different from what I have (I think.)
I am still fairly new to C#, and am trying to test some concepts out.
The first class is something like this:
public class Record
{
CustomPanel _panel;
public void recordFunc(){}
}
The internally created class has something like this:
public class CustomPanel : Panel
{
List<Control> _myControls = new List<Control>;
// _myControls[0] += new EventHandler(myFunc);
public void myFunc(object sender, EventArgs e)
{
// parentClass.recordFunc();
}
}
My objective is to create a Record at run-time from a database call. At that point, it creates a Panel (from my CustomPanel class) that gets added to a FlowLayoutControl. When events are fired from the panel's internal controls, I need to have it update parts of the parent Record class.
Any help is greatly appreciated.
I'm not 100% sure what you're asking, but it seems you want to know how to call a function on a class, when you don't know the class type at runtime, but it could be one or many record types. Is that correct?
If so, a way to cleanly achieve the above is to implement an interface on your derived types and call the interface method. For instance, if you have multiple "Record" classes and don't know the type at runtime, try the following:
public interface IRecord
{
void RecordFunc();
}
public class ARecord : IRecord
{
public void RecordFunc()
{
Console.WriteLine("ARecord.RecordFunc");
}
}
public class AnotherRecord : IRecord
{
public void RecordFunc()
{
Console.WriteLine("AnotherRecord.RecordFunc");
}
}
public class CustomPanel : Panel
{
private IRecord _parentRecord;
// Where parentRecord could be ARecord or AnotherRecord
public class CustomPanel(IRecord parentRecord)
{
_parentRecord = parentRecord;
}
public void MyFunc(object sender, EventArgs e)
{
_parentRecord.RecordFunc();
}
}
If that's not what you're looking for, please clarify.
There is no magic instance of the Record class available from within a CustomPanel just because a Record instance contains a CustomPanel. You'll have to set up such a relationship yourself. E.g.
public class Record
{
CustomPanel _panel;
public CustomPanel panel
{
get { return _panel; }
set { _panel = value; _panel.parent = this; }
}
public void recordFunc(){}
}
public class CustomPanel : Panel
{
public Record parent { get; set; }
public void myFunc(object sender, EventArgs e)
{
parent.recordFunc();
}
}