I have wrote a frame application, it's a windows form app as parent form.When it starts, it will find dlls in /modules and load them as extensions. And when I click the menuItem in the parent form, the specific dll will work.If the dll is a Form app, it will show.But when i try to use shortcuts(only build-ins,eg : CTRL-C...) in the childForm ,the hotkeys does not work. Anyone kindly tell me why and how can i fix the issue? Here's my code:
//parent.exe--ModuleEntrance.cs
public abstract class ModuleEntrance {
public abstract string[] GetMenuNames();
public abstract string[] GetMenuItemNames();
public abstract EventHandler[] GetEventHandlers();
}
//parent.exe--ParentForm.cs
public partial class MDIParent : Form {
public MDIParent() { //CTOR
InitializeComponent();
ModuleEntrance oneEntrance;
string oneMenuName, oneMenuItemName;
ToolStripMenuItem theMenu, theMenuItem;
for(){ //iterate dlls in /modules, if it implement ModuleEntrance, load it.
//And 1)load menu&menuItem.
//2) connect handler to menuItem.click through
//<code:theMenuItem.Click += new EventHandler(oneEntrance.GetEventHandlers()[i]);>
}
}
//--------------
//child.dll-- EntranceImp.cs //implement AC
public class EntranceImp : ModuleEntrance {
public override string[] GetMenuNames() {
return new string[] { "MENU"};
}
public override string[] GetMenuItemNames() {
return new string[] { "OpenChildForm"};
}
public override EventHandler[] GetEventHandlers() {
return new EventHandler[]{
(EventHandler)delegate(object sender, EventArgs e) { //Anonymous method
childForm form = new childForm();
//find MDIparent and connect them
ToolStripMenuItem mi = (ToolStripMenuItem)sender;
form.MdiParent = (Form)(mi.OwnerItem.Owner.Parent); //It works!
form.Show();
}
};
}
}
//child.dll--childForm.dll
//...
The child form has to notify the parent form about the key-down events somehow.
A way to do this is to have the child form expose key-down events that the parent form can listen to. Remember to remove the parent event handlers every time that you change child form, or you'll end up with a memory leak since objects are not garbage collected until you release all references to them, including event handlers.
class Parent
{
KeyEventHandler KeyDownHandler;
public Parent()
{
KeyDownHandler = new KeyEventHandler(form_TextBoxKeyDown);
}
void SetChildForm(Child form)
{
form.TextBoxKeyDown += KeyDownHandler;
}
void RemoveChildForm(Child form)
{
form.TextBoxKeyDown -= KeyDownHandler;
}
void form_TextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Control)
{
switch (e.KeyCode)
{
case Keys.C:
break;
case Keys.X:
break;
case Keys.V:
break;
}
}
}
}
class Child
{
TextBox txtBox;
public event KeyEventHandler TextBoxKeyDown;
internal Child()
{
txtBox.KeyDown += new KeyEventHandler(txtBox_KeyDown);
}
void txtBox_KeyDown(object sender, KeyEventArgs e)
{
if (TextBoxKeyDown != null)
TextBoxKeyDown(sender, e);
}
}
Related
//MdiParent mainparent.cs
public static void lodforweb()
{
frm_webcs frmload_webcs = new frm_web
{
MdiParent = this
};
frmload_webcs.Show();
}
//Context menu class
//Cl_contextmenu.cs
public bool OnContextMenuCommand()
{
if (commandId == (2020)
{
mainparent.lodforweb();
return true;
}
}
}
// having a problem with "this" using static method
// instantiating does not work also.
From the comments:
once I right click the child form context menu popups. I want to
generate another child form and should be child of mainparent
Also provided from the comments:
private void childform_Load(object sender, EventArgs e) {
cl_chromebrowser urload = new cl_chromebrowser();
panel1.Controls.Add(urload.choniumeload("www.site.com"));
urload.chromebrowser.MenuHandler= new cl_contexmenu();
}
So your child form is called "childform". Add a static member that holds a reference to the MDI parent, and set it in the Load() event:
public static Form MyMdiParent;
private void childform_Load(object sender, EventArgs e)
{
// ... your existing code from above ...
childform.MyMdiParent = this.MdiParent;
}
Now you can use that MDI parent directly from your context menu:
public bool OnContextMenuCommand()
{
if (commandId == 2020)
{
if (childform.MyMdiParent != null)
{
childform.MyMdiParent.Invoke((MethodInvoker)delegate ()
{
frm_webcs frmload_webcs = new frm_webcs();
frmload_webcs.MdiParent = childform.MyMdiParent;
frmload_webcs.Show();
});
}
}
return true;
}
I have multiple forms with the same method called "UpdateTheme" which changes the back colour of the form. I want to be able to call all of these methods from another form.
I tried to make a base form with the "UpdateTheme" method then have all other forms inherit from the base form, But I didnt know how/ if it was possible to then call every instance of the derived forms methods from a separate "Settings" form.
public abstract class CustomForm : Form
{
public void UpdateTheme(string theme)
{
if (theme == "dark")
{
this.BackColor = Color.Black;
}
else if (theme == "light")
{
this.BackColor = Color.White;
}
}
}
In the settings form I would have something like
public void btnSetThemeToDark_Click(object sender, EventArgs e)
{
foreach (instance of derived form)
{
derivedForm.UpdateTheme("dark");
}
}
Whats the best way to do this?
You could create a singleton called StyleManager that contains the global style properties. This singleton has an event called style changed that can be handled by all forms, or a base form. So all of your forms get the information from one source.
StyleManager
public class StyleManager
{
#region singleton
public static StyleManager Instance { get; } = new StyleManager();
private StyleManager()
{
}
#endregion
#region events
public event EventHandler StyleChanged;
private void OnStyleChanged()
{
this.StyleChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region properties
public Color BackColor { get; set; }
#endregion
#region methods
public void UpdateBackColor(Color color)
{
this.BackColor = color;
this.OnStyleChanged();
}
#endregion
}
and use it in your forms like this:
public Form()
{
this.InitializeComponent();
//Attach to the event
StyleManager.Instance.StyleChanged += this.StyleChanged;
}
//Handle event
private void StyleChanged(object sender, EventArgs eventArgs)
{
this.BackColor = StyleManager.Instance.BackColor;
}
//set backcolor of all forms
StyleManager.Instance.UpdateBackColor(Color.Yellow);
Assuming this forms are MdiChildren of the form, you would do it this way:
foreach (var form in this.MdiChildren)
{
var castedForm = form as CustomForm;
if (myObjRef != null)
{
castedForm.UpdateTheme("dark");
}
}
This functionality works beyond themes to any common method of the child forms.
I don't think this is the best way to do this. But, you can archive what you want by using this code.
CustomForm mainFrm = (CustomForm)Application.OpenForms["YouCustomFormNameHere"];
mainFrm.UpdateTheme("dark");
Replace 'YouCustomFormNameHere' with your CustomForm form name.
I have one MainControl that contains a ChildControl. The ChildControl has a hide button that would hide itself.
When hidden I expect the MainControl to hook the event and dispose it.
MainControl
ChildControl > Hide button
Can't figure out how I should hook those.
Any tip? Thank you!
You can create an event that will notify the main control that the child control is hidden, and in your main control, handling the event, you can dispose of your control.
Below is a small sample code of how you can go about creating your event for the hidden action.
class MainControl
{
ChildControl childControl;
public MainControl()
{
childControl = new ChildControl();
childControl.VisibilityChanged += childControl_VisibilityChanged;
}
void childControl_VisibilityChanged(object sender, HiddenEvent e)
{
if (e.isHidden)
{
//close control here
}
}
}
public class HiddenEvent : EventArgs
{
public HiddenEvent(bool propertyValue)
{
this.isHidden = propertyValue;
}
public bool isHidden { get; set; }
}
public class ChildControl
{
public event EventHandler<HiddenEvent> VisibilityChanged;
public ChildControl()
{
}
private bool _isHidden;
public bool Control
{
get
{
return _isHidden;
}
set
{
_isHidden = value;
Hidden_Handler(value);
}
}
private void Hidden_Handler(bool isHidden)
{
var handler = VisibilityChanged;
if (handler != null)
VisibilityChanged(this, new HiddenEvent(isHidden));
}
}
As an option you could bind ChildControl's button to a remove command on the main control (using RelativeSource) and let MainControl do all the work
I have many controls in a window. Requirement is to know which control gets the focus from the lost focus event of a control.
Say, A Text box and it has the focus. Now I am clicking a button. while doing this, need to know that i am moving the focus to button from the Text box lost focus event.
So how could i achieve this..
This is what I did and its working for me
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
lostFocusControl = e.OldFocus;
}
private void PauseBttn_PreviewKeyDown(object sender, KeyEventArgs e)
{
/**invoke OnPreviewLostKeyboardFocus handller**/
}
Hope it will help
You can use FocusManager to handle this,
In your LostFocusEvent, Use FocusManager.GetFocusedElement()
uiElement.LostFocus+=(o,e)=>
{
var foo=FocusManager.GetFocusedElement();
}
The following class watches the FocusManager for changes in focus, it's a looped thread so you have to put up with the fact that it's running but when focus changes it will just raise an event letting you know what changed.
Just add these two classes to your project.
public class FocusNotifierEventArgs : EventArgs
{
public object OldObject { get; set; }
public object NewObject { get; set; }
}
public class FocusNotifier : IDisposable
{
public event EventHandler<FocusNotifierEventArgs> OnFocusChanged;
bool isDisposed;
Thread focusWatcher;
Dispatcher dispatcher;
DependencyObject inputScope;
int tickInterval;
public FocusNotifier(DependencyObject inputScope, int tickInterval = 10)
{
this.dispatcher = inputScope.Dispatcher;
this.inputScope = inputScope;
this.tickInterval = tickInterval;
focusWatcher = new Thread(new ThreadStart(FocusWatcherLoop))
{
Priority = ThreadPriority.BelowNormal,
Name = "FocusWatcher"
};
focusWatcher.Start();
}
IInputElement getCurrentFocus()
{
IInputElement results = null;
Monitor.Enter(focusWatcher);
dispatcher.BeginInvoke(new Action(() =>
{
Monitor.Enter(focusWatcher);
results = FocusManager.GetFocusedElement(inputScope);
Monitor.Pulse(focusWatcher);
Monitor.Exit(focusWatcher);
}));
Monitor.Wait(focusWatcher);
Monitor.Exit(focusWatcher);
return results;
}
void FocusWatcherLoop()
{
object oldObject = null;
while (!isDisposed)
{
var currentFocus = getCurrentFocus();
if (currentFocus != null)
{
if (OnFocusChanged != null)
dispatcher.BeginInvoke(OnFocusChanged, new object[]{ this, new FocusNotifierEventArgs()
{
OldObject = oldObject,
NewObject = currentFocus
}});
oldObject = currentFocus;
}
}
Thread.Sleep(tickInterval);
}
}
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
}
}
}
Then in your code behind, create a new instance of the Focus Notifier class and hook on to it's OnFocusChanged event, remember to dispose it at the end or the thread will keep your app open.
public partial class MainWindow : Window
{
FocusNotifier focusNotifier;
public MainWindow()
{
InitializeComponent();
focusNotifier = new FocusNotifier(this);
focusNotifier.OnFocusChanged += focusNotifier_OnFocusChanged;
}
void focusNotifier_OnFocusChanged(object sender, FocusNotifierEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.OldObject);
System.Diagnostics.Debug.WriteLine(e.NewObject);
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
focusNotifier.Dispose();
base.OnClosing(e);
}
}
have you tried to register your controls to Control.LostFocus event and there you can check for Form.ActiveControl, to determine which control currently has the focus
Can someone tell me how can I have a feature in my UserControl, that can let the host windowsform know what is the control is doing?
For example my usercontrol has a filebrowser, and if user uses this file browser to open a file I want in the statusstrip bar of my form to write "Loading file(s)".
Will this require using events? if so, how can I have a single event inside usercontrol to report anything it does (then I guess I have to call this event in all methods in the usercontrol).
Simple
Yes, expose an event on the user control that the Form can subscribe to. You should use the standard event pattern:
class MyUserControl : UserControl
{
public event EventHandler<EventArgs> FileOpened;
protected virtual void OnFileOpened(EventArgs e)
{
EventHandler<EventArgs> handler = FileOpened;
if (handler != null)
handler(this, e);
}
}
Then when the file is opened you call OnFileOpened(EventArgs.Empty) which fires the event.
With custom EventArgs
Now the Form probably needs to know what file was opened. You could expose a property on the user control that the Form can use to find out, or you can provide that information in your event like so:
public class FileOpenedEventArgs : EventArgs
{
private string filename;
public FileOpenedEventArgs(string filename)
{
this.filename = filename;
}
public string Filename { get { return filename; } }
}
class MyUserControl : UserControl
{
public event EventHandler<FileOpenedEventArgs> FileOpened;
protected virtual void OnFileOpened(FileOpenedEventArgs e)
{
EventHandler<FileOpenedEventArgs> handler = FileOpened;
if (handler != null)
handler(this, e);
}
}
Then you fire the event with OnFileOpened(new FileOpenedEventArgs(filename)).
Optimal
When you create an event handler public event delegate Name;, you are allocating storage for the delegate on your object. Objects (especially Controls) often have a huge number of events that are never subscribed to. That's a whole lot of allocated storage not being used. There's an optimization built into the framework in the form of a EventHandlerList. This handy object stores event handlers only when they are actually used. All System.Windows.Forms.Control objects derive from System.ComponentModel.Component and it already provides an (protected) EventHandlerList that you can access in your derived Control.
To use it, you first create a static object that uniquely identifies your event, and then you provide the add {} and remove {} methods manually. Like so:
class MyUserControl : UserControl
{
private static readonly object FileOpenedKey = new Object();
public event EventHandler<FileOpenedEventArgs> FileOpened
{
add { Events.AddHandler(FileOpenedKey, value); }
remove { Events.RemoveHandler(FileOpenedKey, value); }
}
protected virtual void OnFileOpened(FileOpenedEventArgs e)
{
var handler = (EventHandler<FileOpenedEventArgs>)Events[FileOpenedKey];
if (handler != null)
handler(this, e);
}
}
Yes, you will need to create an event and subscribe to it. One suggestion following the standard pattern for events:
enum ControlStatus {Idle, LoadingFile, ...}
class StatusChangedEventArgs : EventArgs
{
public ControlStatus Status {get; private set;}
public StatusChangedEventArgs(ControlStatus status)
: base()
{
this.Status = status;
}
}
partial class MyControl : UserControl
{
public ControlStatus Status {get; private set;}
public event EventHandler<StatusChangedEventArgs> StatusChanged;
protected virtual void OnStatusChanged(StatusChangedEventArgs e)
{
var hand = StatusChanged;
if(hand != null) hand(this, e);
}
void LoadFiles()
{
...
Status = ControlStatus.LoadingFiles;
OnStatusChanged(new StatusChangedEventArgs(this.Status));
...
Status = ControlStatus.Idle;
OnStatusChanged(new StatusChangedEventArgs(this.Status));
}
}
partial class MyHostWindowsForm : Form
{
public MyHostWindowsForm()
{
var ctl = new MyControl();
...
ctl.StatusChanged += ctl_StatusChanged;
}
void ctl_StatusChanged(object sender, StatusChangedEventArgs e)
{
switch(e.Status)
{
case ControlStatus.Idle:
statusStripBar.Text = null;
break;
case ControlStatus.LoadingFiles:
statusStripBar.Text = "Loading file(s)";
break;
...
}
}
}