locked
How to create a sidebar? RRS feed

Answers

  • Here's the code I'm using to build the gradient bar at the title text:

     

            public static Rectangle BuildTitleBar(Graphics g, Rectangle area)  
            {  
                LinearGradientBrush brush = new LinearGradientBrush(area, Color.White, Color.FromArgb(57, 105, 165), 0f);  
     
                g.FillRectangle(brush, area);  
                g.DrawImage(Properties.Resources.applications_24, new Point(3, 3));  
                g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;  
                g.DrawString(Properties.Resources.DiskManagement, new Font(new FontFamily("Tahoma"), 10f, FontStyle.Bold), new SolidBrush(Color.FromArgb(57, 69, 90)), 28f, 6f);  
                g.DrawLine(new Pen(Color.FromArgb(57, 105, 165)), new Point(0, area.Height - 1), new Point(area.Width, area.Height - 1));  
                return area;  
            } 

     


    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Sunday, December 14, 2008 7:18 PM
    Moderator

All replies

  • Hi Dilbert,

    Which part of the sidebar are you trying to duplicate? The header, the colours, or the pie chart?

    The pie chart uses the Xceed libraries in the C:\Program Files\Windows Home Server\ folder. We don't have a license to use them; they're Microsoft only (unless you pay money to Xceed of course!).

    If you just want to duplicate the look and feel of the sidebar (header, colours, fonts etc), the easiest way to get that information is to open the Server Storage DLL in Reflector (http://www.red-gate.com/products/reflector/).

    It's not a pre-built control you can drop into your add-in, unfortunately. If I recall, it's a series of Panels and PictureBoxes, and the header is being painted with a LinearGradientBrush.
    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Sunday, December 14, 2008 6:24 PM
    Moderator
  • Hi Sam,

    Thank you. Yes the company i am working just needs a sidebar (look and feel and not the charting) i tried to put in panel and paint it that background color and dock it right but it doesnt look like the default MS provided bar. I did look with reflector in ConsoleTab.Storage & StorageManagerControl but got overwhelmed and could not find anything, not even the default width size or how its aligned?

    Maybe i need to paint 2panels or some border around it? If you look closely there is like a 2 pixel gab between the listview and the panel?
    I dont think those controls are docked maybe absolute positioning?

    Please let me know if there is anything you could think of that would help me alot.
    Thank you in advance.

    Dilbert

    Sunday, December 14, 2008 6:35 PM
  • Hi Dilbert,

    You can get that gap effect in the designer by placing controls next to each other. They'll "bump" into each other at their Margin value and not want to get closer.

    You could also drop a SplitContainer into the main body of your add-in and change the IsSplitterFixed, SplitterDistance, and SplitterDistance properties. That'll give you a couple of panels X pixels apart that you can dock controls in.
    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Sunday, December 14, 2008 7:15 PM
    Moderator
  • Here's the code I'm using to build the gradient bar at the title text:

     

            public static Rectangle BuildTitleBar(Graphics g, Rectangle area)  
            {  
                LinearGradientBrush brush = new LinearGradientBrush(area, Color.White, Color.FromArgb(57, 105, 165), 0f);  
     
                g.FillRectangle(brush, area);  
                g.DrawImage(Properties.Resources.applications_24, new Point(3, 3));  
                g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;  
                g.DrawString(Properties.Resources.DiskManagement, new Font(new FontFamily("Tahoma"), 10f, FontStyle.Bold), new SolidBrush(Color.FromArgb(57, 69, 90)), 28f, 6f);  
                g.DrawLine(new Pen(Color.FromArgb(57, 105, 165)), new Point(0, area.Height - 1), new Point(area.Width, area.Height - 1));  
                return area;  
            } 

     


    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Sunday, December 14, 2008 7:18 PM
    Moderator
  • wow thank you so much this is helping me alot. I think i am starting to understand it's just the location of these controls and how to align these controls that is giving me a hard time:

    MainTabUserControl has a fancylistview and sidepanel, i tried docking which didnt work right and it doesnt give me that space.
    I am doing a search for Location info using reflector but i am not finding anything showing how to algin - really puzzling
    Sunday, December 14, 2008 9:36 PM
  • There's a SetSizes() method that's called during construction of the MainTabUserControl for the Server Storage tab, after the controls have been added to the form:

    private void SetSizes()  
    {  
        thisthis.fancyListView.Top = this.topBar.Top + this.topBar.Height;  
        this.fancyListView.Width = base.ClientRectangle.Width - this.sidebarWidth;  
        this.fancyListView.Height = base.ClientRectangle.Height - this.topBar.Height;  
        this.sideBar.SetBounds(this.fancyListView.Left + this.fancyListView.Width, this.fancyListView.Top, this.sidebarWidth, this.fancyListView.Height);  
        this.chartControl.SetBounds(2, 0, this.sideBar.Width - 2, this.sideBar.Height);  
        this.statusHeader.SetBounds(1, 1, this.chartControl.Width - 2, Math.Max(Me.Resource.Int("StorageStatusImgSize"), Me.Resource.Int("StorageStatusHeight")));  
        int y = (this.statusHeader.Top + this.statusHeader.Height) + Me.Resource.Int("StorageSizeVertBuffer");  
        this.storageSizeLbl.SetBounds(this.chartControl.Location.X + Me.Resource.Int("StorageSizeHorzBuffer"), y, Me.Resource.Int("StorageSizeLabelWidth"), Me.Resource.Int("StorageSizeLabelHeight"));  
        this.storageSizeText.SetBounds(this.storageSizeLbl.Left + this.storageSizeLbl.Width, y, ((this.chartControl.Width - 4) - this.storageSizeLbl.Width) - 1, Me.Resource.Int("StorageSizeLabelHeight"));  
        this.freeSpaceLbl.SetBounds(this.storageSizeLbl.Left, this.storageSizeLbl.Top + this.storageSizeLbl.Height, this.storageSizeLbl.Width, this.storageSizeLbl.Height);  
        this.freeSpaceText.SetBounds(this.storageSizeText.Left, this.storageSizeText.Top + this.storageSizeText.Height, this.storageSizeText.Width, this.storageSizeText.Height);  
    }  
     

    So it looks to me like the controls are being sized programmatically when the StorageManagerControl is constructed. StorageManagerControl implements Panel as well (i.e. internal class StorageManagerControl : Panel).

    This is the constructor for StorageManagerControl (after cutting out all the add-in specific construction code):

    public StorageManagerControl(int width, int height, IConsoleServices services)  
    {  
        this.sidebarWidth = Me.Resource.Int("SidebarWidth");  
        this.statusImage = Me.Resource.Image("storage_alloc_24.png");  
        ...  
        base.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);  
        base.Width = width;  
        base.Height = height;  
        ...  
        this.fancyListView = new FancyListView();  
        thisthis.fancyListView.Parent = this;  
        ...  
        this.sideBar = new Panel();  
        this.sideBar.BackColor = Color.White;  
        ...  
        this.topBar = new ConsoleToolBar();  
        thisthis.topBar.Parent = this;  
        ...  
        base.Controls.Add(this.topBar);  
        base.Controls.Add(this.fancyListView);  
        base.Controls.Add(this.sideBar);  
        this.BackColor = CommonSettings.ConsoleBackgroundColor;  
        ...  
        this.SetSizes();  
    }  
     
       
     

    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Sunday, December 14, 2008 9:51 PM
    Moderator
  • Thanks to your fantastic help I am a lot more at ease with the WHS & SDK and happy to report back with limited success, my final hurdle is that I am trying to load an icon in the header (png 24x24 with transparency) - reflecting code i dont see what i am missing but yet my icon always looks horrible.
    Any ideas? 

    Cheers

    Dilbert
    Monday, December 15, 2008 6:57 AM
  • The MainTabUserControl icon should be 32x32 (pretty sure the WHS Console will resize anything smaller than that upwards). I'm using a PNG with alpha channel (transparency) and it works fine.

    Edit: Hmm, or do you mean in the sidebar, not the main tab icon?

    Transparency doesn't work well through Remote Desktop (which is what the WHS Console really is). Drawing directly to the screen tends to use the video driver, which doesn't look so hot.

    I found that I had to create a new Image offscreen, add my pictures and text, and then set the PictureBox.Image to my edited Image.
    Monday, December 15, 2008 7:26 AM
    Moderator
  • Hi Sam

    Using your BuildTitleBar i am trying to draw an icon, tried OnPaint/OnPaintBackground and different icons 24x24,32x32 with alpha etc but they all have little dotz around them as if they are selected via mouse which totally confuses me as this only happens at runtime it looks perfect in the designer.

    All i am doing is something like this:

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)

    {

    base.OnPaint(e);

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // tried none etc

    e.Graphics.DrawImage(headerImage.Image, 3f, 3f, headerImage.Width, headerImage.Height);

    }

    Monday, December 15, 2008 7:33 AM
  • As a test, try creating a method that returns new image to fill your TitleBar PictureBox.Image, rather than using OnPaint. Call it during construction of MainTabUserControl.
    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Monday, December 15, 2008 7:36 AM
    Moderator
  • if i use a picturebox the little 24x24 image loads perfectly at runtime but i have lag/delay that is there is a white artificat for 1sec while it loads
    Monday, December 15, 2008 7:49 AM
  • You could probably stop it being a white box by changing the background colour of the PictureBox to transparent, but you'll most likely still get the form elements loading slower than you'd like.

    The 0.5-1.0 sec time lag it takes to load form elements is present on just about all Add-Ins I've seen (including the Server Storage tab), depending on the speed of the server.

    Here's my paint event handler:

            private void HeaderPanel_Paint(object sender, PaintEventArgs e)  
            {  
                Gui.ImageBuilder.BuildGradientBar(e, this.labelHeaderText);  
            } 

    (It's actually a label, not a panel)

            public static void BuildGradientBar(PaintEventArgs target, Label label)  
            {  
                Graphics g = target.Graphics;  
                g.SmoothingMode = SmoothingMode.AntiAlias;  
                Rectangle area = target.ClipRectangle;  
     
                if (!target.ClipRectangle.Equals(label.ClientRectangle))  
                {  
                    area = label.ClientRectangle;  
                }  
     
                area = Gui.ImageBuilder.BuildTitleBar(g, area);  
            } 

    You can see I'm building the Rectangle offscreen, and then assigning it to the label, rather than using the Graphics object directly.
    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Monday, December 15, 2008 7:56 AM
    Moderator
  • Having problems editing posts. Here's the event handler that fires the previous method:

            private void HeaderPanel_Paint(object sender, PaintEventArgs e)  
            {  
                Gui.ImageBuilder.BuildGradientBar(e, this.labelHeaderText);  
            } 

    It's a Label, not a Panel that does this. I just called it HeaderPanel because it sounded like a good idea at the time :)

    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Monday, December 15, 2008 8:01 AM
    Moderator
  • I have tried:

    this.headerImage = new PictureBox();
    this.headerImage.Location = new Point(3, 3);
    this.headerImage.BackColor = Color.Transparent;
    this.headerImage.SizeMode = PictureBoxSizeMode.AutoSize;
    this.Controls.Add(headerImage);


    this still produces the white background square artificat although it being set as Color.Transparent???? but the image once loaded is in perfect condition. if at all possible i would really like to draw it and not use a picturebox but i just dont understand why it looks so bad 

    I am trying to understand,why did you do:

    if (!target.ClipRectangle.Equals(label.ClientRectangle))  
                {  
                    area = label.ClientRectangle;  
                }  
     


    Monday, December 15, 2008 8:15 AM
  • Unfortunate that transparent background doesn't get you a clean box :)
     
    Pretty sure the image is looking bad because of the Remote Desktop video driver. The video driver will render whatever you write to the graphics object, if that object is on the screen, and it doesn't handle transparency very well (it uses dithering instead of alpha blending). You want to render the image off-screen, and then assign it to your form element.

    I ran into the same issue trying to get my partially transparent wireframes to work.

    In that code, I'm grabbing the entire ClientRectangle and redrawing it every time a paint event occurs. What I found is that when another window overlaps a form element, OnPaint is called. If I don't redraw the entire ClientRectangle, I get nasty white lines around things that weren't part of the ClipRectangle (another artifact of the Remote Desktop video driver, I'm guessing).

    Try using my OnPaint event handler and see if that helps.

    1) OnPaint handler gets called, passes through PaintEventArgs and the instance of the label I'm redrawing (not great methodology, I know, but it works).
    2) BuildGradientBar works out what our rectangle is and makes sure we're redrawing the entire Label and not just an overlapped part.
    3) The rectangle is then set to output from BuildTitleBar
    Tentacle Blog: http://www.tentaclesoftware.com/blog/ WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Monday, December 15, 2008 8:25 AM
    Moderator
  • Sam you are like a one man army; this forum would be dead without you mate.
    I feel like I know you already - all the times you have helped me and others.

    Dilbert, if Sam answered your question you can mark it as an answer




    [Great now i am paranoid that my list view and GDI stuff is flickering too much #$$#%]

    Monday, December 15, 2008 8:45 PM
    Moderator

  • Alexander Kent said:

    Sam you are like a one man army; this forum would be dead without you mate.
    I feel like I know you already - all the times you have helped me and others.



    Thanks! The more WHS developers we have, the better the overall ecosystem to sell our own products, right? :)


    Tentacle Blog: http://www.tentaclesoftware.com/blog/
    WHS Disk Management: http://www.tentaclesoftware.com/WHSDiskManagement/
    Tuesday, December 16, 2008 9:09 PM
    Moderator