none
setwindowtext does not set control text RRS feed

  • Question

  • Hello,

    is there anybody who can explain me why the function below in the timer section will not update the labelDateTimePinvoke by calling

    the "SetWindowText(LabelPinvokeHandle, DateTime.Now.ToString());" function?

    There is no error reported but the label is not updated.

    The label labelDateTime is correct updated.

    Regards de-Kekse

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace TestFunctions
    {
        public partial class Form1 : Form
        {
            private System.ComponentModel.IContainer components = null;
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
            private void InitializeComponent()
            {
                this.labelDateTime = new System.Windows.Forms.Label();
                this.labelDateTimePinvoke = new System.Windows.Forms.Label();
                this.SuspendLayout();
                // 
                // labelDateTime
                // 
                this.labelDateTime.AutoSize = true;
                this.labelDateTime.Location = new System.Drawing.Point(52, 59);
                this.labelDateTime.Name = "labelDateTime";
                this.labelDateTime.Size = new System.Drawing.Size(51, 20);
                this.labelDateTime.TabIndex = 0;
                this.labelDateTime.Text = "label1";
                // 
                // labelDateTimePinvoke
                // 
                this.labelDateTimePinvoke.AutoSize = true;
                this.labelDateTimePinvoke.Location = new System.Drawing.Point(54, 150);
                this.labelDateTimePinvoke.Name = "labelDateTimePinvoke";
                this.labelDateTimePinvoke.Size = new System.Drawing.Size(51, 20);
                this.labelDateTimePinvoke.TabIndex = 1;
                this.labelDateTimePinvoke.Text = "label1";
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(800, 450);
                this.Controls.Add(this.labelDateTimePinvoke);
                this.Controls.Add(this.labelDateTime);
                this.Name = "Form1";
                this.Text = "Form1";
                this.ResumeLayout(false);
                this.PerformLayout();
    
            }
            private System.Windows.Forms.Label labelDateTime;
            private System.Windows.Forms.Label labelDateTimePinvoke;
    
            Timer TimeDate = new Timer();
    
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
            public static extern bool SetWindowText(IntPtr hWnd, string Text);
    
            IntPtr LabelHandle;
            IntPtr LabelPinvokeHandle;
            
            public Form1()
            {
                InitializeComponent();
    
                LabelHandle = labelDateTime.Handle;
                LabelPinvokeHandle = labelDateTimePinvoke.Handle;
    
                TimeDate.Tick += TimeDate_Tick;
                TimeDate.Interval = 500;
                TimeDate.Enabled = true;
            }
    
            private void TimeDate_Tick(object sender, EventArgs e)
            {
                TimeDate.Enabled = false;
    
                Control c = Control.FromHandle(LabelHandle);
                c.Text = DateTime.Now.ToString();
    
                bool Result = SetWindowText(LabelPinvokeHandle, DateTime.Now.ToString());
                if(!Result)
                {
                    int Error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                    Debug.Write("Error:" + Error);
                }
    
                TimeDate.Enabled = true;
            }
        }
    }
    

    • Moved by CoolDadTx Tuesday, June 30, 2020 7:30 PM Winforms related
    Tuesday, June 30, 2020 5:36 PM

Answers

  • Weird, I just copied your code and added labelDateTimePinvoke.Invalidate();

    and the text is updated correctly (VS 2015, .NET 4.7.2, Windows 10)

    (to avoid Refresh which does Invalidate + Update)
    • Marked as answer by de Kekse Wednesday, July 1, 2020 3:05 PM
    Tuesday, June 30, 2020 6:58 PM

