CheckedListBox - Collection of FileInfo objects - Display Custom ToString - c#

I have a CheckedListBox in a WinForms app (3.5 runtime), and I am adding a bunch of FileInfo objects to the Items ObjectCollection. The problem is that I don't like what is displayed in the CheckedListBox (since the FileInfo was from a Directory.GetFiles() it just shows the FileInfo.Name of the file in the listbox).
Is there any easy way to change what is displayed in the CheckedListBox without having to create a seperate custom class/object.
I am basically doing
checkedListBox.Items.Add(fileInfo)
and the result is just the file name of the file.
Changing display member works but I can't create something custom, only the existing properties in the FileInfo class.
I want to be able to display something like Name - FullName
Example (desired):
File1.txt - C:\Path\SubPath\File1.txt

Actually, it seems like it should be possible after all. The CheckedListBox has a FormattingEnabled property and a Format event inherited from ListBox which is called before each item is displayed. So something along these lines should work:
myCheckedListBox.FormattingEnabled = true;
myCheckedListBox.Format += (s, e) => { e.Value = string.Format("{0} - {1}", ((FileInfo)e.ListItem).Name, ((FileInfo)e.ListItem).FullName); };
Haven't tested it though. See also MSDN
Old answer:
I don't think you can do it without creating a wrapper. Although 10 lines of code don't seem all that bad to me:
class FileInfoView
{
public FileInfo Info { get; private set; }
public FileInfoView(FileInfo info)
{
Info = info;
}
public override string ToString()
{
// return whatever you want here
}
}
The additional advantage to having a view model is that you can decorate it further for display purposes all the way you like.

I dont know if there is work around for this except creating a custom class and include an instance of FileInfo inside it
and in this way you either create a new property and include a custom data in it or override the ToString() function
something like (this for demonstration purposes)
MyFileInfo
{
public FileInfo TheFileInfo;
public string CustomProperty
{
get
{
if(this.TheFileInfo != null)
return this.TheFileInfo.FileName + this.TheFileInfo.FullName;
return string.Empty;
}
}
}

Related

Get TextBox name from WPF application with MVVM model in helper class

So I am trying to build a MVVM application with WPF and i am stuck with this problem:
In the view class, I have a list of textBoxes that I want to acces in order to validate the inputs.
So I managed to do this by writing in the view class, and it works:
var list = mainGrid.Children.OfType<TextBox>().ToList();
var dictOfTb = new Dictionary<string, string>();
foreach (var item in list)
{
dictOfTb.Add(item.Name, item.Text);
}
But the problem is that I am trying to respect the MVVM pattern and I should to this in a helper class, lets call it ModelsPageHelper, and access it from the View, because here what I am trying to do is to only get data from the UI and pass it to the viewModel to get a result, and then to display it.
So in this class I wrote a method,GetValuesFromTextBoxes(List<TextBox> textBoxes) and I am writing the same code, but now I get a message saying that TextBox does not contain a definition for Name.
So the question is, how can I do the same thing in the helper class to acces the names of those textBoxes?
To do MVVM properly you have to stop thinking about code to validate text boxes, and think about a ViewModel that validates itself, and a view that displays the state of the ViewModel.
For example, if you have a property called Foo which must have the value Bar, the code would look like this:
public string FooError { get; private set; }
private string foo;
public string Foo
{
get => return foo;
set
{
foo = value;
if (foo == "Bar") FooError = "";
else FooError = "Foo must be Bar";
NotifyPropertyChanged();
NotifyPropertyChanged(nameof(FooError));
}
}
and your XAML would look something like this:
<TextBox Text={Binding Foo}/>
<TextBlock Text={Binding FooError}/>
Then perhaps your save button could check for errors too.
I'd strongly advise you to look into INotifyDataErrorInfo, which is a great way to organise your errors in a way that WPF can easily display (instead of properties like FooError). It might seem like a lot of work the first time you use it, but it's great once you've got lots of controls and validation rules.

Saving a list between applications in the .NET property settings

I have a simple list, like this:
fruitList = new FruitList();
Apple fruit1 = new Apple("red", "small");
Banana fruit2 = new Banana("yellow", "big");
fruitList.AddFruit(fruit1);
fruitList.AddFruit(fruit2);
My program displays this in a Textbox:
textbox.Text = fruitList.DescribeCurrentFruit()
+
public string DescribeCurrentFruit()
{
string description;
if (fruitStock.Count > 0)
{
description = fruitStock[fruitCurrentlyDisplayed].Description();
}
else
{
description = "No Fruits in stock";
}
return description;
}
Currently, the List's two current objects/items (fruit1, fruit2) are automatically loaded as they are a part of my Window Form's load_event. However, if they're not a part of the load_event, or if I want to add more items/objects to the list at runtime, then permanently save them, how can I do so?
Well, I can do so by saving items in the project's property settings. (Serialization is an alternative option, but far too complex for me, and I want the simplest solution.) How do I go about this? Is there any sample code? I understand I first need to add items into my properties, but struggle even at this step.

