मैं एक नियंत्रण और उसके बच्चों के लिए पेंटिंग को कैसे निलंबित कर सकता हूं?


184

मेरे पास एक नियंत्रण है जिसके लिए मुझे बड़े संशोधन करने होंगे। मैं इसे पूरी तरह से फिर से देखने से रोकना चाहूंगा, जबकि मैं ऐसा करता हूं - सस्पेंडलैयूट और रिज्यूमलैट पर्याप्त नहीं हैं। मैं एक नियंत्रण और उसके बच्चों के लिए पेंटिंग को कैसे निलंबित कर सकता हूं?


3
क्या कोई मुझे समझा सकता है कि इस संदर्भ में यहाँ क्या है या चित्र क्या है? (मैं नया हूँ। नेट) कम से कम एक लिंक प्रदान करता हूँ।
Mr_Green

1
यह वास्तव में शर्म की बात है (या हँसने योग्य) कि .Net 15 साल से अधिक समय से बाहर है, और यह अभी भी एक समस्या है। अगर Microsoft स्क्रीन फ्लिकर जैसी वास्तविक समस्याओं को ठीक करने में ज्यादा समय लगाता है, तो विंडोज एक्स मालवेयर प्राप्त करें , तो यह बहुत समय पहले तय हो जाता था।
jww

@jww उन्होंने इसे ठीक किया; इसे WPF कहा जाता है।
फिलु

जवाबों:


304

अपनी पिछली नौकरी में हम तुरंत और आसानी से पेंट करने के लिए अपने समृद्ध यूआई ऐप को पाने के लिए संघर्ष कर रहे थे। हम मानक .Net नियंत्रण, कस्टम नियंत्रण और डेवेक्सप्रेस नियंत्रण का उपयोग कर रहे थे।

बहुत सारे googling और रिफ्लेक्टर उपयोग के बाद मैं WM_SETREDRAW win32 संदेश में आया। जब आप उन्हें अपडेट करते हैं तो यह वास्तव में नियंत्रण को रोक देता है और माता-पिता / युक्त पैनल के लिए IIRC लागू किया जा सकता है।

यह एक बहुत ही सरल वर्ग है जो इस संदेश का उपयोग करने का प्रदर्शन करता है:

class DrawingControl
{
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

    private const int WM_SETREDRAW = 11; 

    public static void SuspendDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
    }

    public static void ResumeDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
        parent.Refresh();
    }
}

इस पर फुल चर्चाएँ हैं - C # और WM_SETREDRAW के लिए google, उदा

सी # जिटर

लेआउट को निलंबित करना

और यह किससे चिंतित हो सकता है, यह VB में समान उदाहरण है:

Public Module Extensions
    <DllImport("user32.dll")>
    Private Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As Integer, ByVal wParam As Boolean, ByVal lParam As IntPtr) As Integer
    End Function

    Private Const WM_SETREDRAW As Integer = 11

    ' Extension methods for Control
    <Extension()>
    Public Sub ResumeDrawing(ByVal Target As Control, ByVal Redraw As Boolean)
        SendMessage(Target.Handle, WM_SETREDRAW, True, 0)
        If Redraw Then
            Target.Refresh()
        End If
    End Sub

    <Extension()>
    Public Sub SuspendDrawing(ByVal Target As Control)
        SendMessage(Target.Handle, WM_SETREDRAW, False, 0)
    End Sub

    <Extension()>
    Public Sub ResumeDrawing(ByVal Target As Control)
        ResumeDrawing(Target, True)
    End Sub
End Module

44
क्या शानदार जवाब और ज़बरदस्त मदद! मैंने सस्पेंशन क्लास के लिए सस्पेंडड्राविंग और रिज्यूमेड्रॉइंग एक्सटेंशन के तरीके बनाए, इसलिए मैं उन्हें किसी भी संदर्भ में किसी भी नियंत्रण के लिए कॉल कर सकता हूं।
जैच जॉनसन

2
एक पेड़ जैसा नियंत्रण था जो केवल अपने बच्चों को पुन: व्यवस्थित करने पर एक नोड को ठीक से ताज़ा करेगा यदि आप ढह गए तो इसका विस्तार किया, जिससे बदसूरत झिलमिलाहट हुई। इसने इसके चारों ओर जाने के लिए पूरी तरह से काम किया। धन्यवाद! (पर्याप्त रूप से, नियंत्रण ने पहले से ही SendMessage को आयात किया, और WM_SETREDRAW को परिभाषित किया, लेकिन वास्तव में इसे किसी भी चीज़ के लिए उपयोग नहीं किया। अब यह करता है।)
neminem

