Showing strings in ListView columns dynamically - not working - c#

My goal is the following:
The user enters the stuff as shown in this picture. The 3 items (source file, destination directory and if file exists) are saved in a string array. User clicks OK.
After clicking OK the previous window is closed and the user is taken to the main form shown in this picture. The previously mentioned source file and the destination directory are shown in the table.
I am using the following code to show the entered file & directory in the table:
private void okButton_Clicked(object sender, EventArgs e)
{
//saving user's input
userInput = new string[3];
userInput[0] = sourceFileTextBox.Text;
userInput[1] = destinationDirComboBox.SelectedItem.ToString();
userInput[2] = ifFileExistsComboBox.SelectedItem.ToString();
//creating a new ListView object - the object is derived from the ListView Class
and has absolutely nothing in the constructor or anywhere
Classes.ListViewDerivative lvd = new Classes.ListViewDerivative();
ListViewItem item1 = new ListViewItem(userInput[0]);
item1.SubItems.Add(userInput[1]);
lvd.Items.AddRange(new ListViewItem[] { item1 });
this.DialogResult = DialogResult.OK;
this.Hide();
}
It doesn't work. The table is empty no matter where I put this code - I've tried to put this adapted code in the ListViewDerivative constructor, a function in the ListViewDerivative Class and in the editFileEntry(first picture) class. The right text is saved in the array but is not shown in the table. Please help!

the lvd variable is not used anywere. You have to add the 'lvd' -control to your mainview. You can use the Designer for this and in the Dialog Result of your "FilePickerDialog" you can assign the results to your datagrid.
some pseudo code for your MainView
void Config_Clicked()
{
ConfigDlg dlg = new ConfigDlg();
if(dlg.ShowDialog() == OK)
{
this.myListView1.Items.Add(dlg.userInput[0]);
}
}

Related

Winforms TextBox keeps value in UserControl

