2009-07-21 14 views
19

मेरे पास एक सी # .NET 2.0 WinForms ऐप है। मेरे ऐप में एक नियंत्रण है जो दो बाल नियंत्रणों के लिए एक कंटेनर है: एक लेबल, और कुछ प्रकार के संपादन नियंत्रण। आप इस, जहां बाहरी बॉक्स जनक नियंत्रण है के रूप में विचार कर सकते हैं:अभिभावक नियंत्रण माउस बाल नियंत्रण के साथ प्रविष्टियां/छोड़ें

 
+---------------------------------+ 
| [Label Control] [Edit Control] | 
+---------------------------------+

मैं जब माउस प्रवेश करती है या माता पिता के नियंत्रण छोड़ देता है कुछ करने के लिए कोशिश कर रहा हूँ, लेकिन मुझे परवाह नहीं है अगर माउस चालें अपने बच्चों में से एक में। मैं एक एकल ध्वज का प्रतिनिधित्व करना चाहता हूं "माउस माता-पिता या बच्चों के अंदर कहीं है" और "माउस पैरेंट नियंत्रण सीमाओं के बाहर स्थानांतरित हो गया है"।

मैंने माता-पिता और माउसलाइव पर माउसइंटर और माउसलेव को संभालने का प्रयास किया है, लेकिन इसका मतलब है कि कार्रवाई कई बार समाप्त होती है और माउस नियंत्रण के दौरान कई बार समाप्त होती है। दूसरे शब्दों में, मैं इस मिल:

 
Parent.OnMouseEnter  (start doing something) 
Parent.OnMouseLeave  (stop) 
Child.OnMouseEnter  (start doing something) 
Child.OnMouseLeave  (stop) 
Parent.OnMouseEnter  (start doing something) 
Parent.OnMouseLeave  (stop)

मध्यवर्ती OnMouseLeave घटनाओं जो कुछ भी रूप में मैं कर रहा हूँ शुरू कर दिया जाता है और फिर बंद कर दिया कुछ अनचाहे प्रभाव होता है। मैं इससे बचना चाहता हूं।

मैं माउस को कैप्चर नहीं करना चाहता क्योंकि माता-पिता को माउस खत्म हो जाता है, क्योंकि बच्चे के नियंत्रण को उनके माउस ईवेंट की आवश्यकता होती है, और मैं मेनू और अन्य शॉर्टकट कुंजियों को काम करना चाहता हूं।

क्या .NET ढांचे के अंदर ऐसा करने का कोई तरीका है? या मुझे विंडोज माउस हुक का उपयोग करने की ज़रूरत है?

उत्तर

8

अधिक शोध के बाद, मैंने Application.AddMessageFilter method की खोज की। इस का उपयोग करते हुए, मैं एक माउस हुक के नेट संस्करण बनाया:

class MouseMessageFilter : IMessageFilter, IDisposable 
{ 
    public MouseMessageFilter() 
    { 
    } 

    public void Dispose() 
    { 
     StopFiltering(); 
    } 

    #region IMessageFilter Members 

    public bool PreFilterMessage(ref Message m) 
    { 
     // Call the appropriate event 
     return false; 
    } 

    #endregion 

    #region Events 

    public class CancelMouseEventArgs : MouseEventArgs 
    {...} 

    public delegate void CancelMouseEventHandler(object source, CancelMouseEventArgs e); 
    public event CancelMouseEventHandler MouseMove; 
    public event CancelMouseEventHandler MouseDown; 
    public event CancelMouseEventHandler MouseUp; 

    public void StartFiltering() 
    { 
     StopFiltering(); 
     Application.AddMessageFilter(this); 
    } 

    public void StopFiltering() 
    { 
     Application.RemoveMessageFilter(this); 
    } 
} 

फिर, मैं अपने कंटेनर नियंत्रण में MouseMove घटना संभाल कर सकते हैं, अगर माउस मेरे माता-पिता नियंत्रण के अंदर है देखने के लिए जाँच, और काम शुरू ।

---- संपादित ----

