MVVM Tutorial

This commit is contained in:
vczh
2015-09-26 13:25:59 -07:00
parent 17659534e1
commit 2049c4116b
14 changed files with 536 additions and 6 deletions
@@ -16,7 +16,7 @@
<Instance name="MainWindowResource">
<Instance ref.CodeBehind="false" ref.Class="helloworld::MainWindow">
<Window Text="Hello, world!" ClientSize="x:480 y:320">
<att.BoundsComposition-set AlignmentToParent="left:0 top:0 right:0 bottom:0"/>
<att.BoundsComposition-set PreferredMinSize="x:480 y:320"/>
<Label Text="Welcome to GacUI Library!">
<att.Font>fontFamily:{Segoe UI} size:32 antialias:true</att.Font>
</Label>
@@ -0,0 +1,7 @@
#include <GacUI.h>
#include <Windows.h>
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
{
return SetupWindowsDirect2DRenderer();
}
@@ -80,13 +80,24 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\CppXml\WinMain.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="UI\Source\HelloWorldPartialClasses.cpp" />
<ClCompile Include="UI\Source\MainWindow.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Lib\GacUI\GacUI.vcxproj">
<Project>{8018d622-66ba-4e65-9d03-bdac37ea9a54}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Xml Include="UI\Resource.xml" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="UI\Source\HelloWorld.h" />
<ClInclude Include="UI\Source\HelloWorldPartialClasses.h" />
<ClInclude Include="UI\Source\MainWindow.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
@@ -13,10 +13,38 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="UI">
<UniqueIdentifier>{7285afdf-8248-4423-a988-ffbd98abbaa0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UI\Source\HelloWorldPartialClasses.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="UI\Source\MainWindow.cpp">
<Filter>UI</Filter>
</ClCompile>
<ClCompile Include="..\CppXml\WinMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Xml Include="UI\Resource.xml">
<Filter>Resource Files</Filter>
</Xml>
</ItemGroup>
<ItemGroup>
<ClInclude Include="UI\Source\HelloWorld.h">
<Filter>UI</Filter>
</ClInclude>
<ClInclude Include="UI\Source\HelloWorldPartialClasses.h">
<Filter>UI</Filter>
</ClInclude>
<ClInclude Include="UI\Source\MainWindow.h">
<Filter>UI</Filter>
</ClInclude>
</ItemGroup>
</Project>
+82 -5
View File
@@ -1,11 +1,88 @@
#include <GacUI.h>
#include <Windows.h>
#include "UI/Source/HelloWorld.h"
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow)
using namespace vl::collections;
using namespace vl::stream;
using namespace vl::regex;
class ViewModel : public Object, public virtual vm::IViewModel
{
return SetupWindowsDirect2DRenderer();
}
private:
WString userName;
WString password;
Regex regexLcLetters;
Regex regexUcLetters;
Regex regexNumbers;
public:
ViewModel()
:regexLcLetters(L"[a-z]")
, regexUcLetters(L"[A-Z]")
, regexNumbers(L"[0-9]")
{
}
WString GetUserName()override
{
return userName;
}
void SetUserName(WString value)override
{
userName = value;
UserNameErrorChanged();
}
WString GetUserNameError()override
{
if (userName == L"")
{
return L"User name should not be empty.";
}
return L"";
}
WString GetPassword()override
{
return password;
}
void SetPassword(WString value)override
{
password = value;
PasswordErrorChanged();
}
WString GetPasswordError()override
{
if (password == L"")
{
return L"Password should not be empty.";
}
bool containsLowerCases = regexLcLetters.Match(password);
bool containsUpperCases = regexUcLetters.Match(password);
bool containsNumbers = regexNumbers.Match(password);
if (!containsLowerCases || !containsUpperCases || !containsNumbers)
{
return L"Password should contains at least one lower case letter, one upper case letter and one digit.";
}
return L"";
}
bool SignUp()override
{
return true;
}
};
void GuiMain()
{
{
List<WString> errors;
FileStream fileStream(L"../UIRes/MVVM.bin", FileStream::ReadOnly);
auto resource = GuiResource::LoadPrecompiledBinary(fileStream, errors);
GetInstanceLoaderManager()->SetResource(L"Resource", resource);
}
helloworld::MainWindow window(new ViewModel);
window.MoveToScreenCenter();
GetApplication()->Run(&window);
}
@@ -0,0 +1 @@
..\..\..\..\Tools\GacGen.exe Resource.xml
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Resource>
<Folder name="GacGenConfig">
<Folder name="Cpp">
<Text name="Output">Source</Text>
<Text name="Include">GacUIReflection.h</Text>
<Text name="Name">HelloWorld</Text>
<Text name="Prefix"></Text>
</Folder>
<Folder name="Res">
<Text name="Output">..\..\UIRes</Text>
<Text name="PrecompiledBinary">MVVM.bin</Text>
</Folder>
</Folder>
<Folder name="MainWindow">
<Script name="ViewModelResource">
<Workflow-ViewModel>
<![CDATA[
module viewmodel;
namespace vm
{
interface IViewModel
{
func GetUserName() : string;
func SetUserName(value : string) : void;
prop UserName : string {GetUserName, SetUserName}
func GetPassword() : string;
func SetPassword(value : string) : void;
prop Password : string {GetPassword, SetPassword}
func GetUserNameError() : string;
event UserNameErrorChanged();
prop UserNameError : string {GetUserNameError : UserNameErrorChanged}
func GetPasswordError() : string;
event PasswordErrorChanged();
prop PasswordError : string {GetPasswordError : PasswordErrorChanged}
func SignUp() : bool;
}
}
]]>
</Workflow-ViewModel>
</Script>
<Instance name="MainWindowResource">
<Instance ref.CodeBehind="true" ref.Class="helloworld::MainWindow">
<ref.Parameter Name="ViewModel" Class="vm::IViewModel"/>
<ref.Property Name="HasLoggedIn" Type="bool" Value="false"/>
<Window ref.Name="self" Text="Let's Sign Up!" ClientSize="x:320 y:280">
<att.BoundsComposition-set PreferredMinSize="x:320 y:280"/>
<att.ViewModel-set UserName-bind="textBoxUserName.Text" Password-bind="textBoxPassword.Text"/>
<Table CellPadding="5" AlignmentToParent="left:0 top:0 right:0 bottom:0">
<att.Rows>
<CellOption>composeType:Absolute absolute:90</CellOption>
<CellOption>composeType:MinSize</CellOption>
<CellOption>composeType:MinSize</CellOption>
<CellOption>composeType:MinSize</CellOption>
<CellOption>composeType:MinSize</CellOption>
<CellOption>composeType:Absolute absolute:12</CellOption>
<CellOption>composeType:Percentage percentage:1.0</CellOption>
<CellOption>composeType:MinSize</CellOption>
</att.Rows>
<att.Columns>
<CellOption>composeType:MinSize</CellOption>
<CellOption>composeType:Percentage percentage:1.0</CellOption>
</att.Columns>
<Cell Site="row:0 column:0 columnSpan:2">
<SolidLabel Text="www.gaclib.net" HorizontalAlignment="Center" VerticalAlignment="Center">
<att.Font>fontFamily:{Segoe UI} size:40 antialias:true</att.Font>
</SolidLabel>
</Cell>
<Cell Site="row:1 column:0">
<SolidLabel Text="Username: " VerticalAlignment="Center">
<att.Font>fontFamily:{Segoe UI} size:12 antialias:true</att.Font>
</SolidLabel>
</Cell>
<Cell Site="row:1 column:1">
<SinglelineTextBox ref.Name="textBoxUserName">
<att.BoundsComposition-set AlignmentToParent="left:0 top:0 right:0 bottom:0" PreferredMinSize="x:0 y:24"/>
</SinglelineTextBox>
</Cell>
<Cell Site="row:2 column:1">
<SolidLabel Color="#FF0000" WrapLine="true" WrapLineHeightCalculation="true" Text-bind="ViewModel.UserNameError">
<att.Font>fontFamily:{Segoe UI} size:12 antialias:true</att.Font>
</SolidLabel>
</Cell>
<Cell Site="row:3 column:0">
<SolidLabel Text="Password: " VerticalAlignment="Center">
<att.Font>fontFamily:{Segoe UI} size:12 antialias:true</att.Font>
</SolidLabel>
</Cell>
<Cell Site="row:3 column:1">
<SinglelineTextBox ref.Name="textBoxPassword" PasswordChar="*">
<att.BoundsComposition-set AlignmentToParent="left:0 top:0 right:0 bottom:0" PreferredMinSize="x:0 y:24"/>
</SinglelineTextBox>
</Cell>
<Cell Site="row:4 column:1">
<SolidLabel Color="#FF0000" WrapLine="true" WrapLineHeightCalculation="true" Text-bind="ViewModel.PasswordError">
<att.Font>fontFamily:{Segoe UI} size:12 antialias:true</att.Font>
</SolidLabel>
</Cell>
<Cell Site="row:7 column:0 columnSpan:2">
<Button ref.Name="buttonSignUp" Text="Sign Up!" ev.Clicked="buttonSignUp_Clicked">
<att.BoundsComposition-set AlignmentToParent="left:0 top:0 right:-1 bottom:0" PreferredMinSize="x:100 y:24"/>
<att.Enabled-bind>
(not self.HasLoggedIn) and ViewModel.UserNameError == "" and ViewModel.PasswordError == ""
</att.Enabled-bind>
</Button>
<Button ref.Name="buttonCancel" Text="Close">
<att.BoundsComposition-set AlignmentToParent="left:-1 top:0 right:0 bottom:0" PreferredMinSize="x:100 y:24"/>
<ev.Clicked-eval>
self.Close();
</ev.Clicked-eval>
</Button>
</Cell>
</Table>
</Window>
</Instance>
</Instance>
</Folder>
</Resource>
@@ -0,0 +1,17 @@
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
GacUI::HelloWorld
This file is generated by: Vczh GacUI Resource Code Generator
************************************************************************
DO NOT MODIFY
***********************************************************************/
#ifndef VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld
#define VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld
#include "HelloWorldPartialClasses.h"
#include "MainWindow.h"
#endif
@@ -0,0 +1,87 @@
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
GacUI::Partial Classes
This file is generated by: Vczh GacUI Resource Code Generator
************************************************************************
DO NOT MODIFY
***********************************************************************/
#include "HelloWorld.h"
namespace vl
{
namespace reflection
{
namespace description
{
#define _ ,
IMPL_CPP_TYPE_INFO(vm::IViewModel)
IMPL_CPP_TYPE_INFO(helloworld::MainWindow)
BEGIN_CLASS_MEMBER(vm::IViewModel)
CLASS_MEMBER_BASE(vl::reflection::IDescriptable)
CLASS_MEMBER_METHOD(GetUserName, NO_PARAMETER);
CLASS_MEMBER_METHOD(SetUserName, { L"value" });
CLASS_MEMBER_METHOD(GetPassword, NO_PARAMETER);
CLASS_MEMBER_METHOD(SetPassword, { L"value" });
CLASS_MEMBER_METHOD(GetUserNameError, NO_PARAMETER);
CLASS_MEMBER_EVENT(UserNameErrorChanged)
CLASS_MEMBER_METHOD(GetPasswordError, NO_PARAMETER);
CLASS_MEMBER_EVENT(PasswordErrorChanged)
CLASS_MEMBER_METHOD(SignUp, NO_PARAMETER);
CLASS_MEMBER_PROPERTY(UserName, GetUserName, SetUserName)
CLASS_MEMBER_PROPERTY(Password, GetPassword, SetPassword)
CLASS_MEMBER_PROPERTY_EVENT_READONLY(UserNameError, GetUserNameError, UserNameErrorChanged)
CLASS_MEMBER_PROPERTY_EVENT_READONLY(PasswordError, GetPasswordError, PasswordErrorChanged)
END_CLASS_MEMBER(vm::IViewModel)
BEGIN_CLASS_MEMBER(helloworld::MainWindow)
CLASS_MEMBER_BASE(vl::presentation::controls::GuiWindow)
CLASS_MEMBER_CONSTRUCTOR(helloworld::MainWindow*(Ptr<vm::IViewModel>), { L"ViewModel" })
CLASS_MEMBER_GUIEVENT_HANDLER(buttonSignUp_Clicked, vl::presentation::compositions::GuiEventArgs)
CLASS_MEMBER_PROPERTY_READONLY_FAST(ViewModel)
CLASS_MEMBER_EVENT(HasLoggedInChanged)
CLASS_MEMBER_PROPERTY_EVENT_FAST(HasLoggedIn, HasLoggedInChanged)
END_CLASS_MEMBER(helloworld::MainWindow)
#undef _
class HelloWorldResourceLoader : public Object, public ITypeLoader
{
public:
void Load(ITypeManager* manager)
{
ADD_TYPE_INFO(vm::IViewModel)
ADD_TYPE_INFO(helloworld::MainWindow)
}
void Unload(ITypeManager* manager)
{
}
};
class HelloWorldResourcePlugin : public Object, public vl::presentation::controls::IGuiPlugin
{
public:
void Load()override
{
GetGlobalTypeManager()->AddTypeLoader(new HelloWorldResourceLoader);
}
void AfterLoad()override
{
}
void Unload()override
{
}
};
GUI_REGISTER_PLUGIN(HelloWorldResourcePlugin)
}
}
}
@@ -0,0 +1,119 @@
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
GacUI::Partial Classes
This file is generated by: Vczh GacUI Resource Code Generator
************************************************************************
DO NOT MODIFY
***********************************************************************/
#ifndef VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld_PARTIAL_CLASSES
#define VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld_PARTIAL_CLASSES
#include "GacUIReflection.h"
namespace vm
{
class IViewModel;
}
namespace helloworld
{
class MainWindow;
}
namespace vm
{
class IViewModel : public virtual vl::reflection::IDescriptable, public vl::reflection::Description<IViewModel>
{
public:
virtual vl::WString GetUserName() = 0;
virtual void SetUserName(vl::WString value) = 0;
virtual vl::WString GetPassword() = 0;
virtual void SetPassword(vl::WString value) = 0;
virtual vl::WString GetUserNameError() = 0;
vl::Event<void()> UserNameErrorChanged;
virtual vl::WString GetPasswordError() = 0;
vl::Event<void()> PasswordErrorChanged;
virtual bool SignUp() = 0;
};
}
namespace helloworld
{
template<typename TImpl>
class MainWindow_ : public vl::presentation::controls::GuiWindow, public vl::presentation::GuiInstancePartialClass<vl::presentation::controls::GuiWindow>, public vl::reflection::Description<TImpl>
{
friend struct vl::reflection::description::CustomTypeDescriptorSelector<TImpl>;
private:
Ptr<vm::IViewModel> ViewModel_;
bool HasLoggedIn_;
protected:
vl::presentation::controls::GuiButton* buttonCancel;
vl::presentation::controls::GuiButton* buttonSignUp;
vl::presentation::controls::GuiWindow* self;
vl::presentation::controls::GuiSinglelineTextBox* textBoxPassword;
vl::presentation::controls::GuiSinglelineTextBox* textBoxUserName;
void InitializeComponents(Ptr<vm::IViewModel> ViewModel)
{
ViewModel_ = ViewModel;
if (InitializeFromResource())
{
GUI_INSTANCE_REFERENCE(buttonCancel);
GUI_INSTANCE_REFERENCE(buttonSignUp);
GUI_INSTANCE_REFERENCE(self);
GUI_INSTANCE_REFERENCE(textBoxPassword);
GUI_INSTANCE_REFERENCE(textBoxUserName);
}
else
{
ViewModel_ = 0;
}
}
public:
MainWindow_()
:vl::presentation::GuiInstancePartialClass<vl::presentation::controls::GuiWindow>(L"helloworld::MainWindow")
,vl::presentation::controls::GuiWindow(vl::presentation::theme::GetCurrentTheme()->CreateWindowStyle())
,buttonCancel(0)
,buttonSignUp(0)
,self(0)
,textBoxPassword(0)
,textBoxUserName(0)
{
}
Ptr<vm::IViewModel> GetViewModel()
{
return ViewModel_;
}
vl::Event<void()> HasLoggedInChanged;
bool GetHasLoggedIn()
{
return HasLoggedIn_;
}
void SetHasLoggedIn(bool value)
{
HasLoggedIn_ = value;
HasLoggedInChanged();
}
};
}
namespace vl
{
namespace reflection
{
namespace description
{
DECL_TYPE_INFO(vm::IViewModel)
DECL_TYPE_INFO(helloworld::MainWindow)
}
}
}
#endif
@@ -0,0 +1,30 @@
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
GacUI::MainWindow
This file is generated by: Vczh GacUI Resource Code Generator
***********************************************************************/
#include "HelloWorld.h"
namespace helloworld
{
// #region CLASS_MEMBER_GUIEVENT_HANDLER (DO NOT PUT OTHER CONTENT IN THIS #region.)
void MainWindow::buttonSignUp_Clicked(GuiGraphicsComposition* sender, vl::presentation::compositions::GuiEventArgs& arguments)
{
if (this->GetViewModel()->SignUp())
{
this->SetHasLoggedIn(true);
this->buttonSignUp->SetText(L"Suggess!");
}
}
// #endregion CLASS_MEMBER_GUIEVENT_HANDLER
MainWindow::MainWindow(Ptr<vm::IViewModel> ViewModel)
{
InitializeComponents(ViewModel);
}
}
@@ -0,0 +1,30 @@
/***********************************************************************
Vczh Library++ 3.0
Developer: Zihan Chen(vczh)
GacUI::MainWindow
This file is generated by: Vczh GacUI Resource Code Generator
***********************************************************************/
#ifndef VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld_MainWindow
#define VCZH_GACUI_RESOURCE_CODE_GENERATOR_HelloWorld_MainWindow
#include "HelloWorldPartialClasses.h"
namespace helloworld
{
class MainWindow : public helloworld::MainWindow_<helloworld::MainWindow>
{
friend class helloworld::MainWindow_<helloworld::MainWindow>;
friend struct vl::reflection::description::CustomTypeDescriptorSelector<helloworld::MainWindow>;
protected:
// #region CLASS_MEMBER_GUIEVENT_HANDLER (DO NOT PUT OTHER CONTENT IN THIS #region.)
void buttonSignUp_Clicked(GuiGraphicsComposition* sender, vl::presentation::compositions::GuiEventArgs& arguments);
// #endregion CLASS_MEMBER_GUIEVENT_HANDLER
public:
MainWindow(Ptr<vm::IViewModel> ViewModel);
};
}
#endif
Binary file not shown.
Binary file not shown.