Read assembly MethodBody, parse its Name in a ListBox and its IL in a TextBox?

I have a WPF MainWindow Form with a ListBox and a TextBox that looks like this:
Figure A. WPF MainWindow with Sample Text.
Now, the Load Assembly... OnClick button event allows me to select a .NET Assembly and load it up using DnLib
Then, if I want to display the Methods bodies I would do it like so:
Assembly asm = Assembly.LoadFile(filename);
foreach (Module mod in asm.GetModules())
{
foreach (Type types in mod.GetTypes())
{
foreach (MethodInfo mdInfo in types.GetMethods())
{
listBox.Items.Add(mdInfo.Name);
}
}
}
This adds each found Method name to the ListBox on the left, resulting like so:
Figure B. Showing the ListBox Filled with Methods Names
Now the trick part, I would like to for whichever method I select from the ListBox to display its respective MethodBody IL on the TextBox
How can I achieve such thing?
«Phew!» Finally Solved it!
Here's the solution for whoever tries to do the same thing in the future.
Make an instance of 'List' and then iterate through the methods and assign the names to such list, then whenever your SelectedItem index value changes, I can simply call GetMethodBodyByName and then I can surely solve this issue
Here's how to implement the function GetMethodBodyByName:
public string GetMethodBodyByName(string methodName)
{
ModuleDefMD md = ModuleDefMD.Load(filename);
foreach (TypeDef type in md.Types)
{
foreach (MethodDef method in type.Methods)
{
for (int i = 0; i < type.Methods.Count; i++)
{
if (method.HasBody)
{
if (method.Name == methodName)
{
var instr = method.Body.Instructions;
return String.Join("\r\n", instr);
}
}
}
}
}
return "";
}
The idea is that 'GetMethodBodyByName' will receive the method name as a parameter, then it will iterate through methods and see if a method matches the given name, then if found, the function will just simply iterate through that method and output the method's body.
Here's how my ListBox_SelectedItemChanged event looks like:
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
textBox.Text = "";
textBox.Text = GetMethodBodyByName(method[listBox.SelectedIndex].Name);
}
That's All Folks!
Note: Be careful when doing this approach as if when you request names, different methods can have the same names. But that's a cake for another day, I'm done for now! take care bye-bye!
Working our way up for the Ultimate Solution!
The WPF MainWindow Forms carry with themselves two little useful properties, they are: Tag and Content, the idea is the following one:
With the Tag and Content Property we can assign any values to it that later it can be retrieved On-The-Fly without having to depend on Methods names specifically for this task.
So you would instead of looping each method and get its name respectively you can just do the way I did:
Iterate through the Method, and assign its body to the Tag property, and its name to the Content property, as this last property is the one that handles the actual Title property, so disregarding anything you do with the method in the future and even if it had the same name of another one, it will work no matter what.
How Can We Implement It?
Simply:
<...>
// Inside Method Body iteration routine...
<...>
var instr = mdInfo.Body.Instructions;
// Allocate in a new `ListBoxItem` each method and add it to the current listbox with their
// ... respective Tag and Content information... // Many Thanks Kao :D
newItem = new ListBoxItem();
newItem.Content = mdInfo.Name;
newItem.Tag = string.Join("\r\n", instr);
method.Add(mdInfo);
listBox.Items.Add(newItem);
Then on your SelectedItem Index-Value-Changed Event put this:
MSILTextBox.Clear();
// Retrieve them given the selected index...
// ... the returned value will be the Tag content of the ...
// ... previously saved item.
string getTag= ((ListBoxItem)listBox.SelectedItem).Tag.ToString();
MSILTextBox.Text = getTag;

Sort ListBox accoring to filename if Items are filepaths