I am making an app in which there are multiple UserControls stacked onto each other. So the elements go like this: MainForm -> User clicks on a UserControl1 on the MainForm which (UserControl1) has a panel on which there is displayed another UserControl2 with a button. When the user clicks on it, it displays another UserControl3 which is then displayed in the panel beneath the button, where finally the user enters some text in the textbox. I need the data from the textbox in the MainForm so I have MainForm and UserControls connected via EventHandlers and pass my ResponseModel in which there is some dat a that I need to pass to MainForm. The first time this works, an item is created and displayed, after the item there is this "button" (User controls) displayed, in case the user wants to create another one. But then comes the problem when the user types in a different text for a new item, it creates an item with the same text!! Like the textbox was never changed (I have a debugging point set on the constructor to see every time that the textbox is empty). Below is some code and an image, for you to see how this should work. Also when I first delete the item it then doesn't work to create a new item for some reason.
This is how I send the data from the last UserControl:
if (tbx_list_name.Text == "")
MessageBox.Show("You can't create new list without a name!", "Can't create new list!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
else
CreateListTextBoxHandler?.Invoke(this, new ListCreationResponseModel() {
Code = id, ListName = tbx_list_name.Text
});
This is how I create the last UserControl which has the textbox:
control.CreateListTextBoxHandler += GetHandlerData;
panel.Controls.Add(control);
And this is how I get the data one stage down (this practice continues through couple more stages back to MainForm):
public void GetHandlerData(object sender, ListCreationResponseModel e)
{
try
{
panel.Controls.Clear();
CreateListButtonHandler?.Invoke(this, e);
}
catch (Exception ex)
{
_ = new ErrorHandler(ex);
}
}
You seem to have a recursion here. GetHandlerData is added to the CreateListTextBoxHandler event (or delegate) and invokes CreateListTextBoxHandler again, which will call GetHandlerData again...?? But tbx_list_name.Text is passed to the model only once at the top level down to all the other calls.
You can fix this by passing a reference to the textbox instead of the text itself. Then you will always be able to retrieve the current text of the textbox.
public class ListCreationResponseModel
{
private readonly TextBox _listNameTextBox;
public ListCreationResponseModel(TextBox listNameTextBox)
{
_listNameTextBox = listNameTextBox;
}
public int Code { get; set; }
public string ListName => _listNameTextBox .Text;
}
Now, when you retrieve the ListName you don't get a stored value but the actual text of the textbox.
You can create the handler like this:
CreateListTextBoxHandler?.Invoke(this, new ListCreationResponseModel(tbx_list_name) {
Code = id
});

How to fix ListView.LargeImageList showing images twice

I am working on a piece of software, which compares memes and helps users organize memes on their computer. As a part of this I am using Windows.Forms to build a UI. This UI lets the user add folders to be checked for images, which can be compared to a set of known meme templates.
My issue arises when I try to show the user the found images. To do this I am using a ListView and the property LargeImageList to contain a tuple of the image and the name of the image file.
Here is the piece of code in question:
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
var ic = new ImageCollection();
var fbd = new FolderBrowserDialog();
fbd.Description = "Select meme folder or image.";
if (fbd.ShowDialog() == DialogResult.OK)
{
string[] files = Directory.GetFiles(fbd.SelectedPath);
foreach (var file in files)
{
if (!ic.CheckIfImage(file)) continue;
imageList1.Images.Add(Image.FromFile(file));
}
foreach (var file in files)
{
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
}
}
}
This is an example of what the user sees when they first load in a folder. When the user tries to load in another folder this happens. It shows the images from the first folder, with the names of the image files from the second folder.
Does anyone know a fix for this issue? I have tried a variety of options in order to get around the issue. All from trying to clear the ImageList used to contain the images, to trying my hand at controlling when the ListView updates. None of this has worked. I have also tried googling the issue, but with no luck of finding a fix.
Thank you in advance.
If you want to show the content of a single folder at the time, then dispose of the objects in your ImageList.
If you instead want to show the content of more than one folder, you need to specify the new index of the image added. You're instead adding a new Item in the ListView using the same index reference:
int i = 0;
//(...)
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
The indexer (i) always starts from 0, thus the ListView Item will use the images in your Imagelist starting from the Image at Index[0] each time. The new images won't ever be shown.
You can use the ImageList.Images.Count value, representing the number of Images already added to the ImageList, as base and increment the indexer starting from this value:
private void button1_Click(object sender, EventArgs e)
{
int i = imageList1.Images.Count;
var ic = new ImageCollection();
var fbd = new FolderBrowserDialog();
fbd.Description = "Select meme folder or image.";
if (fbd.ShowDialog() == DialogResult.OK)
{
foreach (var file in Directory.GetFiles(fbd.SelectedPath))
{
if (!ic.CheckIfImage(file)) continue;
imageList1.Images.Add(new Bitmap(file, true));
listView1.Items.Add($"{Path.GetFileNameWithoutExtension(file)}", i++);
}
}
}
If you allow to remove an Image from the ListView, you should also remove it from the ImageList: this implies that you need to re-index all the ListView Items starting from the Item that follows the one removed.
Remember to dispose of the Images you remove from the ImageList.

How do I get the Bitmap name when I click the image in datagridview?

