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);
}
Related
I am trying to catch the MouseDown and MouseUp events in a listview's column header, to make the parent form movable via insider listview's header.
I could actually achieve this overriding the WndPrc but it only works within the listview area, not the columnheader. While the idea of moving a form via it's insider listview's header can be good (since my listview is not sortable by column click), moving the form from anywhere inside the listview is definetely not. It will cause to move on every item click which can be very disturbing.
This page says we can create a custom header control for a list/listview control but I could not find an example for C#/VB.Net despite all my search.
Anyone knows how to implement this or have an example ?
Thanks a lot by now.
Actually, it was very simple.
I share the code that I developed in case anyone will ever need this.
public class lw : ListView
{
const int LVM_FIRST = 0x1000;
const int LVM_GETHEADER = (LVM_FIRST + 31);
ListViewColumnHeaderWindow Header;
public lw()
{
//add 3 items for experiment
ListViewItem lwItem = new ListViewItem("a");
lwItem.SubItems.Add("b");
lwItem.SubItems.Add("c");
Items.Add(lwItem);
lwItem = new ListViewItem("d");
lwItem.SubItems.Add("e");
lwItem.SubItems.Add("f");
Items.Add(lwItem);
lwItem = new ListViewItem("g");
lwItem.SubItems.Add("h");
lwItem.SubItems.Add("i");
Items.Add(lwItem);
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
//here we need to wrap the header window to listen it's WndProc messages
IntPtr hHeader = (IntPtr)SendMessage(Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine("Creating ListViewColumnHeaderWindow for LVM_GETHEADER IntPtr {0}", hHeader);
Header = new ListViewColumnHeaderWindow(hHeader, this);
}
public void OnColumnMouseDown(object state)
{
Console.WriteLine("Header.MouseDown #{0}", (Point)state);
//now I can start to move the form from this point...
}
public void OnColumnMouseUp()
{
Console.WriteLine("Header.MouseUp");
}
public void MoveFromHeader(Point p) {
Console.WriteLine("Mouse moved to #{0}", p);
}
}
class ListViewColumnHeaderWindow : NativeWindow
{
lw Parent;
bool MouseIsDown;
System.Threading.Timer MTIMER;
public ListViewColumnHeaderWindow(IntPtr hHeader, lw parent)
{
Parent = parent;
MouseIsDown = false;
AssignHandle(hHeader);
}
protected override void WndProc(ref Message m)
{
if (MouseIsDown && m.Msg == 4608)
{
//this message is being sent when user starts to RESIZE
//the column header by clicking the column resizer.
//We should immediately cancel the mousedown event
//to allow user to resize the column (instead of moving the form)
MouseIsDown = false;
MTIMER?.Dispose();
}
switch (msg)
{
case 513: // WM_LBUTTONDOWN
MouseIsDown = true;
//due to possible column header resizing,
//I don't call OnColumnMouseDown event immediately here,
//but set a delay to detect the message '4608'
//which is being sent right after WM_LBUTTONDOWN
//that indicates the mouse is down to column resizing.
MTIMER?.Dispose();
MTIMER = new System.Threading.Timer(Parent.OnColumnMouseDown, new Point(m.LParam.ToInt32()), 200, System.Threading.Timeout.Infinite);
break;
case 512: // WM_MOUSEMOVE
if (MouseIsDown)
Parent.MoveFromHeader(new Point(m.LParam.ToInt32()));
break;
case 514: // WM_LBUTTONUP
if (MouseIsDown)
{
MouseIsDown = false;
Parent.OnColumnMouseUp();
}
break;
}
base.WndProc(ref m);
}
}
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='*'\"")
I have a TextBox component in C# WinForms.
When I Right-click on it, I would like to disable the paste option, whilst still allowing users to Cut and Copy.
I have investigated a few options, neither of which fully meet my requirements -
If I use this option, it will prevent Cut and Copy along with Paste which I do not want.
txt.ShortcutsEnabled = false;
If I override the ContextMenu of TextBox, I will have to write the Cut and Copy Features myself in the new context menu.
txt.ContextMenu = new ContextMenu(); // or some other
Are there any options I can use to disable only the Paste option of the default context menu, retaining Cut and Copy?
Assuming the Paste menu item is always the fifth element in the textbox context menu (zero-based and a separator counts as item too), you could subclass the TextBox class (here: CustomMenuTextBox) and override the WndProc method to disable that specific menu item:
public static class User32
{
[DllImport("user32.dll")]
public static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
}
public class CustomMenuTextBox : TextBox
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0093 /*WM_UAHINITMENU*/ || m.Msg == 0x0117 /*WM_INITMENUPOPUP*/ || m.Msg == 0x0116 /*WM_INITMENU*/)
{
IntPtr menuHandle = m.Msg == 0x0093 ? Marshal.ReadIntPtr(m.LParam) : m.WParam;
// MF_BYPOSITION and MF_GRAYED
User32.EnableMenuItem(menuHandle, 4, 0x00000400 | 0x00000001);
}
base.WndProc(ref m);
}
}
Based on Add item to the default TextBox context menu.
The following two steps disable the Copy/Paste feature in a textbox:
Disable the default menu and associate the textbox with an empty
context menu that has no menu items (mouse actions).
The user can still use the shortcut keys on the keyboard and perform
these operations.
So, override the ProcessCmdKey method as shown below:
// Constants
private const Keys CopyKeys = Keys.Control | Keys.C;
private const Keys PasteKeys = Keys.Control | Keys.V;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if((keyData == CopyKeys) || (keyData == PasteKeys))
{
return true;
}
else
{
return base.ProcessCmdKey(ref msg, keyData);
}
}
Note: Return true, which supresses the base class functionality.
OR You can use :
ContextMenuStrip mnu = new ContextMenuStrip();
ToolStripMenuItem mnuCopy = new ToolStripMenuItem("Copy");
ToolStripMenuItem mnuCut = new ToolStripMenuItem("Cut");
mnuCopy.Click += new EventHandler(mnuCopy_Click);
mnuCut.Click += new EventHandler(mnuCut_Click);
mnu.MenuItems.AddRange(new MenuItem[] { mnuCopy, mnuCut});
txt.ContextMenu = mnu;
Note : You can't disable Past option in default context menu for that you have to add contextMenuStrip in your form.
this.contextMenuStrip1.Items.AddRange(new
System.Windows.Forms.ToolStripItem[] {
this.Undo,
this.Cut,
this.Copy,
this.Paste,
this.Delete,
this.SelectAll});
this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening);
this.txt.ContextMenuStrip = this.contextMenuStrip1;
Hope Useful for you link
If you only need Cut Copy and Paste options, Maybe it is better create your custom contentMenuStrip, you can do that visually in Visual Studio and it is very easy to implement the copy cut and paste options. Then you can control in your program when you want to have any option enable or not.
For example this code in the Opening event of your custom ContentMenuStrip, disable copy and cut when you haven't selected text in a text-box, and only enable Paste if your clipboard contain text or images.
private void contextSuperEditor_Opening(object sender, CancelEventArgs e)
{
if (tbText.SelectionLength > 0)
{
MenuCopy.Enabled = true;
MenuCut.Enabled = true;
MenuPaste.Enabled = false;
}
else
{
MenuCopy.Enabled = false;
MenuCut.Enabled = false;
if (Clipboard.ContainsText() | Clipboard.ContainsImage())
{
MenuPaste.Enabled = true;
}
}
}
I'm having an EditText and a Button in my Frame using C#. After writing inside the edit field and clicking on the Button, I want to hide the virtual soft keyboard.
Add a dummy button and set focus to it and the keyboard will be hidden.
Thanks for your question.
i have get a better solution for this problem. like this
first we can add handler in xaml
<Grid x:Name= Tapped="Grid_Tapped_1">
......
</Grid >
then we focus current page like follow. it works well.
private void Grid_Tapped_1(object sender, TappedRoutedEventArgs e)
{
this.Focus(FocusState.Programmatic);
}
You cannot. There is more information on the behavior of the Input Hosting Manager and Soft Keyboard and you can register to know when it shows or becomes hidden. But, you cannot programmatically control whether it's up or down.
When the textbox that showed the virtual keyboard has it’s propery IsEnabled set to false, the virtual keyboard disappears. We can immediately set is to true after that and the virtual keyboard will remain hidden. Just like this:
MyTextBox.KeyDown += (s, a) => {
if (a.Key == VirtualKey.Enter) {
MyTextBox.IsEnabled = false;
MyTextBox.IsEnabled = true;
}
};
Try to set the IsReadOnly property of the Textbox`.
I'm doing something "similar"
private void textbox_input_LostFocus(object sender, RoutedEventArgs e)
{
textbox_input.IsReadOnly = false;
}
private void textbox_input_Tapped(object sender, TappedRoutedEventArgs e)
{
if(e.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse)
textbox_input.IsReadOnly = true;
else
textbox_input.IsReadOnly = false;
}
With this snipped I suppress the keyboard if the user isn't using the mouse...
Also the KeyDown event is fired while the textbox is readonly so you could use the data directly to set your viewmodel and update over it your textbox ;)
There is a solution which can hide the touch-keyboard by setting the container's IsTabStop=true automaticly after clicking your Button as "submit".
But, btw, I've noticed that the next time entering that page, the EditText (supposed to be a TextBox) will be auto-focused, and have the touch-keyboard showed. Maybe you'd better Disable the EditText after submitting. (seems to finish and block the input operation)
I had the same problem, only with a little difference.
When I switched from a textbox to a datepicker the softkeyboard won't disappear.
I tried all of your suggestions, but nothing worked like it should. Every time my datepicker had a strange behaviour, after I tried one of the above solutions (Or some of other stackoverflow threads).
After some time I found something via Google, which worked like a charm. HERE
In the comment section Dusher16 wrote a very clean solution, which works also for WinRT / Win8 / Win8.1 / Metro or how you will call it.
Create a new class:
using System.Runtime.InteropServices;
using Windows.Devices.Input;
namespace Your.Namespace
{
public static class TouchKeyboardHelper
{
#region < Attributes >
private const int WmSyscommand = 0x0112; // Flag to received/send messages to the system.
private const int ScClose = 0xF060; // Param to indicate we want to close a system window.
#endregion < Attributes >
#region < Properties >
public static bool KeyboardAttached
{
get { return IsKeyboardAttached(); }
}
#endregion < Properties >
#region < Methods >
[DllImport("user32.dll")]
private static extern int FindWindow(string lpClassName, string lpWindowName); // To obtain an active system window handler.
[DllImport("user32.dll")]
private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam); // To send a message to the system.
/// <summary>
/// To detect if a real keyboard is attached to the dispositive.
/// </summary>
/// <returns></returns>
private static bool IsKeyboardAttached()
{
KeyboardCapabilities keyboardCapabilities = new KeyboardCapabilities(); // To obtain the properties for the real keyboard attached.
return keyboardCapabilities.KeyboardPresent != 0 ? true : false;
}
/// <summary>
/// To close the soft keyboard
/// </summary>
public static void CloseOnscreenKeyboard()
{
// Retrieve the handler of the window
int iHandle = FindWindow("IPTIP_Main_Window", ""); // To find the soft keyboard window.
if (iHandle > 0)
{
SendMessage(iHandle, WmSyscommand, ScClose, 0); // Send a close message to the soft keyboard window.
}
}
#endregion < Methods >
}
}
And in for example some XAML.cs file you add the following lines:
private void DatePicker_GotFocus(object sender, RoutedEventArgs e)
{
if (TouchKeyboardHelper.KeyboardAttached)
TouchKeyboardHelper.CloseOnscreenKeyboard();
}
I need to lock the entire form if the execution of a perticular process is in progress.
My form contains many controls like buttons, comboboxes.
all the controls should be in disabled state if the process is running
Now i'n using the two methods from user32.dll
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(String sClassName, String sAppName);
[DllImport("user32.dll")]
public static extern bool EnableWindow(IntPtr hwnd, bool bEnable);
but its not working properly.
Is there any other idea to do this
Thanks in advance
What do you mean with lock?
If you want to prevent the user from making inputs you can set
this.Enabled = false;
on you main form, which will disable all child controls, too.
A solution to prevent the events from fireing is to implement a message filter: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.addmessagefilter.aspx and intercept the left mouse button.
// Creates a message filter.
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public class TestMessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
// Blocks all the messages relating to the left mouse button.
if (m.Msg >= 513 && m.Msg <= 515)
{
Console.WriteLine("Processing the messages : " + m.Msg);
return true;
}
return false;
}
}
public void SomeMethod()
{
this.Cursor = Cursors.WaitCursor;
this.Enabled = false;
Application.AddMessageFilter(new TestMessageFilter(this));
try
{
Threading.Threat.Sleep(10000);
}
finally
{
Application.RemoveMessageFilter(new TestMessageFilter(this));
this.Enabled = true;
this.Cursor = Cursors.Default;
}
}
Form.Enabled = false;
Not working?
When a control Enabled property is set to false, the interaction with that control and all its children is disabled. you can use that in your scenario, by placing all the controls in a ContainerControl parent, and set its Enabled = false.
In fact, you already have such a ContainerContol - your Form.
this.Enable=false; Thread.Sleep(5000); this.Enable=true;
Doing the processing in the GUI thread is bad practice, you should use a BackgroundWorker.
A quick and dirty fix would be to call Application.DoEvents() just before enabling your form.
this.Enable=false; Thread.Sleep(5000); Application.DoEvents(); this.Enable=true;