generate button for each list - c#

I have a series of lists in a static class (used as a global class)
public static class globalClass
{
public static List<classA> aList = new List<classA>();
public static List<classB> bList = new List<classB>();
public static List<classC> cList = new List<classC>();
}
I want to generate a xaml button for each list, and was told reflection was a bad idea. This is how I handled it using reflection.
//get FieldInfo for globalClass
TypeInfo typeInfo = IntrospectionExtensions.GetTypeInfo(typeof(globalClass));
IEnumerable<FieldInfo> FieldInfoList = typeInfo.DeclaredFields;
foreach (FieldInfo f in FieldInfoList)
{
//Only look at lists
if(f.FieldType.ToString().StartsWith("System.Collections.Generic.List`1")){
StackPanel s = new StackPanel();
s.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.FontSize = 45;
textBlock.Text = f.Name.ToString();
Button addButton = new Button();
addButton.Click += delegate(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(addObjectToLibraryPage), f);
};
addButton.Margin = new Thickness(10);
addButton.Name = "addButton";
addButton.Content = "add";
Button deleteButton = new Button();
deleteButton.Click += delegate(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(deleteObjectFromLibraryPage), f);
};
deleteButton.Margin = new Thickness(10);
deleteButton.Name = "deleteButton";
deleteButton.Content = "delete";
s.Children.Add(addButton);
s.Children.Add(deleteButton);
//add new textBlock and stackpanel to existing xaml
stackPanel.Items.Add(textBlock);
stackPanel.Items.Add(s);
}
}
Is there any cleaner way to do this? Hopefully I would like to be able to pass the actual list instead of a FieldInfo.
I don't want to have to handle each list individually because I may end up with 20+ lists and am using them all in a very similar way.
An example of what I am trying to do:
Suppose I have a grocery/nutrition App, and I want users to be able to record what they eat/need from the store. They can select from a list of Fruit, Vegetables, Meat, Dairy, Sweets, Canned Goods, Etc..
But, I want them to be able to (as an advanced option) be able to edit the list of possible fruits, or any other food category. And I don't want to just have a list of "food" because meat will record things like minimum cooking temperature or something like that.
So, under advanced options, I would want two buttons for each category (add to fruit, delete from fruit). And theoretically add an Import/Export page so I can share my list of fruits with other people or something.
It doesn't seem like the answers pointing to using a superclass will work. See: C# polymorphism simple question

You can create a list to contain all the existing lists you have. You can then iterate over the list to create the buttons. If you wish to maintain a label for each list you could use a dictionary with the key as the label text and list as the value.
The proposed solution aside, do take into account the comments given from Sayse.

Related

How to create a type that Is just a variable containing another type?

