How can I create a full screen C# Windows Forms application in Visual Studio Express 2010? I have tried this link, but it just shows http://pixpipeline.com/d/57a8554712e8.png
No special tricks are necessary. Set the FormBorderStyle property to None, WindowState to Maximized.
http://www.vesic.org/english/blog/winforms/full-screen-maximize/
Example: http://www.vesic.org/blog/upload/MaxWinForm.zip
/// <summary>
/// Selected Win AI Function Calls
/// </summary>
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN);}
}
public static int ScreenY
{
get { return GetSystemMetrics(SM_CYSCREEN);}
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
/// <summary>
/// Class used to preserve / restore state of the form
/// </summary>
public class FormState
{
private FormWindowState winState;
private FormBorderStyle brdStyle;
private bool topMost;
private Rectangle bounds;
private bool IsMaximized = false;
public void Maximize(Form targetForm)
{
if (!IsMaximized)
{
IsMaximized = true;
Save(targetForm);
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
targetForm.TopMost = true;
WinApi.SetWinFullScreen(targetForm.Handle);
}
}
public void Save(Form targetForm)
{
winState = targetForm.WindowState;
brdStyle = targetForm.FormBorderStyle;
topMost = targetForm.TopMost;
bounds = targetForm.Bounds;
}
public void Restore(Form targetForm)
{
targetForm.WindowState = winState;
targetForm.FormBorderStyle = brdStyle;
targetForm.TopMost = topMost;
targetForm.Bounds = bounds;
IsMaximized = false;
}
}
Kiosk mode are the words you want to use for a search.
form.MaximizeBox = false;
form.MinimizeBox = false;
form.TopMost = true;
form.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
form.WindowState = System.Windows.Forms.FormWindowState.Maximized;
In properties of form set 'Window state' to 'Maximized'
(https://i.stack.imgur.com/UfCvY.jpg)
For make a full screen application you have to do something like this...
this.FormBorderStyle = FormBorderStyle.None;
this.WindowState = FormWindowState.Maximized;
this is the name of the form.
Related
I'm encountering an "IndexOutOfRangeException" on the ff = pfc.Families[0]; line when trying to load my .tff font file from my VS form application resources. You think you could look at my code and tell me why and how to fix it?
public partial class MainForm : Form
{
[DllImport("gdi32.dll")]
private static extern IntPtr AddFontMemResourceEx(IntPtr pbfont, uint cbfont, IntPtr pdv, [In] ref uint pcFonts);
FontFamily ff;
Font font;
bool NameFieldPopulated = false;
bool TitleFieldPopulated = false;
bool LocationFieldPopulated = false;
bool ExtFieldPopulated = false;
bool OutputFieldPopulated = false;
public static string NameInput;
public static string DirName;
public static string TitleInput;
public static string LocationInput;
public static string PhoneNumber;
public static string ExtensionInput;
public static string DirInput;
public Form2 overwrite;
public SucessForm success;
public OverwriteSucessForm succ2;
public MainForm main;
public MainForm()
{
InitializeComponent();
GenerateButton.Enabled = false;
}
private void loadFont()
{
byte[] fontArray = SF_Signature_Generator.Properties.Resources.bignoodletitling;
int dataLength = SF_Signature_Generator.Properties.Resources.bignoodletitling.Length;
IntPtr ptrData = Marshal.AllocCoTaskMem(dataLength);
Marshal.Copy(fontArray, 0, ptrData, dataLength);
uint cFonts = 0;
AddFontMemResourceEx(ptrData, (uint)fontArray.Length, IntPtr.Zero, ref cFonts);
PrivateFontCollection pfc = new PrivateFontCollection();
pfc.AddMemoryFont(ptrData, dataLength);
Marshal.FreeCoTaskMem(ptrData);
ff = pfc.Families[0];
font = new Font(ff, 15f, FontStyle.Regular);
}
private void AllocFont(Font f, Control c, float size)
{
FontStyle fontStyle = FontStyle.Regular;
c.Font = new Font(ff, size, fontStyle);
}
private void MainForm_Load(object sender, EventArgs e)
{
loadFont();
AllocFont(font, this.NameLabel, 20);
}
}
I am following this article, http://www.vesic.org/english/blog-eng/net/full-screen-maximize/
This makes my windows form to go full screen but it also hides Title bar.
I want to hide task bar but like to have my title bar.
" targetForm.FormBorderStyle = FormBorderStyle.None;" hides my title bar but removing this shows task bar so is there anyway to hide task bar and keep title bar in windows app? Thanks
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace myClasses
{
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN); }
}
public static int ScreenY
{
get { return GetSystemMetrics(SM_CYSCREEN); }
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
/// <summary>
/// Class used to preserve / restore state of the form
/// </summary>
public class FormState
{
private FormWindowState winState = FormWindowState.Normal;
private FormBorderStyle brdStyle = FormBorderStyle.Sizable;
private bool topMost;
private Rectangle bounds;
private bool IsMaximized = false;
public void Maximize(Form targetForm)
{
if (!IsMaximized)
{
IsMaximized = true;
Save(targetForm);
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
//targetForm.TopMost = true;
WinApi.SetWinFullScreen(targetForm.Handle);
}
}
public void Save(Form targetForm)
{
winState = targetForm.WindowState;
brdStyle = targetForm.FormBorderStyle;
topMost = targetForm.TopMost;
bounds = targetForm.Bounds;
}
public void Restore(Form targetForm)
{
targetForm.WindowState = winState;
targetForm.FormBorderStyle = brdStyle;
targetForm.TopMost = topMost;
targetForm.Bounds = bounds;
IsMaximized = false;
}
}
}
You can set it to FormBorderStyle.FixedToolWindow;, than only the taskbar is hidden.
I want to write a WPF application that docks to an application running in another process (this is a 3rd party app I have no control of). Ideally I would like to be able to define if the app docks on the left or right.
Here's an example of what I want to do:
I have tried to implement the following 2 examples with no success.
Attach window to window of another process - Button_Click gives the following error:
Attach form window to another window in C# - Button_Click_1 docks it the title bar but I cannot see the entire app:
The following is the code:
namespace WpfApplicationTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public static int GWL_STYLE = -16;
public static int WS_CHILD = 0x40000000;
[DllImport("user32")]
private static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int x,
int y,
int cx,
int cy,
uint uFlags);
private IntPtr _handle;
private void SetBounds(int left, int top, int width, int height)
{
if (_handle == IntPtr.Zero)
_handle = new WindowInteropHelper(this).Handle;
SetWindowPos(_handle, IntPtr.Zero, left, top, width, height, 0);
}
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Process hostProcess = Process.GetProcessesByName("notepad").FirstOrDefault();
IntPtr hostHandle = hostProcess.MainWindowHandle;
//MyWindow window = new MyWindow();
this.ShowActivated = true;
HwndSourceParameters parameters = new HwndSourceParameters();
parameters.WindowStyle = 0x10000000 | 0x40000000;
parameters.SetPosition(0, 0);
parameters.SetSize((int)this.Width, (int)this.Height);
parameters.ParentWindow = hostHandle;
parameters.UsesPerPixelOpacity = true;
HwndSource src = new HwndSource(parameters);
src.CompositionTarget.BackgroundColor = Colors.Transparent;
src.RootVisual = (Visual)this.Content;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Process hostProcess = Process.GetProcessesByName("notepad").FirstOrDefault();
if (hostProcess != null)
{
Hide();
//this.WindowStyle;
//new WindowInteropHelper(this).SetBounds(0, 0, 0, 0, BoundsSpecified.Location);
//SetWindowPos(new WindowInteropHelper(this).Handle, IntPtr.Zero, 0, 0, 0, 0, 0);
SetBounds(0, 0, 0, 0);
IntPtr hostHandle = hostProcess.MainWindowHandle;
IntPtr guestHandle = new WindowInteropHelper(this).Handle;
SetWindowLong(guestHandle, GWL_STYLE, GetWindowLong(guestHandle, GWL_STYLE) | WS_CHILD);
SetParent(guestHandle, hostHandle);
Show();
}
}
}
You implementation is totally wrong, you are trying to make your window as a child window of the window you want to snap to.
I wrote a small helper class for snapping to another window by it's title, I hope this helps.
WindowSnapper.cs
public class WindowSnapper
{
private struct Rect
{
public int Left { get; set; }
public int Top { get; set; }
public int Right { get; set; }
public int Bottom { get; set; }
public int Height
{
get { return Bottom - Top; }
}
public static bool operator !=(Rect r1, Rect r2)
{
return !(r1 == r2);
}
public static bool operator ==(Rect r1, Rect r2)
{
return r1.Left == r2.Left && r1.Right == r2.Right && r1.Top == r2.Top && r1.Bottom == r2.Bottom;
}
}
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);
private DispatcherTimer _timer;
private IntPtr _windowHandle;
private Rect _lastBounds;
private Window _window;
private string _windowTitle;
public WindowSnapper(Window window, String windowTitle)
{
_window = window;
_window.Topmost = true;
_windowTitle = windowTitle;
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(10);
_timer.Tick += (x, y) => SnapToWindow();
_timer.IsEnabled = false;
}
public void Attach()
{
_windowHandle = GetWindowHandle(_windowTitle);
_timer.Start();
}
public void Detach()
{
_timer.Stop();
}
private void SnapToWindow()
{
var bounds = GetWindowBounds(_windowHandle);
if (bounds != _lastBounds)
{
_window.Top = bounds.Top;
_window.Left = bounds.Left - _window.Width;
_window.Height = bounds.Height;
_lastBounds = bounds;
}
}
private Rect GetWindowBounds(IntPtr handle)
{
Rect bounds = new Rect();
GetWindowRect(handle, ref bounds);
return bounds;
}
private IntPtr GetWindowHandle(string windowTitle)
{
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains(windowTitle))
{
return pList.MainWindowHandle;
}
}
return IntPtr.Zero;
}
}
Usage example:
public partial class MainWindow : Window
{
private WindowSnapper _snapper;
public MainWindow()
{
InitializeComponent();
_snapper = new WindowSnapper(this, "Notepad");
_snapper.Attach();
}
}
I want every time you start my window form, he set at the bottom of the screen (above the taskbar)
public void goBottomWindow(Form targetForm)
{
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
targetForm.TopMost = true;
WinApi.SetWinFullScreen(targetForm.Handle);
}
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN); }
}
public static int ScreenY
{
get {return 60;}
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
Using this code, it leaves my window form at the top of the screen .... but what I need is the form window positioned below.
It is possible to do this?
Sorry, my English is very bad :(
Why targetForm.WindowState = FormWindowState.Maximized; ?
isn't FormWindowState.Normal better for you?
Just fetch the dimension of your desktop/screen
var rect = Screen.PrimaryScreen.WorkingArea;
targetForm.Witdh = rect.Width;
targetForm.Top = rect.Height - targetForm.Height;
I saw that in your first lines it says targetForm.TopMost = true;
That means that your form will be TOPMOST, if you want your form to be BottomMost, you should change that to false;
Hope this helps! - CCB
I want to customize DrawNode in OwnerDrawText mode in a TreeView. I found it very slow even with this handler:
void RegistryTreeDrawNode(object sender, DrawTreeNodeEventArgs e)
{
e.DrawDefault = true;
}
Am I doing something wrong?
Thanks.
I think you may need to show a bit more code for what you're trying to do. There shouldn't be anything noticeably different drawing like that, versus not owner drawing at all; you're basically overriding the default draw and then undoing it in what you posted. It's ugly for no gain... but shouldn't be a perf hit.
So switching away from the lack of code and going after your core desire of a custom drawn tree, let me tell you that there is NOT a lot of good information out in the wild right now.
I've been doing my own custom treeview work over the past few days and will probably end up writing a tutorial on all that I've learned. In the meantime feel free to take a look at my code and see if it helps you out.
Mine was simply a custom drawn explorer treeview. The code that populates the treeview is separate from the TreeView drawing code. You'll probably need to add your own +/- images if you wanted to run my code.
Utilities\IconReader.cs
using System;
using System.Runtime.InteropServices;
namespace TreeViewTestProject.Utilities
{
public class IconReader
{
public enum IconSize
{
Large = 0,
Small = 1
};
public enum FolderType
{
Open = 0,
Closed = 1
};
/// <summary>
/// Returns an icon for a given file - indicated by the name parameter.
/// </summary>
/// <param name="name">Pathname for file.</param>
/// <param name="size">Large or small</param>
/// <param name="linkOverlay">Whether to include the link icon</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
{
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if(true == linkOverlay) flags |= Shell32.SHGFI_LINKOVERLAY;
/* Check the size specified for return. */
if(IconSize.Small == size)
{
flags |= Shell32.SHGFI_SMALLICON;
}
else
{
flags |= Shell32.SHGFI_LARGEICON;
}
Shell32.SHGetFileInfo(name,
Shell32.FILE_ATTRIBUTE_NORMAL,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
// Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
/// <summary>
/// Used to access system folder icons.
/// </summary>
/// <param name="size">Specify large or small icons.</param>
/// <param name="folderType">Specify open or closed FolderType.</param>
/// <returns>System.Drawing.Icon</returns>
public static System.Drawing.Icon GetFolderIcon(string Foldername, IconSize size, FolderType folderType)
{
// Need to add size check, although errors generated at present!
uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;
if(FolderType.Open == folderType)
{
flags |= Shell32.SHGFI_OPENICON;
}
if(IconSize.Small == size)
{
flags |= Shell32.SHGFI_SMALLICON;
}
else
{
flags |= Shell32.SHGFI_LARGEICON;
}
// Get the folder icon
Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
Shell32.SHGetFileInfo(Foldername,
Shell32.FILE_ATTRIBUTE_DIRECTORY,
ref shfi,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
flags);
System.Drawing.Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle
// Now clone the icon, so that it can be successfully stored in an ImageList
System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
User32.DestroyIcon(shfi.hIcon); // Cleanup
return icon;
}
}
public class Shell32
{
public const int MAX_PATH = 256;
[StructLayout(LayoutKind.Sequential)]
public struct SHITEMID
{
public ushort cb;
[MarshalAs(UnmanagedType.LPArray)]
public byte[] abID;
}
[StructLayout(LayoutKind.Sequential)]
public struct ITEMIDLIST
{
public SHITEMID mkid;
}
[StructLayout(LayoutKind.Sequential)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszTitle;
public uint ulFlags;
public IntPtr lpfn;
public int lParam;
public IntPtr iImage;
}
// Browsing for directory.
public const uint BIF_RETURNONLYFSDIRS = 0x0001;
public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
public const uint BIF_STATUSTEXT = 0x0004;
public const uint BIF_RETURNFSANCESTORS = 0x0008;
public const uint BIF_EDITBOX = 0x0010;
public const uint BIF_VALIDATE = 0x0020;
public const uint BIF_NEWDIALOGSTYLE = 0x0040;
public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
public const uint BIF_BROWSEFORPRINTER = 0x2000;
public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
public const uint BIF_SHAREABLE = 0x8000;
[StructLayout(LayoutKind.Sequential)]
public struct SHFILEINFO
{
public const int NAMESIZE = 80;
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
public string szTypeName;
};
public const uint SHGFI_ICON = 0x000000100; // get icon
public const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
public const uint SHGFI_TYPENAME = 0x000000400; // get type name
public const uint SHGFI_ATTRIBUTES = 0x000000800; // get attributes
public const uint SHGFI_ICONLOCATION = 0x000001000; // get icon location
public const uint SHGFI_EXETYPE = 0x000002000; // return exe type
public const uint SHGFI_SYSICONINDEX = 0x000004000; // get system icon index
public const uint SHGFI_LINKOVERLAY = 0x000008000; // put a link overlay on icon
public const uint SHGFI_SELECTED = 0x000010000; // show icon in selected state
public const uint SHGFI_ATTR_SPECIFIED = 0x000020000; // get only specified attributes
public const uint SHGFI_LARGEICON = 0x000000000; // get large icon
public const uint SHGFI_SMALLICON = 0x000000001; // get small icon
public const uint SHGFI_OPENICON = 0x000000002; // get open icon
public const uint SHGFI_SHELLICONSIZE = 0x000000004; // get shell size icon
public const uint SHGFI_PIDL = 0x000000008; // pszPath is a pidl
public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010; // use passed dwFileAttribute
public const uint SHGFI_ADDOVERLAYS = 0x000000020; // apply the appropriate overlays
public const uint SHGFI_OVERLAYINDEX = 0x000000040; // Get the index of the overlay
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
[DllImport("Shell32.dll")]
public static extern IntPtr SHGetFileInfo(
string pszPath,
uint dwFileAttributes,
ref SHFILEINFO psfi,
uint cbFileInfo,
uint uFlags
);
}
public class User32
{
/// <summary>
/// Provides access to function required to delete handle. This method is used internally
/// and is not required to be called separately.
/// </summary>
/// <param name="hIcon">Pointer to icon handle.</param>
/// <returns>N/A</returns>
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
}
}
ExplorerTreeView.cs :
using System;
using System.IO;
using System.Windows.Forms;
using TreeViewTestProject.Utilities;
using System.Collections;
namespace TreeViewTestProject
{
public partial class ExplorerTreeView : TreeViewEx
{
#region ExplorerNodeSorter Class
private class ExplorerNodeSorter : IComparer
{
public int Compare(object x, object y)
{
TreeNode nx = x as TreeNode;
TreeNode ny = y as TreeNode;
bool nxDir = (nx.ImageKey == kDirectoryImageKey);
bool nyDir = (ny.ImageKey == kDirectoryImageKey);
if(nxDir && !nyDir)
{
return -1;
}
else if(nyDir && !nxDir)
{
return 1;
}
else
{
return string.Compare(nx.Text, ny.Text);
}
}
}
#endregion
private const string kDirectoryImageKey = "directory";
private const string kReplacementText = "C43C65D1-D40F-46F0-BC5E-57265322DDFC";
public ExplorerTreeView()
{
InitializeComponent();
this.BeforeExpand += new TreeViewCancelEventHandler(ExplorerTreeView_BeforeExpand);
this.ImageList = m_FileIcons;
this.TreeViewNodeSorter = new ExplorerNodeSorter();
this.LabelEdit = true;
// Create the root of the tree and populate it
PopulateTreeView(#"C:\");
}
private void PopulateTreeView(string DirectoryName)
{
this.BeginUpdate();
string rootDir = DirectoryName;
TreeNode rootNode = CreateTreeNode(rootDir);
rootNode.Text = rootDir;
this.Nodes.Add(rootNode);
PopulateDirectory(rootNode);
this.EndUpdate();
}
private bool PathIsDirectory(string FullPath)
{
FileAttributes attr = File.GetAttributes(FullPath);
return ((attr & FileAttributes.Directory) == FileAttributes.Directory);
}
private TreeNode CreateTreeNode(string FullPath)
{
string key = FullPath.ToLower();
string name = "";
object tag = null;
if(PathIsDirectory(key))
{
DirectoryInfo info = new DirectoryInfo(FullPath);
key = kDirectoryImageKey;
name = info.Name;
tag = info;
}
else
{
FileInfo info = new FileInfo(FullPath);
name = info.Name;
tag = info;
}
if(!m_FileIcons.Images.ContainsKey(key))
{
if(key == "directory")
{
m_FileIcons.Images.Add(key, IconReader.GetFolderIcon(Environment.CurrentDirectory, IconReader.IconSize.Small, IconReader.FolderType.Open).ToBitmap());
}
else
{
m_FileIcons.Images.Add(key, IconReader.GetFileIcon(FullPath, IconReader.IconSize.Small, false));
}
}
TreeNode node = new TreeNode(name);
node.Tag = tag;
node.ImageKey = key;
node.SelectedImageKey = key;
return node;
}
private void PopulateDirectory(TreeNode ParentNode)
{
DirectoryInfo parentInfo = ParentNode.Tag as DirectoryInfo;
foreach(DirectoryInfo subDir in parentInfo.GetDirectories())
{
TreeNode child = CreateTreeNode(subDir.FullName);
PopulateForExpansion(child);
ParentNode.Nodes.Add(child);
}
foreach(FileInfo file in parentInfo.GetFiles())
{
ParentNode.Nodes.Add(CreateTreeNode(file.FullName));
}
}
private void PopulateForExpansion(TreeNode ParentNode)
{
// We need the +/- to show up if this directory isn't empty... but only want to populate the node on demand
DirectoryInfo parentInfo = ParentNode.Tag as DirectoryInfo;
try
{
if((parentInfo.GetDirectories().Length > 0) || (parentInfo.GetFiles().Length > 0))
{
ParentNode.Nodes.Add(kReplacementText);
}
}
catch { }
}
private void ReplacePlaceholderDirectoryNode(TreeNode ParentNode)
{
if((ParentNode.Nodes.Count == 1) && (ParentNode.Nodes[0].Text == kReplacementText))
{
ParentNode.Nodes.Clear();
PopulateDirectory(ParentNode);
}
}
private void ExplorerTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
this.BeginUpdate();
ReplacePlaceholderDirectoryNode(e.Node);
this.EndUpdate();
}
}
}
TreeViewEx.cs:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TreeViewTestProject
{
public partial class TreeViewEx : TreeView
{
// Notes: TextRenderer uses GDI to render the text, whereas Graphics uses GDI+. "TreeView" has existed for a long long time
// and thus uses GDI under the covers. For User Drawing TreeNode's, we need to make sure we use the TextRenderer version
// of text rendering functions.
#region Properties
private DashStyle m_SelectionDashStyle = DashStyle.Dot;
public DashStyle SelectionDashStyle
{
get { return m_SelectionDashStyle; }
set { m_SelectionDashStyle = value; }
}
private DashStyle m_LineStyle = DashStyle.Solid;
public DashStyle LineStyle
{
get { return m_LineStyle; }
set { m_LineStyle = value; }
}
private bool m_ShowLines = true;
public new bool ShowLines // marked as 'new' to replace base functionality fixing ShowLines/FullRowSelect issues in base.
{
get { return m_ShowLines; }
set { m_ShowLines = value; }
}
protected override CreateParams CreateParams
{
get
{
// Removes all the flickering of repainting node's
// This is the only thing I found that works properly for doublebuffering a treeview.
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_CLIPCHILDREN
return cp;
}
}
#endregion
[DllImport("user32.dll", ExactSpelling = false, CharSet = CharSet.Auto)]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, HandleRef lParam);
private const int GWL_STYLE = -16;
private const int WS_VSCROLL = 0x00200000;
private const uint TV_FIRST = 0x1100;
private const uint TVM_EDITLABELA = (TV_FIRST + 14);
private const uint TVM_EDITLABELW = (TV_FIRST + 65);
private bool m_SelectionChanged = false;
private bool m_DoubleClicked = false;
private bool m_HierarchyChanged = false;
public TreeViewEx()
{
InitializeComponent();
// ShowLines must be "false" for FullRowSelect to work - so we're overriding the variable to correct for that.
base.ShowLines = false;
this.FullRowSelect = true;
this.ItemHeight = 21;
this.DrawMode = TreeViewDrawMode.OwnerDrawAll;
this.DrawNode += OnDrawNode;
}
private void OnDrawNode(object sender, DrawTreeNodeEventArgs e)
{
e.DrawDefault = false;
if(e.Node.Bounds.IsEmpty) return;
// Clear out the previous contents for the node. If we don't do this, when you mousehover the font will get slightly more bold
Rectangle bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - 1, e.Node.Bounds.Height - 1);
e.Graphics.FillRectangle(SystemBrushes.Window, bounds);
// Draw everything
DrawNodeFocusedHighlight(e);
DrawNodeLines(e);
DrawPlusMinus(e);
DrawNodeIcon(e);
DrawNodeText(e);
}
private void DrawNodeFocusedHighlight(DrawTreeNodeEventArgs e)
{
if(SelectedNode != e.Node) return;
int scrollWidth = 0;
if(VScrollVisible())
{
scrollWidth = SystemInformation.VerticalScrollBarWidth;
}
Rectangle bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - scrollWidth, e.Node.Bounds.Height - 1);
if(!e.Node.IsEditing)
{
e.Graphics.FillRectangle(SystemBrushes.Highlight, bounds);
}
using(Pen focusPen = new Pen(Color.Black))
{
focusPen.DashStyle = SelectionDashStyle;
bounds = new Rectangle(0, e.Node.Bounds.Y, this.Width - scrollWidth - 5, e.Node.Bounds.Height - 2);
e.Graphics.DrawRectangle(focusPen, bounds);
}
}
private void DrawNodeText(DrawTreeNodeEventArgs e)
{
if(e.Node.Bounds.IsEmpty) return;
if(e.Node.IsEditing) return;
Rectangle bounds = e.Node.Bounds;
using(Font font = e.Node.NodeFont)
{
bounds.Width = TextRenderer.MeasureText(e.Node.Text, font).Width;
bounds.Y -= 1;
bounds.X += 1;
if(IsRootNode(e.Node))
{
bounds = new Rectangle(this.Margin.Size.Width + Properties.Resources.minus.Width + 9, 0, bounds.Width, bounds.Height);
}
Color fontColor = SystemColors.InactiveCaptionText;
if(this.Focused)
{
fontColor = e.Node.IsSelected?SystemColors.HighlightText:this.ForeColor;
}
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, bounds, fontColor);
}
}
private bool IsRootNode(TreeNode Node)
{
return (Node.Level == 0 && Node.PrevNode == null);
}
private void DrawNodeLines(DrawTreeNodeEventArgs e)
{
DrawNodeLineVertical(e);
DrawNodeLineHorizontal(e);
}
private void DrawNodeLineVertical(DrawTreeNodeEventArgs e)
{
if(IsRootNode(e.Node)) return;
if(!ShowLines) return;
Pen linePen = new Pen(Color.Black);
linePen.DashStyle = LineStyle;
for(int x = 0; x < e.Node.Level; x++)
{
int xLoc = this.Indent + (x * this.Indent) + (Properties.Resources.minus.Width / 2);
int height = e.Bounds.Height;
if(ShouldDrawVerticalLineForLevel(e.Node, x))
{
e.Graphics.DrawLine(linePen, xLoc, e.Bounds.Top, xLoc, e.Bounds.Top + height);
}
}
// Draw the half line for the last node
if(e.Node.Parent.LastNode == e.Node)
{
int halfLoc = (e.Node.Level * this.Indent) + (Properties.Resources.minus.Width / 2);
e.Graphics.DrawLine(linePen, halfLoc, e.Bounds.Top, halfLoc, (e.Bounds.Top + e.Bounds.Height / 2) - 1);
}
}
private bool ShouldDrawVerticalLineForLevel(TreeNode Current, int Level)
{
TreeNode node = Current;
TreeNode c = Current;
while(node.Level != Level)
{
c = node;
node = node.Parent;
}
return !(node.LastNode == c);
}
private void DrawNodeLineHorizontal(DrawTreeNodeEventArgs e)
{
if(IsRootNode(e.Node)) return;
if(!ShowLines) return;
Pen linePen = new Pen(Color.Black);
int xLoc = (e.Node.Level * this.Indent) + (Properties.Resources.minus.Width / 2);
int midY = (e.Bounds.Top + e.Bounds.Bottom) / 2 - 1;
e.Graphics.DrawLine(linePen, xLoc, midY, xLoc + 7, midY);
}
private void DrawNodeIcon(DrawTreeNodeEventArgs e)
{
if(this.ImageList == null) return;
int indent = (e.Node.Level * this.Indent) + this.Margin.Size.Width;
int iconLeft = indent + this.Indent;
int imgIndex = this.ImageList.Images.IndexOfKey(e.Node.ImageKey);
if(!IsRootNode(e.Node))
{
if(imgIndex >= 0)
{
Image img = this.ImageList.Images[imgIndex];
int y = (e.Bounds.Y + e.Bounds.Height / 2) - (img.Height / 2) - 1;
e.Graphics.DrawImage(img, new Rectangle(iconLeft, y, img.Width, img.Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
}
}
}
private void DrawPlusMinus(DrawTreeNodeEventArgs e)
{
if(e.Node.Nodes.Count == 0) return;
int indent = (e.Node.Level * this.Indent) + this.Margin.Size.Width;
int iconLeft = indent + this.Indent;
Image img = Properties.Resources.plus;
if(e.Node.IsExpanded) img = Properties.Resources.minus;
e.Graphics.DrawImage(img, iconLeft - img.Width - 2, (e.Bounds.Y + e.Bounds.Height / 2) - (img.Height / 2) - 1);
}
private bool VScrollVisible()
{
int style = GetWindowLong(this.Handle, GWL_STYLE);
return ((style & WS_VSCROLL) != 0);
}
private void BeginEditNode()
{
if(this.SelectedNode == null) return;
if(!this.LabelEdit) throw new Exception("This TreeView is not configured with LabelEdit=true");
IntPtr result = SendMessage(new HandleRef(this, this.Handle), TVM_EDITLABELA, IntPtr.Zero, new HandleRef(this.SelectedNode, this.SelectedNode.Handle));
if(result == IntPtr.Zero)
{
throw new Exception("Failed to send EDITLABEL message to TreeView control.");
}
}
private void TreeViewEx_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
if(m_DoubleClicked)
{
m_DoubleClicked = false;
return;
}
if(m_SelectionChanged)
{
e.CancelEdit = true;
m_SelectionChanged = false;
}
}
private void TreeViewEx_MouseDoubleClick(object sender, MouseEventArgs e)
{
if(m_HierarchyChanged)
{
m_HierarchyChanged = false;
return;
}
if((e.Button & MouseButtons.Left) > 0)
{
if(this.LabelEdit && (this.SelectedNode != null))
{
m_DoubleClicked = true;
BeginInvoke(new MethodInvoker(delegate() { this.SelectedNode.BeginEdit(); }));
}
}
}
private void TreeViewEx_AfterCollapse(object sender, TreeViewEventArgs e)
{
m_HierarchyChanged = true;
}
private void TreeViewEx_AfterExpand(object sender, TreeViewEventArgs e)
{
m_HierarchyChanged = true;
}
}
}