7
यह विशेष रूप से उपयोगी नहीं है। यह वही है जो WinForms के सभी के Controlलिए आधार वर्ग पहले से ही और विधियों के लिए नियंत्रित करता है । संदेश भेजना आपके लिए उन तरीकों का उपयोग करने से बेहतर नहीं है जो आप के लिए भारी उठाने के लिए करते हैं, और निश्चित रूप से विभिन्न परिणाम नहीं दे सकते हैं। BeginUpdateEndUpdate
कोड़ी ग्रे

13
@Cody ग्रे - TableLayoutPanels में BeginUpdate नहीं है, उदाहरण के लिए।
TheBlastOne

4
सावधान रहें यदि आपका कोड नियंत्रण प्रदर्शित होने से पहले इन तरीकों को कॉल करना संभव बनाता है - कॉल करने के Control.Handleलिए विंडो हैंडल को बनाए जाने के लिए मजबूर करेगा और प्रदर्शन को प्रभावित कर सकता है। उदाहरण के लिए यदि आप प्रदर्शित होने से पहले किसी प्रपत्र पर नियंत्रण स्थानांतरित कर रहे थे, यदि आप इसे SuspendDrawingपहले से कॉल करते हैं, तो आपकी चाल धीमी हो जाएगी। संभवत: if (!parent.IsHandleCreated) returnदोनों तरीकों में जांच होनी चाहिए ।
ओट्सोडा

53

निम्नलिखित ng5000 का एक ही समाधान है, लेकिन P / Invoke का उपयोग नहीं करता है।

public static class SuspendUpdate
{
    private const int WM_SETREDRAW = 0x000B;

    public static void Suspend(Control control)
    {
        Message msgSuspendUpdate = Message.Create(control.Handle, WM_SETREDRAW, IntPtr.Zero,
            IntPtr.Zero);

        NativeWindow window = NativeWindow.FromHandle(control.Handle);
        window.DefWndProc(ref msgSuspendUpdate);
    }

    public static void Resume(Control control)
    {
        // Create a C "true" boolean as an IntPtr
        IntPtr wparam = new IntPtr(1);
        Message msgResumeUpdate = Message.Create(control.Handle, WM_SETREDRAW, wparam,
            IntPtr.Zero);

        NativeWindow window = NativeWindow.FromHandle(control.Handle);
        window.DefWndProc(ref msgResumeUpdate);

        control.Invalidate();
    }
}

मैंने इसे आजमाया है, लेकिन सस्पेंड और रिज्यूमे के बीच के मध्यवर्ती चरण में, यह सिर्फ अमान्य है। यह अजीब लग सकता है, लेकिन क्या यह क्षणिक स्थिति में निलंबित होने से पहले अपने राज्य को पकड़ सकता है?
यूजर

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

4
अच्छा जवाब, लेकिन यह दिखाना बेहतर होगा कि कहां Messageऔर क्या NativeWindowहैं; नाम के एक वर्ग के लिए दस्तावेज़ीकरण खोजना Messageवास्तव में यह सब मनोरंजक नहीं है।
दर्डा

1
@pelesl 1) स्वचालित नाम स्थान आयात (प्रतीक, ALT + Shift + F10 पर क्लिक करें) का उपयोग करें, 2) अमान्य () द्वारा चित्रित नहीं किए जाने वाले बच्चे अपेक्षित व्यवहार हैं। आपको थोड़े समय के अंतराल पर ही माता-पिता के नियंत्रण को निलंबित कर देना चाहिए और सभी संबंधित बच्चों के अमान्य होने पर इसे फिर से शुरू करना चाहिए।
ceztko

2
Invalidate()Refresh()जब तक एक के बाद एक अच्छे काम नहीं करता है।
यूजीन रियात्सेव

16

मैं आमतौर पर एनजीलिंक के उत्तर के थोड़ा संशोधित संस्करण का उपयोग करता हूं ।

public class MyControl : Control
{
    private int suspendCounter = 0;