मेरी प्रपत्र वर्ग में मैं बनाने (मैं भी पिछले माता पिता नियंत्रण पर moused ट्रैक करने के लिए तो मैं रोक सकता है कि पहले माता-पिता शुरू कर दिया था।), और फिल्टर जोड़ने का काम :

public class MyForm : Form 
{ 
    MouseMessageFilter msgFilter; 

    public MyForm() 
    {... 
     msgFilter = new MouseMessageFilter(); 
     msgFilter.MouseDown += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseDown); 
     msgFilter.MouseMove += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseMove); 
    } 

    private void msgFilter_MouseMove(object source, MouseMessageFilter.CancelMouseEventArgs e) 
    { 
     if (CheckSomething(e.Control) 
      e.Cancel = true; 
    } 
} 
2

मुझे नहीं लगता कि आपको इसे हल करने के लिए संदेश पंप को हुक करने की आवश्यकता है। आपके यूआई में कुछ फ़्लैगिंग चाल चलनी चाहिए। मैं सोच रहा हूं कि आप एक नियंत्रण चर बनाते हैं, जैसे कि नियंत्रण _someParent, आपके नियंत्रण वर्ग में, जो आपके OnMouseEnter हैंडलर को कॉल करते समय अभिभावक नियंत्रण का संदर्भ लेगा। फिर, OnMouseLeave में, _someParent "ध्वज" के मान की जांच करें और यदि यह वर्तमान प्रेषक के समान है तो वास्तव में अपनी प्रसंस्करण को रोकना न करें, बस वापस आएं। केवल जब माता-पिता अलग होते हैं तो क्या आप रोकते हैं और _omeome को शून्य पर रीसेट करते हैं।

+0

अगर मैं वर्तमान माता पिता को छोड़, मैं नहीं जानता कि अग्रिम में मैं एक बच्चे के नियंत्रण में जा रहा हूं या वास्तव में माता-पिता को छोड़ रहा हूं। और यदि मैंने सुझाव दिया है, तो माता-पिता OnMouseLeave को "हे, यह मैं हूं, इसलिए प्रसंस्करण जारी रखें" कहना होगा, इसलिए नियंत्रण के बाहर आगे बढ़ना और किसी अन्य नियंत्रण में नहीं, प्रसंस्करण को रोक नहीं पाएगा। –

+0

यह एक अच्छा मुद्दा है, हालांकि यदि आपका नियंत्रण रोकथाम संरचना बहुत लचीली नहीं है, यानी एन-स्तरीय रोकथाम और/या आप माता-पिता के प्रकार को ध्यान में रखते हैं तो आप अभी भी इस विधि का उपयोग कर सकते हैं। उदाहरण के लिए, आपको एक एंटर इवेंट मिलता है, फिर आप उस नियंत्रण को लेते हैं जब तक आप समूहबॉक्स या पैनल (जो भी आपके बटन वाले होते हैं) देखते हैं और उस मान के संदर्भ पर मूल्यांकन करते हैं, तब तक आप नियंत्रण पेड़ पर जाते हैं। जब यह बदलता है। –

3

आप पता लगाएँ कि क्या माउस इस तरह अपने नियंत्रण की सीमा के भीतर है पा सकते हैं (इस कोड को संभालने अपने कंटेनर नियंत्रण में रहता है, अगर नहीं, कंटेनर नियंत्रण के लिए एक संदर्भ के साथ this डालें):

private void MyControl_MouseLeave(object sender, EventArgs e) 
{ 
    if (this.ClientRectangle.Contains(this.PointToClient(Cursor.Position))) 
    { 
     // the mouse is inside the control bounds 
    } 
    else 
    { 
     // the mouse is outside the control bounds 
    } 
} 
+0

दिलचस्प विचार है, लेकिन ऐसा लगता है कि अभ्यास में काम नहीं करना है। कभी-कभी किसी दिए गए पैरेंट नियंत्रण के लिए घटनाओं का अंतिम सेट चाइल्ड.माउसइंटर और चाइल्ड.माउसलेव है। मेरे बच्चे में। माउसलेव हैंडलर, कभी-कभी कर्सर। स्थिति अभी भी माता-पिता की सीमा में है। कोड सोचता है कि कर्सर अभी भी अंदर है, इसलिए "माउस पैरेंट के अंदर है" कार्रवाई कभी नहीं रुक जाती है। –

