Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Add SFML to Windows Form - Visual Studio Design Mode - C++/CLI .NET Managed Code  (Read 10771 times)

0 Members and 1 Guest are viewing this topic.

Senzin

  • Newbie
  • *
  • Posts: 15
    • View Profile
SFML-2.0 for C++
Win 7 Pro x64
Visual Studio 2010 and 11 Beta (CLI IntelliSense!)

Like the subject says, I'd like to add SFML views to a Windows form. I've played with the Win32 examples, but the designer in Visual Studio is very nice and I'd like to use it.

The most obvious way to use the designer is to make a Windows Forms program, which puts it under CLI managed code. But if there's a way to use the designer with the example Win32 code, that would be fine as well.

I've looked around the forums, but I've found very few threads talking about this, and those I've found were incomplete and 2-3 years old.

http://en.sfml-dev.org/forums/index.php?topic=1666.0 <-- This is what I've tried to use, though I decided to make a new thread instead of resurrecting one that is three years old.

My test Windows Forms project has the same settings added as the working Win32 example (include path, lib path, dependencies: sfml-system-d.lib;sfml-window-d.lib;sfml-graphics-d.lib;).

I attempted modifying my form's constructor:

public ref class MyForm : public System::Windows::Forms::Form
{
public:
        MyForm(void) : renderView(NULL) // <-- init pointer
        {
                InitializeComponent();
                HWND ptr = (HWND)renderWindow->Handle.ToPointer(); // Get the Label's handle (renderWindow is a Label)
                renderView = new sf::RenderWindow(ptr); // Pass it to the sf::RenderWindow
                renderWindow->Paint += gcnew PaintEventHandler(this, &MyForm::MyForm_Paint);
        }
        [more code...]

The line "renderView = new sf::RenderWindow(ptr);" generates linker errors:

1>WindowsForms.obj : error LNK2028: unresolved token (0A00000E) "private: virtual bool __clrcall sf::RenderWindow::activate(bool)" (?activate@RenderWindow@sf@@$$FEAM_N_N@Z) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::RenderTarget'}''(void)" (???__E??_SRenderWindow@sf@@6BRenderTarget@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2028: unresolved token (0A00000F) "public: virtual class sf::Vector2<unsigned int> __clrcall sf::RenderWindow::getSize(void)const " (?getSize@RenderWindow@sf@@$$FUBM?AV?$Vector2@I@2@XZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::RenderTarget'}''(void)" (???__E??_SRenderWindow@sf@@6BRenderTarget@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2028: unresolved token (0A000010) "protected: virtual void __clrcall sf::RenderWindow::onResize(void)" (?onResize@RenderWindow@sf@@$$FMAMXXZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::Window'}''(void)" (???__E??_SRenderWindow@sf@@6BWindow@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2028: unresolved token (0A000011) "protected: virtual void __clrcall sf::RenderWindow::onCreate(void)" (?onCreate@RenderWindow@sf@@$$FMAMXXZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::Window'}''(void)" (???__E??_SRenderWindow@sf@@6BWindow@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2028: unresolved token (0A000017) "public: virtual __clrcall sf::RenderWindow::~RenderWindow(void)" (??1RenderWindow@sf@@$$FUAM@XZ) referenced in function "public: virtual void * __clrcall sf::RenderWindow::`vector deleting destructor'(unsigned int)" (??_ERenderWindow@sf@@$$FUAMPAXI@Z)
1>WindowsForms.obj : error LNK2028: unresolved token (0A000042) "public: __clrcall sf::RenderWindow::RenderWindow(struct HWND__ *,struct sf::ContextSettings const &)" (??0RenderWindow@sf@@$$FQAM@PAUHWND__@@ABUContextSettings@1@@Z) referenced in function "public: __clrcall WindowsForms::MyForm::MyForm(void)" (??0MyForm@WindowsForms@@$$FQ$AAM@XZ)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "public: virtual __clrcall sf::RenderWindow::~RenderWindow(void)" (??1RenderWindow@sf@@$$FUAM@XZ) referenced in function "public: virtual void * __clrcall sf::RenderWindow::`vector deleting destructor'(unsigned int)" (??_ERenderWindow@sf@@$$FUAMPAXI@Z)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "public: __clrcall sf::RenderWindow::RenderWindow(struct HWND__ *,struct sf::ContextSettings const &)" (??0RenderWindow@sf@@$$FQAM@PAUHWND__@@ABUContextSettings@1@@Z) referenced in function "public: __clrcall WindowsForms::MyForm::MyForm(void)" (??0MyForm@WindowsForms@@$$FQ$AAM@XZ)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "protected: virtual void __clrcall sf::RenderWindow::onResize(void)" (?onResize@RenderWindow@sf@@$$FMAMXXZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::Window'}''(void)" (???__E??_SRenderWindow@sf@@6BWindow@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "protected: virtual void __clrcall sf::RenderWindow::onCreate(void)" (?onCreate@RenderWindow@sf@@$$FMAMXXZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::Window'}''(void)" (???__E??_SRenderWindow@sf@@6BWindow@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "private: virtual bool __clrcall sf::RenderWindow::activate(bool)" (?activate@RenderWindow@sf@@$$FEAM_N_N@Z) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::RenderTarget'}''(void)" (???__E??_SRenderWindow@sf@@6BRenderTarget@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>WindowsForms.obj : error LNK2019: unresolved external symbol "public: virtual class sf::Vector2<unsigned int> __clrcall sf::RenderWindow::getSize(void)const " (?getSize@RenderWindow@sf@@$$FUBM?AV?$Vector2@I@2@XZ) referenced in function "void __clrcall `dynamic initializer for 'const sf::RenderWindow::`local vftable'{for `sf::RenderTarget'}''(void)" (???__E??_SRenderWindow@sf@@6BRenderTarget@1@@@@YMXXZ@?A0x679daca2@@$$FYMXXZ)
1>D:\Zytrex-Development\VisualStudio\WindowsForms\Debug\WindowsForms.exe : fatal error LNK1120: 12 unresolved externals

In the thread I've tried to work from (the link I posted), it sounded like the guy got his to work. Unfortunately, he didn't mention anything about his project settings, so I don't know what I'm missing that would get rid of the linker errors.
« Last Edit: July 06, 2012, 11:47:22 am by Senzin »

Andrei15193

  • Guest
Hello,

First of all C++/CLI is considered to be a separate programming language from C++/STD. Altho VS uses the same compiler for both (which allows you to combine C with C++/STD with C++/CLI) it's better not to mix those up. SFML is written for C++/STD and all of it's classes are "unmanaged". You can't have an "unmanaged" type inside a "managed" one, unless you do some weird stuff. In the end you will have an unportable application.

If you want Windows Forms-like apps and mix it up with SFML, it's better to look into Qt since both are made for C++/STD. For .NET game development look into XNA Framework.

Senzin

  • Newbie
  • *
  • Posts: 15
    • View Profile
Thanks Andrei15193,
When you said, "You can't have an "unmanaged" type inside a "managed" one", I assume you were being literal, referring to having an unmanaged class as a member of a managed class? Can you not even have pointers to unmanaged classes? And I assume this has to do with garbage collection, etc, because I know you can mix them, as long as you know which are handled by what. That's part of the purpose of using the ^ operator instead of *, as I understand it.

Anyway, I just noticed that in the thread I linked in my first post, the author said he was using .NET and there is a .NET version of SFML-2.0. I downloaded it to take a look, but it only included the dll files sfmlnet-audio-2.dll, sfmlnet-graphics-2.dll and sfmlnet-windows-2.dll. Don't I need different .lib and .pdb files too, as well as debug versions for compilation?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Quote
Don't I need different .lib and .pdb files too, as well as debug versions for compilation?
SFML.Net is made of regular assemblies, that work with any .Net language. It's not C++, you don't need import libraries or debug files.
Laurent Gomila - SFML developer

Andrei15193

  • Guest
In a "managed" class you can't have a pointer. If you do not use dynamic allocation, as far as I know, you can have an "unmanaged" class instance inside a "managed" one, however I can't promise it will work (it probably interprets the class as a struct, therefore it will allocate memory on Stack), you can try it out.

The difference between ^ and * is not just who gets a piece of memory where. * (from standard C) defines a pointer type, same in C++/STD since it inherits C. In C++/CLI there are no pointers, there are handles. If you put a ^ next to a type declaration, that is a handle type (or reference), it works similar to a pointer, but it is not a pointer!

Keep in mind that using the .NET Framework will limit you to machines that have the .NET Framework installed. This means something like Windows only, if you limit yourself to .NET Compact Framework 2.0 you may get your app to run on a Xbox. Can't say anything about Unix/Linux since I haven't looked into GTK yet.

Senzin

  • Newbie
  • *
  • Posts: 15
    • View Profile
Thanks Andrei15193, it looks like I have some reading to do. As for portability, well the whole point of this thread is that I'm trying to use Visual Studio to make a Windows Forms program with Design Mode, so I'm definitely targeting Windows. :)

Laurent, looks like I have it working. I removed the C++ paths and libs and added references to the .NET libs. I'll post my code at the bottom (I'm just going to post the entire file so there are no assumptions).

It works great! I didn't have to do any funky stuff with the label's style or with the on paint events like in that other thread. I just gave the label's handle to the RenderWindow constructor and called Clear, Draw and Display from a timer and everything looks nice, with smooth (though very simple so far) animation.

I also have a couple lines of code commented out that will create a separate SFML window instead of using the label. It works just fine as well.

You'll notice all the SFML setup is in a button's click event. I originally had it in the Form's constructor. However the program was starting and then crashing before I ever saw anything. I moved the code to the button just so I could see the form actually appear before I make it crash. But when I hit the button, the exception window popped up and told me what the problem was. It seems before the program was crashing before the popup exception handler was loaded. So I'm just keeping the SFML setup in the button click for now. (Edit: Moving the SFML setup to the form load event solves this problem. SFML starts immediately and automatically and any exceptions are handled by the popup exception handler so you know about them.)

Anyway, the exception was "Unable to load DLL 'csfml-graphics-2': The specified module could not be found." This surprised me, but I went ahead and downloaded csfml, put the dll in the debug folder and it worked! I did the same for audio and window. Is this how it's supposed to be done?

Oh, and one other thing. I noticed that the examples that come with the 2.0 .NET release are just executables. There isn't actually any example code. And all of them crash immediately when I try to run them. I checked the 1.6 .NET release and they do have the source code. But the binaries that come with them also crash immediately. Since the 1.6 version has the code, I figured you probably intended to put the code in the 2.0 version, so I wanted to let you know. I don't know about the crashing though. I tried putting all the dlls in with the binaries, but it didn't work.

#pragma once

namespace SFMLWinForm {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    using namespace SFML::Audio;
    using namespace SFML::Window;
    using namespace SFML::Graphics;

    /// <summary>
    /// Summary for Form1
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
    public:
        Form1(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~Form1()
        {
            if (components)
            {
                delete components;
            }
        }

    private:
        RenderWindow^ window;
        Music^ music;
        Texture^ texture;
        Sprite^ sprite;
        SFML::Graphics::Font^ font;
        SFML::Graphics::Text^ text;

    private: System::Windows::Forms::Label^  lblWindow;
    private: System::Windows::Forms::Button^  btnQuit;
    private: System::Windows::Forms::Button^  btnStart;
    private: System::Windows::Forms::Timer^  tmrSFML;
    private: System::ComponentModel::IContainer^  components;

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>


#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->components = (gcnew System::ComponentModel::Container());
            this->lblWindow = (gcnew System::Windows::Forms::Label());
            this->btnQuit = (gcnew System::Windows::Forms::Button());
            this->btnStart = (gcnew System::Windows::Forms::Button());
            this->tmrSFML = (gcnew System::Windows::Forms::Timer(this->components));
            this->SuspendLayout();
            //
            // lblWindow
            //
            this->lblWindow->Location = System::Drawing::Point(12, 9);
            this->lblWindow->Name = L"lblWindow";
            this->lblWindow->Size = System::Drawing::Size(760, 515);
            this->lblWindow->TabIndex = 2;
            this->lblWindow->Text = L"Label Window";
            //
            // btnQuit
            //
            this->btnQuit->DialogResult = System::Windows::Forms::DialogResult::Cancel;
            this->btnQuit->Location = System::Drawing::Point(697, 527);
            this->btnQuit->Name = L"btnQuit";
            this->btnQuit->Size = System::Drawing::Size(75, 23);
            this->btnQuit->TabIndex = 1;
            this->btnQuit->Text = L"Quit";
            this->btnQuit->UseVisualStyleBackColor = true;
            this->btnQuit->Click += gcnew System::EventHandler(this, &Form1::btnQuit_Click);
            //
            // btnStart
            //
            this->btnStart->Location = System::Drawing::Point(15, 527);
            this->btnStart->Name = L"btnStart";
            this->btnStart->Size = System::Drawing::Size(75, 23);
            this->btnStart->TabIndex = 0;
            this->btnStart->Text = L"Start";
            this->btnStart->UseVisualStyleBackColor = true;
            this->btnStart->Click += gcnew System::EventHandler(this, &Form1::btnStart_Click);
            //
            // tmrSFML
            //
            this->tmrSFML->Interval = 1;
            this->tmrSFML->Tick += gcnew System::EventHandler(this, &Form1::tmrSFML_Tick);
            //
            // Form1
            //
            this->AcceptButton = this->btnStart;
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->CancelButton = this->btnQuit;
            this->ClientSize = System::Drawing::Size(784, 562);
            this->Controls->Add(this->btnStart);
            this->Controls->Add(this->btnQuit);
            this->Controls->Add(this->lblWindow);
            this->Name = L"Form1";
            this->Text = L"SFML";
            this->ResumeLayout(false);

        }
#pragma endregion

    private:System::Void btnQuit_Click(System::Object^  sender, System::EventArgs^  e)
            {
                window->Close(); // For separate SFML window
                this->Close();
            }

    private:System::Void OnClose(System::Object^  sender, System::EventArgs^  e)
            {
                // For separate SFML window close
                ((RenderWindow^)sender)->Close();
            }

    private:System::Void btnStart_Click(System::Object^  sender, System::EventArgs^  e)
            {
                static bool runOnce = false;
                if (runOnce) return;
                runOnce = true;

                // Creates a separate window (in addition to Form1) for rendering SFML
                //window = gcnew RenderWindow(VideoMode(800, 600), "SFML Window");
                //window->Closed += gcnew EventHandler(this, &Form1::OnClose); // For separate SFML window close

                // Uses lblWindow (a label) as render window instead of separate window
                window = gcnew RenderWindow(lblWindow->Handle);

                music = gcnew Music("../media/music.ogg");
                music->Play();
                music->Loop = true;

                texture = gcnew Texture("../media/cute_image.jpg");
                sprite = gcnew Sprite(texture);

                font = gcnew SFML::Graphics::Font("../media/arial.ttf");
                text = gcnew SFML::Graphics::Text("WHY DO I EXIST ?", font, 50);
                text->Position = Vector2f(8, 335);
                text->Color = SFML::Graphics::Color::Black;

                tmrSFML->Enabled = true;
            }

    private:System::Void tmrSFML_Tick(System::Object^  sender, System::EventArgs^  e)
            {
                // Process events
                window->DispatchEvents();

                // Animate sprite and text
                float speed = 1;
                static float i = speed;
                static float j = speed;
                if (sprite->Position.X + texture->Size.X + 1 > window->Size.X) i = -speed;
                if (sprite->Position.X - 1 < 0) i = speed;
                if (sprite->Position.Y + texture->Size.Y + 1 > window->Size.Y) j = -speed;
                if (sprite->Position.Y - 1 < 0) j = speed;
                sprite->Position += Vector2f(i, j);
                text->Position += Vector2f(i, j);

                // Clear screen
                window->Clear();

                // Draw the sprite
                window->Draw(sprite);

                // Draw the string
                window->Draw(text);

                // Update the window
                window->Display();
            }

    };
}
 