Sorry for my verry badly written title, I'm a beginner programmer and I just started on a c# winforms app. In one function I create an object of some type and then in other functions I iterate through a list of that type of objects, however I'm switching the type of control I'm using and when I do, I have to change the type declaration of my object in over twenty places. Is there a way to create a variable that holds that type and than define all my objects off that variable so I only have to specify the type once and then change that variable. Because I'm using winforms controls as my class types all the functions I call are all then same no matter what type my objects are, so all I need to do is change the type declaration and that's it, sorry if this is a stupid question and any help would be appreciated.
Here is a snippet of my code for context:
private void function1(object sender, EventArgs e) //not my actual function because the real function has lots of other unrelated code
{
ListView PlaceType = new ListView(); // these ListView types i would like to replace with a placeholder if possible
ListView listview = new ListView();
int count2 = autolayoutGroups.Controls.OfType<ListView>().ToList().Count();
listview.Size = new System.Drawing.Size(150, 100);
listview.BackColor = normalColor;
listview.BorderStyle = BorderStyle.Fixed3D;
listview.ForeColor = System.Drawing.Color.Black;
listview.Name = "Group" + count2;
listview.MouseDown += Select;
}
private void function2(object sender, EventArgs e)
{
List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder
foreach (ListView l in list_of_groups)
{
// do something here
}
}
private void function3(object sender, EventArgs e) // I have several functions like this
//and if I change the control I'm using I have to change the types in every function
{
List<ListView> list_of_groups = autolayoutGroups.Controls.OfType<ListView>().ToList(); // a place where I need some type of placeholder
foreach (ListView l in list_of_groups)
{
// do something here
}
}
If I understand your problem correctly, you currently have some code where you use a ListView, and you want the same code, but instead of ListView you want some other class, for instance a DataGridView, or a ComboBox. Of course this other class must also have the methods that you used on the ListView.
In C# this concept is called a generic. You have generic classes and generic methods.
You define the generic by typing an identifier instead of the part that you want to replace with another type.
In your case: you want to replace ListView by DataGridView. In function1 you create a ListView, and set some properties. First we'll put this creation in a separate method, you will have something like this:
private ListView CreateListView()
{
ListView listview = new ListView();
int count2 = autolayoutGroups.Controls.OfType<ListView>().Count();
listview.Size = new System.Drawing.Size(150, 100);
listview.BackColor = normalColor;
listview.BorderStyle = BorderStyle.Fixed3D;
listview.ForeColor = System.Drawing.Color.Black;
listview.Name = "Group" + count2;
listview.MouseDown += Select;
return listView;
}
private void function1(object sender, EventArgs e)
{
ListView createdListView = this.CreateListView();
// TODO: do something with the created ListView
}
(Small optimization, out of scope of the question): to calculate count2, don't create a List of all ListViews, and then Count them; use Count() on the IEnumerable<ListView>.
To change CreateListView such that it can create anything of type TMyType, define the generic method like this:
private TMyType Create<TMyType>()
{
TMyType createdObject = new TMyType();
int count2 = autolayoutGroups.Controls
.OfType<TMyType>()
.Count();
createdObject.Size = new System.Drawing.Size(150, 100);
createdObject.BackColor = normalColor;
createdObject.BorderStyle = BorderStyle.Fixed3D;
createdObject.ForeColor = System.Drawing.Color.Black;
createdObject.Name = "Group" + count2;
createdObject.MouseDown += Select;
return createdObject ;
}
So all I did was, that whenever I save ListView, I replaced it with TMyType, the type that should be created.
Usage:
ListView createdListView = this.Create<ListView>();
DataGridView createdDataGridView = this.Create<DataGridView>();
ComboBox createdComboBox = this.Create<ComboBox>();
There is only one problem. You'll have to tell the compiler that TMyType has a default constructor (you want to do new TMyControl()), and that is has methods like Size, BackColor, ForeColor, etc.
If TMyType would be a class derived from Control, then you would be certain that it has the desired constructor and knows all methods that you need to use.
To say that a generic type is derived from a certain type, you use the following structure:
private TMyType Create<TMyType>() where TMyType: Control
{
// because you are certain the TMyType is derived from Control
// you can use all methods of class Control
}
This answers your question: create a generic method
Some other things about generics
Another example: If you want to inform the compiler that the generic type implements IComparable:
private T Process<T>(T input) where T: IComparable {...}
Or multiple:
private T Process<T>(T input) where T: IComparable, IEquatable {...}
And finally if you want to require that the generic type has a default constructor:
private T Process<T> () where T: new
It's not really clear what the goal is but I suppose you can make a property that returns your commonly used list:
private List<ListView> AutolayoutGroupControls =>
autolayoutGroups.Controls.OfType<ListView>().ToList()
Then you can
foreach(var lv in AutolayoutGroupControls)}
...
}
But it doesn't offer much; if you change that prop to return something else you still have a stack of changes to make. If your loops always do the same thing put it into a method and call it from N event handlers

c#, wpf, Dynamically naming controls