+0

यह बहुत अच्छा काम करता है! मैंने सभी बच्चों की घटनाओं को पैरेंट क्लास में एक ही हैंडलर पर लगाया और फिर माउस स्थान की जांच की। मैंने एक बुलियन बचाया जो बचाता है कि माउस नियंत्रण में है या नहीं (बच्चे के माता-पिता के माउस चाल आदि को संभालने के लिए –

+0

यह तब होता है जब कोई शीर्ष खिड़की (जैसे कार्य प्रबंधक) आंशिक रूप से शीर्ष पर होता है स्क्रीन आयत का।यह कोड इस तरह कार्य करेगा कि माउस तब नहीं छोड़ा है जब माउस नियंत्रण को कवर करने वाली शीर्षतम विंडो के हिस्से में प्रवेश करता है। – jnm2

5

मुझे लगता है कि मुझे वर्तमान में शीर्ष स्वीकृत समाधान की तुलना में एक बेहतर समाधान मिला है।

अन्य प्रस्तावित समाधानों के साथ समस्या यह है कि वे या तो काफी जटिल हैं (सीधे निम्न स्तर के संदेशों को संभालने)।

या वे कोने के मामलों में असफल हो जाते हैं: माउसलेव पर माउस की स्थिति पर भरोसा करने से माउस को बाहर निकलने का कारण बन सकता है यदि माउस सीधे बच्चे के नियंत्रण में कंटेनर के बाहर से जाता है।

हालांकि यह समाधान पूरी तरह से सुरुचिपूर्ण नहीं है, यह स्पष्ट है और काम करता है:

एक पारदर्शी नियंत्रण है कि कंटेनर है कि आप के लिए mouseenter और mouseleave घटनाओं प्राप्त करना चाहते हैं के संपूर्ण स्थान लेता है जोड़ें। Making a control transparent

कौन सा मैं तो इस के लिए नीचे छीन लिया:

public class TranspCtrl : Control 
{ 
    public TranspCtrl() 
    { 
     SetStyle(ControlStyles.SupportsTransparentBackColor, true); 
     SetStyle(ControlStyles.Opaque, true); 
     this.BackColor = Color.Transparent; 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      cp.ExStyle = cp.ExStyle | 0x20; 
      return cp; 
     } 
    } 
} 

उदाहरण उपयोग:

public class ChangeBackgroundOnMouseEnterAndLeave 
{ 
    public Panel Container; 
    public Label FirstLabel; 
    public Label SecondLabel; 

    public ChangeBackgroundOnMouseEnterAndLeave() 
    { 
     Container = new Panel(); 
     Container.Size = new Size(200, 60); 

     FirstLabel = new Label(); 
     FirstLabel.Text = "First Label"; 
     FirstLabel.Top = 5; 

     SecondLabel = new Label(); 
     SecondLabel.Text = "Second Lable"; 
     SecondLabel.Top = 30; 

     FirstLabel.Parent = Container; 
     SecondLabel.Parent = Container; 

     Container.BackColor = Color.Teal; 

     var transparentControl = new TranspCtrl(); 
     transparentControl.Size = Container.Size; 

     transparentControl.MouseEnter += MouseEntered; 
     transparentControl.MouseLeave += MouseLeft; 

     transparentControl.Parent = Container; 
     transparentControl.BringToFront(); 
    } 

    void MouseLeft(object sender, EventArgs e) 
    { 
     Container.BackColor = Color.Teal; 
    } 

    void MouseEntered(object sender, EventArgs e) 
    { 
     Container.BackColor = Color.Pink; 
    } 
} 

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     var test = new ChangeBackgroundOnMouseEnterAndLeave(); 
     test.Container.Top = 20; 
     test.Container.Left = 20; 
     test.Container.Parent = this; 
    } 
} 

उचित mouseleave और mouseenter का आनंद लें

मैं Amed के जवाब यहाँ में एक अच्छा पारदर्शी नियंत्रण पाया आयोजन!