I am trying to click on an image in a datagridview and then write its image/file name into a textbox so I can access this from elsewhere.
First I try just a small app to make sure I can make it all work. A Dialog contains the dataviewgrid and I put a bitmap into it as below:
public ChooseFormat()
{
InitializeComponent();
dataGridView1[0,0].Value = new Bitmap(#"C:\a\eggs\grid_app\grid_app\bin\Debug\graphics\1L5HQ60.bmp");
}
Now I click on the image but all the things I have tried I cannot get hold of the file name. The closest I get is below but this returns "System.Drawing.Bitmap" and not the file name. I am sure this must just be a tweak here to make it work but I have tried teh few things I know and nothing is working.
void DataGridView1CellContentClick(object sender, DataGridViewCellEventArgs e)
{
txtbx_choice.Text = dataGridView1[0,0].Value.ToString();
}
Drilling into the cells's data in the debugger doesn't bring up any info on the source of it. Maybe I have overlooked something..
One simple solution is to store the filename in the cell's Tag property:
string fileName = #"C:\a\eggs\grid_app\grid_app\bin\Debug\graphics\1L5HQ60.bmp";
dataGridView1[0,0].Value = new Bitmap(fileName );
dataGridView1[0,0].Tag = fileName ;
Now you can always access it:
string displayedFile = dataGridView1[0, someRow].Tag.ToString();
I have placed a Picture Box on the same form and this how I am displaying the ImageColumn's data (an Image ) in picture box
pictureBox1.Image = (Image)dataGridView1[0, 0].Value;

Can't Add Tab Page To TabControl Inside The Constructor Of My WinForms Application

I have a C# Form application that has a TabControl in the main form. This TabControl is used to display multilple TabPages that contain a CustomControl. This CustomControl is just a Panel with a few buttons and a PictureBox.
Here is a picture of my app when it starts up. As you can see the tab control (the white area) is empty:
If the user clicks the "Add Image" button they are presented with an OpenFileDialog to select the image then the addImage method is called with the selected file:
private void doAddImage()
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = Constants.Global.IMAGE_FILE_FILTER();
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string imageFileName = openFileDialog.FileName;
addImage(imageFileName);
}
}
private void addImage(string imageFileName)
{
// Create a new bitmap and image preview custom control. Then create a new tab
// page and add the custom control to the tab page.
Bitmap bitMap = new Bitmap(imageFileName);
ImagePreviewCustomControl previewControl = new ImagePreviewCustomControl(bitMap);
previewControl.Dock = DockStyle.Fill;
TabPage tabPage = new TabPage(Path.GetFileNameWithoutExtension(imageFileName));
tabPage.Controls.Add(previewControl);
// Insert the new tab page to the right of the currently selected tab page
int selectedTabIndex = imagesTabControl.SelectedIndex;
imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex + 1;
}
As you can see, in the addImage method I create the Bitmap, CustomControl, and TabPage and then insert it into the TabControl.
I start my application, click the "Add Image" button, everything works just fine.
Here is a picture with a tab page added:
While I am testing my app I don't want to have to add an image manually using the OpenFileDialog every time so in my constructor I just call addImage with some fixed image file name that I want to test with:
public ImageViewerApp()
{
InitializeComponent();
addImage(#"C:\MyImages\Calculator-3-icon.png");
}
The problem I am having is that when I try to add the image in my constructor it doesn't show up in the TabControl. The application starts up blank (like the first picture).
As stated above when the application is already running and I click the "Add Image" button it gets added just fine.
I found a property in the TabControl class called Created which states:
"Gets a value indicating whether the control has been created"
So to try and figure out what's going on I write the value of Created to the console just before I call addImage in the constructor. (I have a custom console for debugging my Form applications.)
public ImageViewerApp()
{
InitializeComponent();
TestConsole.WriteLine(imagesTabControl.Created);
addImage(#"D:\Development\Work\Other\Stardock\Start8\_downloaded\Calculator-3-icon.png");
}
The value of Created just before the call to addImage in the constructor is:
False
I put another console output inside the addImage method:
private void doAddImage()
{
TestConsole.WriteLine(imagesTabControl.Created);
OpenFileDialog openFileDialog = new OpenFileDialog();
...
...
}
The value of Created after the app has started and the user presses the "Add Image" button is:
True
Why is it that the TabControl is not Created inside my constructor (even after the InitializeComponent() call) and the once the application is running it is Created?
=UPDATE========================================================================
Based on the suggestion by Hans Passant I have added the following code to my addImage method:
int selectedTabIndex = -1;
if (imagesTabControl.TabCount > 0)
{
selectedTabIndex = imagesTabControl.SelectedIndex;
}
else
{
selectedTabIndex = imagesTabControl.SelectedIndex + 1;
}
imagesTabControl.TabPages.Insert(selectedTabIndex, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex;
This doesn't work.
===============================================================================
=UPDATE2=======================================================================
int selectedTabIndex = imagesTabControl.SelectedIndex;
if (imagesTabControl.TabCount == 0) selectedTabIndex = -1;
imagesTabControl.TabPages.Insert(selectedTabIndex, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex;
This causes the following Exception:
{"InvalidArgument=Value of '-1' is not valid for 'index'.\r\nParameter name: index"}
===============================================================================
=UPDATE3=======================================================================
I tried the folllowing code:
int selectedTabIndex = imagesTabControl.SelectedIndex;
if (imagesTabControl.TabCount == 0) selectedTabIndex = -1;
imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage);
imagesTabControl.SelectedIndex = selectedTabIndex + 1;
This one doesn't throw an exception but again no tab page added after calling
addImage in the constructor.
===============================================================================
=UPDATE4=======================================================================
I have kindof given up on adding an image in the constructor. So instead I am using an enum RunMode and a variable RUN_MODE of that type. Then, if RUN_MODE == RunMode.TESTI call a method to add a random image when I click the button. (The OpenFileDialog is not used. I just parse through all the image files in the fixed directory IMAGE_DIRECTORY.
enum RunMode { NORMAL, TEST }
private static string IMAGE_DIRECTORY = #"D:\\Work\Images";
...
...
private void doAddImage()
{
if (RUN_MODE == RunMode.TEST)
{
addRandomImage();
return;
}
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = Constants.Global.IMAGE_FILE_FILTER();
if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string imageFileName = openFileDialog.FileName;
addImage(imageFileName);
}
}
private void addRandomImage()
{
string[] allFiles = Directory.GetFiles(IMAGE_DIRECTORY);
List<string> imageFileNames = new List<string>();
foreach (string file in allFiles)
{
bool isImageFile = Constants.Global.IMAGE_EXTENSIONS.Contains(Path.GetExtension(file));
if (isImageFile)
{
imageFileNames.Add(file);
}
}
int randomIndex = new Random().Next(imageFileNames.Count);
addImage(imageFileNames.ElementAt(randomIndex));
}
This works. Now when I click the "Add Image" button during TEST_MODE I skip the
OpenFileDialog and just add a random image.
I would like to understand the issues with TabControl but at this point I just
need to continue development. My current solution works great.
As I person who like to understand everything I would like to use other people's
suggestions so I will keep monitoring this question for a solution.
===============================================================================
JonP's answer gave me the idea to just wait for the window handle to be created before inserting the tab, so I tried some events occuring between Form construction and Tab Control display.
I found it to work with both the Load or Shown events:
Right-click on the Form (the root, not child controls) in the Designer view > Properties > Events (flash icon) > Behavior > enter a method name for the Load or Shown event and confirm. To generate a Load event callback you can also double-click on the Form itself. This should generate something like this:
this.Load += new System.EventHandler(this.Form1_Load);
// or
this.Shown += new System.EventHandler(this.Form1_Shown);
Setup the tabs in the callback:
private void Form1_Load(object sender, EventArgs e)
{
// Add image (this will call imagesTabControl.TabPages.Insert(selectedTabIndex + 1, tabPage))
// This must be done on Load event because Insert requires
// the window handle, which is not ready in the constructor
addImage(#"path_to_image.png");
}
I have had this problem too and have found a workaround; I think it must be a bug with Insert():
Don't use Insert(), it usually does nothing, use Add() instead; this reliably adds a TabPage to the end of the collection.
After adding it swap it with the tab position where you actually want it.
imagesTabControl.TabPages.Add(tabPage);
// Now swap the two tabs:
imagesTabControl.TabPages[imagesTabControl.TabCount - 1] = imagesTabControl.TabPages[selectedTabIndex + 1];
imagesTabControl.TabPage[selectedTabIndex + 1] = tabPage;
Your mileage may vary of course :-)
Stop Press! An even better fix is to read the class's Handle member before calling Insert():
var handle = imagesTabControl.Handle;
Insert() works perfectly after you do that. Obvious isn't it???? The help page for Handle has this possible relevant Remark showing that the object actually does something when you read Handle:
The value of the Handle property is a Windows HWND. If the handle has not yet been created, referencing this property will force the handle to be created.
You could remove the TabControl from the designer and then instead just manually create the TabControl programmatically and add it to the Form immediately after InitializeComponent(). Then after you create the TabControl, call addImage(). Something like:
InitializeComponent();
TabControl tc = new TabControl();
tc.Location = new Point(10, 10);
tc.Size = new Size(100, 100);
tc.Visible = true;
tc.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right | AnchorStyles.Left | AnchorStyles.Top);
this.Controls.Add(tc)
addImage("c:\pathToImage\image.bmp");

Load, Save and Read data in textboxes when ap is opened & closed c# VS 2012

I have an app where the user logins to a list of textboxs where the users can write in them save the data then close the app and when they open it the data is still in the textboxes.
I have been researching .txt and .xml to see which is the best format to use. I have also researched XML Serialization and what code goes in the .xml file but I'm a bit lost with do I have to change the name of the textboxes so that the data loads in the right box? Theres about 15 textboxes on the page itselfs.
I have added using System.Xml.Serialization; to my form.
Also when the user logins in it open an existing form and when the user logouts it just closes the form.
I'm a bit confused on how to load the page with all the data showing, saving the data(Iv created a save button for the textbox page) and also reading the file isn't reading and loading the same?
I'm using visual studio 2012 Winforms c#
You could use the attributes for the XML and name it from the user login.
static public void CreateFile(string username)
{
XmlWriter xmlW = XmlWriter.Create(username + ".xml");
xmlW.WriteStartDocument();
xmlW.WriteStartElement("Listofboxs");
//add the box following this canvas
xmlW.WriteStartElement("box");
xmlW.WriteAttributeString("nameofbox", "exampleName");
xmlW.WriteAttributeString("valueofbox", "exampleValue");
xmlW.WriteEndElement();
//
xmlW.WriteEndElement();
xmlW.WriteEndDocument();
xmlW.Close();
}
this will allow you to create the first file with the username.
Second, to display these informations when reloading your application, here's some code:
static public Dictionary<string, string> getBoxValue(string username)
{
Dictionary<string, string> listofbox = new Dictionary<string, string>();
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(#"./" + username + ".xml");
XmlNode root = xmldoc.DocumentElement;
foreach (XmlNode box in root)
{
listofbox.Add(box.Attributes[0].Value.ToString(),box.Attributes[1].Value.ToString());
}
return listofbox;
}
For each node, the dictionnary will add a pair of string the name of the box and its value.
You can use it to fill your boxes.
I know this code may be a little unefficiency (should use "using" and such) but I hope it could help you.
I can't understanding ur problem completely. show the coding of your program what you want.
just try when your page is loading clear all textbox data.
private void Form1_Load(object sender, EventArgs e)
{
textBox1.Text = string.Empty;
}
To read and write the xml-file you can add a dataset to your project, where you define two columns. One column for the name of the textbox and another one for the value. On the dataset you can use the Methods WriteXml and ReadXml to read and write.
To load the xml at startup you have to subscribe for the load-event of the form. And in the Formclosing-event you can write your data.
public Form1()
{
this.Load += OnLoad();
this.FormClosing += OnFormClosing();
}
private void OnLoad(object sender, EventArgs e)
{
// Read Data from Xml with the dataset (dataset.Readxml...)
}
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
// Write the Data from the textboxes into the xml (dataset.writexml...)
}
To set the values of the textboxes in the load-part you can use the following code:
TextBox tb = this.Controls.Find("buttonName", true);
if(tb != null)
// set the value for the tb

Categories