• Welcome to Dark Bit Factory & Gravity.
 

odd shaped windows in c#

Started by ninogenio, December 09, 2007

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ninogenio

right jims got me intrested in c#,

ive got a web browser window up and running and i want to be able to make a window thats shaped around a bmp skin sort of like the music disk's floating around without a boarder.

i can sort of get the effect by disabling the boarder but then i can't resize my window or close it.

what would be ideal is being able to hide the boarder and my menu strip until i hover the mouse over them.

but i have no idea how to go about any of this and was wondering if you guys could throw me a few pointers.
Challenge Trophies Won:

Jim

It's not something that C# does, so the only way I can see of doing it involves:
a) overriding WndProc and handling WM_NCMOUSEMOVE, WM_NCPAINT and checking whether we should be painting the window frame or not, and redrawing the bits of the desktop that contain the frame if we do.
b) overriding OnPaint and drawing the main part of the window ourselves.
c) You can't do a) without calling direct into Win32 API calls.
So, I can probably get it to work, and I've done some experiments, but it's hardcore.  It's easier in Win32.

The alternative is to have a borderless window and simply add you own X _ [] buttons.  That's practically trivial.

Jim
Challenge Trophies Won:

ninogenio

right i think ill add my own buttons for now, cheers jim!.

ohh and on rendering for demos and stuff. does this link to an opengl wrapper in visual c# look ok jim, the examples are a bit slow because they use system timers for updates but overall it looks not to bad to me.

http://colinfahey.com/csharp_wrapper_opengl_windows/csharp_wrapper_for_opengl.html   
Challenge Trophies Won:

Jim

Looks OK at first glance...

Jim
Challenge Trophies Won:

ninogenio

right im going to try some test's with it then.

the only problem i can see and its not a problem as such is that the examples are all based on paint events and timers to redraw the frames. so letting the program run at full throttle isnt possible.

do you think it might be possible to do somthing similar to what we do in cpp by creating a message loop and putting the main program code in there and ripping out the paint stuff for the gl rendering if not its cool cause i still think a lot could be done with it in paint messages.
Challenge Trophies Won:

ninogenio

i read it can be done but its a bit of a dirty hack.

the code for it looks like this.


    ///////////////////////////////////////

    // Main Loop stuff

    ///////////////////////////////////////

    // Necessary structs and imports

    //

    struct POINTAPI
    {
      public Int32 x;
      public Int32 y;
    }

    struct MSG
    {
      public Int32 hwmd;
      public Int32 message;
      public Int32 wParam;
      public Int32 lParam;
      public Int32 time;
      public POINTAPI pt;
    }

    [ DllImport( "user32.dll", SetLastError = true ) ]
    private static extern bool PeekMessage(
                                   ref MSG lpMsg,
                           Int32 hwnd,
           Int32 wMsgFilterMin,
           Int32 wMsgFilterMax,
           PeekMessageOption wRemoveMsg);

    [ DllImport( "user32.dll", SetLastError = true ) ]
    private static extern bool TranslateMessage(ref MSG lpMsg);

    [ DllImport( "user32.dll", SetLastError = true ) ]   
    private static extern Int32 DispatchMessage(ref MSG lpMsg);

    private enum PeekMessageOption
    {
      PM_NOREMOVE = 0,
      PM_REMOVE
    }

    private static Int32 WM_QUIT = 0x12;

    public void Run()
    {
      MSG msg = new MSG();

      while ( true )
      {
        if ( PeekMessage( ref msg, 0, 0, 0, PeekMessageOption.PM_REMOVE ) )
        {
  if ( msg.message == WM_QUIT )
    break;

  TranslateMessage( ref msg );
  DispatchMessage( ref msg );
        }

Draw();

// sleep a little

System.Threading.Thread.Sleep( 10 );
      }
    }
  }

but will all the normal interupts still fire at the correct times and all the button handling stuff that c# builds for the user act as normal cause if not its not really worth it.
Challenge Trophies Won:

Jim

If you're gonna do that you might as well do it in Win32, since that's what it's doing, all that dllimport stuff.

You could create another thread to control the drawing, and use Form.Invoke() to do the drawing.

That goes something like this

using System.Threading;
using System.Windows.Forms;
...
...
...inside you main form's class somewhere (mine's called Form1)
...
...
        private Thread drawthread;

        public Form1()
        {
            InitializeComponent();

            drawthread = new Thread(new ThreadStart(drawfunc));
            drawthread.Start();
        }

        private delegate void DrawDelegate();

        private void drawfunc()
        {
            DrawDelegate dd = new DrawDelegate(drawing);
            Invoke(dd);
            Thread.Sleep(16);
        }

        private void drawing()
        {
            //do drawing here
        }


Jim
Challenge Trophies Won:

ninogenio

so the invoke will happen every frame and not at paint or update intervals is that right? and because its a new thread all the drawing stuff will happen independently from all the ui stuff thats going on in the form. cool.

it looks much better than my attempt thats for sure!
Challenge Trophies Won:

Jim

#8
Invoke sends a message to the form's message loop saying 'please call the delegate that is my parameter', so in effect GetMessage will get a message and react right away.  It should work nicely along with the UI dialog items, yep.

delegate is C#'s equivalent of a function pointer.

<edit>
So, this technique isn't much better than using a timer.