[attachment deleted by admin]
« Last Edit: July 07, 2012, 08:57:57 am by Senzin »

Celtic Minstrel

  • Jr. Member
  • **
  • Posts: 80
    • View Profile
Keep in mind that using the .NET Framework will limit you to machines that have the .NET Framework installed. This means something like Windows only, if you limit yourself to .NET Compact Framework 2.0 you may get your app to run on a Xbox.
While I agree with the sentiment, it's not true that .NET is limited to Windows. Mono is a cross-platform .NET implementation which works on Mac and Linux (although I think it doesn't implement a few older, deprecated interfaces).

Andrei15193

  • Guest
Keep in mind that using the .NET Framework will limit you to machines that have the .NET Framework installed. This means something like Windows only, if you limit yourself to .NET Compact Framework 2.0 you may get your app to run on a Xbox.
While I agree with the sentiment, it's not true that .NET is limited to Windows. Mono is a cross-platform .NET implementation which works on Mac and Linux (although I think it doesn't implement a few older, deprecated interfaces).

.NET Framework has releases only for Windows Platforms. Mono doesn't fully support .NET. When saying that .NET can run on other platforms via Mono I expect that it can run any feature from that Framework, it isn't true (http://mono-project.com/Compatibility). Therefore .NET runs only on Windows Platforms because those platforms receive .NET releases (which fully implement all features of that Framework). Saying that Mono gives portability to a limited .NET application is true.

Senzin

  • Newbie
  • *
  • Posts: 15
    • View Profile
So since I'm using a Label as the handle that the SFML Render Window (just "SFML" for short) draws on. So I'm no longer using any of SFML's own events. Instead, I'm using the C++/CLI Winforms interface. However there are times when I want key presses and mouse clicks to be directed to SFML (or rather, certain key presses and mouse clicks should activate code that does something to SFML).

Since I'm using a Label, which cannot gain focus, I need to handle key presses globally (within the form at least). In other words, I need hot keys. Here are some simple examples of what kind of hot keys I'd like (please don't worry about how I make things happen in the SFML, the point is catching the hot keys).

1) When I press 'R', the graphics should reset.
2) When I press 'L', new graphics should be loaded.
3) If I hold Ctrl and click on the SFML, something will happen at the location I clicked.
4) If I hold the Spacebar and mouse drag on the SFML, things will be moved around.
5) If some TextBox control has focus, then key presses should go to the text box and not activate the hot keys.
6) If I MouseClick on some Button control and then start clicking on the SFML and at some point press the Spacebar, the Button should not be clicked again. (Remember, this SFML cannot have focus, so the button I pressed before still has focus.)