    private void SuspendDrawing()
    {
        if(suspendCounter == 0) 
            SendMessage(this.Handle, WM_SETREDRAW, false, 0);
        suspendCounter++;
    }

    private void ResumeDrawing()
    {
        suspendCounter--; 
        if(suspendCounter == 0) 
        {
            SendMessage(this.Handle, WM_SETREDRAW, true, 0);
            this.Refresh();
        }
    }
}

इससे सस्पेंड / रेज़्यूमे कॉल्स को नेस्टेड किया जा सकता है। आपको प्रत्येक के SuspendDrawingसाथ मिलान करना सुनिश्चित करना चाहिए ResumeDrawing। इसलिए, उन्हें सार्वजनिक करना एक अच्छा विचार नहीं होगा।


4
यह दोनों कॉल को संतुलित रखने में मदद करता है SuspendDrawing(); try { DrawSomething(); } finally { ResumeDrawing(); }:। एक अन्य विकल्प यह है कि इसे एक IDisposableकक्षा में लागू किया जाए और ड्राइंग भाग को एक अंश में संलग्न किया जाए using। निर्माणकर्ता को हैंडल दिया जाएगा, जो ड्राइंग को निलंबित करेगा।
ओलिवियर जैकोट-डेसकॉम्ब्स

पुराना प्रश्न, मुझे पता है, लेकिन "झूठे" भेजने से सी # / वीएस के हाल के संस्करणों में काम करना दिखाई नहीं देता है। मुझे झूठ को 0 में बदलना था और 1. को सच करना था।
मारी मार्कोविट्ज़

@MauryMarkowitz के रूप में अपनी DllImportघोषणा wParamकरता है bool?
ओजगुर Ozcitak

नमस्ते, इस जवाब को दरकिनार करना एक दुर्घटना थी, क्षमा करें!
ज्योफ

13

रेनेबल ड्राइंग को न भूलने में मदद करने के लिए:

public static void SuspendDrawing(Control control, Action action)
{
    SendMessage(control.Handle, WM_SETREDRAW, false, 0);
    action();
    SendMessage(control.Handle, WM_SETREDRAW, true, 0);
    control.Refresh();
}

उपयोग:

SuspendDrawing(myControl, () =>
{
    somemethod();
});

1
क्या जब action()एक अपवाद फेंकता है? (एक कोशिश / अंत में उपयोग करें)
डेविड शेरेट

8

इंटरॉप का उपयोग किए बिना एक अच्छा समाधान:

हमेशा की तरह, अपने CustomControl पर केवल DoubleBuffered = true को सक्षम करें। फिर, यदि आपके पास कोई भी कंटेनर जैसे कि FlowLayoutPanel या TableLayoutPanel है, तो इन प्रकारों में से प्रत्येक में और निर्माणकर्ताओं से एक वर्ग प्राप्त करें, डबल बफरिंग सक्षम करें। अब, केवल Windows.Forms कंटेनरों के बजाय अपने व्युत्पन्न कंटेनरों का उपयोग करें।

class TableLayoutPanel : System.Windows.Forms.TableLayoutPanel
{
    public TableLayoutPanel()
    {
        DoubleBuffered = true;
    }
}

class FlowLayoutPanel : System.Windows.Forms.FlowLayoutPanel
{
    public FlowLayoutPanel()
    {
        DoubleBuffered = true;
    }
}

2
यह निश्चित रूप से एक उपयोगी तकनीक है - एक जिसे मैं ListViews के लिए बहुत बार उपयोग करता हूं - लेकिन यह वास्तव में redraws को होने से नहीं रोकता है; वे अभी भी ऑफस्क्रीन होते हैं।
सिमोन

4
आप सही हैं, यह टिमटिमाती समस्या को हल करता है, विशेष रूप से ऑफ-स्क्रीन रिडरिंग समस्या को नहीं। जब मैं झिलमिलाहट के समाधान की तलाश में था, तो मैं इस तरह के कई संबंधित थ्रेडों के पार आ गया, और जब मैंने पाया, तो मैंने इसे सबसे अधिक प्रासंगिक थ्रेड में पोस्ट नहीं किया होगा। हालांकि, जब अधिकांश लोग पेंटिंग को निलंबित करना चाहते हैं, तो वे संभवतः ऑन-स्क्रीन पेंटिंग का उल्लेख कर रहे हैं, जो अक्सर अनावश्यक स्क्रीन-पेंटिंग से अधिक स्पष्ट समस्या है, इसलिए मुझे अभी भी लगता है कि अन्य दर्शक इस समाधान को इस धागे में मददगार पा सकते हैं।
यूजीनियो डी होयोस

