I have a treelist that contains folders. Each folder can have files or other folders inside.
I need a function in C# that if I change the visibility of the folder, automatically they have to change the visibility of the folders and files under it.
Could you help me out?
Thanks in advance.
I'm not sure what you are rendering your list in and depending on the UI and/or framework you are using there may be other ways to achieve this but the simple approach to walking your folder structure would be using some simple recursion. An example:
public class Resource
{
public string Name { get; set; }
public string Type { get; set; }
public bool Visible { get; set; }
public List<Resource> Children { get; set; }
}
public class Form
{
public void ChangeVisibility(Resource resource)
{
if (resource.Type != "Folder") return;
resource.Visible = !resource.Visible;
if (resource.Children.Any())
{
resource.Children.ForEach(ChangeVisibility);
}
}
}
Related
I'm having trouble making a dynamic menu using ASP.NET MVC where the menu is organized according to your Groups in Active Directory. To cut through useless details, I have retrieved two lists: one gives me all the folders of the menu, the other gives me all the files. How would one go about recursively creating the menu, knowing that folders can have subfolders, and subsubfolders and so on? In my project they are called I have two models DocumentModel and CategoryModel as follows :
//Files
public class DocumentModel
{
public long iDDocument { get; set; }
public long iDCategory { get; set; }
public string docName { get; set; }
public string link { get; set; }
public string type { get; set; }
}
//Folders
public class CategoryModel
{
public long iDCategory { get; set; }
public string nom { get; set; }
public int iDParentCategory { get; set; }
}
I then pass them to my view using a bigger model:
public class CatDocViewModel
{
public IEnumerable<CategoryModel> catModel { get; set; }
public IEnumerable<DocumentModel> docModel { get; set; }
}
So my idea is to make a recursive method in a partial view. Using Razor helpers I can make a recursive method, but I'm having a hard time with these kinds of methods.
Another problem arising is that I use IEnumerable so I have to loop through them, will that be too costly for the algorithm since I will recurse and loop again etc ...? But I also cannot make it without recursion since the depth of folders is unknown, so I have to recurse until I hit the end and then roll back up. But How?
I don't think anyone can make the algorithm but if you had some directions to point me in so I can maybe delve into recursivity and understand how to do it?
EDIT As suggested in the marked answer, here is what I would do to then build then Child lists:
public List<CategoryModel> getDocumentChilren(List<CategoryModel> categories, List<DocumentModel> documents)
{
foreach(var cat in categories)
{
if (cat.idParentCategory == 0)
continue;
foreach(var nextCat in categories)
{
if (nextCat.idParentCategory == cat.idParentCategory && nextCat.idCategory != cat.idCategory)
cat.childCategories.Add(nextCat);
}
foreach (var nextDoc in documents)
{
if (nextDoc.idCategory == cat.idCategory)
cat.childDocuments.Add(nextDoc);
}
}
return categories;
}
This is O(n^2) I believe so is it ok? If I were to make an estimation, I'd have about 20 entries per loop, so that wouldn't be too greedy?
I would organize the folders in a class more suitable for a tree structure like this:
public class CategoryModel
{
public long iDCategory { get; set; }
public string nom { get; set; }
public List<CategoryModel> ChildCategories {get;set;}
public List<DocumentModel> ChildDocuments {get;set;}
}
Then, instead of using CatDocViewModel , you could use List<CategoryModel> as the view model for the menu.
I am currently developing a file indexing system. I have an interface IDiskDrive that can get immediate file items (files/folders). The interface definition is as follows...
public interface IDiskDrive
{
bool IsReady { get; }
string Name { get; }
string VolumeLabel { get; }
string VolumeLabelName { get; }
DiskDriveType Type { get; }
FolderPath RootFolder { get; }
DiskDriveUsage Usage { get; }
IEnumerable<IFileItem> GetImmediateFileItems(FolderPath path);
}
The ability to read all file/folders is complete and works correctly. Now, I need to actually index the file files and folders. Looking ahead I know I will need some reporting tools. This leads me to think I need another abstraction, based upon IDiskDrive that can read/populate. I also need the ability to select drives for indexing.
My question is should my new class inherit IDiskDrive or should I use composition (possibly a decorator)?
// inheritance
class IndexedDiskDrive : IDiskDrive
{
public IndexedDiskDrive(IDiskDrive drive)
{
...
}
public int Id {get; internal set; } // database id
public bool Selected { get; internal set; }
public DateTime? DateLastIndexed { get; internal set; }
// IDiskDrive implementation
public bool IsReady
{
get { return this.Drive.IsReady; }
}
}
or composition...
class IndexedDiskDrive
{
public IndexDiskDrive(IDiskDrive drive)
{
this.Value = drive;
}
public IDiskDrive Value
{
get;
private set;
}
// additional properties
public int Id { get; internal set; }
public bool Selected { get; internal set;}
public DateTime DateLastIndexed { get; internal set; }
}
Note:
I need access to the underlying IDiskDrive for the UI.
For example, I request user to select drives to index. I initially supply a list of local drives and the ability to add network drives. To try and keep code simple, I thought the idea of a new class with a selected property might help.
This allows the GUI to enumerate a list of IndexedDiskDrives and set/clear the select property.
In both examples you expose the IDiskDrive object from the other object. In the first case you inherit from the same inteface, which means you expose the same methods and in the other case you expose the object via a property.
I don't see a reason yet why you want to do this.
It sounds like a typical constructor DI case to me. Just have a new interface for your new class which is doing a different job and hence requires a different contract, and if it needs the IDiskDrive object as a dependency, then just inject it via the constructor and leave it as it is.
P.S.: I know this is not something you have asked, but you might be interested in Lucense.NET, which is a .NET library to index files. They might have already solved your problem for your:
http://lucenenet.apache.org/
EDIT:
From your current class design I would do the following:
void Main()
{
// Use IoC container in real app:
var diskDrive = new DiskDrive(...);
var fileIndexer = new FileIndexer();
var fileItems = diskDrive.GetImmediateFileItems(filePath);
fileIndexer.IndexFiles(fileItems);
}
// Define other methods and classes here
public interface IDiskDrive
{
bool IsReady { get; }
string Name { get; }
string VolumeLabel { get; }
string VolumeLabelName { get; }
DiskDriveType Type { get; }
FolderPath RootFolder { get; }
DiskDriveUsage Usage { get; }
IEnumerable<IFileItem> GetImmediateFileItems(FolderPath path);
}
public interface IFileIndexer
{
void IndexFiles(IEnumerable<IFileItem> files);
}
public class FileIndexer : IFileIndexer
{
public void IndexFiles(IEnumerable<IFileItem> files)
{
// do stuff
}
}
Over the past two years I developed apps for the CF .NET 3.5 to be runned on warehouse's portable device(windows mobile).
From the beginning I just jumped into the process and made a lot of mistakes that I'm gradually correcting. What has came out are apps made in this way:
a main form to start the whole process which automatically creates a data-form, that will stay alive for the whole time. This data-form will keep all the datas that the user will insert or request from the server. The other forms are basically views of the data with methods to manipulate them.
It works but...am I doing this in the right way? Or maybe am I missing something really fundamental?
So, you created a data form, and you are using it like RAM. You never display the data, you simply store it there to access.
If someone ever has to take over your job (like you leave the company or die), they are going to hate you so bad.
A better technique would be to create a Class that houses all of this data.
The good part is, since you already have a data form, you probably already know how everything is organized!
Now, just use that knowledge of your data to create your class that you can read and write to.
If you have groups of similar items, create other classes that your main class will contain.
If you have several of these similar items, create publically accessible Lists of these items.
Make it as dead simple or as complex as you'd like!
Consider these classes, which are all generic enough to modify however you would need and demonstrate some extras added:
public class DataForm {
private GroupedItem m_item2;
public event EventHandler Item2Changed;
public DataForm() { // this is your constructor
Item1 = new GroupedItem();
Item2 = new GroupedItem();
ItemCollection = new GroupCollectionItems("Group1");
}
public float Value1 { get; set; }
public float Value2 { get; set; }
public GroupedItem Item1 { get; set; }
public GroupedItem Item2 {
get { return m_item2; }
set {
if (m_item2 != value) {
m_item2 = value;
if (Item2Changed != null) {
Item2Changed(this, EventArgs.Empty); // notify whoever is listening for the change
}
}
}
}
public GroupCollectionItems ItemCollection { get; set; }
}
public class GroupedItem {
public GroupedItem() { // this is your constructor
}
public string Name { get; set; }
public object Value { get; set; }
}
public class GroupCollectionItem {
private GroupCollectionItem() { // this is your constructor
}
public static GroupCollectionItem Create(string groupName, string itemName, object itemValue) {
var item = new GroupCollectionItem() {
Group = groupName,
Name = itemName,
Value = itemValue
};
return item;
}
public string Group { get; private set; }
public string Name { get; private set; }
public object Value { get; set; }
}
public class GroupCollectionItems : List<GroupCollectionItem> {
public GroupCollectionItems(string name) { // this is your constructor
Name = name;
}
public string Name { get; private set; }
}
I'm working on a project where I need to construct instances of .cs files dynamically.
I use interfaces to structure the data, something like this (they do vary)
public interface IMetaClass
{
string ParentName { get; set; }
string Namespace { get; set; }
string ClassName { get; set; }
string FriendlyName { get; set; }
string Description { get; set; }
}
I also have classes that inherit from these interfaces
public class XavierMetaClass : IMetaClass
{
public string ParentName { get; set; }
// ...
}
Example of construct and fill
var cell = new XavierMetaClass
{
ParentName = mc.Parent.Name,
Namespace = mc.Namespace,
ClassName = mc.Name,
FriendlyName = mc.FriendlyName,
Description = mc.Description + "test"
};
What I would like to do is take this and construct a new .cs file with the data stored in the variable above with a result looking something like this.
public class CellPhone : IMetaClass
{
public string ParentName { get { return "CatalogEntry"; } }
public string Namespace { get { return "Mediachase.Commerce.Catalog.User"; } }
public string ClassName { get { return "CellPhone"; } }
public string FriendlyName { get { return "Cell sPhone"; } }
public string Description { get { return "Contains meta data about phone test"; } }
private readonly IMetaClass _metaClass;
public CellPhone()
{
// Noop
}
public CellPhone(IMetaClass metaClass)
{
_metaClass = metaClass;
}
}
Are there any know frameworks of methods to help me achieve this?
Thanks
In short you have a few options.
CodeDom (good list of links and documentation from Microsoft here)
T4 Templating
Custom Templating (this would be like building text files with little snippets and just piecing together the file with a tree of classes that emit those little pieces)
I have done all three and I would recommend T4 templating, and here is an exhaustive example on how to use it.
http://www.codeproject.com/Articles/269362/Using-T4-Templates-to-generate-custom-strongly-typ
But you need to be willing to dig in and learn it because there is quite a bit to grasp.
Did you consider using CodeDom ?
I've moved the CS files out the App_Data folder because I get errors with duplicate builds, one builds at compile time one at run time so I was told to move them out that folder.
Two files, Default.aspx.cs
namespace CrystalTech
{
public partial class newVersion_Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
LoginInitialiser loginData = new LoginInitialiser();
loginData.loadData();
}
}
}
And Security.cs
namespace CrystalTech
{
// Handles all our login logic
public class LoginInitialiser
{
public bool isLoggedIn { get; private set; }
public LoginInitialiser()
{
this.isLoggedIn = false;
}
// Fetches the users data
public void loadData()
{
if (HttpContext.Current.Session["loggedIn"] != null)
{
this.isLoggedIn = (bool)HttpContext.Current.Session["loggedIn"];
}
// Fetch the data if we are logged in
if (this.isLoggedIn)
{
}
// Otherwise redirect
else
{
HttpContext.Current.Response.Redirect("../logins/index.asp?action=dn&r=" + CommonFunctions.GetCurrentPageName());
}
}
}
// Holds users information
public class User
{
public int ID { get; private set; }
public string username { get; private set; }
public Company company { get; private set; }
public string title { get; private set; }
public string forenames { get; private set; }
public string surnames { get; private set; }
public string email { get; private set; }
}
// Holds company information
public class Company
{
public int ID { get; private set; }
public string name { get; private set; }
public string telephone { get; private set; }
}
}
Why does Default.aspx.cs throw:
The type or namespace name 'LoginInitialiser' could not be found (are you missing a using directive or an assembly reference?)
If you want your classes to live somewhere other than App_Code, add a "Class Library" project to your solution and put that class there. Don't forget to reference that project in your webproject.
Only code in the App_Code directory is compiled - So unless your files are in there (or a subdirectory), they won't be picked up
And what do you mean by duplicate builds? Depending on project settings, the actual build can be triggered either by the first visit to the site or by the compile itself. It's also possible to do half-and-half. with a little preparation beforehand on (build/publish) and the actual compile on the first site visit
The code files (except the ones related to aspx files) are put in the App_Code folder only to ensure their compilation (This is requirement imposed by ASP.net and now I know from comments it is only for web site projects and not web application projects). If they are anywhere else they are not compiled. Check and you will not find Build Action for the code files outside App_Code folder.