How do i create a new Button/Canvas with a dynamic name?
Button {buttonname read from text file} = new Button;
I have googled this for a while now but i can't find the solution.
Thank you!
I'm not sure if I understood correctly, but that name in your example is not the button name, it's just the reference name used in code to access the button. The button name would be set like this:
buttonRefName.Name = "ButtonName1";
So you can set the name to whatever you want: dynamically generated names inside a loop, names read from a file, etc...
You can use the same reference name for multiple buttons, just be sure to add it to List or to WPF Window, Panel, etc... before creating the new one:
var buttonList = new List<Button>();
var buttonRef = new Button { Name = "YourButtonName" };
buttonList.Add(buttonRef);
buttonRef = new Button { Name = "YourButtonName2" };
buttonList.Add(buttonRef);
It not possible the way you want to do it. If you are reading from a text file better use a List or better a Dictionary... an example use is as follows:
var buttons = new Dictionary<string, Button>();
buttons["yourName"] = new Button();
// logic goes here

Pass number of the button in the name of the variable

I have this lines of code:
button0.BorderBrush = new SolidColorBrush(Colors.Red);
button1.BorderBrush = new SolidColorBrush(Colors.Red);
button2.BorderBrush = new SolidColorBrush(Colors.Red);
...
How can I make correct this:
(button + "numberOfButton").BorderBrush = new SolidColorBrush(Colors.Red);
Any time you find yourself with variables like this:
button0
button1
button2
etc...
What you should have is an array. If the controls themselves are already static on the form, then you can simply build the array when loading the form. Something like this:
public class MyForm : Form
{
private IEnumerable<Button> myButtons;
public MyForm()
{
myButtons = new List<Button>
{
button0, button1, button2 // etc...
};
}
// etc...
}
Then when you need to loop over the buttons, you simply loop over the collection:
foreach (var button in myButtons)
button.BorderBrush = new SolidColorBrush(Colors.Red);
If you need to reference the collection elements by index, use an IList<> instead of an IEnumerable<>. If you need to do more complex things, use any number of collection types.
You can find it by name with method Control.Find:
var button = this.Control.Find("button0", true).FirstOrDefault();
But better to store buttons in array and get them by index:
var buttons = new Control[10];
buttons[0] = button0;
...

FlyoutNavigation Controller

Using MonoDevelop, I have been looking at an IOS implementation of a side slide out menu using FlyoutNavigationController, but have hit a couple of stumbling blocks.
Firstly, how can you access the font elements of the generated list?
I can easily modify row heights etc, but am unsure of how to proceed with modifying the list items, can this be down with a tablesource and item styling?
Secondly, how to open a view from this list?
Currently an empty view is used by default but new views are to be opened from the side menu list, I have tried using the push navigation controller but it fails to open.
Any ideas are more than welcome.
navigation = new FlyoutNavigationController();
navigation.View.Frame = UIScreen.MainScreen.Bounds;
View.AddSubview(navigation.View);
navigation.NavigationRoot = new RootElement ("Menu List")
{
new Section ("Menu List")
{
from page in SlideList
select new StringElement (page.title) as Element
}
};
navigation.NavigationTableView.BackgroundColor = UIColor.DarkGray;
navigation.NavigationTableView.RowHeight = 30;
navigation.NavigationTableView.SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine;
navigation.NavigationTableView.SeparatorColor = UIColor.LightGray;
navigation.NavigationTableView.SectionHeaderHeight = 60;
//navigation.NavigationTableView.DataSource = SlideList;
//navigation.ViewControllers = Array.ConvertAll (MenuItems, title => new UINavigationController (new TaskPageController (navigation, title)));
navigation.ViewControllers = Array.ConvertAll (MenuItems, title => new TaskPageController (navigation, title));
this.NavigationItem.LeftBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Action, delegate {
navigation.ToggleMenu();
});
I haven't used the FlyOutNavigationController before, but I took a look at this example:
https://github.com/xamarin/FlyOutNavigation
It looks like you're supposed to have the same number of StringElements as Controllers. For the ViewControllers array, it looks like you can supply your own custom controllers instead of just plain ViewControllers. After that, clicking a list item should automatically navigate to the appropriate controller.
In regards to styling, looking at the source for this NavigationController, I don't see much in terms of being able to stylize the cells. I did a quick search for how to go about styling MonoTouch Dialog lists and it looks like there isn't an easy way without subclassing elements:
Monotouch Dialog: Styling Elements
However, I can share with you how I've accomplished the two questions you asked without the Dialog framework.
You can create a custom class that extends UITableViewSource:
http://docs.xamarin.com/guides/ios/user_interface/tables/part_2_-_populating_a_table_with_data
In the GetCell method override, you can grab an instance of the cell's label and set the font like so:
cell.TextLabel.Font = UIFont.FromName("TitlingGothicFB Cond", 20);
Another thing you can do with your custom UITableViewSource class is create a custom event:
public event EventHandler ListItemSelected;
Inside the RowSelected method you can dispatch this event:
public override void RowSelected (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
ListItemSelected(this, new MyCustomEventArgs(indexPath.Row));
}
In the controller class that was responsible for instantiating this TableSource, you can listen and handle this event like so:
var customTableSource = new CustomTableSource(myList);
MyTable.Source = customTableSource;
customTableSource.ListItemSelected += (object sender, EventArgs e) => {
if((e as MyCustomEventArgs).rowSelected == 1){
this.NavigationController.PushViewController(new MyNextViewController(), true));
}
}