I have a ListBox that contains ListBoxItems, who's Content are strings that are either full filepaths or cropped paths, if the actual path is too long (the start of the filepath is cropped, e.g. "C:\MyFolder1\MyFolder2\MyFile.df" -> "...1\MyFolder2\MyFile.df"). The Tags of the items are custom objects that contain the full filepath, the filename and if necessary the cropped filepath:
internal class MyClass
{
internal string filePath, filePathCropped, fileName;
internal ListBoxItem listBoxItem;
//the paths are set somewhere here, whenever a file is opened and then an event is
//raised that adds them to the ListBox. filePathCropped is equal to filePath, if
//the path is short enough.
}
public partial class MainWindow : Window
{
internal void AddFileToList(object sender, EventArgs e)
{
MyClass myClass = sender as MyClass ;
myClass.listBoxItem = new ListBoxItem
{
Content = myClass.filePathCropped,
Tag = myClass
};
listBoxOpenFiles.Items.Add(myClass.listBoxItem);
SortFileList();
}
private void SortFileList()
{
//I would like to sort my list here according to fileName
}
}
Unfortunately, I am not really sure how the sorting mechanism exactly works. There are several topics on this on SO, but they mostly concern sorting according to the actual strings in the list but that is not exactly what I am trying to achieve here.
I tried this:
private void SortFileList()
{
listBoxOpenFiles.Items.SortDescriptions.Add(
new System.ComponentModel.SortDescription((Tag as MyClass).fileName,
System.ComponentModel.ListSortDirection.Ascending));
}
But that raises a NullRefenceException since Tag is not set. I am not completely sure, how to either access the TagProperty of my items or how to sort not according to the whole content string, but only the last bit (which is of course also equal to the file name).
First, make sure you're actually using properties, instead of variables:
internal class MyClass
{
internal string filePath, filePathCropped;
internal ListBoxItem listBoxItem;
internal string FileName {get;set;}
}
Secondly, you should specify the property which the sort is applied:
listBoxOpenFiles.Items.SortDescriptions.Add(
new SortDescription("Tag.FileName", ListSortDirection.Ascending));
That said, your code is getting already quite confusing: you should really handle this with XAML only. Also, you do not need to call Sort method. Make sure your underlying collection is using ObservableCollection, and it will be taken care of.
After a bit more research in a different direction, I have found a fairly straightforward solution here, which I haven't thought about before that doesn't use collections but rather an "old fashioned" way.
private void SortFileList()
{
int length = listBoxOpenFiles.Items.Count;
string[] keys = new string[length];
ListBoxItem[] items = new ListBoxItem[length];
for(int i = 0; i < length; ++i)
{
keys[i] = ((listBoxOpenFiles.Items[i] as ListBoxItem).Tag as MyClass).FileName;
items[i] = listBoxOpenFiles.Items[i] as ListBoxItem;
}
Array.Sort(keys, items);
listBoxOpenFiles.Items.Clear();
for (int i = 0; i < length; ++i)
{
listBoxOpenFiles.Items.Add(items[i]);
}
}
This might not be the most elegant solution considering that it's a WPF program, but it works without having to change anything in the previous code.

Click a custom control and show some variables related to it in another control

in my Win Forms app I create an array of dynamic custom controls inside a loop. These, lets call them 'boxes', are like my basic pieces of information. I also create string arrays in other parts of the code that contain the information of this 'boxes', so that for example string[3] is a variable of box[3] and so does stringa[3], stringb[3], stringc[3]... all the arrays with the same index are related to the box with that index. Hope I make myself clear.
Only 2 of this strings are shown in 2 labels inside each custom control 'box' in the array, but the others are there because I want to make something so that when the user clicks one of these controls the other strings can be shown in another control. Sort of something like "More Information...". All the 'boxes' in the array need to have the same event handler because I create +100.
To put it more into context, each custom control 'box' in the array shows the Symbol and the Price of a stock and I want that when the user clicks on each stock more quote information is shown on another special control which is like a placeholder for "More info".
I am thinking of 2 ways to do it:
If I could "detect" the index of the clicked control (which is the same in the strings related to it), I could just set this to an int j and all I have to do is show all the strings a,b,c... with index j. Unfortunately I cannot find a way to do this, maybe it is not even possible.
The other way I have thought is to create some properties for my custom control which "store" this variables, and in my app instead of assigning strings I would set properties for each control, which I could later retrieve when the control is clicked. I haven't tryed this because I don't know exactly how to do it.
What do you think? Do you know how can I achieve this or do you have a different idea that will work? Please help! Thanks in advance.
It's kind of a broad implementation question since there are countless ways you could implement something like this.
If you are creating two collections, one with the buttons and one with the information, you potentially could just assign each of the buttons 'Tag' properties to point to the corresponding info and assign a generic OnClick event handler that displays the info.. something like:
infoControl.text = ((InfoClass)((Button)Sender.Tag)).pieceOfInformation;
But again there are many ways to do this, and the choice comes down to how you store your information.
For your first method, you could have a property of your custom control that is the index.
public class Box : Control
{
// ...existing code
private int index;
public int Index
{
get
{
return index;
}
set
{
index = value;
}
}
}
OR
For your second method, you could have a property of your custom control that is the additional info string.
public class Box : Control
{
// ...existing code
private string extraInfo;
public string ExtraInfo
{
get
{
return extraInfo;
}
set
{
extraInfo = value;
}
}
}
In either case, you could then access the proper information right in your click handler for the "box".
i don't know about the first way - got to noodle around more, but in the second way you can extended your custom or built-in control: for example:
public class ExtendedLabel: Label
{
public string[] MoreInfo { get; set; }
}
and initialize it
public TestForm()
{
InitializeComponent();
ExtendedLabel label = new ExtendedLabel();
label.MoreInfo = new string[] { "test" };
this.Controls.Add(label);
label.AutoSize = true;
label.Location = new System.Drawing.Point(120, 87);
label.Name = "label1";
label.Size = new System.Drawing.Size(35, 13);
label.TabIndex = 0;
label.Text = label.MoreInfo[0];
}
And later in your event handler you can use the inside information

Categories