I have a Windows forms desktop application with a webBrowser control. I navigate to a directory on the server webBrowser1.Navigate(new Uri("\\\\srvername\\share\\directory\\")) to allow users to open files from that directory.
When they double click on files they get a Windows Security message every time.
When they click OK, the file opens as desired.
If I set navigate to a local directory webBrowser1.Navigate(new Uri("C:\\Temp\\")); I don't get the message.
Is there a programmatic way to prevent this from showing or is this something that will have to be added to the browser as a trusted site? Standard users on our network don't have access to add trusted sites. I have tried to check the "Include all network paths UNC" in the local intranet section of IE.
I have tried
webBrowser1.ScriptErrorsSuppressed = true;
but that doesn't work as it seems to be meant for script errors happen from a webpage displayed in the control.
Well I found two resolutions to my question.
Is it possible? As #jacob and all my research suggests, I don't think it is. If you are going to use the webBrowser control to view local or UNC path you will have to make changes to IE security or deal with the message for every file that is opened.
Resulution #1
Change security settings in IE. My organization has everything locked down so changes to IE settings can only be made through group policy.
Add file://servername/* to the Intranet Zone via group policy for the desired domain OU.
Enable the IE setting “Launching applications and files in an IFRAME” policy via the group policy for the desired domain OU.
Resulution #2
Replace the webBrowser control in my app with other controls that can accomplish the same functionality. This is one I went with so I would have more control over all working of the files and directories. I also don't have to worry about the security settings and group policy enforcement being correct and working correctly. I also don't have to worry about changes in these areas effecting me.
Here is the article I used to get started
The walk through as it is works pretty well. I went through it in a one off project before integrating it into my project. I need to change a few things to suit me. Here is my code as it ended up. I made some changes and added some selecting/clicking events.
Boolean FocusTreeview = false;
public frmMain()
{
InitializeComponent();
treeView1.NodeMouseClick += new TreeNodeMouseClickEventHandler(treeView1_NodeMouseClick);
}
// Start by calling this.
private void PopulateTreeView()
{
TreeNode rootNode;
treeView1.Nodes.Clear();
listView1.Items.Clear();
DirectoryInfo info = new DirectoryInfo("Pass the root folder here.");
// Add the root directory.
if (info.Exists)
{
rootNode = new TreeNode(info.Name);
rootNode.Tag = info;
GetDirectories(info.GetDirectories(), rootNode);
treeView1.Nodes.Add(rootNode);
treeView1.Nodes[0].Expand();
// This selects the root directory
NodeSelect(rootNode);
}
}
/********************************************/
private void GetDirectories(DirectoryInfo[] subDirs, TreeNode nodeToAddTo)
{
// This will list all the directories and subdirectories
TreeNode aNode;
DirectoryInfo[] subSubDirs;
foreach (DirectoryInfo subDir in subDirs)
{
aNode = new TreeNode(subDir.Name, 0, 0);
aNode.Name = subDir.ToString();
aNode.Tag = subDir;
aNode.ImageKey = "Directory";
subSubDirs = subDir.GetDirectories();
if (subSubDirs.Length != 0)
{
GetDirectories(subSubDirs, aNode);
}
nodeToAddTo.Nodes.Add(aNode);
}
}
/********************************************/
private void NodeSelect(TreeNode newSelected)
{
//Load all the files and directorys in the selected node from the treeview
listView1.Items.Clear();
DirectoryInfo nodeDirInfo = (DirectoryInfo)newSelected.Tag;
ListViewItem.ListViewSubItem[] subItems;
ListViewItem item = null;
foreach (DirectoryInfo dir in nodeDirInfo.GetDirectories())
{
item = new ListViewItem(dir.Name, 0);
subItems = new ListViewItem.ListViewSubItem[]
{new ListViewItem.ListViewSubItem(item, "Directory"),
new ListViewItem.ListViewSubItem(item,
dir.LastAccessTime.ToShortDateString())};
item.SubItems.AddRange(subItems);
listView1.Items.Add(item);
}
foreach (FileInfo file in nodeDirInfo.GetFiles())
{
item = new ListViewItem(file.Name, 1);
subItems = new ListViewItem.ListViewSubItem[] { new ListViewItem.ListViewSubItem(item, "File"), new ListViewItem.ListViewSubItem(item, file.LastAccessTime.ToShortDateString()) };
item.SubItems.AddRange(subItems);
listView1.Items.Add(item);
}
listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
}
/********************************************/
void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
TreeNode newSelected = e.Node;
NodeSelect(newSelected);
}
/********************************************/
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
// When something is selected load it's contents to the listview if it is a directory
if (listView1.SelectedItems.Count > 0)
{
var item = listView1.SelectedItems[0];
// Don't do anything if it's just a file
if (item.SubItems[1].Text == "File")
{
return;
}
TreeNode[] tns = treeView1.Nodes.Find(item.Text, true);
if (tns.Length > 0)
{
treeView1.Focus();
NodeSelect(tns[0]);
treeView1.SelectedNode = tns[0];
tns[0].Expand();
FocusTreeview = true;
}
}
}
/********************************************/
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
// This needs to be done so the directory appears to stay selected in the treeview
if (FocusTreeview == true)
{
treeView1.Focus();
FocusTreeview = false;
}
}
/********************************************/
private void listView1_DoubleClick(object sender, EventArgs e)
{
// This will open the files that are double clicked on
if (listView1.SelectedItems.Count > 0)
{
var item = listView1.SelectedItems[0];
// Don't do anything for directories
if (item.SubItems[1].Text == "Folder")
{
return;
}
// Open the selected file
Process.Start(globals.szJobFolderPath + item.Text);
}
}
It ended up looking like this.
You do lose the icons of known files like Word docs but that is not a big deal to me. You could add icons for the known file types you will have to your imagelist and put some if statements (or switch cases) in the section where it adds items to the listview. Just analyze the file extensions.
Hope this helps somebody someday!
Related
Beginner here with C# coding. I built a media player that allows the administrator to create and save a playlist. The guest should be able to load the playlist and play the songs on the said playlist. Unfortunately, every time the guest loads a playlist created, error pops out when trying to play any songs on the playlist. It seems to only load the path details but not the actual media.
"An unhandled exception of type 'System.IndexOutOfRangeException' occurred in WindowsFormsApplication1.exe
Additional information: Index was outside the bounds of the array."
This is the code under the list box I have:
axWindowsMediaPlayer1.URL = paths[lbPlaylist.SelectedIndex];
The code code below is coded under the "Create Playlist" button:
private void btnCreate_Click(object sender, EventArgs e)
{
OpenFileDialog newPlaylist = new OpenFileDialog();
newPlaylist.InitialDirectory = "C:\\Users\\mklsingh\\Documents\\Visual Studio 2013\\Projects\\Media Player\\WindowsFormsApplication1\\Media Files";
newPlaylist.Filter = "MP3 Audio File (*.mp3)|*.mp3| Windows Media File (*.wma)|*.wma|WAV Audio File (*.wav)|*.wav|All Files (*.*)|*.*";
newPlaylist.RestoreDirectory = false;
newPlaylist.Multiselect = true;
if (newPlaylist.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
files = newPlaylist.SafeFileNames;
paths = newPlaylist.FileNames;
for (int list = 0; list < files.Length; list++)
{
lbPlaylist.Items.Add(files[list]);
}
}
}
The code below saves the playlist as XML file:
private void btnSave_Click(object sender, EventArgs e)
{
StreamWriter Write;
SaveFileDialog savePlaylist = new SaveFileDialog();
savePlaylist.RestoreDirectory = false;
try
{
savePlaylist.InitialDirectory = "C:\\Users\\mklsingh\\Documents\\Visual Studio 2013\\Projects\\Media Player\\WindowsFormsApplication1\\Media Files\\Playlist";
savePlaylist.Filter = ("XML File|*.xml|All Files|*.*");
savePlaylist.ShowDialog();
Write = new StreamWriter(savePlaylist.FileName);
for (int I = 0; I < lbPlaylist.Items.Count; I++)
{
Write.WriteLine(lbPlaylist.Items[I]);
}
Write.Close();
MessageBox.Show("Playlist saved!");
}
catch //(Exception ex)
{
return;
}
}
The code below loads the saved XML playlist file:
private void btnLoad_Click(object sender, EventArgs e)
{
OpenFileDialog loadPlaylist = new OpenFileDialog();
loadPlaylist.Multiselect = false;
this.lbPlaylist.Items.Clear();
try
{
loadPlaylist.ShowDialog();
loadPlaylist.InitialDirectory = "C:\\Users\\mklsingh\\Documents\\Visual Studio 2013\\Projects\\Media Player\\WindowsFormsApplication1\\Media Files\\Playlist";
//txtLoad.Text = loadPlaylist.Filename;
StreamReader playlist = new StreamReader(loadPlaylist.FileName);
while (playlist.Peek() >= 0)
lbPlaylist.Items.Add(playlist.ReadLine());
txtLoad.Text = loadPlaylist.FileName;
}
catch
{
return;
}
}
Also, if a user selects a single song and play it, it'll work. If the user decides to add a song on the current playlist, the selected song will not play and same error will pop out. If I click "Clear List" and select a new song, it'll work though.
Please let me know your thoughts on my code. I am still a beginner and I find it hard to understand some of the codes I see online. Haha. Just want to make my Save Playlist and Create Playlist button work. Thanks.
Before you do
xWindowsMediaPlayer1.URL = paths[lbPlaylist.SelectedIndex];
Check to make sure selectedIndex actually exists in paths
if (lbPlaylist.SelectedIndex < paths.Length)
{
xWindowsMediaPlayer1.URL = paths[lbPlaylist.SelectedIndex];
}
else
{
// Display an error?
}
This should at least help you with debugging I would suggest putting a break point somewhere and checking to see what the value of paths is. It's possible you didn't initialize or add to it correct so you are trying to access an entry in the array that doesn't exist.
I finally figured out how to print transformed XML without prompting the user or showing an IE window, but now I need to specify a number of copies and possibly other printer settings.
Is there a way to programmatically change printer settings on a WebBrowser control?
The code in question:
private static void PrintReport(string reportFilename)
{
WebBrowser browser = new WebBrowser();
browser.DocumentCompleted += browser_DocumentCompleted;
browser.Navigate(reportFilename);
}
private static void browser_DocumentCompleted
(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser browser = sender as WebBrowser;
if (null == browser)
{
return;
}
browser.Print();
browser.Dispose();
}
The only method I've had success with is modifying the registry on the fly (and changing them back to not affect anything else).
You can find the settings you need at "Software\Microsoft\Internet Explorer\PageSetup" under CurrentUser.
To change the printer, you can use this:
using System.Management
public static bool SetDefaultPrinter(string defaultPrinter)
{
using (ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer"))
{
using (ManagementObjectCollection objectCollection = objectSearcher.Get())
{
foreach (ManagementObject mo in objectCollection)
{
if (string.Compare(mo["Name"].ToString(), defaultPrinter, true) == 0)
{
mo.InvokeMethod("SetDefaultPrinter", null, null);
return true;
}
}
}
}
return false;
}
As for the number of copies, you can always put the WebBrowser.Print in a while loop.
string strKey = "Software\\Microsoft\\Internet Explorer\\PageSetup";
bool bolWritable = true;
RegistryKey oKey = Registry.CurrentUser.OpenSubKey(strKey, bolWritable);
Console.Write(strKey);
if (stringToPrint.Contains("Nalog%20za%20sluzbeno%20putovanje_files"))
{
oKey.SetValue("margin_bottom", 15);
oKey.SetValue("margin_top", 0.19);
}
else
{
//Return onld walue
oKey.SetValue("margin_bottom", 0.75);
oKey.SetValue("margin_top", 0.75);
}
you need to change registry settings via code to change settings for internet explorer or the web browser control. check out the link below, it describes how to do so, also if there's more options you need to alter using the registry, then use regedit.exe to find what other keys internet explorer has.
http://support.microsoft.com/kb/236777
ps: you should note that any changes you make via your code to internet explorer's registry settings will persist on your system/user account.
This worked well for me, however I am on .NET 3.5
this.webBrowser1.ShowPrintDialog();
I tried figuring out the following issue but I am not able to do it as I just started programming a month ago.
I have a listbox of 20 items:
private void loadDownloadXMLListBox()
{
var items = new[] { "BARC", "DEV", "DOM", "EZJ", "GFS",
"IHG", "JD.", "LAD", "LLOY", "MRW",
"NXT", "OCDO", "RBS", "SMWH", "SPD",
"STAN", "SYR", "TALK", "TSCO", "WMH" };
foreach (var item in items) listDownloadXML.Items.Add(item);
listDownloadXML.SelectedIndex = -1;
}
This is my code for downloading a single selected file from a website:
private void btnDownloadXML_Click(object sender, EventArgs e)
{
using (WebClient client = new WebClient())
{
client.DownloadFile("http://www.lse.co.uk/chat/" + listDownloadXML.SelectedItem,
#"..\..\sharePriceXML\" +
listDownloadXML.SelectedItem + ".xml");
}
MessageBox.Show("Download Completed! File has been placed in the folder sharePriceXML!");
}
I want to click on a button "Download All" then, all the 20 items from the website will be downloaded. May I know how can I loop through the 20 items in the listbox and download them all into a folder without selecting item(s)? Thank you.
foreach (var item in listDownloadXML.Items)
{
//... your code to download "item".
}
You could start by externalizing the download a single file functionality into a separate, reusable method:
public void DownloadFile(string item)
{
using (var client = new WebClient())
{
client.DownloadFile(
"http://www.lse.co.uk/chat/" + item,
#"..\..\sharePriceXML\" + item + ".xml"
);
}
MessageBox.Show("Download Completed! File has been placed in the folder sharePriceXML!");
}
and then:
private void btnDownloadXML_Click(object sender, EventArgs e)
{
DownloadFile((string)listDownloadXML.SelectedItem);
}
and now to your question about multiple files => you use a loop and call the DownloadFile method for each element in the listbox:
foreach (string item in listDownloadXML.Items)
{
DownloadFile(item);
}
Okay, I have a TreeView that serves as a directory tree for Windows. I have it loading all the directories and functioning correctly, but it is pausing my GUI while it loads directories with many children. I'm trying to implement multithreading, but am new to it and am having no luck.
This is what I have for my TreeView:
private readonly object _dummyNode = null;
public MainWindow()
{
InitializeComponent();
foreach (string drive in Directory.GetLogicalDrives())
{
DriveInfo Drive_Info = new DriveInfo(drive);
if (Drive_Info.IsReady == true)
{
TreeViewItem item = new TreeViewItem();
item.Header = drive;
item.Tag = drive;
item.Items.Add(_dummyNode);
item.Expanded += folder_Expanded;
TreeViewItemProps.SetIsRootLevel(item, true);
Dir_Tree.Items.Add(item);
}
}
}
private void folder_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = (TreeViewItem)sender;
if (item.Items.Count == 1 && item.Items[0] == _dummyNode)
{
item.Items.Clear();
try
{
foreach (string dir in Directory.GetDirectories(item.Tag as string))
{
DirectoryInfo tempDirInfo = new DirectoryInfo(dir);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
TreeViewItem subitem = new TreeViewItem();
subitem.Header = tempDirInfo.Name;
subitem.Tag = dir;
subitem.Items.Add(_dummyNode);
subitem.Expanded += folder_Expanded;
subitem.ToolTip = dir;
item.Items.Add(subitem);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
Whenever I expand a directory that has a large number of subdirectories, the program appears to be frozen for a few seconds. I would like to display a loading message or animation while it's processing, but I'm not sure how to begin with multithreading. I know I have to use the TreeView's Dispatcher.BeginInvoke method, but other than that I'm kinda lost.
Any help would be greatly appreciated!!!
One of the easiest ways to start a async process is to use an anonymous delegate with BeginInvoke. As an example you could move your code in the constructor to a separate method (say RenderTreeView) and then call it asynchronously to begin a new thread as follows:
Action action = RenderTreeView;
action.BeginInvoke(null, null);
The trick to this is that any time you interact with any UI elements from the async process you need to rejoin the main UI thread, otherwise you will get an exception about cross thread access. This is relatively straight forward as well.
In Windows Forms it's:
if (InvokeRequired)
Invoke(new MethodInvoker({item.Items.Add(subitem)}));
else
item.Items.Add(subitem);
In WPF it's:
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(new Action(() => item.Items.Add(subitem)));
else
item.Items.Add(subitem);
You really need to break up the code to make it more flexible in terms of methods. At the moment everything is bundled in one method which makes it hard to work with and re-factor for async processes.
Update Here you go :)
public partial class MainWindow : Window
{
private readonly object dummyNode = null;
public MainWindow()
{
InitializeComponent();
Action<ItemCollection> action = RenderTreeView;
action.BeginInvoke(treeView1.Items, null, null);
}
private void RenderTreeView(ItemCollection root)
{
foreach (string drive in Directory.GetLogicalDrives())
{
var driveInfo = new DriveInfo(drive);
if (driveInfo.IsReady)
{
CreateAndAppendTreeViewItem(root, drive, drive, drive);
}
}
}
private void FolderExpanded(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem) sender;
if (item.Items.Count == 1 && item.Items[0] == dummyNode)
{
item.Items.Clear();
var directory = item.Tag as string;
if (string.IsNullOrEmpty(directory))
{
return;
}
Action<TreeViewItem, string> action = ExpandTreeViewNode;
action.BeginInvoke(item, directory, null, null);
}
}
private void ExpandTreeViewNode(TreeViewItem item, string directory)
{
foreach (string dir in Directory.GetDirectories(directory))
{
var tempDirInfo = new DirectoryInfo(dir);
bool isSystem = ((tempDirInfo.Attributes & FileAttributes.System) == FileAttributes.System);
if (!isSystem)
{
CreateAndAppendTreeViewItem(item.Items, tempDirInfo.Name, dir, dir);
}
}
}
private void AddChildNodeItem(ItemCollection collection, TreeViewItem subItem)
{
if (Dispatcher.CheckAccess())
{
collection.Add(subItem);
}
else
{
Dispatcher.Invoke(new Action(() => AddChildNodeItem(collection, subItem)));
}
}
private void CreateAndAppendTreeViewItem(ItemCollection items, string header, string tag, string toolTip)
{
if (Dispatcher.CheckAccess())
{
var subitem = CreateTreeViewItem(header, tag, toolTip);
AddChildNodeItem(items, subitem);
}
else
{
Dispatcher.Invoke(new Action(() => CreateAndAppendTreeViewItem(items, header, tag, toolTip)));
}
}
private TreeViewItem CreateTreeViewItem(string header, string tag, string toolTip)
{
var treeViewItem = new TreeViewItem {Header = header, Tag = tag, ToolTip = toolTip};
treeViewItem.Items.Add(dummyNode);
treeViewItem.Expanded += FolderExpanded;
return treeViewItem;
}
}
Multithreading may not help much here because the TreeView has to be updated on it's Dispatcher thread.
TreeViews will pause when loading a large number of entries. One way to get around this is to store the contents into an object that mirrored the TreeView structure, and then programmatically load just the first level of the TreeView.
When a user clicks on a node, load the next level of child nodes and expand the node. When that node is collapsed, delete its child nodes to conserve TreeView memory. This has worked well for me. For exceedingly large structures I've used a local Sqlite database (via System.Data.Sqlite) as my backing store and even then the TreeView loaded quickly and was responsive.
You can also look at using BackgroundWorker; that's easiest way of executing an operation on a separate thread(For me :) ).
BackgroundWorker Component Overview: http://msdn.microsoft.com/en-us/library/8xs8549b.aspx
BackgroundWorker Class: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
You can use it with Command pattern as explained here -
Asynchronous WPF Commands
I want to make a treeview that shows all folders on the system, and only shows music files, such as .mp3 .aiff .wav etc.
I remember reading that I need to use a recursive function or something along those lines.
Usually most computers have thousands of folders and hundreds of thousands of files, so displaying all of them in a TreeView recursively with be very slow and consume a lot of memory, view my answer in this question, citing my answer with some modifications when can get a pretty usable GUI:
// Handle the BeforeExpand event
private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
if (e.Node.Tag != null) {
AddDirectoriesAndMusicFiles(e.Node, (string)e.Node.Tag);
}
}
private void AddDirectoriesAndMusicFiles(TreeNode node, string path)
{
node.Nodes.Clear(); // clear dummy node if exists
try {
DirectoryInfo currentDir = new DirectoryInfo(path);
DirectoryInfo[] subdirs = currentDir.GetDirectories();
foreach (DirectoryInfo subdir in subdirs) {
TreeNode child = new TreeNode(subdir.Name);
child.Tag = subdir.FullName; // save full path in tag
// TODO: Use some image for the node to show its a music file
child.Nodes.Add(new TreeNode()); // add dummy node to allow expansion
node.Nodes.Add(child);
}
List<FileInfo> files = new List<FileInfo>();
files.AddRange(currentDir.GetFiles("*.mp3"));
files.AddRange(currentDir.GetFiles("*.aiff"));
files.AddRange(currentDir.GetFiles("*.wav")); // etc
foreach (FileInfo file in files) {
TreeNode child = new TreeNode(file.Name);
// TODO: Use some image for the node to show its a music file
child.Tag = file; // save full path for later use
node.Nodes.Add(child);
}
} catch { // try to handle use each exception separately
} finally {
node.Tag = null; // clear tag
}
}
private void MainForm_Load(object sender, EventArgs e)
{
foreach (DriveInfo d in DriveInfo.GetDrives()) {
TreeNode root = new TreeNode(d.Name);
root.Tag = d.Name; // for later reference
// TODO: Use Drive image for node
root.Nodes.Add(new TreeNode()); // add dummy node to allow expansion
treeView1.Nodes.Add(root);
}
}
Recursively searching all drives for particular files is not going to work well. It will take about a minute to do so with today's large drives.
One standard trick, used by Windows Explorer, is to only list the top level directories and files. It puts a dummy node in a directory node. When the user opens the node (BeforeExpand event), it searches only that directory and replaces the dummy node with the directories and files found it that directory. Again putting a dummy node in the directories. Etcetera.
You can see this at work by adding an empty subdirectory. The directory node will be shown with the + glyph. When you open it, Explorer discovers that there are no directory or files to be listed and deletes the dummy node. The + glyph disappears.
This is very fast, listing the content of a single directory takes well less than a second. There's a problem however using this approach in your case. The odds that a directory contains a suitable music file are small. The user will constantly be frustrated by finding out that the navigating through a set of subdirectories produces nothing.
That's why Windows has a dedicated place to store specific media files. My Music in this case. Use Environment.GetFolderPath(Environment.SpecialFolder.MyMusic) to find it. Iterating it should not take long.