I have a form, on this form is a flowlayoutpanel with multiple custom made TextBoxes
The form overrides the base methode Refresh(), to do some other things also.
Now I'm digging into the parent to eventueally come on the form and do the refresh
this.Parent.Parent.Parent.Refresh();
I want to re-use the control on other forms, so is there another way to do this?
And I know a While(true) is possible:
Boolean diggToParent = true;
var parent = this.Parent;
while (diggToParent)
{
if (parent.Parent != null)
{
parent = parent.Parent;
}
else
break;
}
parent.Refresh();
But is there a cleaner way to do this?
You can solve this by creating and raising an event that is handled by the parent form:
public class MyUserControl : UserControl
{
// ...
public event EventHandler RequestRefresh;
// Call this method whenever you want the parent to refresh
private void OnRequestRefresh()
{
if (RequestRefresh != null)
RequestRefresh(this, EventArgs.Empty);
}
}
In the parent form (or the container that should be refreshed), you add an event handler, e.g.
public class MyParentForm : Form
{
public MyParentForm()
{
InitializeComponent();
userCtrl.RequestRefresh += userCtrl_RequestRefresh;
}
// Do whatever the parent thinks is necessary to refresh.
public void userCtrl_RequestRefresh(object sender, EventArgs e)
{
Refresh();
}
// ...
}
This way the parent form can decide what to do when the user control requests a refresh. For details on events, see this link.
Related
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 am writing a custom control derived from Component.
In this control I need to be able somehow to get OS messages WM_DEVICEDCHANGED to create some events from.
Usually I would just override WndProc directly in the applications Form, but it is really important that this functionallity lays directly in the control instead.
Even though the control will always be used on a Form it is important that the OS messages are received on the control that is derived from Component so when dropping the control on a form there is no need to add functionality for it manually IN the form.
I have seen some examples mentioning NativeWindow and other solutions, but I have not been able to find head or tail in any of it, so I hope someone here can help me out.
Thanks...
i want to receive the WM_DEVICECHANGED message
Okay, that just requires subclassing the window of the form on which you drop the component. Any top-level window gets that message. Add a new class to your project and paste the code shown below. Build. Drop the new component from the top of the toolbox onto a form. Add an event handler for the DeviceChange event and add any code that's relevant to the kind of device change notification that you are interested in. You could also put that code in the OnDeviceChange() method to further specialize the notification and raise more specific events. It is up to you to take it from here.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Windows.Forms;
public class DeviceChangeNotifier : Component {
public delegate void DeviceChangeDelegate(Message msg);
public event DeviceChangeDelegate DeviceChange;
public DeviceChangeNotifier() {
// Add initialization here
}
public DeviceChangeNotifier(IContainer container) : this() {
// In case you need automatic disposal
container.Add(this);
}
public DeviceChangeNotifier(ContainerControl parentControl) : this() {
// In case you want to use it without the designer
this.ContainerControl = parentControl;
}
public ContainerControl ContainerControl {
// References the parent form
get { return this.parentControl; }
set {
this.parentControl = value;
this.parentControl.HandleCreated += parentControl_HandleCreated;
}
}
private void parentControl_HandleCreated(object sender, EventArgs e) {
// Subclass the form when its handle is created
snooper = new MessageSnooper(this, parentControl.Handle);
}
protected void OnDeviceChange(Message msg) {
// Raise the DeviceChange message
var handler = DeviceChange;
if (handler != null) handler(msg);
}
public override ISite Site {
// Runs at design time, ensures designer initializes ContainerControl
// so we'll have a reference to the parent form without it having to do any work
set {
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
private ContainerControl parentControl;
private MessageSnooper snooper;
private const int WM_DESTROY = 0x0002;
private const int WM_DEVICECHANGE = 0x0219;
private class MessageSnooper : NativeWindow {
// Subclasses the parent window
public MessageSnooper(DeviceChangeNotifier owner, IntPtr handle) {
this.owner = owner;
this.AssignHandle(handle);
}
protected override void WndProc(ref Message m) {
if (m.Msg == WM_DESTROY) this.ReleaseHandle();
if (m.Msg == WM_DEVICECHANGE) owner.OnDeviceChange(m);
base.WndProc(ref m);
}
private DeviceChangeNotifier owner;
}
}
I am trying to obtain a value back to the parent form and below is the code that I was using for this and it was working fine until started loading the child-form in a panel control to avoid popup windows.
Code in the mainform which contains the Panel
MainMovement child = new MainMovement(new_dat, required_time, number);
child.TopLevel = false;
this.pnlmain.Controls.Add(child);
child.Show();
child.BringToFront();
///Obtaining value back from the child form
string updatingc = child.updatestatus; //This is not working, I am proceeding with some more functions depending on this value, but code does not work here after
Child form has a public value as updatestatus and it sets the value before closing the child form.
Please advise how to obtain this value. I believe it is something to do with changing child.ShowDialog() to child.Show(). (In order to load the form into a panel I had to change this, before that this was working fine).
You can pass the object of your main form into child form via constructor. If you pass your object, you will have access to all the methods of parent form in your child. You can call any public method of main class to update your value.
MainMovement child = new MainMovement(this,new_dat, required_time, number);
child.TopLevel = false;
this.pnlmain.Controls.Add(child);
child.ShowDialog();
child.BringToFront();
Put one public method in your main form,
Public void UpdateValue(String pString)
{
// Update your value
}
In your child form, you have to catch "this" with global object.
private oMainForm as MainForm
public void MainMovement(MainForm pObject,String new_dat, String required_time, Int number)
{
oMainForm = pObject;
// Your Code
}
Now you can simply call your 'UpdateValue' method from child form.
oMainForm.UpdateValue("Updated String");
The problem is .ShowDialog() waits for a DialogResult before continuing, whereas Show() just shows the form and continues. It is hard to say without knowing how your child form is working, but my guess is whatever updates or sets updatestatus in your child form doesn't update before your code reaches that line.
One possible solution involves a major refactoring of your code. You can add an event to your MainMovement form that is triggered when updatestatus is changed.
Note that I changed your updatestatus to UpdateStatus and turned it into a property
public MainMovement : Form
{
public event EventHandler Updated;
private void OnUpdateStatus()
{
if (Updated != null)
{
Updated(this, new EventArgs());
}
}
private String updatestatus;
public String UpdateStatus
{
get { return updatestatus; }
private set
{
updatestatus = value;
OnUpdateStatus();
}
}
// rest of your child form code
}
public ParentForm : Form
{
public void MethodInYourExample()
{
// other code?
MainMovement child = new MainMovement(new_dat, required_time, number);
child.Updated += ChildUpdated;
child.TopLevel = false;
this.pnlmain.Controls.Add(child);
child.Show();
child.BringToFront();
}
void ChildUpdated(object sender, EventArgs e)
{
var child = sender as MainMovement;
string updatingc = child.UpdateStatus;
//rest of your code
}
}
I have a custom control that inherits from TreeView. In this CustomTreeView, I handle the OnNodeMouseClick event to perform some process before changing the node.Checked state as the user would expect it:
public class CustomTreeView : TreeView {
// Constructor...
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) {
base.OnNodeMouseClick(e);
// Do something...
e.Node.Checked = !e.Node.Checked;
}
}
My problem is when the developer subscribes to the AfterCheck event on a CustomTreeView, the value of e.Action is always TreeViewAction.Unknown (because the checked state of the node is changed in the code), whereas the developer is waiting for TreeViewAction.ByMouse:
public partial class Form1: Form {
private void customTreeView1_AfterCheck(object sender, TreeViewEventArgs e) {
// e.Action == TreeViewAction.Unknown
// [developer] The user clicked on the node, it should be
// TreeViewAction.ByMouse!?
}
}
What I would like to do is disable the AfterCheck event from firing and call it myself in my CustomTreeView class, that way I would be able to pass parameters with TreeViewAction equal to ByMouse. Something like that:
public class CustomTreeView : TreeView {
// Constructor...
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) {
base.OnNodeMouseClick(e);
// Do something...
// Prevent all AfterCheck events from firing, just for a moment
// ??
e.Node.Checked = !e.Node.Checked;
// Allow AfterCheck events to fire
// ??
// Call myself the AfterCheck event
base.OnAfterCheck(new TreeViewEventArgs(e.Node, TreeViewAction.ByMouse));
}
}
Is this possible?
Sure, just override OnAfterCheck in CustomTreeView and it will work like you intend.
public class CustomTreeView : TreeView {
protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) {
base.OnNodeMouseClick(e);
// your stuff
// Call myself the AfterCheck event
base.OnAfterCheck(new TreeViewEventArgs(e.Node, TreeViewAction.ByMouse));
}
protected override void OnAfterCheck(TreeViewEventArgs e)
{
// do nothing
}
}
I have a custom set of UserControls: NavigationBar and NavigationItem.
I'd like that whenever the user clicks anywhere in the NavigationItem, an event is fired. I don't know how to set this up.
http://i.stack.imgur.com/ocP2D.jpg
I've tried this:
public partial class NavigationBar : UserControl
{
public NavigationBar()
{
InitializeComponent();
SetupEvents();
}
public List<NavigationItem> NavigationItems { private get; set; }
public NavigationItem SelectedItem { get; set; }
private void SetupEvents()
{
navigationItem1.Click += new EventHandler(navigationItemClick);
}
void navigationItemClick(object sender, EventArgs e)
{
MessageBox.Show("Clicked on " + sender.ToString());
}
}
But that event only fires when the user specifically clicks on the NavigationItem control, but not when he clicks on the picture or text. (Those are PictureBox and Label).
What would be the best course of action? I'd like to create something well, not hacky code. Thanks!
Put something like this into your class:
public event EventHandler NavigationItemClick;
This creates a new event in your class named NavigationItemClick. The form designer will even see it.
In your method navigationItemClick you can do this to call the event.
EventHandler handler = this.NavigationItemClick;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
It is important to save the event into the handler variable to avoid race conditions. EventHandler is a delegate, so you call it like a method, hence the line in the if statement. The if itself makes sure that someone has attached to your event.