How do I detect if a Windows device is touch-enabled - c#

How do I detect if a device is touch-enabled in C# for a WinForms app (Not WPF).
I found information on GetSystemMetrics. But I can't find how to use this in C#.
I tried using System.Windows.Input.Tablet class. But it's not coming up in C#, even though I am using .NET Framework 4.5.
I tried using System.Windows.Devices. But it's not coming up in C#, even though I am using .NET Framework 4.5.
I have also checked Detect whether a Windows 8 Store App has a touch screen and How to detect a touch enabled device (Win 8, C#.NET), which would seem to make this question a duplicate. However, neither of these answers my question.

GetSystemMetrics seems to be the right way to go. It should be accessed through P/Invoke like this:
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
public static bool IsTouchEnabled()
{
const int MAXTOUCHES_INDEX = 95;
int maxTouches = GetSystemMetrics(MAXTOUCHES_INDEX);
return maxTouches > 0;
}

As taken from this answer
var hasTouch = Windows.Devices.Input
.PointerDevice.GetPointerDevices()
.Any(p => p.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch);

Related

Update input language of on screen keyboard

I have an Windows Forms application written in C# .NET 5 with a webview2 control which hosts a kiosk based application which will be run full screen on a touchscreen.
The application will use the on screen keyboard for user input. The application is hosted on our own machines, which will only be used for our application so changing the system input language is exactly what we want to achieve.
I need to allow the users to pick their own input language. In 4.7.2 I can achieve this by setting InputLanguage.CurrentInputLanguage, which dynamically updates the on screen keyboard input language, you also see the system input language update in the task bar (which in production will not be visible to users).
In .NET 5, setting the same value does not have the desired effect, the on screen keyboard does not reflect the changed input language, not does the input language update in the taskbar.
I notice the libraries behind the scenes have changed which is clearly why I am having this issue.
I have tried the following without success in a simple dummy app:
private void btnEnglish_Click(object sender, EventArgs e)
{
var language = InputLanguage.FromCulture(System.Globalization.CultureInfo.GetCultureInfo("en-GB"));
if (InputLanguage.InstalledInputLanguages.IndexOf(language) >= 0)
InputLanguage.CurrentInputLanguage = language;
ChangeSystemInputLanguage(language);
}
private static void ChangeSystemInputLanguage(InputLanguage language)
{
Application.OleRequired();
IntPtr handleOld = ActivateKeyboardLayout(new HandleRef(language, language.Handle), 0);
if (handleOld == IntPtr.Zero)
{
throw new ArgumentException("ErrorBadInputLanguage", nameof(language));
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr ActivateKeyboardLayout(HandleRef hkl, int uFlags);
Short of downgrading to 4.7.2 or re-writting in WPF (and such using the InputLanguageManager), can anyone make any recommendations how I might achieve the desired effect in .NET 5.0?
For anyone having this issue, the answer was incredibly simple...
before updating the CurrentInputLanguage, set focus on a control on in your form!

Setting wallpaper through Active Desktop in UWP app changes fit mode

I am using the Active Desktop interface in C# to change desktop wallpaper in Windows. I am only using the IActiveDesktop.SetWallpaper method and never use IActiveDesktop.SetWallpaperOptions, so I would expect only the wallpaper image to change and not its fit (tile, stretch, fill, etc.).
When I my compile my code as a .NET desktop app, this behaves as expected. However, when I use Desktop Bridge to compile my app as a UWP app for the Windows Store, the wallpaper fit changes and not just the image. I don't understand why running my code as a UWP app should make the Active Desktop interface behave any differently.
For example, if I select "Span" in the Windows 10 Settings app to make the wallpaper stretch across my two monitors, my UWP app does not respect this setting. When it changes the wallpaper image, the fit also changes to show the image separately on each monitor. But the .NET desktop version of my app respects the wallpaper fit setting and does not change it.
I have included the relevant part of my code below. The entire file can be found here.
[ComImport]
[Guid("F490EB00-1240-11D1-9888-006097DEACF9")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IActiveDesktop
{
[PreserveSig]
int ApplyChanges(AD_Apply dwFlags);
[PreserveSig]
int SetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string pwszWallpaper, int dwReserved);
}
public class WallpaperChanger
{
public static readonly Guid CLSID_ActiveDesktop =
new Guid("{75048700-EF1F-11D0-9888-006097DEACF9}");
public static IActiveDesktop GetActiveDesktop()
{
Type typeActiveDesktop = Type.GetTypeFromCLSID(WallpaperChanger.CLSID_ActiveDesktop);
return (IActiveDesktop)Activator.CreateInstance(typeActiveDesktop);
}
public static void SetWallpaper(string imagePath)
{
IActiveDesktop iad = GetActiveDesktop();
iad.SetWallpaper(imagePath, 0);
iad.ApplyChanges(AD_Apply.ALL | AD_Apply.FORCE | AD_Apply.BUFFERED_REFRESH);
}
}
Note: I've tried using the SetWallpaperAsync function available in the Windows UWP library, and it has the same problem. Also this problem is not specific to multiple monitors, the same thing happens with just a single one.

How do I arrange or sort the desktop icons in c#?

You know when you right-click the desktop and there's a "Sort by" option that allows you to sort the icons by "Name", "Size", "Item Type", or "Date Modified"? Well, I want to find a way to sort the desktop's icons with a push of a button.
I saw a similar question being asked here on stackoverflow but it's old and the code didn't work for me. The link to the question is: Arranging desktop icons with C# . I'm trying to achieve this in Windows 10.
There was a comment on there that said that the LVM_* and LVA_* values are stored in the commctrl.h file which comes with the SDK. I couldn't find that file for some reason.
Here's what i'm using:
//sort desktop
public const int LVM_ARRANGE = 4118;
public const int LVM_ALIGNLEFT = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
//end of sort desktop
private void organizeBtn_Click(object sender, EventArgs e)
{
var DesktopHandle = GetDesktopWindow();
MessageBox.Show(GetDesktopWindow().ToString());
SendMessage(GetDesktopWindow(), LVM_ARRANGE, LVM_ALIGNLEFT, 0);
}
I've been digging for info or some sort of direction about this topic, especially regarding windows 10, but I can't find much. Please help?
In Windows 10, the desktop (not tile world!) is still a SysListView32, but the GetDesktopWindow API call will return the handle of its grandparent, a Progman window - reminiscence of the ancient "Program Manager" of Windows 3.0. Then there is a shim of the SHELLDLL_DefView class, and below that you find buried the listview you're after.
Use the information from this answer to move down from the shell window to the folder view, which you can eventually send the LVM_ARRANGE message.
This is a brittle approach, as it relies on undocumented properties of the operating system, which may change at any time with updates or new versions. It will probably break also when a user uses a slideshow as desktop background, because Windows then rearranges the desktop window stack. Hack to deal with this here.
Another approach which is documented and less likely to break in future versions, with the downside of involving COM and a nightmare from C#, is via the IFolderView of shell automation, two relevant finds here and here.

C# Get System Audio Volume via Windows API [duplicate]

This question already has answers here:
C# get master volume level/precent
(3 answers)
Closed 8 years ago.
I've been trying to get the current system volume using C# / Windows API. I'm on Windows 8.1 though I would like the solution to also work on Windows 7.
This:
[DllImport("winmm.dll")]
public static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume);
does not work, with the system volume after Windows XP.
I have tried this:
[DllImport("Audioses.dll", EntryPoint = "GetMasterVolume", SetLastError = true)]
static extern int GetMasterVolume(out float pfLevelDB);
public void getVolume()
{
float f = 0.0F;
int i = GetMasterVolume(out f);
MessageBox.Show(f.ToString());
}
However the application never gets to the MessageBox.Show(...), though running line-by-line shows that it gets to GetMasterVolume(out f) then fails. I think something must be wrong with my declaration or usage.
Output shows: System.EntryPointNotFoundException
GetMasterVolumeLevel: http://msdn.microsoft.com/en-us/library/windows/desktop/dd316533(v=vs.85).aspx
EntryPointNotFound indicates that the declaration is indeed mistaken, and your implementation cannot find the method you're requesting within the specified dll. The biggest I see here is that the ISimpleAudioVolume is an interface, and thus the desired method is an instance, not a static, method, and requires more work to grab an instance of the appropriate type and return the desired volume levels. Note that MSDN and on the interface itself mentions needing to start an audio session to operate through this interface.

GetSystemMetrics() returns different results for .NET 4.5 & .NET 4.0

During a .NET 4.0 -> .NET 4.5 application migration process I've discovered an extremely strange behavior. I've been able to track this issue down to this short code snippet:
class Program
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
static void Main(string[] args)
{
const int CXFRAME = 0x20;
const int CYFRAME = 0x21;
var dx = GetSystemMetrics(CXFRAME);
var dy = GetSystemMetrics(CYFRAME);
Console.WriteLine("{0}x{1}", dx, dy);
Console.ReadKey();
}
}
When compiled with Target Framework = 4.0 (and also 2.0, 3.0, 3.5), it outputs 8x8
When compiled with Target Framework = 4.5, it outputs 4x4
Running this sample with MSVS2012 debugger also always outputs 4x4 (with any target framework).
Other options (target framework profile, target platform, enabling/disabling Aero) do not affect the result, the only things that change the output are target framework and running with debugger. I've been able to reproduce this issue on 3 computers, unfortunately they are almost identical in terms of installed software:
Windows 7 Ultmate SP1 (russian, all updates installed) with MSVS2012 (english/rusian) Update 1
Windows 8 (on virtual machine)
Currently I'm thinking about patching some .NET classes (SystemParameters for example), which call GetSystemMetrics() using reflection, but I'm not sure how to get correct metric values.
Questions:
Am I missing something? How can GetSystemMetrics() be affected by target framework?
Is there any way to call GetSystemMetrics() from .NET 4.5 app and get correct results?
I'm open to any suggestions on fixing this issue. Also, if you cannot reproduce the issue, please leave a short system description in comments.
So, it's actually a by-design behavior, and if someone has similar issues, here is the code which always outputs the same result:
const int CXFRAME = 0x20;
const int CYFRAME = 0x21;
const int CXPADDEDBORDER = 92;
var dx = GetSystemMetrics(CXFRAME);
var dy = GetSystemMetrics(CYFRAME);
var d = GetSystemMetrics(CXPADDEDBORDER);
dx += d;
dy += d;
Console.WriteLine("{0}x{1}", dx, dy);
Console.ReadKey();
Also note that RibbonWindow WPF control, which uses WindowChrome and now comes as a part of .NET 4.5 does not know about this changes and displays messy window borders (fortunately, I think it can be fixed using modified styles).
According to Microsoft, this is by-design.
See here for full details:
The SystemParameters.WindowResizeBorderThickness seems to return incorrect value - Microsoft Connect (no archived version available)
Regression: ::GetSystemMetrics delivers different values - Microsoft Connect (archived)
Despite MS saying it's "by design", I still think it's a bug!

Categories