spvessel / spacevil

Simple examples of SpaceVIL implementation for C# / .NET Framework, C# / .NET Core and Java.
https://spvessel.com
MIT License
56 stars 7 forks source link

ComboBox example for C# #2

Open Maobuff opened 5 years ago

Maobuff commented 5 years ago

Hello, im looking for exaple how to use ComboBox in C#, and also is is possible to bind image to a MenuItem?

spvessel commented 5 years ago

Hello! Today later I will create a complete example for ComboBox and MenuItem with styles and others, and you can see it on github. But below I have attached a simple example (including image binding). combobox_example.txt

Update: SpaceVIl has some annoying bugs with ComboBox, and today later a new version of SpaceVIL will be available without these bugs.

Maobuff commented 5 years ago

Also how do I change font for each item in combobox? Do i have to change it in MenuItem? If yes then maybe add feature to set font for all of comboboxs items, like you have with alignment in stacks (both horisontal and vertical)

spvessel commented 5 years ago

If you need to change the font for MenuItem there are a several ways to do this:

  1. Create static method:

    static MenuItem GetMyMenuItem(String text)
    {
    MenuItem mi = New MenuItem(text);
    mi.SetFont(new Font(new FontFamily("Arial"), 16, FontStyle.Regular));
    return mi;
    }
  2. Create a style for MenuItem like this:

    Style style = Style.GetMenuItemStyle();
    style.Font = new Font(new FontFamily("Arial"), 16, FontStyle.Regular);
    return style;

    and apply style after creating MenuItem:

    MenuItem mi = new MenuItem("My MenuItem");
    mi.SetStyle(style);

    or

    MenuItem mi1 = new MenuItem("My MenuItem1");
    MenuItem mi2 = new MenuItem("My MenuItem2");
    MenuItem mi3 = new MenuItem("My MenuItem3");
    style.SetStyle(m1, mi2, mi3);
  3. Create a style for MenuItem and replace the default theme style. In this case, all new instances of MenuItem will automatically apply the new style (the best way to replace default style is to replace it before creating any window, for example in the Main function):

    static void Main(string[] args)
    {
    SpaceVIL.Common.CommonService.InitSpaceVILComponents();
    
    // create new style (this can be done through a static "getStyle-function")
    Style style = Style.GetMenuItemStyle();
    style.Font = new Font(new FontFamily("Arial"), 16, FontStyle.Regular);
    
    // replace default style 
    DefaultsService.GetDefaultTheme().ReplaceDefaultItemStyle(typeof(SpaceVIL.MenuItem), style);
    
    // create and show window
    MainWindow mw = new MainWindow();
    mw.Show();
    }
  4. Create a new class that extends MenuItem and set the desired font in its constructor:

    public class MyMenuItem : SpaceVIL.MenuItem
    {
    public MyMenuItem()
    {
      SetFont(new Font(new FontFamily("Arial"), 16, FontStyle.Regular));
    }
    }

    and use MyMenuItem instead of base MenuItem:

    MyMenuItem mymi = new MyMenuItem();
    ComboBox combo = new ComboBox(mymi);

PS: New version (v0.3.4.2) of SpaceVIL avaliable. Also on github I added Tutorials/ComboBoxExample which may be interesting for you.

Maobuff commented 5 years ago

Well, what about adding image to ComboBox itself, not a MenuItem? TextMargin makes ComboBox smaller instead of shifting Text. And what about VerticalStack SetContentAlignment method, for example, I need to have 4 elements (3 ComboBoxes and one Label in my case) on with this alignment : two of them alight to left and other two to the right. Things i tried: (1)Making VerticalStack SetContentAlignment Left, then manually changing two elements that need alignment to the right. Result invisible elements than go with right alignment

(2)Making VerticalStack SetContentAlignment Right, then manually changing two elements that need alignment to the left. The order of the elements is inverted (first added element closer to left side, well it doesn't matter) and ComboBox which alignment was set to left is invisile

(3)Setting manually Left and Right of each element resulted in invisible ComboBoxes

Tests

spvessel commented 5 years ago
  1. Adding an image to ComboBox: The approach is the same - just add ImageItem to ComboBox (in Tutorials/ComboBoxExample I added an ellipse into ComboBox)

    ComboBox combo = new ComboBox(menu_item1, menu_item2);
    AddItem(combo);
    ImageItem img = new ImageItem(<Bitmap>); //here you must provide Bitmap 
    ... set img properties like size, aspect ratio, etc....
    combo.AddItem(img);

    or create your own modified ComboBox:

    public class MyComboBox : SpaceVIL.ComboBox
    {
    ImageItem img = null;
    public MyComboBox (Bitmap image, params MenuItem[] items) : base(items)
    {
      img = new ImageItem(image);
      SetTextMargin(30, 0, 0, 0);
    }
    public override void InitElements()
    {
        base.InitElements(); //required
        // img attr
        img.SetSizePolicy(SizePolicy.Fixed, SizePolicy.Fixed);
        img.SetSize(20, 20);
        img.SetAlignment(ItemAlignment.Left, ItemAlignment.VCenter);
        img.SetMargin(15, 0, 0, 0);
        img.KeepAspectRatio(true);
    
       // add image
       AddItem(img);
    }
    }

    and use MyComboBox as a ComboBox:

    MyComboBox mcb = new MyComboBox(bitmap, menu_item1, menu_item2);
  2. VerticalStack (do you mean HorizontalStack?), 4 elements with alignment : two of them alight to left and other two to the right. Stack items were not meant to work like this. If you want to achieve such alignment you should use Frame with 2 HorizontalStack: the second one should SetContenAlignment to ItemAlignment.Right:

    
    // prepare layout
    Frame frame = new Frame();
    SetHeightPolicy(SizePolicy.Fixed);
    SetHeight(40); // or any other value
    HorizontalStack hsLeft = new HorizontalStack();
    HorizontalStack hsRight = new HorizontalStack();
    hsRight.SetContentAlignment(ItemAlignment.Right);
    AddItem(frame); // add to window
    frame.AddItems(hsLeft, hsRight);

// prepare content Label label = new Label("Name"); ...set label attr (size, font and etc)...

ComboBox cb1 = new ComboBox(); ...set cb1 attr (size and etc)... ComboBox cb2 = new ComboBox(); ...set cb2 attr (size and etc)... ComboBox cb3 = new ComboBox(); ...set cb3 attr (size and etc)...

// add content hsLeft.AddItems(label, cb1); hsLeft.AddItems(cb2, cb3);


or you can simply manually set a proper attributes to each item:

// prepare layout Frame frame = new Frame(); SetHeightPolicy(SizePolicy.Fixed); SetHeight(40); // or any other value AddItem(frame); // add to window

// prepare content Label label = new Label("Name"); ...set label attr (size, font and etc)...

ComboBox cb1 = new ComboBox(); cb1.SetMargin(label.getWidth() + 5, 0, 0, 0); ...AND SET OTHER cb1 attr (size and etc)...

ComboBox cb2 = new ComboBox(); cb2.SetAlignment(ItemAlignment.Right); ...AND SET OTHER cb2 attr (size and etc)...

ComboBox cb3 = new ComboBox(); cb3.SetAlignment(ItemAlignment.Right); cb3.SetMargin(0, 0, cb2.getWidth() + 5, 0); ...AND SET OTHER cb3 attr (size and etc)...

// add comtent frame.AddItems(label, cb1, cb2, cb3);

or the simplest way - just add Frame between second and third element. Frame will expand them:

// prepare layout HorizontalStack hStack = new HorizontalStack(); ... set hStack attr (size and etc)... Frame frame = new Frame();

// prepare content Label label = new Label("Name"); ...set label attr (size, font and etc)... ComboBox cb1 = new ComboBox(); ...set cb1 attr (size and etc)... ComboBox cb2 = new ComboBox(); ...set cb2 attr (size and etc)... ComboBox cb3 = new ComboBox(); ...set cb3 attr (size and etc)...

// add content AddItem(hStack); hStack.AddItem(label, cb1, frame, cb2, cb3);



PS: I found a new bug in ComboBox so... a new version (v0.3.4.21) of SpaceVIL availiable (required if you want to use "MyComboBox" example).
spvessel commented 5 years ago

Update: I added a new solution for your alignment problem in my previous comment.

Maobuff commented 5 years ago

I managed to put all items as I wanted. But SetTextMargin on ComboBox makes is field smaller like shown in attached image. Yes in theory I can put Image on the right side of ComboBox by shifting it from center, but in design perspective I want it to be at left side. Here what I did:

_status = new ComboBox(_statusAll, _statusDone, _statusWIP); _status.SetWidth(125); _status.SetFontSize(25); _status.SetWidthPolicy(SizePolicy.Fixed); _status.SetTextMargin(new Indents(30, 0, 0, 0)); ImageItem img = new ImageItem(Resources.Done); img.SetSizePolicy(SizePolicy.Fixed, SizePolicy.Fixed); img.SetSize(20, 20); img.SetAlignment(ItemAlignment.Left, ItemAlignment.VCenter); img.KeepAspectRatio(true); ................ _hs.AddItem(_status); ................ _status.AddItem(img);

Capture

spvessel commented 5 years ago

What version of SpaceVIL do you use?

_status.SetTextMargin(new Indents(30, 0, 0, 0))

is was one of the bugs that I fixed in the new version 0.3.4.21. If you do not want to use the new version, than in your case you should create a style for ComboBox:

Style style = Style.GetComboBoxStyle();
style.SetSize(125, 25);
style.Font = DefaultsService.GetDefaultFont(25);
style.WidthPolicy = SizePolicy.Fixed;
Style selectionStyle = style.GetInnerStyle("selection");
if (selectionStyle != null)
   selectionStyle.SetPadding(30, 0, 0, 0);

// apply style
_status = new ComboBox(_statusAll, _statusDone, _statusWIP);
_status.SetStyle(style);

//your image
ImageItem img = new ImageItem(Resources.Done);
img.SetSizePolicy(SizePolicy.Fixed, SizePolicy.Fixed);
img.SetSize(20, 20);
img.SetAlignment(ItemAlignment.Left, ItemAlignment.VCenter);
img.KeepAspectRatio(true);
**img.SetMargin(5, 0, 0, 0);** // do not forget to indent for beauty
Maobuff commented 5 years ago

I think I switched to v0.3.4.2 as soon as I saw you publish new release. Updated to latest version(0.3.4.21) and bug is fixed

spvessel commented 5 years ago

If you have any other questions regarding SpaceVIL framework, feel free to ask me.

Maobuff commented 5 years ago

What about load time? In my project I have about 400 Items in total (~325 of frames/horizontalstack etc.. and about 75 pictures). Project starting is around 10 seconds. All pictures are located in resource file.

spvessel commented 5 years ago

Hmm... In my cases, loading times were always acceptable. For example, the load time of SimpleImageViewer with 100 images of 64x64 pixels (stored on the HDD) is about one second. The load time of CharacterEditor with 1000 generated characters (each character with image of 32x32 pixels) is also about one second (total items: 9078). If you want to know the total number of items:

Console.WriteLine(ItemsLayoutBox.GetLayoutItems(myWindow.GetWindowGuid()).Count);

Place it inside at the end of InitWindow() function.

In my tests only a few cases caused a significant increase in load time:

  1. adding very huge amount of items
    • if over 10 000 unique items added via AddItem
    • if over 100 000 items added in container via SetContent (not via AddItem)
  2. loading big images for icons
  3. if using a Grid with over 50x50 cells

What is size of your images? What is the actual number of items in your project?

PS. SimpleImageViewer and CharacterEditor are examples of using SpaceVIL and can be found in the current github repository.

Maobuff commented 5 years ago

Console.WriteLine(ItemsLayoutBox.GetLayoutItems(myWindow.GetWindowGuid()).Count); outputs 694 I have set of images (about 10 uniques), each is about 512x512. But all of them are located in resource file, decoded to ready to use bitmap. Update: In my case I have Container (HorizontalStack) where I put 14 objects (half of them are simple spacers), 3 of them are icons. I measured time that takes for each of them to load: ~250ms in total and about 70-90ms for each picture(from creating a variable to finish setting up all of its properties), less than 0 for AddItems(). Update 2: Going to change resolution of icons to somethings 32x32 or 20x20

spvessel commented 5 years ago

What are CPU do you have? My CPU: Intel I5-6300U I tried to load 80 images (512x512) form my hdd and it takes 12 ms.

Maobuff commented 5 years ago

AMD Ryzen 7 3700x. Reducing resolution helps in load time.

spvessel commented 5 years ago

Wow. It is very good CPU, but for now, your load time is a mystery for me. Can your antivirus slow down the application loading?

Maobuff commented 5 years ago

I dont have any, only Windows Defender. Update: disabling in make zero difference. Update 2: reducing resolution twice increased total load time for each element to about 50ms Update 3: putting application to an ssd reduced time to about 20-25ms

Maobuff commented 5 years ago

After little bit of testing i figgured out that new ImageItem(Resources.Test); takes 99.99% of measured time. Also noticed that one of my cores is loaded to a full 100% for load time, and I reach average boost clocks (All I wanted to say that this is not a CPU related problem like "Thermal malware detection" or other). What is your method of loading Images? Do you load them by reading file or using resource?

spvessel commented 5 years ago

Hmm... Ok, I will try to find out in which cases images in resource files can increase load time.

Maobuff commented 5 years ago

Update: I also noted that you are using glfw, How are you loading textures from images? Are you using bitmap.LockBits to load textures?

spvessel commented 5 years ago

I load images directly from hdd. Inside new ImageItem(Resources.Test); image converted to a byte array. No I am not using bitmap.LockBits, just conversion to a byte array.

Maobuff commented 5 years ago

Are you using bitmap.GetPixel ? Had performance issues with GetPixel method in past.

spvessel commented 5 years ago

Yes. If you want, I could create a test version of SpaceVIL later today that uses bitmap.LockBits, and you can test it. Are you ok with that?

Maobuff commented 5 years ago

Sure! Also I updated System.Drawing.Common and get around 13ms for each picture (from hdd). Still not happy with the result. Update: Measured load time of SetIcon(Resources.Icon, Resources.Icon);. Result is 250ms.

Maobuff commented 5 years ago

I also find old project with glfw on c# an tried to load texture using GetPixel method. result is 2 full seconds for loading texture(1024x1024) from file. Switched to LockBits method and time is reduce to few ms.

spvessel commented 5 years ago

Good. Can you provide example of using LockBits?

Maobuff commented 5 years ago

Sure! Texture.zip

spvessel commented 5 years ago

Ok, I did it. Try this version (paste it in address line to download): https://spvessel.com/spacevil/downloads/maobuff/spacevil_dotnet_core.zip Make sure that the framework version is 0.3.4.22 Update: this version has not yet been tested for memory leaks, keep this in mind.

Maobuff commented 5 years ago

Loading for first container is 23ms, for next ones less than 1ms (25 copies of HorizontalStack with 14 total elements, 3 of which are pictures). For testing I used same texture for all of pictures (512x512). SetIcon() have same load time of ~240ms (512x512). Loading for other texture is ~2ms (512x512).

Maobuff commented 5 years ago

I can call it a success, but take a look at SetIcon() method. Maybe its still using GetPixel method.

spvessel commented 5 years ago

Yes. I implemented your method only for ImageItem. When I fully test it, I will replace all places where GetPixel is used. And thanks for finding this bug.