1

मुझे बिल्कुल वही आवश्यकता थी। पॉल विलियम्स के जवाब ने मुझे मूल विचार प्रदान किया, लेकिन मुझे कोड को समझने में कठिनाई थी। मैंने एक और here लिया, और साथ में, दोनों उदाहरणों ने मुझे अपना खुद का संस्करण विकसित करने में मदद की।

प्रारंभ करने के लिए, आप ContainerMessageFilter कन्स्ट्रक्टर में रुचि के कंटेनर नियंत्रण को पास करते हैं। कक्षा कंटेनर की खिड़की हैंडल और उसके भीतर सभी बाल नियंत्रण एकत्र करती है।

फिर, ऑपरेशन के दौरान, कक्षा WM_MOUSEMOVE संदेश फ़िल्टर करती है, यह निर्धारित करने के लिए कि माउस के अंदर कौन सा नियंत्रण चल रहा है, संदेशों को HWnd पर जांचें। इस तरह, यह निर्धारित करता है कि जब माउस कंटेनर के भीतर नियंत्रण के सेट के अंदर या बाहर स्थानांतरित हो जाता है।

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows.Forms; 

public class ContainerMessageFilter : IMessageFilter { 
    private const int WM_MOUSEMOVE = 0x0200; 

    public event EventHandler MouseEnter; 
    public event EventHandler MouseLeave; 

    private bool insideContainer; 
    private readonly IEnumerable<IntPtr> handles; 

    public ContainerMessageFilter(Control container) { 
     handles = CollectContainerHandles(container); 
    } 

    private static IEnumerable<IntPtr> CollectContainerHandles(Control container) { 
     var handles = new List<IntPtr> { container.Handle }; 

     RecurseControls(container.Controls, handles); 

     return handles; 
    } 

    private static void RecurseControls(IEnumerable controls, List<IntPtr> handles) { 
     foreach (Control control in controls) { 
      handles.Add(control.Handle); 

      RecurseControls(control.Controls, handles); 
     } 
    } 

    public bool PreFilterMessage(ref Message m) { 
     if (m.Msg == WM_MOUSEMOVE) { 
      if (handles.Contains(m.HWnd)) { 
       // Mouse is inside container 
       if (!insideContainer) { 
        // was out, now in 
        insideContainer = true; 
        OnMouseEnter(EventArgs.Empty); 
       } 
      } 
      else { 
       // Mouse is outside container 
       if (insideContainer) { 
        // was in, now out 
        insideContainer = false; 
        OnMouseLeave(EventArgs.Empty); 
       } 
      } 
     } 

     return false; 
    } 

    protected virtual void OnMouseEnter(EventArgs e) { 
     var handler = MouseEnter; 
     handler?.Invoke(this, e); 
    } 

    protected virtual void OnMouseLeave(EventArgs e) { 
     var handler = MouseLeave; 
     handler?.Invoke(this, e); 
    } 
} 

निम्नलिखित उपयोग उदाहरण में, हम एक Panel के लिए माउस प्रवेश और निकास की निगरानी करना चाहते है और बच्चे को नियंत्रित करता है कि यह शामिल हैं:

public partial class Form1 : Form { 
    private readonly ContainerMessageFilter containerMessageFilter; 

    public Form1() { 
     InitializeComponent(); 

     containerMessageFilter = new ContainerMessageFilter(panel1); 
     containerMessageFilter.MouseEnter += ContainerMessageFilter_MouseEnter; 
     containerMessageFilter.MouseLeave += ContainerMessageFilter_MouseLeave; 
     Application.AddMessageFilter(containerMessageFilter); 
    } 

    private static void ContainerMessageFilter_MouseLeave(object sender, EventArgs e) { 
     Console.WriteLine("Leave"); 
    } 

    private static void ContainerMessageFilter_MouseEnter(object sender, EventArgs e) { 
     Console.WriteLine("Enter"); 
    } 

    private void Form1_FormClosed(object sender, FormClosedEventArgs e) { 
     Application.RemoveMessageFilter(containerMessageFilter); 
    } 
} 
संबंधित मुद्दे