Windows 10 IoT ARM64平台下,UWP应用和MFC程序不一样,同时只能打开一个应用实例。以串口程序为例,如果用户希望同时打开多个应用实例,一个应用实例打开串口1,一个应用实例打开串口2,那么我们可以加载多个页面,在各个页面分别加载功能模块,实现程序功能模块多开。
本文以UWP串口例程为例,介绍如何用代码实现同时打开多个串口模块页面,进行测试。
1. 打开visual studio 2022,点击“创建新项目”。
2. 选择筛选条件,语言C++,平台选择Windows,应用类型选择UWP。
3. 选择“空白应用”,本文以C++/CX为例。
1. 创建工程后,右键点击工程->添加->新建项。

2. 选择XAML空白页,给页面命名为SerialPage.xaml,点击添加。

3. 在工程中可以看到添加的页面SerialPage,将之前写好的UWP串口程序代码COPY过来(参考文章《UWP串口程序开发》),把串口程序的xaml内容拷贝到SerialPage.xaml中,把代码文件.xaml.h和.xaml.cpp的内容也拷贝到对应文件SerialPage.xaml.h和SerialPage.xaml.cpp中。如果原UWP程序页面名称(默认为MainPage)和当前工程名称(SerialPage)不一样,注意全部替换一下。

4. 双击页面SerialPage.xaml,检查下是否添加成功。编译一下,确认代码都正确COPY过来了。

UWP中页面Frame的概念,类似MFC中的窗口Dialog,但功能更强大些。主页面可以打开新的子页面,页面本身也可以执行跳转,回退操作。页面的回收由系统负责,当没有Content指向页面后,页面自动被回收
在主页面中添加一个按钮,按钮中执行代码。
Windows::UI::Xaml::Controls::Frame^ frame= new ref new Windows::UI::Xaml::Controls::Frame(); frame->Navigate(TypeName(SerialPage::typeid));
设置新页面为当前操作页面。
Window.Current.Content = frame; Window.Current.Activate();
每点击一次按钮,就可以加载一个新的SerialPage页面了。
为了便于管理打开的新页面,能在它们之间随意切换,可以使用SplitView控件来实现。SplitView由一个导航区域和一个页面区域组成,可以在导航区域Panel中添加不同按钮,在点击后,在页面区域Content中显示对应的页面。根据需求,可以设置导航区域大小,是否自动隐藏等。
1. 在主页面中添加一个SplitView控件,命名为SplitViewMain。在它的Pane中可以用一个StackPanel来装按钮,给Content命名为FrameMain,在里面随意加一段文字。
<SplitView x:Name="SplitViewMain" DisplayMode="Inline" IsPaneOpen="True" > <SplitView.Pane> <StackPanel x:Name="BtnContainer"> </StackPanel> </SplitView.Pane> <SplitView.Content> <Frame x:Name="FrameMain"> <TextBlock Text="未添加测试接口" VerticalAlignment="Center" HorizontalAlignment="Center"/> </Frame> </SplitView.Content> </SplitView>
2. 在Pane中添加几个按钮,可以加到StackPanel中,让StackPanel来自动排列。
<SplitView.Pane> <StackPanel x:Name="BtnContainer"> <Button Content="Button1" Click="Button1_Click"/> <Button Content="Button2" Click="Button2_Click"/> </StackPanel> </SplitView.Pane>
3. 在按钮中加入代码
void SPT::MainPage:: Button1_Click (Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
if (m_frame[0] == nullptr)
{
m_frame[0] = ref new Windows::UI::Xaml::Controls::Frame();
m_frame[0]->Navigate(TypeName(SerialPage::typeid)); //指定SerialPage
}
FrameMain = m_frame[0];
SplitViewMain->Content = FrameMain;
}这样,就可以通过点击不同按钮,进入对应的页面了
有时候,如果导航按钮的数量不确定,希望根据实际情况添加或删除导航按钮。本文例程中,每点击一次添加按钮,便添加一个串口测试页面,做法如下。
1. 在主页面中添加一个按钮,点击一次,便在SplitView的Pane里加入一个按钮,给这个按钮设置参数Tag,便于区分不同按钮。给按钮绑定点击响应函数OnClick。
void SPT::MainPage::BtnSerial_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
Button^ newButton;
newButton = ref new Button;
m_Count++;
newButton->Tag = m_Count;
newButton->Click += ref new Windows::UI::Xaml::RoutedEventHandler(this, &SPT::MainPage::OnClick);
BtnContainer->Children->Append(newButton);
m_frame[m_Count] = ref new Windows::UI::Xaml::Controls::Frame();
m_frame[m_Count]->Navigate(TypeName(SerialPage::typeid));
FrameMain = m_frame[m_Count];
SplitViewMain->Content = FrameMain;
}2. 在按钮的响应函数中读出按钮参数,根据不同的参数决定如何处理,加载哪个页面。
void SPT::MainPage::OnClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
Button^ clickButton = safe_cast<Button^>(sender);
int id = safe_cast<int>(clickButton->Tag);
if (m_frame[id] == nullptr)
{
return;
}
FrameMain = m_frame[id];
SplitViewMain->Content = FrameMain;
}目前导航页面和加载的子页面相对独立,很多时候主界面需要传递数据给子页面,便于子页面初始化,甚至需要给子页面提供函数接口。子页面退出时也需要回调函数通知主页面,并返回处理后的数据。
本文串口例程中
1. 设置一个数据结构,把所有主页面要传输的数据打包到这个数据接口中。文本例程中打包数据包括子页面ID,和一个回调接口。
public ref class PageData sealed : public Platform::Object
{
public:
PageData(int initValue, Windows::Foundation::EventHandler<int>^ callback)
: _initValue(initValue), _exitCallback(callback){}
property int InitValue {int get() { return _initValue; }}
property Windows::Foundation::EventHandler<int>^ ExitCallback {
Windows::Foundation::EventHandler<int>^ get() { return _exitCallback; }
}
private:
int _initValue;
Windows::Foundation::EventHandler<int>^ _exitCallback;
};2. 在主页面加载子页面时,将打包数据一起传过去,修改Navigate调用。
m_frame[i] = ref new Windows::UI::Xaml::Controls::Frame(); auto exitHandler = ref new Windows::Foundation::EventHandler<int>( this, &MainPage::OnPageExit); auto data = ref new PageData(i, exitHandler); m_frame[i]->Navigate(TypeName(SerialPage::typeid), data); FrameMain = m_frame[i]; SplitViewMain->Content = FrameMain;
3. 子页面SerialPage重载OnNavigatedTo函数,添加初始化代码
virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
Windows::Foundation::EventHandler<int>^ m_ExitCallback;
void SerialPage::OnNavigatedTo(NavigationEventArgs^ e)
{
auto data = dynamic_cast<PageData^>(e->Parameter);
if (data != nullptr)
{
m_id = data->InitValue;
m_ExitCallback = data->ExitCallback;
}
}4. 子页面退出时调用回调函数,传回参数页面ID
void SPT::SerialPage::BtnExit_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
try
{
CancelReadTask();
ClosePort();
}
catch (Platform::Exception^ ex)
{
}
if (Frame->CanGoBack)
{
Frame->GoBack();
}
Frame->Content = nullptr;
if (m_ExitCallback != nullptr)
{
m_ExitCallback->Invoke(this, m_id);
}
}5. 主页面处理回调函数
void SPT::MainPage::OnPageExit(Platform::Object^ sender, int id)
{
for (int i = 0; i < BtnContainer->Children->Size; ++i)
{
if (Button^ btn = dynamic_cast<Button^>(BtnContainer->Children->GetAt(i)))
{
if (btn->Tag != nullptr && safe_cast<int>(btn->Tag) == id)
{
BtnContainer->Children->RemoveAt(i);
return;
}
}
}
}调整控件的边界Margin,用LinearGradientBrush设置页面及控件背景色,在代码中适当调整控件字体样式,使得界面满足设计需求。
打开Win10 IoT板子上的调试助手。

选择工程平台为ARM64,编译运行,示例如下。

需要程序源码可以联系英创工程师获得。
成都英创信息技术有限公司 028-8618 0660