UWP中的Direct2D

原文:UWP中的Direct2D

介绍

DirectX一直是Windows平台中高性能图形的代名词,自Win7开始,微软又推出了Direct2D技术,包装于Direct3D,但专注于2D图形,并且准备取代GDI这样的传统2D图形技术。对于Direct2D是怎么怎么好的具体描述,可以参考附录1.

不过Direct2D是基于COM技术的,看上去有些老旧的气息,而且是非托管的,似乎也和常见的.net语言有些隔阂。

不过微软也为我们提供了一个工具,一个跨越这一边界的工具,那就是SurfaceImageSource一族。该族中SurfaceImageSource继承自Windows.UI.Xaml.Media.ImageSource,VirtualSurfaceImageSource则继承自SurfaceImageSource,它们和BitmapSource在托管领域有着同样的地位。但同时这两个类又将触角伸到了COM的领域,分别可以query interface至ISurfaceImageSourceNative和IVirtualSurfaceImageSourceNative,再与Direct2D技术接轨。至此,XAML快速的界面技术,Direct2D高效的图形功能,得以合二为一。

本文将简单的介绍一下SurfaceImageSource的使用,为大家呈现一个高效图形应用的小例子(演示代码使用XAML和C++/CX)。

 

准备

代码主要是C++的(略有C++/CX扩展),因为要操作Direct2D和COM。大家可以根据需要自行包装自己的组件来调用。

用到了WIC(Windows Imaging Component)等技术,不过不是本文重点。

 

问题

熟悉WPF的读者可能想到,在classical desktop中使用的WPF,里面的一部分组件有一个神奇的属性,OpacityMask,利用它可以给控件的渲染显示加上一个蒙版,实现各种透明渐变和不规则轮廓等等。

不过到了UWP(更早的从Windows Store App出现开始),虽然大家写的还是一样的XAML,但是OpacityMask属性没了。估计是为了性能考虑吧,不给用这么繁琐的东西了。

但难免有时要用到这样的功能,我们可以依靠高效的Direct2D图形技术来实现它。

看看效果先:

 

托管的SurfaceImageSource

上文已经提到了,SurfaceImageSource是Windows.UI.Xaml.Media.ImageSource的子类,可以像使用BitmapSource一样使用它,赋给BitmatBrush的ImageSource什么的。这有点像WriteableBitmap,不过它却是操作Direct2D的入口。

 

我们先用XAML做一个这样的界面:

 1 <Page x:Class="App2.MainPage"
 2       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4       xmlns:local="using:App2"
 5       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 6       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 7       mc:Ignorable="d">
 8
 9     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
10         <Grid.RowDefinitions>
11             <RowDefinition Height="50" />
12             <RowDefinition Height="*" />
13         </Grid.RowDefinitions>
14
15         <Grid.Resources>
16             <Style TargetType="Button">
17                 <Setter Property="Margin" Value="10,0,0,0" />
18             </Style>
19         </Grid.Resources>
20
21         <StackPanel HorizontalAlignment="Center"
22                     Orientation="Horizontal"
23                     x:Name="btns">
24             <Button Tag="Assets/star.png">Star</Button>
25             <Button Tag="Assets/ellipse.png">Ellipse</Button>
26         </StackPanel>
27
28         <Rectangle Grid.Row="1" x:Name="canvas" />
29     </Grid>
30 </Page>

Button的Tag记载的是用来做蒙版的图片,我们的例子里使用的图片,为了方便都是400*400的。并且这两个蒙版图片都是用黑白表示的。黑色表示没有,白色表示全有,灰色就是半透明了,操作的是Alpha通道。

 

这分别是要显示的图片,和两种蒙版。

 

先看看MainPage声明了哪些成员:

1 Microsoft::WRL::ComPtr<IWICImagingFactory> m_factory; // WIC工厂,因为多处使用,可以复用一下
2 Microsoft::WRL::ComPtr<IDXGIDevice> m_dxgiDevice; // DXGI Device
3 Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_d2dDeviceContext; // D2D Device Context
4 Microsoft::WRL::ComPtr<IWICBitmapSource> m_img; // 这个就是要被蒙版处理的原始图片了

我们善用ComPtr,让C++的RAII机制(资源获取就是初始化)来帮我们实现简单的“垃圾回收”。

 

在MainPage::CreateDevice函数中,我们初始化Direct2D的相关设备:

void MainPage::CreateDevice()
{
    static D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    HRESULT hr;
    ComPtr<ID3D11Device> d3dDevice;
    HR(D3D11CreateDevice(nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        0,
        D3D11_CREATE_DEVICE_BGRA_SUPPORT, // 注意,Direct2D画图是BGRA通道顺序,不是常见的RGB
        featureLevels,
        extent<decltype(featureLevels)>::value, // C++ type traits,获取array长度
        D3D11_SDK_VERSION,
        &d3dDevice,
        nullptr,
        nullptr));
    HR(d3dDevice.As(&m_dxgiDevice));
    HR(D2D1CreateDevice(m_dxgiDevice.Get(), nullptr, &m_d2dDevice));
    HR(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dDeviceContext));
}

 

然后我们看看主要的画图流程:

这是那两个有蒙版设置的button,它们的Tag属性记录了要应用的蒙版的路径。

void MainPage::OnButtonClick(Object^ sender, RoutedEventArgs^ e)
{
    HRESULT hr;
    auto btn = safe_cast<Button^>(sender);
    auto maskPath = btn->Tag->ToString();

    SurfaceImageSource^ sis = ref new SurfaceImageSource(400, 400); // SurfaceImageSource创建时就必须指定大小,这个大小相当于画纸的大小
    ComPtr<ISurfaceImageSourceNative> sisn;

    // 跨越托管和非托管的边界
    // 转换成IUnknown*也可以
    HR(reinterpret_cast<IInspectable*>(sis)->QueryInterface(IID_PPV_ARGS(&sisn)));
    HR(sisn->SetDevice(m_dxgiDevice.Get()));

    // 这里需要注意,尽管SurfaceImageSource本身是个ImageSource,但是我们也应该在ImageBrush的层面上完成操作。如果我们将ImageBrush留在XAML上,而只新建和替换(在之后的流程里)SurfaceImageSource,会发生SurfaceImageSource赋值给ImageSource后引用计数增加量,和将ImageSource设为nullptr后引用计数减少量不相等的情况,发生“内存泄漏”。
    auto brush = ref new ImageBrush();
    brush->ImageSource = sis;
    canvas->Fill = brush;

    Draw(sisn.Get(), maskPath->Data());
}

 

Draw函数。画图的操作我们需要在UI线程上完成:

void MainPage::Draw(ISurfaceImageSourceNative* sisn, const wchar_t* mask)
{
    HRESULT hr;
    ComPtr<IDXGISurface> surface;
    RECT rect = { 0, 0, 400, 400 };
    POINT renderTargetOffset; // 可视区域在surface中的偏移量
    // 可以想象成surface是一张大画板,比我们的显示区域400*400要大。每次Direct2D会选择一个区域来画,不一定是(0,0),因为可能有一些缓冲策略,使得每次画图的区域都不一样
    HR(PrepareDraw(sisn, rect, &surface, &renderTargetOffset));

    // 创建所有我们需要的图形
    ComPtr<IWICBitmapSource> maskSrc = GetMask(LoadImageByWIC(mask).Get());
    ComPtr<ID2D1Bitmap> maskBmp;
    ComPtr<ID2D1Bitmap> imgBmp;
    ComPtr<ID2D1BitmapBrush> imgBrush;
    ComPtr<ID2D1Bitmap1> tgrBmp; // Note ID2D1Bitmap1
    HR(m_d2dDeviceContext->CreateBitmapFromWicBitmap(maskSrc.Get(), &maskBmp));
    HR(m_d2dDeviceContext->CreateBitmapFromWicBitmap(m_img.Get(), &imgBmp));
    HR(m_d2dDeviceContext->CreateBitmapBrush(imgBmp.Get(), &imgBrush));
    HR(m_d2dDeviceContext->CreateBitmapFromDxgiSurface(surface.Get(), nullptr, &tgrBmp));

    m_d2dDeviceContext->SetTarget(tgrBmp.Get());
    m_d2dDeviceContext->BeginDraw();
    m_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Translation(renderTargetOffset.x, renderTargetOffset.y)); // 应用可视区域的偏移量来调整device context的位置
    m_d2dDeviceContext->Clear({0, 0, 1, 1}); // 将画布填充成蓝色,让我们的改变变得明显一些    

    m_d2dDeviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); // 必须先设置这个,才能调用下面的函数
    m_d2dDeviceContext->FillOpacityMask(maskBmp.Get(), imgBrush.Get()); // 应用蒙版

    HR(m_d2dDeviceContext->EndDraw());
    m_d2dDeviceContext->SetTarget(nullptr);
    HR(sisn->EndDraw());
}

 

以下是我们的一些辅助函数:

尝试开始画图。这会确定我们需要画图的区域(在surface上)。如果这个开始调用失败了,我们检测一下原因,尝试第二次。

HRESULT MainPage::PrepareDraw(ISurfaceImageSourceNative* sisn, const RECT& updateRect, IDXGISurface** surface, POINT* offset)
{
    HRESULT hr;
    hr = sisn->BeginDraw(updateRect, surface, offset);

    if ((hr == DXGI_ERROR_DEVICE_REMOVED) || (hr == DXGI_ERROR_DEVICE_RESET))
    {
        CreateDevice();
        // 设备有更改,并不是单纯的失败,重新创建设备,再试一次
        return PrepareDraw(sisn, updateRect, surface, offset);
    }
    else
    {
        return hr;
    }
}

 

ComPtr<IWICBitmapSource> MainPage::LoadImageByWIC(const wchar_t* file)函数,通过WIC加载图片,既加载我们的原图像,也加载蒙版图像。

ComPtr<IWICBitmapSource> MainPage::LoadImageByWIC(const wchar_t* file)
{
    ComPtr<IWICBitmapDecoder> decoder;
    ComPtr<IWICBitmapFrameDecode> frame;
    ComPtr<IWICFormatConverter> converter;

    HRESULT hr;
    HR(m_factory->CreateDecoderFromFilename(file, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder));
    HR(decoder->GetFrame(0, &frame));
    HR(m_factory->CreateFormatConverter(&converter));
    HR(converter->Initialize(
        frame.Get(),
        GUID_WICPixelFormat32bppPBGRA, // 这个预处理的BGRA,因为两个透明图层叠加时需要把RGB通道和Alpha通道相乘相加,而预处理就是预先把相乘的步骤完成了,可以增加一点效率
        WICBitmapDitherTypeNone,
        nullptr,
        0,
        WICBitmapPaletteTypeCustom));
    return converter;
}

 

MainPage::GetMask函数把黑白色的图片处理成用alpha通道表示透明度的bitmap。因为只有黑白色的话,颜色是通过RGB通道确定的,alpha通道一直是1.0。而direct2D提供的API呢,却是使用的alpha通道来进行蒙版应用。这样看似合理一些,但我们生成一张利用alpha通道来表现透明度的蒙版图片,肯定是要比我们用单纯的黑白色来表现蒙版要麻烦一些的。

ComPtr<IWICBitmapSource> MainPage::GetMask(IWICBitmapSource* src)
{
    uint32_t width, height;
    src->GetSize(&width, &height);
    size_t len = width * 4 * height;
    unique_ptr<byte[]> pixels(new byte[len]);

    src->CopyPixels(nullptr, width * 4, len, pixels.get());
    for (size_t i = 0; i < width * height; i++)
    {
        pixels[i * 4 + 3] = (pixels[i * 4] + pixels[i * 4 + 1] + pixels[i * 4 + 2]) / 3;
    }

    ComPtr<IWICBitmap> bmp;
    m_factory->CreateBitmapFromMemory(width, height, GUID_WICPixelFormat32bppPBGRA, width * 4, len, pixels.get(), &bmp);
    return bmp;
}

 

用Direct2D的好处

图形应用(包括图像处理,地图),游戏,这些特殊的应用需要一个强悍的图形技术来支撑它们的运作和体验,而Direct2D无疑为我们提供了这样的可能,让我们能在XAML之中,发挥图形技术的强大威力。

 

扩展

对于SurfaceImageSource,除了转换成ISurfaceImageSourceNative接口外,还能转换成ISurfaceImageSourceNativeWithD2D接口,区别就在于withD2D的这一个,可以在后台线程上画图,只要在UI线程上刷新就可以了。

SurfaceImageSource还有一个子类,VirtualSurfaceImageSource,它主要是起虚拟化的作用,用于图像区域比可视区域大的情况,比如地图。

附录

[1] 关于Direct2D:

https://msdn.microsoft.com/zh-cn/library/windows/desktop/dd370987(v=vs.85).aspx

[2] DirectX 和 XAML 互操作

https://msdn.microsoft.com/zh-cn/library/windows/apps/hh825871.aspx

[3] SurfaceImageSource类:

https://msdn.microsoft.com/zh-cn/library/windows/apps/windows.ui.xaml.media.imaging.surfaceimagesource.aspx

[4] ISurfaceImageSourceNative接口:

 https://msdn.microsoft.com/zh-cn/library/windows/apps/hh848322.aspx

 

完整代码

MainPage.xaml.h

//
// MainPage.xaml.h
// Declaration of the MainPage class.
//

#pragma once

#include "MainPage.g.h"

namespace App2
{
/// <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();

    void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
private:
    void OnButtonClick(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
    void CreateDevice();
    Microsoft::WRL::ComPtr<IWICBitmapSource> LoadImageByWIC(const wchar_t* file);
    Microsoft::WRL::ComPtr<IWICBitmapSource> GetMask(IWICBitmapSource* src);
    void Draw(ISurfaceImageSourceNative* sisn, const wchar_t* mask);
    HRESULT PrepareDraw(ISurfaceImageSourceNative* sisn, const RECT& updateRect, IDXGISurface** surface, POINT* offset);

    Microsoft::WRL::ComPtr<IWICImagingFactory> m_factory;
    Microsoft::WRL::ComPtr<IDXGIDevice> m_dxgiDevice;
    Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_d2dDeviceContext;
    Microsoft::WRL::ComPtr<IWICBitmapSource> m_img;
};
}

View Code

MainPage.xaml.cpp

//
// MainPage.xaml.cpp
// Implementation of the MainPage class.
//

#include "pch.h"
#include "MainPage.xaml.h"

using namespace App2;

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
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::Media::Imaging;
using namespace Windows::UI::Xaml::Navigation;

using namespace Microsoft::WRL;
using namespace std;

#define  HR(exp) hr = exp; assert(SUCCEEDED(hr))

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

MainPage::MainPage()
{
    InitializeComponent();
}

void MainPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
{
    for (UIElement^ uiElem : btns->Children)
    {
        auto btn = safe_cast<Button^>(uiElem);
        btn->Click += ref new RoutedEventHandler(this, &MainPage::OnButtonClick);
    }

    CreateDevice();

    HRESULT hr;
    HR(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_factory)));
    m_img = LoadImageByWIC(L"Assets/img.png");
}

void MainPage::CreateDevice()
{
    static D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_3,
        D3D_FEATURE_LEVEL_9_2,
        D3D_FEATURE_LEVEL_9_1
    };

    HRESULT hr;
    ComPtr<ID3D11Device> d3dDevice;
    ComPtr<ID2D1Device> d2dDevice;
    HR(D3D11CreateDevice(nullptr,
        D3D_DRIVER_TYPE_HARDWARE,
        0,
        D3D11_CREATE_DEVICE_BGRA_SUPPORT,
        featureLevels,
        extent<decltype(featureLevels)>::value,
        D3D11_SDK_VERSION,
        &d3dDevice,
        nullptr,
        nullptr));
    HR(d3dDevice.As(&m_dxgiDevice));
    HR(D2D1CreateDevice(m_dxgiDevice.Get(), nullptr, &d2dDevice));
    HR(d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_d2dDeviceContext));
}

ComPtr<IWICBitmapSource> MainPage::LoadImageByWIC(const wchar_t* file)
{
    ComPtr<IWICBitmapDecoder> decoder;
    ComPtr<IWICBitmapFrameDecode> frame;
    ComPtr<IWICFormatConverter> converter;

    HRESULT hr;
    HR(m_factory->CreateDecoderFromFilename(file, nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder));
    HR(decoder->GetFrame(0, &frame));
    HR(m_factory->CreateFormatConverter(&converter));
    HR(converter->Initialize(
        frame.Get(),
        GUID_WICPixelFormat32bppPBGRA, // Pre-multipled BGRA
        WICBitmapDitherTypeNone,
        nullptr,
        0,
        WICBitmapPaletteTypeCustom));
    return converter;
}