All replies

  • Try using Charset = Charset.Unicode in the [DllImort] for SetWindowText
    Tuesday, June 30, 2020 6:03 PM
  • Hello RLWA32,

    it does not matter if i user Ansi or Unicode.

    But one thing i found out inbetween. If i minimize the window and maximize it again it will show up with a part of the timestamp.

    with control from Handle the full time stamp

    with setwindowtext it shows only "30-Jun" like picture.

    EDIT:

    Removed the autosize property from both labels.

    now the label with setwindowtext shows the full text, but only after minimizing and maximizing the window again....



    • Edited by de Kekse Tuesday, June 30, 2020 6:32 PM
    Tuesday, June 30, 2020 6:20 PM
  • Add (after SetWindowText) :

    labelDateTimePinvoke.Invalidate();

    (and AutoSize=false)
    • Edited by Castorix31 Tuesday, June 30, 2020 6:25 PM
    Tuesday, June 30, 2020 6:23 PM
  • Hello Castrorix31

    labelDateTimePinvoke.Refresh() helped and paints it.

    i was expecting that labelDateTimePinvoke.Invalidate() does it but it did nothing.

    So i think finally the question for me should be what is additionaly required to the setwindowtext function to force a repaint of the label?

    The reason i am doing this is to save a few ticks. The sendwindowtext is a small bit faster than the control.fromhandle. And i have to update a big bunch of labels in a short time.

    But if it is required to call the .Refresh()  after setwindowtext its the same time consumption and it makes no sense to modify it.

    Any idea on this?

    Thanks

    de-Kekse

    Tuesday, June 30, 2020 6:49 PM
  • Weird, I just copied your code and added labelDateTimePinvoke.Invalidate();

    and the text is updated correctly (VS 2015, .NET 4.7.2, Windows 10)

    (to avoid Refresh which does Invalidate + Update)
    • Marked as answer by de Kekse Wednesday, July 1, 2020 3:05 PM
    Tuesday, June 30, 2020 6:58 PM
  • Hi de Kekse,
    As Castorix31 said, you can also use labelDateTimePinvoke.Invalidate() to achieve the requirement.
    Based on your code, I made a test and just change the 

     this.labelDateTimePinvoke.AutoSize = true;
     this.labelDateTimePinvoke.Size = new System.Drawing.Size(51, 20);

    to

    this.labelDateTimePinvoke.AutoSize = false;
    this.labelDateTimePinvoke.Size = new System.Drawing.Size(180, 20);

    And add the following code:

    bool Result = SetWindowText(LabelPinvokeHandle, DateTime.Now.ToString());
    labelDateTimePinvoke.Invalidate();

    The result:


    Best Regards,
    Daniel Zhang


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, July 1, 2020 2:33 AM
  • If you need simplest labels that respond to SetWindowText and WM_SETTEXT but do not include features of Label control, then consider a custom control like this:

    public class MyLabel : Control

    {

       protected override CreateParams CreateParams

       {

          get

          {

             CreateParams p = base.CreateParams;

             p.ClassName = "STATIC";

             return p;

          }

       }

    }


    It can be added programmatically or from Toolbox.


    To set the font, execute this in Form_Load:

    SendMessage( myLabel1.Handle, WM_SETFONT, Font.ToHfont( ), IntPtr.Zero );

    where:

    const uint WM_SETFONT = 0x0030;

    [DllImport( "user32" )]

    static extern IntPtr SendMessage( IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam );


    By the way, it seems that WM_SETTEXT, in contrast with SetWindowText, can be used from other threads (if you plan to use threads instead of timers).


    • Edited by Viorel_MVP Wednesday, July 1, 2020 8:09 AM
    Wednesday, July 1, 2020 8:04 AM
  • @ Castorix31, you where definetely right. It was my fault because i invalidated the form and not the label. Your solution works. Thanks. You where the first that directed me to my mistyping and pointed me to the solution. I will mark this as answer.

    @ Daniel_Zhang, same solution than Castorix31 Thank you also for your help.

    @ Viorel, its a very intersting point to remove everything not needed in the label control and create a own one. I will try it in the next few days and show up with the result. And i plan to use threads, the timer is just a test to compare the runtime.

    Here the working test code for now until i have time to implement Viorel's idea. I implemented a winner looser for both calls.

    The amazing thing i do not understand at the moment is if i set the timer to a interval below 500ms the update from handle is the winner after a couple (10..20) of cycles. If i set the timer above 500ms the update with pinvoke is always the winner.

    This tells me that i should use the control.fromhandle on high frequent updates???

    There is nothing really that explains this behavior for me actually and i would be really appreciated for any comment/idea that brings me forward to decide for the fastest update.

    Thanks to all

    de-Kekse

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace TestFunctions
    {
        public partial class Form1 : Form
        {
            [Flags()]
            private enum RedrawWindowFlags : uint
            {
                /// <summary>
                /// Invalidates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
                /// You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_INVALIDATE invalidates the entire window.
                /// </summary>
                Invalidate = 0x1,
    
                /// <summary>Causes the OS to post a WM_PAINT message to the window regardless of whether a portion of the window is invalid.</summary>
                InternalPaint = 0x2,
    
                /// <summary>
                /// Causes the window to receive a WM_ERASEBKGND message when the window is repainted.
                /// Specify this value in combination with the RDW_INVALIDATE value; otherwise, RDW_ERASE has no effect.
                /// </summary>
                Erase = 0x4,
    
                /// <summary>
                /// Validates the rectangle or region that you specify in lprcUpdate or hrgnUpdate.
                /// You can set only one of these parameters to a non-NULL value. If both are NULL, RDW_VALIDATE validates the entire window.
                /// This value does not affect internal WM_PAINT messages.
                /// </summary>
                Validate = 0x8,
    
                NoInternalPaint = 0x10,
    
                /// <summary>Suppresses any pending WM_ERASEBKGND messages.</summary>
                NoErase = 0x20,
    
                /// <summary>Excludes child windows, if any, from the repainting operation.</summary>
                NoChildren = 0x40,
    
                /// <summary>Includes child windows, if any, in the repainting operation.</summary>
                AllChildren = 0x80,
    
                /// <summary>Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND and WM_PAINT messages before the RedrawWindow returns, if necessary.</summary>
                UpdateNow = 0x100,
    
                /// <summary>
                /// Causes the affected windows, which you specify by setting the RDW_ALLCHILDREN and RDW_NOCHILDREN values, to receive WM_ERASEBKGND messages before RedrawWindow returns, if necessary.
                /// The affected windows receive WM_PAINT messages at the ordinary time.
                /// </summary>
                EraseNow = 0x200,
    
                Frame = 0x400,
    
                NoFrame = 0x800
            }
    
    
            private System.ComponentModel.IContainer components = null;
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
            private void InitializeComponent()
            {
                this.labelDateTime = new System.Windows.Forms.Label();
                this.labelDateTimePinvoke = new System.Windows.Forms.Label();
                this.SuspendLayout();
                // 
                // labelDateTime
                // 
                this.labelDateTime.Location = new System.Drawing.Point(52, 59);
                this.labelDateTime.Name = "labelDateTime";
                this.labelDateTime.Size = new System.Drawing.Size(509, 34);
                this.labelDateTime.TabIndex = 0;
                this.labelDateTime.Text = "label1";
                // 
                // labelDateTimePinvoke
                // 
                this.labelDateTimePinvoke.Location = new System.Drawing.Point(54, 150);
                this.labelDateTimePinvoke.Name = "labelDateTimePinvoke";
                this.labelDateTimePinvoke.Size = new System.Drawing.Size(507, 36);
                this.labelDateTimePinvoke.TabIndex = 1;
                this.labelDateTimePinvoke.Text = "label1";
                // 
                // Form1
                // 
                this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
                this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                this.ClientSize = new System.Drawing.Size(800, 450);
                this.Controls.Add(this.labelDateTimePinvoke);
                this.Controls.Add(this.labelDateTime);
                this.Name = "Form1";
                this.Text = "Form1";
                this.ResumeLayout(false);
    
            }
            private System.Windows.Forms.Label labelDateTime;
            private System.Windows.Forms.Label labelDateTimePinvoke;
            private System.Diagnostics.Stopwatch TimeLabelDateTime = new Stopwatch();
            private System.Diagnostics.Stopwatch TimeLabelDateTimePinvoke = new Stopwatch();
            private long TimeLabelDateTimeSum = 0;
            private long TimeLabelDateTimePinvokeSum = 0;
            private long TimeDateExecutions = 0;
    
            private Timer TimeDate = new Timer();
    
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool SetWindowText(IntPtr hWnd, string Text);
    
            [DllImport("user32.dll")]
            static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, RedrawWindowFlags flags);
    
            IntPtr LabelHandle;
            IntPtr LabelPinvokeHandle;
            
            public Form1()
            {
                InitializeComponent();
    
                LabelHandle = labelDateTime.Handle;
                LabelPinvokeHandle = labelDateTimePinvoke.Handle;
    
                TimeDate.Tick += TimeDate_Tick;
                TimeDate.Interval = 500;
                TimeDate.Enabled = true;
            }
    
            private void TimeDate_Tick(object sender, EventArgs e)
            {
                TimeDate.Enabled = false;
    
                TimeLabelDateTime.Start();
                
                Control c = Control.FromHandle(LabelHandle);
                c.Text = DateTime.Now.ToString();
                
                TimeLabelDateTime.Stop();
                TimeLabelDateTimeSum += TimeLabelDateTime.ElapsedTicks;            
                Debug.WriteLine(System.String.Format("Update FromHandle       = {0}",TimeLabelDateTimeSum.ToString("D20")));
    
                TimeLabelDateTimePinvoke.Start();
    
                bool Result = SetWindowText(LabelPinvokeHandle, DateTime.Now.ToString());
                // removed bool Result = from setwindowtext (not nice but there is also no
                // return value from c.Text.
                //if(!Result)
                //{
                //    int Error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
                //    Debug.Write("Error:" + Error);
                //}
                RedrawWindow(LabelPinvokeHandle, IntPtr.Zero, IntPtr.Zero, RedrawWindowFlags.Invalidate);
    
                TimeLabelDateTimePinvoke.Stop();
                TimeLabelDateTimePinvokeSum += TimeLabelDateTimePinvoke.ElapsedTicks;
                Debug.WriteLine(System.String.Format("Update Pinvoke          = {0}", TimeLabelDateTimePinvokeSum.ToString("D20")));
    
                TimeDateExecutions += 1;
                Debug.WriteLine(System.String.Format("Excecutions             = {0}", TimeDateExecutions.ToString("D20")));
    
                if (TimeLabelDateTimePinvokeSum > TimeLabelDateTimeSum)
                    Debug.WriteLine(System.String.Format("Winner FromHandle Delta = {0}", (TimeLabelDateTimePinvokeSum - TimeLabelDateTimeSum).ToString("D20")));
                else if (TimeLabelDateTimePinvokeSum == TimeLabelDateTimeSum)
                    Debug.WriteLine("No Winner");
                else
                    Debug.WriteLine(System.String.Format("Winner Pinvoke Delta    = {0}", (TimeLabelDateTimeSum - TimeLabelDateTimePinvokeSum).ToString("D20")));
    
                TimeDate.Enabled = true;
            }
        }
    }
    

    Wednesday, July 1, 2020 3:05 PM