经过一些工作和深入而肮脏地深入研究了集中在 C# 上的各种不充分的 Microsoft 文档,我有一个工作的初始应用程序,它显示一个模拟钟面,两只手显示小时和分钟并自我更新。
有关向Canvas 添加 Web 控件并在其中显示 YouTube 视频的简要概述,请参见本文末尾。请参阅下面的附录 I。
我花了很多时间阅读并使用 Visual Studio 2017 IDE 来探索各种组件。在某些情况下,C# 源代码使用与 C++ 不同的对象(例如,C# 使用 Vector2 和 Vector3 类,而 C++ 使用来自 Windowsnumerics.h 的 float2 和 float3)。在某些情况下,需要更改涉及使用指针引用 C++ 语法的 C# 语法。
XAML 文件已更改,在 Canvas 中添加了 Ellipse,Grid 中。 height 和 width 是相同的,我们会在开始运行时以编程方式更改 height 和 width。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="60,4,1348,712" RenderTransformOrigin="0.5,0.5">
<Canvas x:Name="MyCanvas" HorizontalAlignment="Left" Height="300" Margin="61,27,0,0" VerticalAlignment="Top" Width="424">
<Ellipse x:Name="ClockFace" Fill="AliceBlue" Height="100" Width="100" Canvas.Left="0" Canvas.Top="0" />
</Canvas>
</Grid>
文件 MainPage.xaml.h 有类成员更改。
//
// MainPage.xaml.h
// Declaration of the MainPage class.
//
#pragma once
#include "MainPage.g.h"
namespace App2_ArmTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public ref class MainPage sealed
{
public:
MainPage();
private:
Windows::UI::Composition::Compositor ^m_compositor;
Windows::UI::Composition::ContainerVisual ^m_root;
Windows::UI::Composition::SpriteVisual ^m_hourhand;
Windows::UI::Composition::SpriteVisual ^m_minutehand;
Windows::UI::Composition::ContainerVisual ^GetVisual(Windows::UI::Xaml::UIElement ^element);
void StartTimerAndRegisterHandler();
void SetHandsCurrentTime(void);
void OnTick(Object^ sender, Object^ e);
};
}
文件 MainPage.xaml.cpp 的变化最为彻底。
//
// MainPage.xaml.cpp
//
// Using the Canvas in the Grid as specified in MainPage.xaml we
// are going to draw and animate an analogue clock with two hands,
// hour and minute, to show the current local time.
//
#include "pch.h"
#include "MainPage.xaml.h"
// include for the system time and conversion functions from C++ run time.
#include <ctime>
// see Windows Numerics and DirectXMath Interop APIs at URL
// https://msdn.microsoft.com/en-us/library/windows/desktop/mt759298(v=vs.85).aspx
// see also https://blogs.msdn.microsoft.com/win2d/2015/06/02/winrt-vector-and-matrix-types-in-windows-10/
// following header provides for Windows::Foundation::Numerics needed for vectors
#include <Windowsnumerics.h>
using namespace App2_ArmTest;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI;
// See https://docs.microsoft.com/en-us/uwp/api/windows.ui.composition.compositionobject
using namespace Windows::UI::Composition;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::UI::Xaml::Hosting;
// See the UWP for Windows 10 and Fluent at https://developer.microsoft.com/en-us/windows/apps
// tick->Offset - The offset of the visual relative to its parent or for a root visual the offset
// relative to the upper-left corner of the windows that hosts the visual.
const float clockCenterPoint = 200.0f; // center of the clock face, a circle, is from left margin and down from top.
const float tickHeight = 20.0f; // the height of tick marks drawn to indicate hours of day.
const float handCenterOffset = 20.0f; // number of units of stub of the hand for center of rotation.
const float hourHandDifference = 40.0f; // number of units difference in length between hour hand and minute hand.
const float degreesInClockFace = 360.0f; // number of degrees in a circle. clock face is a circle.
const float hoursOnClock = 12.0f; // number of hours on a clock face, 12 hours counted 1 through 12.
Windows::UI::Composition::ContainerVisual ^MainPage::GetVisual(Windows::UI::Xaml::UIElement ^element)
{
// Given a UI element from the XAML as specified by the x:Name="" assigned to the
// UI element, lets get a Visual Container so that we can start placing stuff into
// this UI element.
// For this application the UI element will be a Canvas that we are adorning.
auto hostVisual = ElementCompositionPreview::GetElementVisual(element);
auto root = hostVisual->Compositor->CreateContainerVisual();
ElementCompositionPreview::SetElementChildVisual(element, root);
return root;
}
MainPage::MainPage()
{
InitializeComponent();
// See https://xamlbrewer.wordpress.com/2016/01/04/using-the-composition-api-in-uwp-apps/
// See https://blogs.windows.com/buildingapps/2015/12/08/awaken-your-creativity-with-the-new-windows-ui-composition/
// See https://docs.microsoft.com/en-us/windows/uwp/composition/visual-layer
// See Graphics and Animation - Windows Composition Turns 10 https://msdn.microsoft.com/magazine/mt590968
// See https://docs.microsoft.com/en-us/windows/uwp/graphics/drawing-shapes
// See https://blogs.windows.com/buildingapps/2016/09/12/creating-beautiful-effects-for-uwp/
m_root = GetVisual(MyCanvas);
m_compositor = m_root->Compositor;
// set the size of the clock face, an ellipse defined in the XAML
// so that it is the proper size for the adornment we draw on the clock face.
ClockFace->Height = clockCenterPoint * 2.0f;
ClockFace->Width = clockCenterPoint * 2.0f;
// Create the tick marks for the 12 hours around the face of the clock.
// The clock face is a circle which is 360 degrees. Since we have 12 tick marks
// we create each tick mark as a small rectangle at the 12 O'Clock or noon position
// and then we rotate it around the face of the clock by a number of degrees until
// we position it where it needs to go.
// Windows::Foundation::Numerics::float2() is the C++ version of the C# Vector2()
// Windows::Foundation::Numerics::float3() is the C++ version of the C# Vector3()
SpriteVisual ^tick;
for (int i = 0; i < 12; i++)
{
tick = m_compositor->CreateSpriteVisual();
if (i % 3 != 0) {
// for tick marks other than 3, 6, 9, and 12 make them less prominent.
tick->Brush = m_compositor->CreateColorBrush(Windows::UI::Colors::Silver);
tick->Size = Windows::Foundation::Numerics::float2(4.0f, tickHeight); // width and height of sprite
}
else {
// for tick marks for 3, 6, 9, and 12 make them more prominent.
tick->Brush = m_compositor->CreateColorBrush(Windows::UI::Colors::Black);
tick->Size = Windows::Foundation::Numerics::float2(6.0f, tickHeight); // width and height of sprite
}
tick->CenterPoint = Windows::Foundation::Numerics::float3(tick->Size.x / 2.0f, clockCenterPoint, 0.0f); // center point for rotations
tick->Offset = Windows::Foundation::Numerics::float3(clockCenterPoint, 0.0f, 0.0f); // offset from the left only.
tick->RotationAngleInDegrees = i * (degreesInClockFace / hoursOnClock); // degrees divided by number of hour ticks on clock face.
m_root->Children->InsertAtTop(tick);
}
// Draw the clock hands at the initial point of noon, both hands straight up. The hour hand is
// not as tall as the minute hand and the hour hand is a bit wider than the minute hand.
// Differences in size are to allow for visibility when they hands overlap.
//
// We have an hour hand and a minute hand to show the current hour and current minute.
// The hour is from 0 to 11 though the clock face shows 1 to 12. The hour hand sweeps
// around the clock face in 12 hours. The minute hand sweeps around the clock face in
// one hour or 60 minutes. So each tick mark is 5 minutes for the minute hand and one
// hour for the hour hand.
//
// The center point for the hand rotation is half the width of the hand and the height of a
// tick mark from the bottom of the hand. This will put the center of rotation so that
// a bit of the hand will extend past the center of rotation and look more realistic.
// This axis of rotation should be where a line drawn from noon to 6 and a line from 9 to 3
// cross in the center of the clock face.
// Create the sprite for the minute hand of the clock.
// The minute hand is a green rectangle 2.0 wide by 100.0 high
m_minutehand = m_compositor->CreateSpriteVisual();
m_minutehand->Brush = m_compositor->CreateColorBrush(Windows::UI::Colors::Green);
m_minutehand->Size = Windows::Foundation::Numerics::float2(2.0f, clockCenterPoint - handCenterOffset);
m_minutehand->CenterPoint = Windows::Foundation::Numerics::float3(m_minutehand->Size.x / 2.0f, m_minutehand->Size.y - handCenterOffset, 0.0f);
m_minutehand->Offset = Windows::Foundation::Numerics::float3(clockCenterPoint, clockCenterPoint - m_minutehand->CenterPoint.y, 0.0f);
// Create the sprite for the hour hand of the clock.
// The hour hand is a gray rectangle 4.0 wide. It is shorter and wider than the minute hand.
m_hourhand = m_compositor->CreateSpriteVisual();
m_hourhand->Brush = m_compositor->CreateColorBrush(Windows::UI::Colors::Gray);
m_hourhand->Size = Windows::Foundation::Numerics::float2(4.0f, m_minutehand->Size.y - hourHandDifference);
m_hourhand->CenterPoint = Windows::Foundation::Numerics::float3(m_hourhand->Size.x / 2.0f, m_hourhand->Size.y - handCenterOffset, 0.0f);
m_hourhand->Offset = Windows::Foundation::Numerics::float3(clockCenterPoint, clockCenterPoint - m_hourhand->CenterPoint.y, 0.0f);
m_root->Children->InsertAtTop(m_hourhand); // add hour hand first so that it is beneath the minute hand
m_root->Children->InsertAtTop(m_minutehand); // add the minute hand after the hour hand so it is on top.
// Set the hands of the clock to the current time and then start our timer.
// The timer will update the position of the clock hands once a minute.
SetHandsCurrentTime();
StartTimerAndRegisterHandler();
}
void App2_ArmTest::MainPage::StartTimerAndRegisterHandler()
{
// create our time task so that we can change the clock periodically.
auto timer = ref new Windows::UI::Xaml::DispatcherTimer();
TimeSpan ts;
// right now we are using a 2 second timer as part of prototyping this out.
// this allows us to check that the timer is in fact working.
// this needs to be changed from every two seconds to every minute once we
// have the hands of the clock displaying properly.
ts.Duration = 2 * 10000000; // 10,000,000 ticks per second as value units is 100 nanoseconds
timer->Interval = ts;
timer->Start();
auto registrationtoken = timer->Tick += ref new EventHandler<Object^>(this, &MainPage::OnTick);
}
void App2_ArmTest::MainPage::SetHandsCurrentTime(void)
{
// get the current local time which will be used for positioning the
// clock hands. We then use the local time to rotate the hands to the
// correct position on the clock face.
std::time_t result = std::time(nullptr);
std::tm localTime;
localtime_s(&localTime, &result);
m_hourhand->RotationAngleInDegrees = (float)localTime.tm_hour * (degreesInClockFace / hoursOnClock); // degrees divided by number of hour ticks on clock face.
m_minutehand->RotationAngleInDegrees = (float)localTime.tm_min * (degreesInClockFace / 60.0f); // degrees divided by minutes in an hour.
}
void App2_ArmTest::MainPage::OnTick(Object^ sender, Object^ e)
{
// A timer tick is received so lets position the clock hands
// on the clock face to reflect the current time.
SetHandsCurrentTime();
}
此应用程序生成一个带有时钟的窗口,如下所示:
附录 I(2017 年 10 月 25 日):添加 WebView 控件
在 Visual Studio 2017 的工具箱窗格中查看 XAML 设计器时,在“所有 XAML 控件”部分中,有一个控件 WebView,可以将其插入到 XAML 页面中。
在添加它并稍微调整大小后,我最终得到了以下 XAML 代码:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="60,4,966,410" RenderTransformOrigin="0.5,0.5">
<Canvas x:Name="MyCanvas" HorizontalAlignment="Left" Height="482" Margin="10,43,0,0" VerticalAlignment="Top" Width="735">
<Ellipse x:Name="ClockFace" Fill="AliceBlue" Height="200" Width="200" Canvas.Left="0" Canvas.Top="0" />
<WebView x:Name="WebDisplay" Height="462" Canvas.Left="205" Canvas.Top="10" Width="520"/>
</Canvas>
</Grid>
通过添加WebView,我们现在可以在 C++ 代码中访问该控件以设置 URI 并在 Web 上显示网页或资源。
在我的初始化代码中添加了以下两行将YouTube视频嵌入到WebView控件中:
Uri ^targetUri = ref new Uri(L"https://www.youtube.com/embed/21JhWTIPQSw");
WebDisplay->Navigate(targetUri);