ComPtr<IWICBitmapSource> MainPage::GetMask(IWICBitmapSource* src)
{
    uint32_t width, height;
    src->GetSize(&width, &height);
    size_t len = width * 4 * height;
    unique_ptr<byte[]> pixels(new byte[len]);

    src->CopyPixels(nullptr, width * 4, len, pixels.get());
    for (size_t i = 0; i < width * height; i++)
    {
        // BGRA. The average of RGB channels is used as Alpha channel.
        pixels[i * 4 + 3] = (pixels[i * 4] + pixels[i * 4 + 1] + pixels[i * 4 + 2]) / 3;
    }

    ComPtr<IWICBitmap> bmp;
    m_factory->CreateBitmapFromMemory(width, height, GUID_WICPixelFormat32bppPBGRA, width * 4, len, pixels.get(), &bmp);
    return bmp;
}

void MainPage::OnButtonClick(Object^ sender, RoutedEventArgs^ e)
{
    HRESULT hr;
    auto btn = safe_cast<Button^>(sender);
    auto maskPath = btn->Tag->ToString();

    SurfaceImageSource^ sis = ref new SurfaceImageSource(400, 400);
    ComPtr<ISurfaceImageSourceNative> sisn;
    HR(reinterpret_cast<IInspectable*>(sis)->QueryInterface(IID_PPV_ARGS(&sisn)));
    HR(sisn->SetDevice(m_dxgiDevice.Get())); // set device as DXGI device.

    // Note this
    auto brush = ref new ImageBrush();
    brush->ImageSource = sis;
    canvas->Fill = brush;

    Draw(sisn.Get(), maskPath->Data());
}

void MainPage::Draw(ISurfaceImageSourceNative* sisn, const wchar_t* mask)
{
    HRESULT hr;
    ComPtr<IDXGISurface> surface;
    RECT rect = { 0, 0, 400, 400 };
    POINT renderTargetOffset; // view port offset in surface.
    HR(PrepareDraw(sisn, rect, &surface, &renderTargetOffset));

    ComPtr<IWICBitmapSource> maskSrc = GetMask(LoadImageByWIC(mask).Get());
    ComPtr<ID2D1Bitmap> maskBmp;
    ComPtr<ID2D1Bitmap> imgBmp;
    ComPtr<ID2D1BitmapBrush> imgBrush;
    ComPtr<ID2D1Bitmap1> tgrBmp; // Note ID2D1Bitmap1
    HR(m_d2dDeviceContext->CreateBitmapFromWicBitmap(maskSrc.Get(), &maskBmp));
    HR(m_d2dDeviceContext->CreateBitmapFromWicBitmap(m_img.Get(), &imgBmp));
    HR(m_d2dDeviceContext->CreateBitmapBrush(imgBmp.Get(), &imgBrush));
    HR(m_d2dDeviceContext->CreateBitmapFromDxgiSurface(surface.Get(), nullptr, &tgrBmp));

    m_d2dDeviceContext->SetTarget(tgrBmp.Get());
    m_d2dDeviceContext->BeginDraw();
    m_d2dDeviceContext->SetTransform(D2D1::Matrix3x2F::Translation(renderTargetOffset.x, renderTargetOffset.y));
    m_d2dDeviceContext->Clear({0, 0, 1, 1}); // Clear with blue color.

    m_d2dDeviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); // Must set antialias mode.
    m_d2dDeviceContext->FillOpacityMask(maskBmp.Get(), imgBrush.Get());

    HR(m_d2dDeviceContext->EndDraw());
    m_d2dDeviceContext->SetTarget(nullptr);
    HR(sisn->EndDraw());
}

HRESULT MainPage::PrepareDraw(ISurfaceImageSourceNative* sisn, const RECT& updateRect, IDXGISurface** surface, POINT* offset)
{
    HRESULT hr;
    hr = sisn->BeginDraw(updateRect, surface, offset);

    if ((hr == DXGI_ERROR_DEVICE_REMOVED) || (hr == DXGI_ERROR_DEVICE_RESET))
    {
        CreateDevice();
        // Device changed, try again.
        return PrepareDraw(sisn, updateRect, surface, offset);
    }
    else
    {
        return hr;
    }
}

View Code

MainPage.xaml见正文。

时间: 2024-10-30 23:32:00

UWP中的Direct2D的相关文章

吸顶大法 -- UWP中的工具栏吸顶的实现方式之一

原文:吸顶大法 -- UWP中的工具栏吸顶的实现方式之一 如果一个页面中有很长的列表/内容,很多应用都会在用户向下滚动时隐藏页面的头,给用户留出更多的阅读空间,同时提供一个方便的吸顶工具栏,比如淘宝中的店铺页面. 下面是一个比较简单的实现,如果有同学有更好的实现,欢迎留言,让我们共同进步. 首先假设我们的页面整体包含3部分;     页面头:随页面滚动慢慢消失/重现     工具栏: 开始时随页面滚动,在页面头消失后,吸顶,固定不动     可滚动内容:一个listview 结构代码如下,为了区