How to create a textbox which has same attributes as another textbox?

I need to create and add some TextBoxes which has same attribute as some other TextBoxes.
Is there a way to copy the attributes to another ?
I'm looking for a one like solution. I know I can set variable one by one.
TextBox Old = new TextBox() {
Size = new System.Drawing.Size(25,25),
Location = new Point(a.row*25, a.col*25),
Multiline = true
};
TextBox New = new TextBox(); //which has same location,size as old one ?
EDIT The TextBox might be any other .NET controls !
You can use this Solution. You can write a extention and that get via Reflection all propertys
Please use the search function in future.
Create an initializer method:
private void InitializeTextBox(TextBox textBox)
{
textBox.Size = new System.Drawing.Size(25, 25);
textBox.Location = new Point(a.row * 25, a.col * 25);
textBox.Multiline = true;
}
And use like this:
TextBox t1 = new TextBox(), t2 = new TextBox();
InitializeTextBox(t1);
InitializeTextBox(t2);
Or a copier method:
private void CopyTextBoxProps(TextBox source, TextBox dest)
{
dest.Size = source.Size;
dest.Location = source.Location;
dest.Multiline = source.Multiline;
//...
}
and use it accordingly.
Probably the most straightforward way is this:
TextBox New = new TextBox {
Size = Old.Size,
Location = Old.Location,
Multiline = Old.Multiline
};
If this is something you need to do a lot, you could write an extension method that does the same thing:
public static class TextBoxExtensions {
public static TextBox Copy(this TextBox textBoxToCopy) {
var copiedTextBox = new TextBox {
copiedTextBox = textBoxToCopy.Size,
copiedTextBox = textBoxToCopy.Location,
copiedTextBox = textBoxToCopy.Multiline
};
}
}
Usage:
var copyOfOld = Old.Copy();
If you are going to add a lot more properties to copy, I'd think about using AutoMapper and defining a map between TextBox and TextBox. If you're interested in that path, let me know and I'll post a sample.
It would turn this into a one liner, but you'd need a dependency on AutoMapper, but it's available on NuGet: http://nuget.org/packages/AutoMapper/2.2.0
First, take a dependency on AutoMapper.
Define the mapping somewhere in your project:
Mapper.CreateMap<TextBox, TextBox>();
Usage:
var newTextBox = Mapper.Map<TextBox, TextBox>(Old);
or, if you already have an instance you want to stuff it into:
Mapper.Map(Old, newTextBox);
AFAIK, there is no built in, one line solution, so it's either the extension method, or take a dependency on AutoMapper. The extension method does not have to do it that way, you can use reflection or other choices there.
I use AutoMapper in just about all of my projects and it's invaluable.
You can define many mappings in your map definition, then all your copies become one liners. Well, besides the definition :)

Categories

Resources