Why is ListView rendering so slow for certain characters? - c#

Update 1: I've written both a MFC-C++ implementation and an old-school Win32 app and recorded a video demonstrating how bad the issue really is:
https://www.youtube.com/watch?v=f0CQhQ3GgAM
Since the old-school Win32 app is not exhibiting this issue, this leads me to believe that C# and MFC both use the same rendering API that must cause this issue (basically discharging my suspicion that the problem might be at the OS / graphics driver level).
Original post:
While having to display some REST data inside a ListView, I encountered a very peculiar problem:
For certain inputs, the ListView rendering would literally slow to a crawl while scrolling horizontally.
On my system and with the typical subclassed ListView with "OptimizedDoubleBuffer", having a mere 6 items in a ListView will slow down rendering during scrolling to the point that i can see the headers "swimming", i.e. the rendering of the items and headers during the scrolling mismatches.
For a regular non-subclassed ListView with 10 items, I can literally see each item being drawn separately while scrolling (the repainting takes around 1-2s).
Here's example code (and yes, I am aware that these look like bear and butterfly emotes; this issue was found from user-provided data, after all):
using System;
using System.Windows.Forms;
namespace SlowLVRendering
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
const string slow = "ヽ( ´。㉨°)ノ Ƹ̴Ӂ̴Ʒ~ ღ ( ヽ( ´。㉨°)ノ ༼ つ´º㉨º ༽つ ) (」゚ペ)」ヽ( ´。㉨°)ノ Ƹ̴Ӂ̴Ʒ~ ღ ( ヽ( ´。㉨°)ノ ༼ つ´º㉨º ༽つ ) (」゚ペ)」";
ListView lv = new ListView();
lv.Dock = DockStyle.Fill;
lv.View= View.Details;
for (int i = 0; i < 2; i++) lv.Columns.Add("Title "+i, 500);
for (int i = 0; i < 10; i++)
{
var lvi = lv.Items.Add(slow);
lvi.SubItems.Add(slow);
}
Controls.Add(lv);
}
}
}
Can someone explain what the issue is, and how to resolve it?

After trying a few different things, here is the fastest solution I found. There is still a little hesitation, but not anywhere near as your original solution. Until Microsoft decides to use something better than GDI+, it doesn't get better unless you go to WPF with .NET 4 and above. Oh well.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SlowLVRendering
{
[System.Runtime.InteropServices.DllImport("user32")]
private static extern bool SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
private uint LVM_SETTEXTBKCOLOR = 0x1026;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.Load += new System.EventHandler(this.Form1_Load);
}
private void Form1_Load(object sender, EventArgs e)
{
const string slow = "ヽ( ´。㉨°)ノ Ƹ̴Ӂ̴Ʒ~ ღ ( ヽ( ´。㉨°)ノ ༼ つ´º㉨º ༽つ ) (」゚ペ)」ヽ( ´。㉨°)ノ Ƹ̴Ӂ̴Ʒ~ ღ ( ヽ( ´。㉨°)ノ ༼ つ´º㉨º ༽つ ) (」゚ペ)」";
ListView lv = new BufferedListView();
// new ListView();
//new ListViewWithLessSuck();
lv.Dock = DockStyle.Fill;
lv.View = View.Details;
for (int i = 0; i < 2; i++) lv.Columns.Add("Title " + i, 500);
for (int i = 0; i < 10; i++)
{
var lvi = lv.Items.Add(slow);
lvi.SubItems.Add(slow);
}
Controls.Add(lv);
//SendMessage(lv.Handle, LVM_SETTEXTBKCOLOR, IntPtr.Zero, unchecked((IntPtr)(int)0xFFFFFF));
}
}
public class BufferedListView : ListView
{
public BufferedListView()
: base()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
class ListViewWithLessSuck : ListView
{
[StructLayout(LayoutKind.Sequential)]
private struct NMHDR
{
public IntPtr hwndFrom;
public uint idFrom;
public uint code;
}
private const uint NM_CUSTOMDRAW = unchecked((uint)-12);
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x204E)
{
NMHDR hdr = (NMHDR)m.GetLParam(typeof(NMHDR));
if (hdr.code == NM_CUSTOMDRAW)
{
m.Result = (IntPtr)0;
return;
}
}
base.WndProc(ref m);
}
}
You can see for yourself the difference. If you uncomment the SendMessage and change new BufferedListView(); to new ListViewWithLessSuck(); You can see the change.