<edit2, edit3 & edit4>
Actually, it might get you somewhere if you do something like this

        private void drawfunc()
        {
            DrawDelegate dd = new DrawDelegate(drawing);
...calculate scene
            Invoke(dd);//draws the scene
...instead of sleep, do SwapBuffers at the end of drawing
..NB. Important, no OpenGL calls in drawfunc, it's on the wrong thread.
        }

        private void drawing()
        {
            //do drawing here
...
            SwapBuffers();
        }


Jim
Challenge Trophies Won:

ninogenio

thank you jim k+,

this is exactly what i was looking for ill need to put it all together now and see how well it works.

im really glad i dont have to go down the dll import path!!
Challenge Trophies Won:

Paul

If i understand what you want to do, you could have a look here: http://dbfinteractive.com/index.php?topic=1094.0
I will bite you - http://s5.bitefight.se/c.php?uid=31059
Challenge Trophies Won:

stormbringer

to create regular windows but with any kind of shape, just check out how to set the window region, in the Win32 API:

have a look at this: http://www.codeguru.com/cpp/w-d/dislog/non-rectangulardialogs/article.php/c5037/
We once had a passion
It all seemed so right
So young and so eager
No end in sight
But now we are prisoners
In our own hearts
Nothing seems real
It's all torn apart

Jim

Thanks Paul and Stormbringer! - that's exactly what nino wants to do, one difference being that if the mouse is in the non-client area (frame+title bar) then the NC area should be shown, otherwise hidden (this is what media player does in skin mode).  It would be easy enough to add the NC area to the region based on the mouse position.

It'd be really cool to find a way to do it in C# without having to resort to Win32.  I was looking at something like this

        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics gfx = e.Graphics;
            gfx.SmoothingMode = SmoothingMode.HighQuality;

            GraphicsPath path = new GraphicsPath();
            path.AddRectangle(ClientRectangle);

            gfx.SetClip(path, CombineMode.Replace);

            Pen p = new Pen(Color.Yellow);
            gfx.FillRectangle(p.Brush, ClientRectangle);

            //base.OnPaint(e);
        }

Here a GraphicsPath is the same as a Win32 Region and gfx.SetClip should replace the painted region.  But it didn't work first time I tried it.

Jim
Challenge Trophies Won:

ninogenio

cheers stormbringer and paul thats exactly the effect im after!!

i really dont want to use the win32 api from c# as the languge wasnt created for it and by importing api calls from win32 c# becomes a bit pointless and it gets to the point where your better of coding in cpp.

@jim that looks quite promising! wonder where it goes wrong.
Challenge Trophies Won:

Jim

It partly goes wrong (or doesn't do what you want) because the Paint method only deals with the client area of the window, not the title bar and frame.  I'm not sure (apart from Win32) how to get the paint events for that area.

Jim
Challenge Trophies Won:

ninogenio

#15
one thing that could be done jim is monitoring the mouse x/y and changing this.

//show boarder         
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
//hide boarder
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

based on where the mouse is..dont know why i didnt think of it sooner.
Challenge Trophies Won:

stormbringer

#16
Quote from: Jim on December 11, 2007
It partly goes wrong (or doesn't do what you want) because the Paint method only deals with the client area of the window, not the title bar and frame.  I'm not sure (apart from Win32) how to get the paint events for that area.

Jim

Unfortunatley, Win32 is your only way to success here. It's really there where you can "control" what's going on with windows.

the NC area paint messages can be intercepted, just check this: http://msdn2.microsoft.com/en-us/library/ms534905.aspx

Besides that, if you want my opinion, C# is not the best way to create graphic-oriented applications. Also with XP and crap-Vista manages window compositing in a weird way... patches and patches of stupid code...

We once had a passion
It all seemed so right
So young and so eager
No end in sight
But now we are prisoners
In our own hearts
Nothing seems real
It's all torn apart

Jim

#17
I have something like this (butchered and not expected to work)

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            switch (m.Msg)
            {
                case WM_NCMOUSEMOVE:
                    Rectangle r = Bounds;
                    r.Location = PointToScreen(r.Location);
                    r.X = 0;
                    r.Y = 0;
                    r.Width = 1600;
                    r.Height = 1200;
                    //SafeNativeMethods.UpdateDesktop(r);
                    this.Invalidate();
                    Win32API.UpdateDesktop();
                    base.WndProc(ref m);
                    break;

                case WM_NCPAINT:
                    Point p;
                    p = Control.MousePosition;
                    p = PointToClient(p);

                    Rectangle c = this.ClientRectangle;
                    //c.Location = PointToScreen(c.Location);

                    Rectangle b = this.Bounds;
                    //b.Location = PointToScreen(b.Location);

                    if (c.Contains(p))
                        m.Result = System.IntPtr.Zero;
                    else if (b.Contains(p))
                        base.WndProc(ref m);
                    else
                        m.Result = System.IntPtr.Zero;
                    return;
                default:
                    base.WndProc(ref m);
                    break;
            }

That was supposed to hide the NC area.  It would work, if I could get Windows to repaint the NC area (by invalidating it), but that's proving the tricky bit (without Win32).

<edit>nino - that's a great idea - it just might work ;)

Jim
Challenge Trophies Won:

ninogenio

just tried it and it does work.

ive not added the mouse move stuff. i just quickly added a form check box and added the hide show boarder code and it seems to work fine!
Challenge Trophies Won:

Jim

The Window jumps around, so you'd need to tinker with its 'Location' property as well.  It's still hard to tell when a mouse enters the NC area and leaves it or is in it.  Especially when there isn't one because you just turned it off!

Jim
Challenge Trophies Won: