The .net framework of course has the openfiledialog and savefiledialog components build in which do all the rough work. Also the AutoComplete source in .net 2.0 makes it very easy to use the file system as an auto complete source.
However, manually assigning those each time, adding a button to browse, etc. got a bit tiresome. Hence time for a little control.
The class below is a simple control that uses autocompletion and/or a browsebutton to get input for a file (with the option to switch between save mode or open mode).
Update 16-3 : drop functionality. Dropping from explorer and the likes was easy enough. The framework has good implementations for that, but outlook attachments was a bit harder: http://blogs.vbcity.com/hotdog/archive/2006/03/16/5896.aspx
Update 18-3: icon is shown as well.
Code listing:
NB: as the comments in top of the code mention you need the 2 other classes to have this function right. Those blocks are build up, so you only have to copy and paste them underneath (or above) this class and you're good to go. Sorry for the inconvenience, but if I would have added them here, it would have been too difficult in maintenance :-/ The classes are
http://blogs.vbcity.com/hotdog/archive/2006/03/16/5896.aspx and http://blogs.vbcity.com/hotdog/archive/2006/03/07/5873.aspx
namespace Subro.Controls
{
/*
* For complete functionality, you need these 2 components (if not added allready)
* the OutlookDropCatcher: http://blogs.vbcity.com/hotdog/archive/2006/03/16/5896.aspx
* the fileextension class: http://blogs.vbcity.com/hotdog/archive/2006/03/07/5873.aspx
*
*/
using System;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.IO;
using Subro.IO;
using System.Drawing;
using Subro.Outlook;
///
/// Control to quickly select files
/// [
DefaultProperty(
"FileName")]
[
DefaultEvent(
"FileNameChanged")]
public class FileSelect :
UserControl,
INotifyPropertyChanged{
#region Constructor
/// Source : http://blogs.vbcity.com/hotdog/archive/2006/03/03/5861.aspx
///
private FileBox txtFile;
private BrowseButton btn =
new BrowseButton();
public FileSelect()
{
CreateTextBox();
btn.Bounds = new Rectangle(Width - btn.Width - pad, pad, btn.Width, Height - 2 * pad-1);
btn.Anchor = AnchorStyles.Right | AnchorStyles.Top;
Controls.Add(btn);
btn.Click += new EventHandler(btn_Click);
base.AllowDrop = true;
file.FileChanged += new EventHandler(file_FileChanged);
}
#endregion
#region textbox
const int pad = 2;
void CreateTextBox()
{
txtFile = new FileBox(this);
SetControl(txtFile);
}
class FileBox :
TextBox{
readonly
FileSelect owner;
public FileBox(
FileSelect Owner)
{
this.owner = Owner;
AutoCompleteMode = AutoCompleteMode.SuggestAppend;
AutoCompleteSource = AutoCompleteSource.FileSystem;
AutoSize = false;
}
protected
override void OnHandleCreated(
EventArgs e)
{
base.OnHandleCreated(e);
UserPaint = true;
}
protected override void OnValidated(EventArgs e)
{
owner.EndEditCurrentFile();
base.OnValidated(e);
}
protected override void OnPaint(PaintEventArgs e)
{
owner.file.DrawFile(e.Graphics, DisplayRectangle, Font,true);
base.OnPaint(e);
}
protected
override void OnDoubleClick(
EventArgs e)
{
SelectAll();
base.OnDoubleClick(e);
}
bool UserPaint
{
set
{
SetStyle(ControlStyles.UserPaint, value);
}
}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
SelectAll();
UserPaint = false;
}
protected override void OnLeave(EventArgs e)
{
UserPaint = true;
base.OnLeave(e);
} }
#endregion
#region ListView (used for detailed and MultiSelect view)
FileList list;
class FileList :
ListBox{
public FileList(
FileSelect owner)
{
this.DrawMode = DrawMode.OwnerDrawFixed;
}
protected
override void OnDrawItem(
DrawItemEventArgs e)
{
FileData data = Items[e.Index]
as FileData;
if (data ==
null)
{
Font f = new Font(Font, FontStyle.Italic | FontStyle.Underline);
e.Graphics.DrawString("New", f, Brushes.Blue, 0, 0);
}
else
data.DrawFile(e.Graphics, e.Bounds, e.Font, true);
base.OnDrawItem(e);
}
protected
override void OnSelectedIndexChanged(
EventArgs e)
{
base.OnSelectedIndexChanged(e);
}
}
#endregion
#region Keys
protected override bool ProcessCmdKey(
ref Message msg,
Keys keyData)
{
if (keyData ==
Keys.F3)
{
Browse();
return true;
}
if (keyData ==
Keys.Enter)
{
EndEditCurrentFile();
}
return base.ProcessCmdKey(
ref msg, keyData);
}
#endregion
#region properties
private FileData file =
new FileData();
[
Description(
"The current file name")]
[
SettingsBindable(
true)]
[
DefaultValue(
null)]
public string FileName
{
get {
return file.FileName; }
set{
file.FileName = value;
}
}
void file_FileChanged(
object sender,
EventArgs e)
{
txtFile.Text = file.FileName;
OnPropertyChanged("FileName", FileNameChanged);
}
[
DesignerSerializationVisibility(
DesignerSerializationVisibility.Hidden)]
public FileData Info
{
get { return file; }
}
[
Browsable(
false)]
[
DesignerSerializationVisibility(
DesignerSerializationVisibility.Hidden)]
public string Folder
{
get {
return file.Folder; }
set{
file.Folder = value;
}
}
public event EventHandler FileNameChanged;
void EndEditCurrentFile()
{
FileName = txtFile.Text;
}
///
/// Returns if the selected file exists
///
public bool IsValid
{
get
{
return file.IsValid;
} }
[
DefaultValue(
true)]
[
Description(
"If true (Default) the user can type in files manually. If false, only the dialog (browse button) can be used")]
public bool AllowTextEdit
{
get {
return !txtFile.ReadOnly; }
set{
if (value == AllowTextEdit) return;
txtFile.ReadOnly = !value;
txtFile.BackColor = value ? Color.Empty : Color.White;
OnPropertyChanged("AllowTextEdit", null);
} }
private bool savemode;
[DefaultValue(false)]
[Description("The mode the file dialog is used. If this value is true, a savedialog is used, if false (default) an open dialog is used")]
public bool SaveMode
{
get {
return savemode; }
set{
if (savemode == value) return;
savemode = value;
fd = null;
OnPropertyChanged("SaveMode", null);
} }
string filter;
[DefaultValue(null)]
public string Filter
{
get
{
return filter;
}
set
{
if (
value !=
null &&
value.Trim().Length == 0)
value = null;
filter = value; } }
#endregion
#region Dialog
FileDialog fd;
///
/// Gets the underlying .
/// The type of dialog depends on the setting
///
public FileDialog FileDialog
{
get
{
if (fd ==
null)
fd = savemode ? (FileDialog)new SaveFileDialog() : new OpenFileDialog();
return fd;
} }
///
/// Opens the browse dialog
///
public void Browse()
{
if (file !=
null)
{
FileDialog.InitialDirectory = Folder;
if(file.IsValid)
FileDialog.FileName = file.Info.Name;
}
fd.Filter = filter;
if (fd.ShowDialog(
this) ==
DialogResult.OK)
{
FileName = fd.FileName;
}
}
#endregion
#region Drag/drop
[
DefaultValue(
true)]
public override bool AllowDrop
{
get
{
return base.AllowDrop;
}
set
{
base.AllowDrop = value;
} }
protected override void OnDragEnter(DragEventArgs e)
{
if (e.Data.GetDataPresent(
DataFormats.FileDrop))
e.Effect = DragDropEffects.Link;
else
if (e.Data.GetDataPresent(
OutlookAttachmentDropCatcher.GroupIdentifier))
e.Effect = DragDropEffects.Copy;
base.OnDragEnter(e);
}
protected
override void OnDragDrop(
DragEventArgs e)
{
if (e.Data.GetDataPresent(
DataFormats.FileDrop))
AddFiles(
(string[])e.Data.GetData(DataFormats.FileDrop));
else
if (e.Data.GetDataPresent(
OutlookAttachmentDropCatcher.GroupIdentifier))
{
AddFiles(OutlookAttachmentDropCatcher.GetFiles(e));
}
base.OnDragDrop(e);
}
#endregion
#region Selected files
public
const int
DefaultHeightSingle = 20,
DefaultHeightMulti = 60;
///
/// more than 1?....
/// Not supported yet...
/// [
DefaultValue(
false)]
bool MultiSelect
{
get
{
return list != null;
}
set
{
if (
value == MultiSelect)
return;
txtFile.Visible = !
value;
if (
value)
{
list =
new FileList(
this);
SetControl(list);
if(FileName !=
null)
list.Items.Add(file);
list.Items.Add(
"New");
}
else{
list.Dispose();
list = null;
}
if (DesignMode)
Height = value ? DefaultHeightMulti : DefaultHeightSingle;
} }
void AddFiles(
params string[] files)
{
if (files ==
null || files.Length == 0)
return;//FileName = null;
else if (MultiSelect)
{
list.Items.RemoveAt(list.Items.Count - 1);
foreach (
string s
in files)
{
list.Items.Add(new FileData(s));
}
list.Items.Add(
"New");
}
else
FileName = files[0];
}
#endregion
#region Browse button
void btn_Click(
object sender,
EventArgs e)
{
Browse();
txtFile.SelectAll();
txtFile.Focus();
}
class BrowseButton :
Button{
public BrowseButton()
{
FlatStyle = FlatStyle.Flat;
TabStop = false;
BackColor = Color.LightGray;
}
protected override Size DefaultSize
{
get
{
return new Size(18, 18);
} }
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
pevent.Graphics.DrawString("...", Font, Brushes.Black, 2, 0);
} }
#endregion
#region Appearance
protected override Size DefaultSize
{
get
{
return new Size(200, DefaultHeightSingle);
} }
void SetControl(
Control c)
{
if(c
is TextBox)
(c as TextBox).BorderStyle = System.Windows.Forms.BorderStyle.None;
else
(c as ListBox).BorderStyle = System.Windows.Forms.BorderStyle.None;
//no docking used because the displayrectangle property uses the Padding property as well
c.Bounds = new Rectangle(pad, pad, Width - btn.Width - pad * 2 - 1, Height - pad * 2);
c.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
Controls.Add(c);
}
protected override void OnPaint(
PaintEventArgs e)
{
Rectangle r = DisplayRectangle;
ControlPaint.DrawBorder3D(e.Graphics, r, bstyle);
}
static Border3DStyle defaultBorderStyle = Border3DStyle.Etched;
Border3DStyle bstyle = defaultBorderStyle;
public new Border3DStyle BorderStyle
{
get {
return bstyle; }
set{
bstyle = value;
Invalidate();
}
}
bool ShouldSerializeBorderStyle()
{
return BorderStyle != defaultBorderStyle;
}
[Browsable(false)]
public override Color BackColor
{
get
{
return Color.White;
}
set { } }
bool ShouldSerializeBackColor()
{
return false;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(
string Name,
EventHandler ChangedHandler)
{
if (ChangedHandler !=
null)
ChangedHandler(this, EventArgs.Empty);
if (PropertyChanged !=
null)
PropertyChanged(this, new PropertyChangedEventArgs(Name));
}
#endregion
}
}
. . .