I believe I have narrowed the problem down to Visual Styles. Commenting out Application.EnableVisualStyles(); in static void Main results in a huge performance boost during scrolling, though nowhere near the performance of the Win32 app as shown in the video that I mentioned in Update 1.
The downside of this of course is that all controls in your application will look "old". I've therefore experimented with selectively disabling / enabling of visual styles through
[DllImport("uxtheme", ExactSpelling = true, CharSet = CharSet.Unicode)]
public extern static Int32 SetWindowTheme(IntPtr hWnd, String textSubAppName, String textSubIdList);
and using Win32.SetWindowTheme(lv.Handle, " ", " "); as described in the MSDN docs. The most logical thing would be to keep Visual Styles active for most of the controls and turn if off for performance critical ones. However, a part of the ListView seems to deliberately ignore whether visual styles are disabled or enabled, namely the column headers of the listview in report mode:
(Note how the column header looks in comparison to the scroll bars)
So unless someone knows how to force visual styles off on listview column headers, this is a "pick your poison" kind of situation: Either comment out Application.EnableVisualStyles(); and have an ugly looking UI or leave it in and risk unpredictable renderer slowdowns.
If you go for the first choice, you can get another huge performance boost by subclassing the ListView and short-circuiting the WM_REFLECT_NOTIFY message (thanks to SteveFerg for the original):
public class ListViewWithoutReflectNotify : ListView
{
[StructLayout(LayoutKind.Sequential)]
private struct NMHDR
{
public IntPtr hwndFrom;
public uint idFrom;
public uint code;
}
private const uint NM_CUSTOMDRAW = unchecked((uint) -12);
public ListViewWithoutReflectNotify()
{
}
protected override void WndProc(ref Message m)
{
// WM_REFLECT_NOTIFY
if (m.Msg == 0x204E)
{
m.Result = (IntPtr)0;
return;
//the if below never was true on my system so i 'shorted' it
//delete the 2 lines above if you want to test this yourself
NMHDR hdr = (NMHDR) m.GetLParam(typeof (NMHDR));
if (hdr.code == NM_CUSTOMDRAW)
{
Debug.WriteLine("Hit");
m.Result = (IntPtr) 0;
return;
}
}
base.WndProc(ref m);
}
}
Disabling visual styles and subclassing allow for rendering speeds nearly on par of that of the Win32 C app. However, I do not fully understand the potential ramifications of shorting WM_REFLECT_NOTIFY, so use with care.
I've also checked the Win32 app and confirmed that you can literally kill the rendering performance of your app simply by adding a manifest, for example like so:
// enable Visual Styles
#pragma comment( linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' \
language='*'\"")

Related

Updating UI from NFC-Tag-Reader event handler is very slow