UWP开发-在UWP中使用sqlite

原文:UWP开发-在UWP中使用sqlite sqlite是一种轻量级的数据库,对于一些资源紧张又需要数据库的开发非常好用. SQLite 是一个开源的无服务器嵌入式数据库. 这些年来,它已作为面向存储在许多平台和设备上的数据的主要设备端技术出现. 通用 Windows 平台 (UWP) 支持并建议使用 SQLite 实现跨所有 Windows 10 设备系列的本地存储. SQLite 最适用于手机应用.面向 Windows 10 IoT 核心版(IoT 核心版)的嵌入式应用程序,以及作为企业关

UWP中两个,system.data.dll与Microsoft.NETCore.UniversalWindowsPlatform冲突

问题描述 UWP中两个,system.data.dll与Microsoft.NETCore.UniversalWindowsPlatform冲突usingSystem.Configuration;//usingSystem.Data;usingSystem.Data.Common;publicclassServerDB{privatestaticConnectionStringSettingsdbSetting=ConfigurationManager.ConnectionStrings["te

uwp中listview分组

问题描述 在线求一个uwp中listview使用GroupStyle分组的案例,分组依据是从数据库读取,每个分组依据下对应的小类别也是从数据库读取.并且点击小类别会打开小类别对应的内容,所有数据都是从数据读取.在线等,急~~~~~ 解决方案 解决方案二:自己顶一下~~~~解决方案三:来个人撒,来个人解决方案四:木有人吗,,,,,,

UWP中String 转为Path Data

定义方法: Geometry PathMarkupToGeometry(string pathMarkup) { string xaml = "<Path " + "xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>" + "<Path.Data>" + pathMarkup + "</Path.Data></Pa

Viewbox在UWP开发中的应用

原文:Viewbox在UWP开发中的应用 Windows 8.1 与Windows Phone 8.1的UAP应用,终于在Windows 10上统一到了UWP之下.原来3个不同的project也变为一个.没有了2套xaml页面,我们需要用同一个页面适应所有的设备.更麻烦的是在Desktop设备上,我们的应用窗口是可以随意拖动大小的.这对我们的UI布局提出了更高的要求.当然我们也有了新的工具来应对: VisualState中增加了StateTriggers,通过不同的条件触发Trigger并自动切

Win10 UWP开发中的重复性静态UI绘制小技巧 2

原文:Win10 UWP开发中的重复性静态UI绘制小技巧 2 小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html 介绍 我们在上一篇博文中展示了通过Shape.Stroke族属性实现静态重复性UI绘制,使得UWP界面的实现变得稍微灵活一些了. 但这一技巧还是有不少局限的,毕竟折腾StrokeDashArray属性看上去并不是那么直观和适用(还存在用扇形欺骗观众这样的"问题"啦). 这一篇博文我们将为大家介绍一种更为适用,同时也更为灵

WP8.1 Silverlight应用如何迁移到Win10 UWP

  自Win10发布后,微软一直向开发者宣传UWP通用平台的好处,而现在大多数应用还停留在WP8.1Silverlight阶段,微软现在鼓励开发者将应用迁移到最新的Windows10 UWP,并给出官方指导. Windows Dev Center发布了官方指导,包括很多细节,一步一步指导如何将Windows Phone Silverlight应用迁移到UWP.Silverlight移植到Win10通道将帮助开发者自动完成80%的代码转换. 下载桥接工具 点击Mobilize.NET你可以很容易发

Win10 UWP 开发系列:使用SQLite

原文:Win10 UWP 开发系列:使用SQLite 在App开发过程中,肯定需要有一些数据要存储在本地,简单的配置可以序列化后存成文件,比如LocalSettings的方式,或保存在独立存储中.但如果数据多的话,还是需要本地数据库的支持.在UWP开发中,可以使用SQLite.本篇文章说一下如何在UWP中使用SQLite.因为SQLite是跨平台的,版本众多,我刚开始用的时候不知道要装哪个,什么WP8的.WP8.1的.Win RT的--简直摸不着头脑.希望这篇文章能让大家少走点弯路. 其实这篇文