Related
I want to change this code so it can save raw depth data as uint16 format. but at this stage, it saves the image in uint8 format by converting the values 0-255. but i need to save the value as it come from camera or kinect such as uint16 format.
Code:depthBasics:
//------------------------------------------------------------------------------
// <copyright file="MainWindow.xaml.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace Microsoft.Samples.Kinect.DepthBasics
{
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Kinect;
/// <summary>
/// Interaction logic for MainWindow
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
/// <summary>
/// Map depth range to byte range
/// </summary>
private const int MapDepthToByte = 8000 ;
/// <summary>
/// Active Kinect sensor
/// </summary>
private KinectSensor kinectSensor = null;
/// <summary>
/// Reader for depth frames
/// </summary>
private DepthFrameReader depthFrameReader = null;
/// <summary>
/// Description of the data contained in the depth frame
/// </summary>
private FrameDescription depthFrameDescription = null;
/// <summary>
/// Bitmap to display
/// </summary>
private WriteableBitmap depthBitmap = null;
/// <summary>
/// Intermediate storage for frame data converted to color
/// </summary>
private byte[] depthPixels = null;
/// <summary>
/// Current status text to display
/// </summary>
private string statusText = null;
/// <summary>
/// Initializes a new instance of the MainWindow class.
/// </summary>
public MainWindow()
{
// get the kinectSensor object
this.kinectSensor = KinectSensor.GetDefault();
// open the reader for the depth frames
this.depthFrameReader = this.kinectSensor.DepthFrameSource.OpenReader();
// wire handler for frame arrival
this.depthFrameReader.FrameArrived += this.Reader_FrameArrived;
// get FrameDescription from DepthFrameSource
this.depthFrameDescription = this.kinectSensor.DepthFrameSource.FrameDescription;
// allocate space to put the pixels being received and converted
this.depthPixels = new byte[this.depthFrameDescription.Width * this.depthFrameDescription.Height];
// create the bitmap to display
this.depthBitmap = new WriteableBitmap(this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray8, null);
// set IsAvailableChanged event notifier
this.kinectSensor.IsAvailableChanged += this.Sensor_IsAvailableChanged;
// open the sensor
this.kinectSensor.Open();
// set the status text
this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
: Properties.Resources.NoSensorStatusText;
// use the window object as the view model in this simple example
this.DataContext = this;
// initialize the components (controls) of the window
this.InitializeComponent();
}
/// <summary>
/// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Gets the bitmap to display
/// </summary>
public ImageSource ImageSource
{
get
{
return this.depthBitmap;
}
}
/// <summary>
/// Gets or sets the current status text to display
/// </summary>
public string StatusText
{
get
{
return this.statusText;
}
set
{
if (this.statusText != value)
{
this.statusText = value;
// notify any bound elements that the text has changed
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("StatusText"));
}
}
}
}
/// <summary>
/// Execute shutdown tasks
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void MainWindow_Closing(object sender, CancelEventArgs e)
{
if (this.depthFrameReader != null)
{
// DepthFrameReader is IDisposable
this.depthFrameReader.Dispose();
this.depthFrameReader = null;
}
if (this.kinectSensor != null)
{
this.kinectSensor.Close();
this.kinectSensor = null;
}
}
/// <summary>
/// Handles the user clicking on the screenshot button
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void ScreenshotButton_Click(object sender, RoutedEventArgs e)
{
if (this.depthBitmap != null)
{
// create a png bitmap encoder which knows how to save a .png file
BitmapEncoder encoder = new PngBitmapEncoder();
// create frame from the writable bitmap and add to encoder
encoder.Frames.Add(BitmapFrame.Create(this.depthBitmap));
string time = System.DateTime.UtcNow.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat);
string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string path = Path.Combine(myPhotos, "KinectScreenshot-Depth-" + time + ".png");
// write the new file to disk
try
{
// FileStream is IDisposable
using (FileStream fs = new FileStream(path, FileMode.Create))
{
encoder.Save(fs);
}
this.StatusText = string.Format(CultureInfo.CurrentCulture, Properties.Resources.SavedScreenshotStatusTextFormat, path);
}
catch (IOException)
{
this.StatusText = string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedScreenshotStatusTextFormat, path);
}
}
}
/// <summary>
/// Handles the depth frame data arriving from the sensor
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Reader_FrameArrived(object sender, DepthFrameArrivedEventArgs e)
{
bool depthFrameProcessed = false;
using (DepthFrame depthFrame = e.FrameReference.AcquireFrame())
{
if (depthFrame != null)
{
// the fastest way to process the body index data is to directly access
// the underlying buffer
using (Microsoft.Kinect.KinectBuffer depthBuffer = depthFrame.LockImageBuffer())
{
// verify data and write the color data to the display bitmap
if (((this.depthFrameDescription.Width * this.depthFrameDescription.Height) == (depthBuffer.Size / this.depthFrameDescription.BytesPerPixel)) &&
(this.depthFrameDescription.Width == this.depthBitmap.PixelWidth) && (this.depthFrameDescription.Height == this.depthBitmap.PixelHeight))
{
// Note: In order to see the full range of depth (including the less reliable far field depth)
// we are setting maxDepth to the extreme potential depth threshold
ushort maxDepth = ushort.MaxValue;
// If you wish to filter by reliable depth distance, uncomment the following line:
//// maxDepth = depthFrame.DepthMaxReliableDistance
this.ProcessDepthFrameData(depthBuffer.UnderlyingBuffer, depthBuffer.Size, depthFrame.DepthMinReliableDistance, maxDepth);
depthFrameProcessed = true;
}
}
}
}
if (depthFrameProcessed)
{
this.RenderDepthPixels();
}
}
/// <summary>
/// Directly accesses the underlying image buffer of the DepthFrame to
/// create a displayable bitmap.
/// This function requires the /unsafe compiler option as we make use of direct
/// access to the native memory pointed to by the depthFrameData pointer.
/// </summary>
/// <param name="depthFrameData">Pointer to the DepthFrame image data</param>
/// <param name="depthFrameDataSize">Size of the DepthFrame image data</param>
/// <param name="minDepth">The minimum reliable depth value for the frame</param>
/// <param name="maxDepth">The maximum reliable depth value for the frame</param>
private unsafe void ProcessDepthFrameData(IntPtr depthFrameData, uint depthFrameDataSize, ushort minDepth, ushort maxDepth)
{
// depth frame data is a 16 bit value
ushort* frameData = (ushort*)depthFrameData;
// convert depth to a visual representation
for (int i = 0; i < (int)(depthFrameDataSize / this.depthFrameDescription.BytesPerPixel); ++i)
{
// Get the depth for this pixel
ushort depth = frameData[i];
// To convert to a byte, we're mapping the depth value to the byte range.
// Values outside the reliable depth range are mapped to 0 (black).
this.depthPixels[i] = (byte)(depth >= minDepth && depth <= maxDepth ? (depth / MapDepthToByte) : 0);
}
}
/// <summary>
/// Renders color pixels into the writeableBitmap.
/// </summary>
private void RenderDepthPixels()
{
this.depthBitmap.WritePixels(
new Int32Rect(0, 0, this.depthBitmap.PixelWidth, this.depthBitmap.PixelHeight),
this.depthPixels,
this.depthBitmap.PixelWidth,
0);
}
/// <summary>
/// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged).
/// </summary>
/// <param name="sender">object sending the event</param>
/// <param name="e">event arguments</param>
private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e)
{
// on failure, set the status text
this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText
: Properties.Resources.SensorNotAvailableStatusText;
}
}
}
private unsafe void ProcessDepthFrameData(IntPtr depthFrameData, uint depthFrameDataSize, ushort minDepth, ushort maxDepth)
{
// CONVERT IN TO ANY VALID FORMAT YOU WANT
// THEN HERE YOU HAVE DEPTH DATA ARRAY, SAVE IT AS YOU LIKE
ushort* frameData = (ushort*)depthFrameData;
}
If you need more efficiency, you can try something like
depthFrame.CopyFrameDataToBuffer(buffer);
In Reader_FrameArrived function.
The example provided in the Kinect SDK saves the PNG's in 8-bit format (0-255) therefore you loose the depth values. The issue is that WPF(?) cannot display 16-bit image data on screen (i believe due to GDI+ restrictions as mentioned in another SO question)
To save the depth images as Raw 16-bit PNG's; a couple of things need to be done.
WriteableBitmap item with PixelFormats.Gray16 needs to be declared, instead of the PixelFormats.Gray8
Two separate WritePixels need to be called, one for displaying the image in (byte) format and another for saving the depth image in 16-bit (ushort) format
Lastly, the depth data acquired needs to be processed twice, once in a byte array for displaying on screen, and once in ushort array for saving.
Note that the above can be modified as required (or for performance reasons if req) but for me, this works just fine and i can display the depth images and save the raw depth data aswell.
The code is as follows:
Initialize a new var:
private ushort[] raw = null;
private WriteableBitmap rawPNG = null;
In MainWindow Create two writeableBitmap's
this.depthBitmap = new WriteableBitmap(this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray8, null);
this.rawPNG= new WriteableBitmap( this.depthFrameDescription.Width, this.depthFrameDescription.Height, 96.0, 96.0, PixelFormats.Gray16, null );
In ProcessDepthFrameData assign the values:
// FOR DISPLAYING ON SCREEN:
// To convert to a byte, we're mapping the depth value to the byte range.
// Values outside the reliable depth range are mapped to 0 (black).
this.depthPixels[i] = (byte)(depth >= minDepth && depth <= maxDepth ? (depth / MapDepthToByte) : 0);
// FOR SAVING RAW PNG 16-BIT DATA
// Keeping the raw depth values after clipping them to the max depth. The data is converted to ushort which is 16bit data
raw[i] = (ushort)(depth >= minDepth && depth <= maxDepth ? (depth) : 0);
In RenderDepthPixels, create teh two images:
// Rendering frame for displaying on the screen, in 8-Bit grayscale format
depthBitmap.WritePixels(
new Int32Rect( 0, 0, depthBitmap.PixelWidth, depthBitmap.PixelHeight ),
depthPixels,
depthBitmap.PixelWidth,
0 );
// Rendering frame for saving as raw-16-bit PNG file
rawPNG.WritePixels(
new Int32Rect( 0, 0, depthBitmap.PixelWidth, depthBitmap.PixelHeight ),
raw,
depthBitmap.PixelWidth * 2,
0 );
And lastly, in ScreenshotButton_Click, save the file:
// TO SAVE THE DISPLAYED 8-BIT PNG
//encoder.Frames.Add( BitmapFrame.Create( this.depthBitmap) );
// TO SAVE THE RAW 16-BIT PNG
encoder.Frames.Add( BitmapFrame.Create( this.rawPNG ) );
The results can be verified by loading the depth image in Matlab. Imread shows a Uint16 image format and the depth values are retained in the matrix. Or by just checking the png file properties :)
I have shared the complete code that is working for me at Pastebin. Hopefully it should work for you aswell, and solves the issue.
I have an application (that always runs in minimized mode) that constantly listens to keys typed on the keyboard.
When certain key is pressed, I want the C# application to post a command on the message queue of the active window to minimize, close, maximize, etc.
I know it is possible to get the handle to the active window, but, how can I post a message on its message queue (as we can do in win32).
Thanks.
You can use interop to call the native WINAPI functions. Using the p/invoke website I have created the following solution:
var proc = Process.GetProcesses().First(p => p.ProcessName.Contains("firefox"));
PostMessageSafe(
new HandleRef(proc, proc.MainWindowHandle),
(uint) WM.WM_SYSCOMMAND,
new IntPtr((int) SysCommands.SC_MAXIMIZE),
IntPtr.Zero);
What I basically do here is find a WindowHandle I'm interested in and then invoke PostMessage with the WM_SYSCOMMAND in Msg and the appropriate syscommand in the wParam, in this case Maximize, with the value 0xF030. The lParam is set to 0.
Please be aware that if your only goal is to change the window state you better use the specialized API endpoint for that, it's called ShowWindow
Its signature looks like this:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
and it is invoked like this:
ShowWindow(proc.MainWindowHandle, ShowWindowCommands.Maximize);
When either of these methods are called, a window from a process named firefox will be maximized.
Here are the helper methods and enums you need as a wrapper around PostMessage
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
// http://www.pinvoke.net/default.aspx/Enums/WindowsMessages.html
public enum WM : uint
{
WM_SYSCOMMAND = 0x0112
}
// http://www.pinvoke.net/default.aspx/Enums/SysCommands.html
enum SysCommands : int
{
SC_SIZE = 0xF000,
SC_MOVE = 0xF010,
SC_MINIMIZE = 0xF020,
SC_MAXIMIZE = 0xF030,
SC_NEXTWINDOW = 0xF040,
SC_PREVWINDOW = 0xF050,
SC_CLOSE = 0xF060,
SC_VSCROLL = 0xF070,
SC_HSCROLL = 0xF080,
SC_MOUSEMENU = 0xF090,
SC_KEYMENU = 0xF100,
SC_ARRANGE = 0xF110,
SC_RESTORE = 0xF120,
SC_TASKLIST = 0xF130,
SC_SCREENSAVE = 0xF140,
SC_HOTKEY = 0xF150,
//#if(WINVER >= 0x0400) //Win95
SC_DEFAULT = 0xF160,
SC_MONITORPOWER = 0xF170,
SC_CONTEXTHELP = 0xF180,
SC_SEPARATOR = 0xF00F,
//#endif /* WINVER >= 0x0400 */
//#if(WINVER >= 0x0600) //Vista
SCF_ISSECURE = 0x00000001,
//#endif /* WINVER >= 0x0600 */
/*
* Obsolete names
*/
SC_ICON = SC_MINIMIZE,
SC_ZOOM = SC_MAXIMIZE,
}
// http://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html
enum ShowWindowCommands
{
/// <summary>
/// Hides the window and activates another window.
/// </summary>
Hide = 0,
/// <summary>
/// Activates and displays a window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when displaying the window
/// for the first time.
/// </summary>
Normal = 1,
/// <summary>
/// Activates the window and displays it as a minimized window.
/// </summary>
ShowMinimized = 2,
/// <summary>
/// Maximizes the specified window.
/// </summary>
Maximize = 3, // is this the right value?
/// <summary>
/// Activates the window and displays it as a maximized window.
/// </summary>
ShowMaximized = 3,
/// <summary>
/// Displays a window in its most recent size and position. This value
/// is similar to <see cref="Win32.ShowWindowCommand.Normal"/>, except
/// the window is not activated.
/// </summary>
ShowNoActivate = 4,
/// <summary>
/// Activates the window and displays it in its current size and position.
/// </summary>
Show = 5,
/// <summary>
/// Minimizes the specified window and activates the next top-level
/// window in the Z order.
/// </summary>
Minimize = 6,
/// <summary>
/// Displays the window as a minimized window. This value is similar to
/// <see cref="Win32.ShowWindowCommand.ShowMinimized"/>, except the
/// window is not activated.
/// </summary>
ShowMinNoActive = 7,
/// <summary>
/// Displays the window in its current size and position. This value is
/// similar to <see cref="Win32.ShowWindowCommand.Show"/>, except the
/// window is not activated.
/// </summary>
ShowNA = 8,
/// <summary>
/// Activates and displays the window. If the window is minimized or
/// maximized, the system restores it to its original size and position.
/// An application should specify this flag when restoring a minimized window.
/// </summary>
Restore = 9,
/// <summary>
/// Sets the show state based on the SW_* value specified in the
/// STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application.
/// </summary>
ShowDefault = 10,
/// <summary>
/// <b>Windows 2000/XP:</b> Minimizes a window, even if the thread
/// that owns the window is not responding. This flag should only be
/// used when minimizing windows from a different thread.
/// </summary>
ForceMinimize = 11
}
Currently I need to process Arabic string (اسمي مصير الطفل. من أي بلد أنت) in my project. If the string is displayed in a single line, it is displayed correctly.
However, when the font size is much bigger, the display (I am using label) is wrong.
The string is printed starting from the second line. I found that on the .Net framework we could use drawstring with StringFormatFlags.DirectionRightToLeft. However, that is not available in compact framework. So, how could i print Arabic string in multiple lines? Any advice is appreciated, thanks.
I am sorry, the following is incorrect:
Since WinCE 5 and CompactFramework v 2.0 controls like the Textbox support a RicghtToLeft property (see also http://www.danielmoth.com/Blog/rtl-problem.aspx). So you should ensure you are using CF>=2.0 and WinCE 5 base sdk (ie Windows Mobile 6.x).
Looking at the help for Textbox class, the RightToLeft is marked as NOT AVAILABLE for Compact Framework.
So, you need to write your own DrawText class that splits the words and positions them from right to left.
The native DrawText API supports the uFormat flag DT_RTLREADING (according to the online help for Windows CE 5 Platform Builder):
DT_RTLREADING
Layout in right-to-left reading order for bi-directional text when the font selected into the hdc is a Hebrew or Arabic font. The default reading order for all text is left-to-right.
There is also an option DT_WORDBREAK which I would choose for multiline text and a large enough drawing rectangle.
BUT, that gives the following result using two rectangles and two font sizes to force a wordbreak:
As I can not read that I am not sure, but I assume the wordbreak flag does not work correctly. I assume the second line in the upper part has to start from right too.
Native Code for the above:
...
#define ARABIC_TEXT L"اسمي مصير الطفل. من أي بلد أنت"
#define MulDiv(a,b,c) (((a)*(b))/(c))
...
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
LOGFONT lf;
HFONT hFontNew, hFontOld;
...
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
// Clear out the lf structure to use when creating the font.
memset(&lf, 0, sizeof(LOGFONT));
wsprintf(lf.lfFaceName,L"Arial Unicode MS");
GetClientRect(hWnd, &rect);
hFontNew = CreateFontIndirect(&lf);
hFontOld = (HFONT) SelectObject(hdc, hFontNew);
rect.bottom=rect.bottom/2;
lf.lfHeight=-MulDiv(16, GetDeviceCaps(hdc, LOGPIXELSY), 72);
if(DrawText(hdc, ARABIC_TEXT, -1, &rect, DT_RTLREADING | DT_WORDBREAK)==0){
DEBUGMSG(1, (L"DrawText failed with %i\n", GetLastError()));
}
GetClientRect(hWnd, &rect);
lf.lfHeight=-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
hFontNew = CreateFontIndirect(&lf);
hFontOld = (HFONT) SelectObject(hdc, hFontNew);
rect.top=rect.bottom/2;
if(DrawText(hdc, ARABIC_TEXT, -1, &rect, DT_RTLREADING | DT_WORDBREAK)==0){
DEBUGMSG(1, (L"DrawText failed with %i\n", GetLastError()));
}
EndPaint(hWnd, &ps);
SelectObject(hdc, hFontOld);
DeleteObject(hFontNew);
break;
The following form is an example of calling DrawText (see josef's answer) from C#:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Menu = null;
DrawText(e.Graphics, "اسمي مصير الطفل. من أي بلد أنت", Font, ClientRectangle);
}
private void DrawText(Graphics g, string text, Font font, Rectangle rectangle)
{
IntPtr dc = g.GetHdc();
RECT rect = (RECT)rectangle;
IntPtr hFont = IntPtr.Zero;
IntPtr previousFont = IntPtr.Zero;
try
{
hFont = font.ToHfont();
previousFont = SelectObject(dc, hFont);
DrawText(dc, text, text.Length, ref rect, DrawTextFlags.RightToLeft | DrawTextFlags.Right | DrawTextFlags.WordBreak);
}
finally
{
if (previousFont != IntPtr.Zero)
{
SelectObject(dc, previousFont);
}
if (hFont != IntPtr.Zero)
{
DeleteObject(hFont);
}
g.ReleaseHdc(dc);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
internal int Left;
internal int Top;
internal int Right;
internal int Bottom;
public static explicit operator RECT(Rectangle rect)
{
return new RECT()
{
Left = rect.Left,
Top = rect.Top,
Right = rect.Right,
Bottom = rect.Bottom
};
}
}
[DllImport("coredll.dll", CharSet = CharSet.Unicode)]
internal static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref RECT lpRect, DrawTextFlags flags);
[DllImport("coredll.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject([In] IntPtr hObject);
[DllImport("coredll.dll")]
internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[Flags]
public enum DrawTextFlags : uint
{
/// <summary>
/// Use default values.
/// </summary>
None = 0x00000000,
/// <summary>
/// Justifies the text to the top of the rectangle.
/// </summary>
Top = 0x00000000,
/// <summary>
/// Aligns text to the left.
/// </summary>
Left = 0x00000000,
/// <summary>
/// Centers text horizontally in the rectangle.
/// </summary>
Center = 0x00000001,
/// <summary>
/// Aligns text to the right.
/// </summary>
Right = 0x00000002,
/// <summary>
/// Centers text vertically. This value is used only with the SingleLine value.
/// </summary>
VerticalCenter = 0x00000004,
/// <summary>
/// Justifies the text to the bottom of the rectangle. This value is used only with the
/// SingleLine value.
/// </summary>
Bottom = 0x00000008,
/// <summary>
/// Breaks words. Lines are automatically broken between words if a word would extend past the
/// edge of the rectangle specified by the lpRect parameter. A carriage return-line feed sequence
/// also breaks the line. If this is not specified, output is on one line.
/// </summary>
WordBreak = 0x00000010,
/// <summary>
/// Displays text on a single line only. Carriage returns and line feeds do not break the line.
/// </summary>
SingleLine = 0x00000020,
/// <summary>
/// Expands tab characters. The default number of characters per tab is eight.
/// </summary>
ExpandTabs = 0x00000040,
/// <summary>
/// Sets tab stops. Bits 15-8 (high-order byte of the low-order word) of the uFormat parameter
/// specify the number of characters for each tab. The default number of characters per tab is
/// eight.
/// </summary>
Tabstop = 0x00000080,
/// <summary>
/// Draws without clipping.
/// </summary>
NoClip = 0x00000100,
/// <summary>
/// Includes the font external leading in line height. Normally, external leading is not included
/// in the height of a line of text.
/// </summary>
ExternalLeading = 0x00000200,
/// <summary>
/// Determines the width and height of the rectangle. If there are multiple lines of text, DrawText
/// uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the
/// rectangle to bound the last line of text. If the largest word is wider than the rectangle, the
/// width is expanded. If the text is less than the width of the rectangle, the width is reduced.
/// If there is only one line of text, DrawText modifies the right side of the rectangle so that it
/// bounds the last character in the line. In either case, DrawText returns the height of the
/// formatted text but does not draw the text.
/// </summary>
CalcRect = 0x00000400,
/// <summary>
/// Turns off processing of prefix characters. Normally, DrawText interprets the mnemonic-prefix
/// character & as a directive to underscore the character that follows, and the mnemonic-prefix
/// characters && as a directive to print a single &. By specifying DT_NOPREFIX, this processing
/// is turned off.
/// </summary>
NoPrefix = 0x00000800,
/// <summary>
/// Uses the system font to calculate text metrics.
/// </summary>
Internal = 0x00001000,
/// <summary>
/// Duplicates the text-displaying characteristics of a multiline edit control. Specifically,
/// the average character width is calculated in the same manner as for an edit control, and
/// the function does not display a partially visible last line.
/// </summary>
EditControl = 0x00002000,
/// <summary>
/// For displayed text, if the end of a string does not fit in the rectangle, it is truncated
/// and ellipses are added. If a word that is not at the end of the string goes beyond the
/// limits of the rectangle, it is truncated without ellipses.
/// </summary>
EndEllipsis = 0x00008000,
/// <summary>
/// Layout in right-to-left reading order for bidirectional text when the font selected into the
/// hdc is a Hebrew or Arabic font. The default reading order for all text is left-to-right.
/// </summary>
RightToLeft = 0x00020000,
/// <summary>
/// Truncates any word that does not fit in the rectangle and adds ellipses.
/// </summary>
WordEllipsis = 0x00040000
}
}
(As you can see I've ignored the return values of each native method)
It seems that there is no solid solution to this issue, so I created a temporary workaround. I am assuming that my Label size is fixed, and if my string size is larger than the width of my Label, I will split it into two. The method I use to split the string is to find a splitting point (currently I am doing it at 0.2 of the total string length).
private void processArabic(string arabicString)
{
string[] stringArray = new string[2];
double index = 0.8 * arabicString.Length;
index = Math.Ceiling(index);
int Index = (int)index;
for (int i = Index; i != 0; i--)
{
if (Char.IsWhiteSpace(arabicString, i))
{
stringArray[1] = arabicString.Substring(0, Index + 1);
stringArray[0] = arabicString.Substring(Index + 1, arabicString.Length - (Index + 1));
break;
}
}
label1.Text = stringArray[0];
label1.Text += "\n";
label1.Text += stringArray[1];
}
Although Arabic string is printed from right to left, index is still counted from left to right. The above snippet is rather rigid, and only apply if you want to split the string into two. I am sure there are lot of ways to improve the above code, any comment/suggestion is welcome.
I am using UIAutomation and trying to get the window Id of any control in 3rd party applications. I want to know how to find out the ID of the control at a specific coordinate on the screen.
Example: I have Calculator, Notepad, and Word running on the desktop. All of them are running and sharing the screen partially. I want to be able to run my program and then click at any location on the screen and get the window ID of the underlying control (if there is any under the mouse).
What do I need to use to achieve this functionality. I understand that I need to have some sort of mouse hook, but the real problem is how to get the window ID (not the window handle) of the control on the screen where the mouse was clicked.
AutomationElement.FromPoint() will return the automation element at a given point. Once you have that, you can trivially get the automation ID:
private string ElementFromCursor()
{
// Convert mouse position from System.Drawing.Point to System.Windows.Point.
System.Windows.Point point = new System.Windows.Point(Cursor.Position.X, Cursor.Position.Y);
AutomationElement element = AutomationElement.FromPoint(point);
string autoIdString;
object autoIdNoDefault = element.GetCurrentPropertyValue(AutomationElement.AutomationIdProperty, true);
if (autoIdNoDefault == AutomationElement.NotSupported)
{
// TODO Handle the case where you do not wish to proceed using the default value.
}
else
{
autoIdString = autoIdNoDefault as string;
}
return autoIdString;
}
If i understand it Correctly, what your are trying to achieve is->
Click at any point of screen, get the window id of the underlying element ,if any, from the running elements:
If that's the case,the following should help/give an idea(NOTE: This would extend for not only cursor position but will continue search along X-axis for 100 pixels with a interval of 10):
/// <summary>
/// Gets the automation identifier of underlying element.
/// </summary>
/// <returns></returns>
public static string GetTheAutomationIDOfUnderlyingElement()
{
string requiredAutomationID = string.Empty;
System.Drawing.Point currentLocation = Cursor.Position;//add you current location here
AutomationElement aeOfRequiredPaneAtTop = GetElementFromPoint(currentLocation, 10, 100);
if (aeOfRequiredPaneAtTop != null)
{
return aeOfRequiredPaneAtTop.Current.AutomationId;
}
return string.Empty;
}
/// <summary>
/// Gets the element from point.
/// </summary>
/// <param name="startingPoint">The starting point.</param>
/// <param name="interval">The interval.</param>
/// <param name="maxLengthToTraverse">The maximum length to traverse.</param>
/// <returns></returns>
public static AutomationElement GetElementFromPoint(Point startingPoint, int interval, int maxLengthToTraverse)
{
AutomationElement requiredElement = null;
for (Point p = startingPoint; ; )
{
requiredElement = AutomationElement.FromPoint(new System.Windows.Point(p.X, p.Y));
Console.WriteLine(requiredElement.Current.Name);
if (requiredElement.Current.ControlType.Equals(ControlType.Window))
{
return requiredElement;
}
if (p.X > (startingPoint.X + maxLengthToTraverse))
break;
p.X = p.X + interval;
}
return null;
}
I'm trying to display the tooltip by calling "ToolTip.Show(String, IWin32Window, Point)", but I wanted to do it like what Windows explorer does - displays the tooltip at the lower left corner of the cursor.
I could get the mouse position by "MousePosition", but how could I get its lower left corner position?
Thanks,
If nobody comes up with a better answer you can try this:
toolTip1.Show("Am I where you want me to be?", this, this.PointToClient(MousePosition).X,
this.PointToClient(MousePosition).Y + Cursor.Size.Height * 2);
Adjust the text positioning by playing with the x/y parameters. It works on my machine but I'm not sure how it would look under different settings.
ToolTip fun tip: put this line in your Form's MouseMove event.
I think Explorer puts the tooltip under the cursor's hotspot so you don't have to correct the X-position. This looked good:
private void panel1_MouseClick(object sender, MouseEventArgs e) {
int x = e.X;
int y = e.Y + Cursor.Current.Size.Height - Cursor.Current.HotSpot.Y;
toolTip1.Show("test", panel1, x, y);
}
The only way to do this is to scan the cursors MASK and find the distance between the last set pixel in the cursor mask and the cursors Y hotspot, I had to do this up today, so heres the code:
#define useUnsafe
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System;
using System.Windows.Forms;
namespace Utils
{
/// <summary>
/// Provides extension methods for the Cursor class
/// </summary>
/// <remarks>By Aaron Murgatroyd</remarks>
public static class CursorExtensionMethods
{
#region API Functions
/// <summary>
/// Contains the icon information for a Windows API icon
/// </summary>
private struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
/// <summary>
/// Gets the icon information for a Windows API icon
/// </summary>
/// <param name="hIcon">The icon to get the info for</param>
/// <param name="pIconInfo">The object to receive the info</param>
/// <returns>True on success, false on failure</returns>
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);
#endregion
#region Private Static Methods
/// <summary>
/// Scans bits in bitmap data for a set or unset bit
/// </summary>
/// <param name="byteData">The pointer to the first byte of the first scanline</param>
/// <param name="start">The vertical position to start the scan</param>
/// <param name="lineInc">The number of bytes to move per line</param>
/// <param name="maxLines">The number of lines to scan</param>
/// <param name="set">True to scan for set bits, false to scan for unset bits</param>
/// <param name="fromBottom">True to scan from the bottom of the bitmap, false to scan from the top</param>
/// <returns>The number of lines scanned before a bit was found, or -1 if none found before reaching max lines</returns>
#if useUnsafe
private static unsafe int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#else
private static int ScanBits(IntPtr byteData, int start, int lineInc, int maxLines, bool set, bool fromBottom)
#endif
{
// Calculate the starting byte of the first scanline
#if useUnsafe
byte* lbLine = ((byte*)byteData) + (start * lineInc);
#else
int lbLine = ((int)(byteData) + (start * lineInc));
#endif
int liLine = 0;
// Use lineInc to determines bytes per line
int liBytesPerLine = (lineInc < 0 ? -lineInc : lineInc);
// If we want to search in reverse order
if (fromBottom)
{
// Move to the START of the line
lbLine += lineInc * (maxLines - 1);
// Negate the line increment
lineInc = -lineInc;
}
while (maxLines > 0)
{
// Setup the line scan
#if useUnsafe
byte* lbData = lbLine;
#else
int lbData = lbLine;
#endif
int liByte = liBytesPerLine;
// For each byte in the line
while (liByte > 0)
{
#if !useUnsafe
byte lbByte = Marshal.ReadByte((IntPtr)lbData);
#endif
// If we want set bits, and a bit is set
#if useUnsafe
if (set && *lbData != 0)
#else
if (set && lbByte != 0)
#endif
// Return the line number
return liLine;
else
// If we want unset bits and any bits arent set
#if useUnsafe
if (!set && *lbData != byte.MaxValue)
#else
if (!set && lbByte != byte.MaxValue)
#endif
// Return the line number
return liLine;
// Next byte for scan line
liByte--;
lbData++;
}
// Next scan line
liLine++;
maxLines--;
lbLine += lineInc;
}
// If all lines were scanned, return -1
if (maxLines == 0)
return -1;
else
// Return number of lines scanned
return liLine;
}
#endregion
#region Public Static Methods
/// <summary>
/// Gets the number of pixels between the Y hotspot
/// and the last physical line of a cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>
/// The number of lines between the Y hotspot
/// and the last physical line of the cursor
/// </returns>
public static int GetBaseLineHeight(this Cursor cursor)
{
return GetBaseLine(cursor) - cursor.HotSpot.Y;
}
/// <summary>
/// Gets the physical base line of the cursor, that is,
/// the distance between the top of the virtual cursor
/// and the physical base line of the cursor
/// </summary>
/// <param name="cursor">The cursor to scan</param>
/// <returns>The number of lines between the top of the virtual cursor
/// and the physical base line of the curosr</returns>
public static int GetBaseLine(this Cursor cursor)
{
IconInfo liiInfo = new IconInfo();
if (!GetIconInfo(cursor.Handle, ref liiInfo))
return cursor.Size.Height;
Bitmap lbmpBitmap = Bitmap.FromHbitmap(liiInfo.hbmMask);
try
{
BitmapData lbdData = lbmpBitmap.LockBits(
new Rectangle(0, 0, lbmpBitmap.Width, lbmpBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
try
{
// Calculate number of lines in AND scan before any found
int liLine = ScanBits(lbdData.Scan0, 0, lbdData.Stride, cursor.Size.Height, false, true);
// If no AND scan bits found then scan for XOR bits
if (liLine == -1 && lbdData.Height == cursor.Size.Height * 2)
liLine = ScanBits(lbdData.Scan0, cursor.Size.Height, lbdData.Stride, cursor.Size.Height, true, true);
return cursor.Size.Height-liLine;
}
finally
{
lbmpBitmap.UnlockBits(lbdData);
}
}
finally
{
DeleteObject(liiInfo.hbmMask);
DeleteObject(liiInfo.hbmColor);
lbmpBitmap.Dispose();
}
}
#endregion
}
}
You can undefine the conditional define "useUnsafe" at the top so you dont have to enable unsafe code if you like, but be warned it will run slower in this mode.
So this uses extension methods, so all you have to do is add Cursor.Current.GetBaseLineHeight() to your Cursor.Position.Y and that will be the first blank line under the cursor.
ie.
Point lptBlankLineUnderCursor = new Point(Cursor.Position.X, Cursor.Position.Y + Cursor.Current.GetBaseLineHeight())