I have an ACR122U NFC reader where I read MiFare UltraLight NFC tags. I downloaded a framework for my reader. However when the event handler is called, the update of the GUI is very slow.
I have not much experience in threaded programming and event handling. Maybe the solution for my question is very easy.
However it only updates 'label by label' in the UpdateInterface()-method and one all by once. I can see each label disappearing. So the updating of the UI is very slow.
I guess this has something to do with the (hardware-)events called by the reader-classes in background.
The program is generally working only it is very slow.
private void StartMonitor()
{
//Function called when starting the Windows Forms Application
IMonitorFactory monitorfactory = MonitorFactory.Instance;
monitor = monitorfactory.Create(SCardScope.System);
monitor.Start(ReaderNames[0]);
monitor.StatusChanged += Monitor_StatusChanged;
}
private void Monitor_StatusChanged(object sender, StatusChangeEventArgs e)
{
strPassportNo = "";
strPassportNo = lPassportNumberNo.Text;
if (e.NewState.ToString().ToLower().Contains("empty".ToLower()) == true)
{
//Interface should be deleted all by once now as no NFC tag is on the reader.
UpdateInterface();
}
}
private void UpdateInterface()
{
if (InvokeRequired)
{
this.BeginInvoke(new Action(() =>
{
lPassportNumberNo.Text = "";
lPassengerName.Text = "";
lPax.Text = "";
lTableNo.Text = "";
lRoomNo.Text = "";
pbTables.Hide();
pbPasspic.Hide();
this.BackgroundImage = BackgroundWelcome;
}));
}
else
{
lPassportNumberNo.Text = "";
lPassengerName.Text = "";
lPax.Text = "";
lTableNo.Text = "";
lRoomNo.Text = "";
pbTables.Hide();
pbPasspic.Hide();
this.BackgroundImage = BackgroundWelcome;
}
}
The expected output should be that the performance of the UI is a lot faster than it is actually. Or at least the labels with data in it should appear and disappear at once.
Not sure what is this actually: this.BackgroundImage = BackgroundWelcome; but is it really necessary to assign such static data every time?
Also i dont think that frequency of your update event is too high - information on the screen should be human-readable so it's definitely only one update of all data required after the entire tag is readed. If there are many read events for every single tag - then store all of them into temporary list and update the form at the read end (entire tag).
When i say "tag" i mean the entire mifare card content.
The UI-Update was in my case to just clear the labels (see UpdateInterface()). However I found the "error". It was the ".jpg" file als BackgroundImage I loaded on every refresh. Although it was "just" 1280x1024, it was too much for the frontend to update.
When I cleared the Images, the refresh was immediately.
My solution now is:
this.BackgroundImage = ((System.Drawing.Image(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.SetStyle(System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer | System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true);
in the Form1.Designer.cs
and Suspending and Resuming the Layout with the following class (which I found somewhere on the Internet)
public static class ControlHelper
{
#region Redraw Suspend/Resume
[DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private const int WM_SETREDRAW = 0xB;
public static void SuspendDrawing(this Control target)
{
SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
}
public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
public static void ResumeDrawing(this Control target, bool redraw)
{
SendMessage(target.Handle, WM_SETREDRAW, 1, 0);
if (redraw)
{
target.Refresh();
}
}
#endregion
}
Also I changed the ".jpg" files to ".bmp" files. Now the Interface is smooth and fast.
And #Jesting:
Not sure what is this actually: this.BackgroundImage = BackgroundWelcome; but is it really necessary to assign such static data every time?
No, I could have found a better solution. However as the application is very small and I only have four background images, I found it easier to "hard code" it.
Finally, the tag(-reader) was not really the problem.
And for updating the UI, I use this quite easy function:
private void UpdateInterface(PassportDatabaseReader.Passport _passport)
{
if (InvokeRequired)
{
this.BeginInvoke(new Action<PassportDatabaseReader.Passport>(UpdateInterface), new object[] { _passport });
return;
}
else
{
ControlHelper.SuspendDrawing(this);
new Thread(() => ChangeBackground(BackgroundPassport)).Start();
lPassportNumberNo.Text = _passport.PassportNo;
strPassportNo = _passport.PassportNo;
lPassengerName.Text = _passport.Name.Replace('$','\n');
lPax.Text = _passport.Pax;
lTableNo.Text = _passport.TableNo;
lRoomNo.Text = _passport.RoomNo;
pbTables.Hide();
try
{
pbPasspic.Load("..\\..\\Pics\\" + _passport.PassportNo + ".png");
pbPasspic.Show();
pbTables.ImageLocation = "..\\..\\Tischordnung_" + _passport.TableNo + ".png";
pbTables.Show();
}
catch (Exception)
{
throw;
}
ControlHelper.ResumeDrawing(this);
}
}
The application is a "wedding passport" reader which shows the name, the guests room number, table number as well as the "passport" picture on screen.

Hyperlinks without underline in RichTextBox on Windows 10 1803

I'm displaying RTF document in RichTextBox ("upgraded" to RichEdit50W). Keywords in the document are linked to a webpage using a syntax:
{\field{\*\fldinst{HYPERLINK ""https://www.example.com/"" }}{\fldrslt{\cf1 keyword\cf0 }}}
I do not want to underline the keywords. Until Windows 10 version 1803 (and in all previous versions of Windows, including XP, Vista, 8), whenever a color was set on the anchor (note the \cf1), the anchor was not underlined.
But this no longer works in Windows 10 version 1803. I'm going to report this to Microsoft. But I'm not really sure, if I was not relying on an undocumented behavior. I can imagine that this change is actually not a bug, but rather a fix. So I wonder whether there is not a more correct way to prevent hyperlinks from being underlined.
Sample code:
public class ExRichText : RichTextBox
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr LoadLibraryW(string path);
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
LoadLibraryW("MsftEdit.dll");
cp.ClassName = "RichEdit50W";
return cp;
}
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ExRichText rtb = new ExRichText();
rtb.Parent = this;
rtb.SetBounds(10, 10, 200, 100);
rtb.Rtf = #"{\rtf1 {\colortbl ;\red255\green0\blue0;}bar {\field{\*\fldinst{HYPERLINK ""https://www.example.com/"" }}{\fldrslt{\cf1 link\cf0 }}} bar}";
}
}
(unwanted) result on Windows 10 version 1803:
(desired) result on Windows 10 version 1706:
and the same result on Windows 7:
For Windows 8 and above, you can use the SendMessage function to send the EM_SETEDITSTYLEEX message to richedit control to disable the underlining of friendly links by specifying SES_EX_HANDLEFRIENDLYURL for the lParam argument and zero for the wParam` argument.
SES_EX_HANDLEFRIENDLYURL
Display friendly name links with the same text color and underlining as automatic links, provided that temporary formatting isn’t used or uses text autocolor (default: 0).
Even though the underlining is supposedly disabled by default, the RichTextBox control has it enabled.
Add the following to your ExRichText class to provide a method (UnderlineFriendlyLink) to disable the underlining.
private const Int32 WM_User = 0x400;
private const Int32 EM_SETEDITSTYLEEX = WM_User + 275;
private const Int32 EM_GETEDITSTYLEEX = WM_User + 276;
/// <summary>Display friendly name links with the same text color and underlining as automatic links, provided that temporary formatting isn’t used or uses text autocolor (default: 0)</summary>
private const Int32 SES_EX_HANDLEFRIENDLYURL = 0x100;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private extern static Int32 SendMessage(HandleRef hWnd, Int32 msg, Int32 wParam, Int32 lParam);
public static void UnderlineFriendlyLink(RichTextBox rtb, bool enabled = false)
{
if (rtb.IsHandleCreated)
{
Int32 wParam = enabled ? SES_EX_HANDLEFRIENDLYURL : 0;
Int32 lParam = SES_EX_HANDLEFRIENDLYURL; // settings mask
Int32 res = SendMessage(new HandleRef(null, rtb.Handle), EM_SETEDITSTYLEEX, wParam, lParam);
}
}
Example usage:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
exRichText1.Rtf = #"{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}{\colortbl ;\red0\green0\blue255;}{\*\generator Riched20 10.0.16299}\viewkind4\uc1 \pard\f0\fs29 Hello {\b{\field{\*\fldinst{HYPERLINK ""http://www.fred.com""}}{\fldrslt{Link}}}}\f0\fs29\par\par https://www.google.com \par\par sd {{\field{\*\fldinst{HYPERLINK ""http://www.fred.com""}}{\fldrslt{klasl}}}}\f0\fs29 wed asdasd \par\par}";
}
private void exRichText1_LinkClicked(object sender, LinkClickedEventArgs e)
{
System.Diagnostics.Debug.Print(e.LinkText);
}
private void button1_Click(object sender, EventArgs e)
{
ExRichText.UnderlineFriendlyLink(exRichText1, false);
}
}
Your post did not indicate how you are detecting the clicking of the links, but be advised that if you are relying on the LinkClicked event as shown in the above example that event may not fire due to a logic bug in the RichTextBox CharRangeToString method. In particular this code fragment.
//Windows bug: 64-bit windows returns a bad range for us. VSWhidbey 504502.
//Putting in a hack to avoid an unhandled exception.
if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) {
return string.Empty;
}
If you try the sample code, you will notice that the event fires only for the first link. If CharRangeToString returns String.Empty, the event is not raised. This limiting condition uses the Text.Length property (58 for the example) that returns the as displayed length. I believe that it should instead use the TextLength property (120 for the example).
By monitoring the control's parent for the EM_Notify message and processing the mouse click notification, it is possible to extract the link using the CharRange structure when compiling for x86 or AnyCPU(prefer 32-bit). When running as a 64-bit assembly, the CharRange structure does return invalid values as indicated in the source code.
Edit: All testing was done on Windows 10 version 1706 as I will not install 1803 at this time.

Scrolling list view when another list view is scrolled

in my win forms app, I am using two List views to compare two files.
when user selects two files using folder browser, files are loaded in List views.
I compare the files and lines that are not matching are shown with different color.
this works absolutely fine.
now when I scroll one List view, I want the other List view should also be scrolled with same amount.
I tried but you cannot set Horizontal scroll or vertical scroll property of a Listview.
how can I do this ?
thanks in advance.
you need to create a custom List view so that you can detect it scrolling and pass the scroll message to the other text box so it will scroll in sync.
class SyncListView: ListView
{
public SyncListView()
{
}
public Control Buddy { get; set; }
private static bool scrolling; // In case buddy tries to scroll us
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// Trap WM_VSCROLL message and pass to buddy
if ((m.Msg == 0x115 || m.Msg == 0x20a) && !scrolling && Buddy != null && Buddy.IsHandleCreated)
{
scrolling = true;
SendMessage(Buddy.Handle, m.Msg, m.WParam, m.LParam);
scrolling = false;
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
I had similar task. Two lists that must show theirs items next to each-other.
I found this thread with the Iorn Man's answer, that looked too difficult for me, because I did not have enough experience in C#.
I've found an easier solution for that.
I added a timer to a form. For its tick event, I added this:
Form active_form = Form.ActiveForm;
if (active_form == null) return;
Control control = Form.ActiveForm.ActiveControl;
if (control == newFilesList)
{
Sync_lists(newFilesList);
}
else
{
Sync_lists(oldFilesList);
}
It checks which list is active and calls Sync_list routine with this list as argument.
private void Sync_lists(ListView sender)
{
if ((newFilesList.Items.Count > 0) && (oldFilesList.Items.Count > 0))
{
int cur_top_index = sender.TopItem.Index;
ListViewItem future_top_item;
if (sender == oldFilesList)
{
future_top_item = newFilesList.Items[cur_top_index];
newFilesList.TopItem = future_top_item;
}
else
{
future_top_item = oldFilesList.Items[cur_top_index];
oldFilesList.TopItem = future_top_item;
}
}
}
It just get TopItem property of the base list and sets item with the same index as a top for another list.
It is not so right as a custom ListView. But a little bit simpler. Hope it'll help.
You can also do this in Better ListView or Better ListView Express using only managed code:
public class CustomListView : BetterListView
{
public void SynchronizeScroll(BetterListView listView)
{
VScrollBar.Value = listView.VScrollProperties.Value;
}
}
then handling its VScrollPropertiesChanged event with something like this:
private void ListViewVScrollPropertiesChanged(object sender, BetterListViewScrollPropertiesChangedEventArgs eventArgs)
{
CustomListView listViewThis = (sender as CustomListView);
listViewThis.SynchronizeScroll(this.listViewAnother);
}

Problem with SendMessage

I am developing an application using C# having similar functionality of copy,paste as in Windows.
I have added menu items and linked with respective applications.
Please look at the following image for getting more idea.
Items added to shell menu http://softwaregenius.net/myimages/menu.jpg
Like we select multiple items in windows explorer, you need to select multiple files and/or folders and then select OS Util->FastCopy. A form is opened as shown below
Form shown on FastCopy http://softwaregenius.net/myimages/fastcopy1.jpg
The application is working perfectly. The major problem here is that after selecting the files all these files are opening up within there respective softwares. That is if i selected word document then the filename is added to FastCopy form but the is also opening up within Word also.
When i investigate i found that this problem is due to SendMessage. I have to use PostMessage instead of SendMessage. But when i do so the application is not working.
Below is my Main function coding in C# 2005
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE92}");
[STAThread]
static void Main(string[] args)
{
string fileName = "";
if (args.Length > 0)
{
fileName = args[0];
}
if (mutex.WaitOne(TimeSpan.Zero, true))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
frmFastCopy frm = new frmFastCopy();
frm.AddItemToList(fileName);
Application.Run(frm);
}
else
{
//The following message is sent just to show up the form
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
//Send the filename
SendFileName(fileName);
}
}
static void SendFileName(string s)
{
Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
cds.cbData = (s.Length + 1) * 2;
cds.lpData = Win32.LocalAlloc(0x40, cds.cbData);
Marshal.Copy(s.ToCharArray(), 0, cds.lpData, s.Length);
cds.dwData = (IntPtr)1;
Win32.SendMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
//NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, cds.lpData, IntPtr.Zero);
}
}
}
Below is the copy for WndProc and other code from within the Form
public partial class frmFastCopy : Form
{
delegate void AddItemToListDelegate(string itm);
public frmFastCopy()
{
InitializeComponent();
}
public void AddItemToList(string itm)
{
if (lvFilesAndFolders.InvokeRequired)
{
AddItemToListDelegate m = new AddItemToListDelegate(AddItemToList);
this.Invoke(m, new object[] { itm });
}
else
{
lvFilesAndFolders.Items.Add(itm);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg==NativeMethods.WM_SHOWME)
{
ShowMe();
}
if (m.Msg==Win32.WM_COPYDATA)
{
//string s = Marshal.PtrToStringUni(m.LParam);
MessageBox.Show("Got message");
Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
string strData = Marshal.PtrToStringUni(st.lpData);
AddItemToList(strData);
}
base.WndProc(ref m);
}
private void ShowMe()
{
this.Show();
if (WindowState == FormWindowState.Minimized)
{
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
Here is the NativeCode class
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
I know you guys are genius. Could someone tell me where should i make changes to that the selected files should be opened or rather how i should use postmessage.
Thanks for sharing your valuable time.
Regards
Irfan
Please look at my comment (I wonder why you don't use the Clipboard class here). But ignoring that: Why do you broadcast the message?
Can you locate your application (by name, window class, whatever) and only send the message to your own application?
To elaborate on the message handling:
You say regarding HWND_BROADCAST in the comments below:
Thats nothing but the global handle to
my application.
No, it's not. It is a special value that tells Windows "this message is for all applications". You are sending a WM_SHOWME to all applications. Which is why I asked why you would want to do that?
Please see this post on the old new things blog regarding message broadcasts.

How to make a window always stay on top in .Net?

I have a C# winforms app that runs a macro in another program. The other program will continually pop up windows and generally make things look, for lack of a better word, crazy. I want to implement a cancel button that will stop the process from running, but I cannot seem to get the window to stay on top. How do I do this in C#?
Edit: I have tried TopMost = true; , but the other program keeps popping up its own windows over top. Is there a way to send my window to the top every n milliseconds?
Edit: The way I solved this was by adding a system tray icon that will cancel the process by double-clicking on it. The system tray icon does no get covered up. Thank you to all who responded. I read the article on why there is not a 'super-on-top' window... it logically does not work.
Form.TopMost will work unless the other program is creating topmost windows.
There is no way to create a window that is not covered by new topmost windows of another process. Raymond Chen explained why.
I was searching to make my WinForms application "Always on Top" but setting "TopMost" did not do anything for me. I knew it was possible because WinAmp does this (along with a host of other applications).
What I did was make a call to "user32.dll." I had no qualms about doing so and it works great. It's an option, anyway.
First, import the following namespace:
using System.Runtime.InteropServices;
Add a few variables to your class declaration:
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
Add prototype for user32.dll function:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
Then in your code (I added the call in Form_Load()), add the call:
SetWindowPos(this.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
[Reference][1]
[1]: http://www.c-sharpcorner.com/uploadfile/kirtan007/make-form-stay-always-on-top-of-every-window/
If by "going crazy" you mean that each window keeps stealing focus from the other, TopMost will not solve the problem.
Instead, try:
CalledForm.Owner = CallerForm;
CalledForm.Show();
This will show the 'child' form without it stealing focus. The child form will also stay on top of its parent even if the parent is activated or focused. This code only works easily if you've created an instance of the child form from within the owner form. Otherwise, you might have to set the owner using the API.
Set Form.TopMost
I had a momentary 5 minute lapse and I forgot to specify the form in full like this:
myformName.ActiveForm.TopMost = true;
But what I really wanted was THIS!
this.TopMost = true;
Set the form's .TopMost property to true.
You probably don't want to leave it this way all the time: set it when your external process starts and put it back when it finishes.
The way i solved this was by making a system tray icon that had a cancel option.
Why not making your form a dialogue box:
myForm.ShowDialog();
The following code makes the window always stay on top as well as make it frameless.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StayOnTop
{
public partial class Form1 : Form
{
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
public Form1()
{
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
TopMost = true;
}
private void Form1_Load(object sender, EventArgs e)
{
SetWindowPos(this.Handle, HWND_TOPMOST, 100, 100, 300, 300, TOPMOST_FLAGS);
}
protected override void WndProc(ref Message m)
{
const int RESIZE_HANDLE_SIZE = 10;
switch (m.Msg)
{
case 0x0084/*NCHITTEST*/ :
base.WndProc(ref m);
if ((int)m.Result == 0x01/*HTCLIENT*/)
{
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
if (clientPoint.Y <= RESIZE_HANDLE_SIZE)
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)13/*HTTOPLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)12/*HTTOP*/ ;
else
m.Result = (IntPtr)14/*HTTOPRIGHT*/ ;
}
else if (clientPoint.Y <= (Size.Height - RESIZE_HANDLE_SIZE))
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)10/*HTLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)2/*HTCAPTION*/ ;
else
m.Result = (IntPtr)11/*HTRIGHT*/ ;
}
else
{
if (clientPoint.X <= RESIZE_HANDLE_SIZE)
m.Result = (IntPtr)16/*HTBOTTOMLEFT*/ ;
else if (clientPoint.X < (Size.Width - RESIZE_HANDLE_SIZE))
m.Result = (IntPtr)15/*HTBOTTOM*/ ;
else
m.Result = (IntPtr)17/*HTBOTTOMRIGHT*/ ;
}
}
return;
}
base.WndProc(ref m);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style |= 0x20000; // <--- use 0x20000
return cp;
}
}
}
}
What is the other application you are trying to suppress the visibility of? Have you investigated other ways of achieving your desired effect? Please do so before subjecting your users to such rogue behaviour as you are describing: what you are trying to do sound rather like what certain naughty sites do with browser windows...
At least try to adhere to the rule of Least Surprise. Users expect to be able to determine the z-order of most applications themselves. You don't know what is most important to them, so if you change anything, you should focus on pushing the other application behind everything rather than promoting your own.
This is of course trickier, since Windows doesn't have a particularly sophisticated window manager. Two approaches suggest themselves:
enumerating top-level windows
and checking which process they
belong to, dropping their
z-order if so. (I'm not sure if
there are framework methods for
these WinAPI functions.)
Fiddling with child process permissions to prevent it from accessing the desktop... but I wouldn't try this until the othe approach failed, as the child process might end up in a zombie state while requiring user interaction.
Here is the SetForegroundWindow equivalent:
form.Activate();
I have seen people doing weird things like:
this.TopMost = true;
this.Focus();
this.BringToFront();
this.TopMost = false;
http://blog.jorgearimany.com/2010/10/win32-setforegroundwindow-equivalent-in.html
I know this is old, but I did not see this response.
In the window (xaml) add:
Deactivated="Window_Deactivated"
In the code behind for Window_Deactivated:
private void Window_Deactivated(object sender, EventArgs e)
{
Window window = (Window)sender;
window.Activate();
}
This will keep your window on top.
Based on clamum's answer, and Kevin Vuilleumier's comment about the other flag responsible for the behavior, I made this toggle that switches between on-top and not on-top with a button press.
private void button1_Click(object sender, EventArgs e)
{
if (on)
{
button1.Text = "yes on top";
IntPtr HwndTopmost = new IntPtr(-1);
SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
on = false;
}
else
{
button1.Text = "not on top";
IntPtr HwndTopmost = new IntPtr(-2);
SetWindowPos(this.Handle, HwndTopmost, 0, 0, 0, 0, TopmostFlags);
on = true;
}
}
I did something i little bit differnt kinda found it much easier
so first on Form Load
private void Form1_Load(object sender, EventArgs e)
{
this.Shown += new EventHandler(Form1_Shown);//let your form show up here
}
private void Form1_Shown(Object sender, EventArgs e)
{
Form1.ActiveForm.TopMost = true //and then do your TopMost logic
}

Categories