ओवररिप्ट को ओवरराइड करें।

6

Ng5000 के उत्तर के आधार पर, मुझे यह एक्सटेंशन उपयोग करना पसंद है:

        #region Suspend
        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
        private const int WM_SETREDRAW = 11;
        public static IDisposable BeginSuspendlock(this Control ctrl)
        {
            return new suspender(ctrl);
        }
        private class suspender : IDisposable
        {
            private Control _ctrl;
            public suspender(Control ctrl)
            {
                this._ctrl = ctrl;
                SendMessage(this._ctrl.Handle, WM_SETREDRAW, false, 0);
            }
            public void Dispose()
            {
                SendMessage(this._ctrl.Handle, WM_SETREDRAW, true, 0);
                this._ctrl.Refresh();
            }
        }
        #endregion

उपयोग:

using (this.BeginSuspendlock())
{
    //update GUI
}

4

यहाँ Ceztko's और ng5000 का संयोजन VB एक्सटेंशन वर्जन लाने के लिए है जो पिनवोक का उपयोग नहीं करता है

Imports System.Runtime.CompilerServices

Module ControlExtensions

Dim WM_SETREDRAW As Integer = 11

''' <summary>
''' A stronger "SuspendLayout" completely holds the controls painting until ResumePaint is called
''' </summary>
''' <param name="ctrl"></param>
''' <remarks></remarks>
<Extension()>
Public Sub SuspendPaint(ByVal ctrl As Windows.Forms.Control)

    Dim msgSuspendUpdate As Windows.Forms.Message = Windows.Forms.Message.Create(ctrl.Handle, WM_SETREDRAW, System.IntPtr.Zero, System.IntPtr.Zero)

    Dim window As Windows.Forms.NativeWindow = Windows.Forms.NativeWindow.FromHandle(ctrl.Handle)

    window.DefWndProc(msgSuspendUpdate)

End Sub

''' <summary>
''' Resume from SuspendPaint method
''' </summary>
''' <param name="ctrl"></param>
''' <remarks></remarks>
<Extension()>
Public Sub ResumePaint(ByVal ctrl As Windows.Forms.Control)

    Dim wparam As New System.IntPtr(1)
    Dim msgResumeUpdate As Windows.Forms.Message = Windows.Forms.Message.Create(ctrl.Handle, WM_SETREDRAW, wparam, System.IntPtr.Zero)

    Dim window As Windows.Forms.NativeWindow = Windows.Forms.NativeWindow.FromHandle(ctrl.Handle)

    window.DefWndProc(msgResumeUpdate)

    ctrl.Invalidate()

End Sub

End Module

4
मैं w / WPF ऐप पर काम कर रहा हूं, जिसमें wpf फॉर्म के साथ जुड़े हुए विफॉर्मफॉर्म का उपयोग किया जाता है, और स्क्रीन झिलमिलाहट से निपटने के लिए। मैं असमंजस में हूँ कि इस कोड का लाभ कैसे उठाया जाए - क्या यह विनफॉर्म या wpf विंडो में जाएगा? या यह मेरी विशेष स्थिति के लिए अनुकूल नहीं है?
नोकरईर

3

मुझे पता है कि यह एक पुराना प्रश्न है, पहले से ही उत्तर दिया गया है, लेकिन यहाँ इस पर मेरा विचार है; मैंने अपडेट के निलंबन को एक आइडीसोपायरी में बदल दिया - इस तरह मैं उन बयानों को संलग्न कर सकता हूं जिन्हें मैं एक usingबयान में चलाना चाहता हूं ।

class SuspendDrawingUpdate : IDisposable
{
    private const int WM_SETREDRAW = 0x000B;
    private readonly Control _control;
    private readonly NativeWindow _window;

    public SuspendDrawingUpdate(Control control)
    {
        _control = control;

        var msgSuspendUpdate = Message.Create(_control.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);

        _window = NativeWindow.FromHandle(_control.Handle);
        _window.DefWndProc(ref msgSuspendUpdate);
    }