So that's what I'd like to do. Here is what I've tried so far:

A) To catch all hot keys, I set the form's KeyPreview property to true. This makes all key presses go to the form before any other controls. Then I simply handle KeyDown, KeyUp, or KeyPress events for the form.

B) To make sure typing in text boxes doesn't activate hot keys, in the KeyDown, KeyUp, or KeyPress events for the form, I check if any text boxes have focus before doing anything with the event, e.g.:

if (ActiveControl->Name != "textBox1" && ActiveControl->Name != "textBox2")
{ handleHotKey(e); }

TextBoxes actually have a PreviewKeyDown event that fires before the Form's KeyPreview, but I'm not sure how I would use it. It fires even before the text enters the TextBox, so setting the event.handled to true actually prevents the text from appearing.

C) To prevent buttons from being activated by the Spacebar, I used the MouseClick event for the buttons instead of the Click event, so only an actual MouseClick on the button will make anything happen. While this works, the Spacebar still activates the button's clicking animation.

Instead of a Label, I've also considered using a TextBox as the handle given to the SFML Render Window. But there are a few problems with this as well. When the TextBox gains or loses focus, it redraws itself, causing a flicker in SFML. When it has focus, even with no text and read only, it still shows the typing/selection caret blinking (disabling the TextBox fixes that, but then it can no longer gain focus). If there was a way to prevent the TextBox from repainting, that could fix the problem. However, even if those problems were fixed, the idea that the TextBox could lose focus by clicking on Buttons and other things seems bad. If I click on some Button (giving it focus), and then hit some hot key, it should still fire off the event.

Basically, it should be assumed that all keyboard events could be doing something to the SFML Render Window, where the exceptions are things like text boxes having focus. Another way to think of it is that the SFML Render Window should always have focus (again, except for TextBoxes, etc).

So far, my methods with the Label all seems to work, but they just feel a bit clumsy. Any suggestions on how to improve this would be appreciated.

Edit: Someone on Stack Overflow had this to say: "Well, don't pick a Label. And definitely not a TextBox. Just make your own Control derived class." Not so sure about that.