    public void Dispose()
    {
        var wparam = new IntPtr(1);  // Create a C "true" boolean as an IntPtr
        var msgResumeUpdate = Message.Create(_control.Handle, WM_SETREDRAW, wparam, IntPtr.Zero);

        _window.DefWndProc(ref msgResumeUpdate);

        _control.Invalidate();
    }
}

2

यह और भी सरल है, और शायद हैकी - जैसा कि मैं इस धागे पर जीडीआई की बहुत सारी मांसपेशियों को देख सकता हूं , और स्पष्ट रूप से केवल कुछ परिदृश्यों के लिए एक अच्छा फिट है। YMMV

मेरे परिदृश्य में, मैं "माता-पिता" UserControl के रूप में संदर्भित करता हूं कि मैं इसका उपयोग करता हूं - और Loadघटना के दौरान , मैं बस माता-पिता के .Controlsसंग्रह से नियंत्रण-से-जोड़-तोड़ किया जा सकता हूं , और माता-पिता केOnPaint बच्चे को पूरी तरह से चित्रित करने का ख्याल रखते हैं जो भी विशेष तरीके से नियंत्रण .. पूरी तरह से बच्चे की पेंट क्षमताओं को ऑफ़लाइन लेना।

अब, मैं अपने बच्चे की दिनचर्या को एक विस्तृत विधि के लिए सौंपता हूं, जो कि विंडोज़ के रूपों को प्रिंट करने के लिए माइक गोल्ड से इस अवधारणा पर आधारित है ।

यहां मुझे लेआउट के लंबवत रेंडर करने के लिए लेबलों के उप-सेट की आवश्यकता है :

आपके विज़ुअल स्टूडियो आईडीई का सरल आरेख

फिर, मैं बच्चे को पेंट ParentUserControl.Loadहैंडलर में इस कोड के साथ पेंट करने से छूट देता हूं :

Private Sub ParentUserControl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    SetStyle(ControlStyles.UserPaint, True)
    SetStyle(ControlStyles.AllPaintingInWmPaint, True)

    'exempt this control from standard painting: 
    Me.Controls.Remove(Me.HostedControlToBeRotated) 
End Sub

फिर, एक ही ParentUserControl में, हम नियंत्रण-से-जोड़-तोड़ कर जमीन से ऊपर की ओर आते हैं:

Protected Overrides Sub OnPaint(e As PaintEventArgs)
    'here, we will custom paint the HostedControlToBeRotated instance...

    'twist rendering mode 90 counter clockwise, and shift rendering over to right-most end 
    e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
    e.Graphics.TranslateTransform(Me.Width - Me.HostedControlToBeRotated.Height, Me.Height)
    e.Graphics.RotateTransform(-90)
    MyCompany.Forms.CustomGDI.DrawControlAndChildren(Me.HostedControlToBeRotated, e.Graphics)

    e.Graphics.ResetTransform()
    e.Graphics.Dispose()

    GC.Collect()
End Sub

आप ParentUserControl कहीं की मेजबानी करने के बाद, उदाहरण के लिए एक Windows प्रपत्र - मैं खोजने कर रहा हूँ कि मेरी दृश्य स्टूडियो 2015 renders प्रपत्र सही ढंग से डिजाइन समय पर अच्छी तरह से क्रम के रूप में के रूप में: ParentUserControl एक विंडोज फॉर्म या शायद अन्य उपयोगकर्ता नियंत्रण में होस्ट किया गया है

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

अगर मेरे उद्देश्यपूर्ण अनाथ नियंत्रण के लिए हॉट स्पॉट और कंट्रोल-नेस को फिर से लागू करने के तरीके हैं - मुझे उस दिन के बारे में सीखना अच्छा लगेगा (इस परिदृश्य के लिए नहीं, निश्चित रूप से, लेकिन .. बस सीखने के लिए)। बेशक, WPF ऐसे पागलपन OOTB का समर्थन करता है .. लेकिन .. हे .. WinForms अभी भी बहुत मज़ा है, अमीर?


-4

या बस उपयोग Control.SuspendLayout()और Control.ResumeLayout()


9
लेआउट और पेंटिंग दो अलग-अलग चीजें हैं: लेआउट यह है कि उनके कंटेनर में बाल नियंत्रण कैसे व्यवस्थित होते हैं।
लैरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.