Windows 10 RTM Release - October 2015 Update 4
This commit is contained in:
Родитель
22bbec2c41
Коммит
2b585583c3
19
README.md
19
README.md
|
@ -146,46 +146,49 @@ For additional Windows samples, see [Windows on GitHub](http://microsoft.github.
|
|||
<tr>
|
||||
<td><a href="Samples/ContextMenu">Context menu</a></td>
|
||||
<td><a href="Samples/XamlContextMenu">Context menu (XAML)</a></td>
|
||||
<td><a href="Samples/UserSelection">Disabling selection</a></td>
|
||||
<td><a href="Samples/XamlDataVirtualization">Data virtualization</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/UserSelection">Disabling selection</a></td>
|
||||
<td><a href="Samples/DisplayOrientation">Display orientation</a></td>
|
||||
<td><a href="Samples/DWriteTextLayoutCloudFont">Downloadable fonts (DirectWrite)</a></td>
|
||||
<td><a href="Samples/XamlCloudFontIntegration">Downloadable fonts (XAML)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/XamlCloudFontIntegration">Downloadable fonts (XAML)</a></td>
|
||||
<td><a href="Samples/XamlDragAndDrop">Drag and drop</a></td>
|
||||
<td><a href="Samples/XamlFocusVisuals">Focus visuals</a></td>
|
||||
<td><a href="Samples/FullScreenMode">Full screen mode</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/FullScreenMode">Full screen mode</a></td>
|
||||
<td><a href="Samples/DWriteLineSpacingModes">Line spacing (DirectWrite)</a></td>
|
||||
<td><a href="Samples/XamlListView">ListView and GridView</a></td>
|
||||
<td><a href="Samples/LockScreenApps">Lock screen apps</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/LockScreenApps">Lock screen apps</a></td>
|
||||
<td><a href="Samples/MessageDialog">Message dialog</a></td>
|
||||
<td><a href="Samples/MultipleViews">Multiple views</a></td>
|
||||
<td><a href="Samples/OCR">OCR</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/OCR">OCR</a></td>
|
||||
<td><a href="Samples/Printing">Printing</a></td>
|
||||
<td><a href="Samples/XamlPullToRefresh">Pull-to-refresh</a></td>
|
||||
<td><a href="Samples/XamlResponsiveTechniques">Responsiveness techniques</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/XamlResponsiveTechniques">Responsiveness techniques</a></td>
|
||||
<td><a href="Samples/DpiScaling">Scaling according to DPI</a></td>
|
||||
<td><a href="Samples/SplashScreen">Splash screen</a></td>
|
||||
<td><a href="Samples/XamlStateTriggers">State triggers</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/XamlStateTriggers">State triggers</a></td>
|
||||
<td><a href="Samples/XamlTailoredMultipleViews">Tailored multiple views</a></td>
|
||||
<td><a href="Samples/TitleBar">Title bar</a></td>
|
||||
<td><a href="Samples/XamlUIBasics">UI basics (XAML)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/XamlUIBasics">UI basics (XAML)</a></td>
|
||||
<td><a href="Samples/UserInteractionMode">User interaction mode</a></td>
|
||||
<td><a href="Samples/WebView">WebView control (HTML)</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="Samples/ResizeAppView">Window resizing</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!---
|
||||
category: ContactsAndCalendar
|
||||
-->
|
||||
|
||||
# Contact Card integration sample
|
||||
|
||||
This sample shows how to integrate your app into the People App's contact card and the Mini Contact card by using [**annotations**](https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactannotationlist.aspx).
|
||||
|
||||
**Note** The Windows universal samples require Visual Studio 2015 to build and Windows 10 to execute.
|
||||
|
||||
To obtain information about Windows 10 development, go to the [Windows Dev Center](https://dev.windows.com)
|
||||
|
||||
To obtain information about Microsoft Visual Studio 2015 and the tools for developing Windows apps, go to [Visual Studio 2015](http://go.microsoft.com/fwlink/?LinkID=532422)
|
||||
|
||||
## Related topics
|
||||
|
||||
### Samples
|
||||
|
||||
[**ContactCards**](../ContactCards)
|
||||
|
||||
[**ContactPicker**](../ContactPicker)
|
||||
|
||||
### Reference
|
||||
|
||||
[**ContactAnnotationList**](https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactannotationlist.aspx)
|
||||
|
||||
[**ContactAnnotation**](https://msdn.microsoft.com/en-us/library/windows/apps/windows.applicationmodel.contacts.contactannotation.aspx)
|
||||
|
||||
[**Windows.ApplicationModel.Contacts**](http://msdn.microsoft.com/library/windows/apps/br225002)
|
||||
|
||||
[**Handling URI activation (XAML)**](https://technet.microsoft.com/en-us/windowsserver/hh779670)
|
||||
|
||||
## System requirements
|
||||
|
||||
**Client:** Windows 10
|
||||
|
||||
**Server:** Windows Server 2016 Technical Preview
|
||||
|
||||
**Phone:** Windows 10
|
||||
|
||||
## Build the sample
|
||||
|
||||
1. If you download the samples ZIP, be sure to unzip the entire archive, not just the folder with the sample you want to build.
|
||||
2. Start Microsoft Visual Studio 2015 and select **File** \> **Open** \> **Project/Solution**.
|
||||
3. Starting in the folder where you unzipped the samples, go to the Samples subfolder, then the subfolder for this specific sample, then the subfolder for your preferred language (C++, C#, or JavaScript). Double-click the Visual Studio?2015 Solution (.sln) file.
|
||||
4. Press Ctrl+Shift+B, or select **Build** \> **Build Solution**.
|
||||
|
||||
## Run the sample
|
||||
|
||||
The next steps depend on whether you just want to deploy the sample or you want to both deploy and run it.
|
||||
|
||||
### Deploying the sample
|
||||
|
||||
- Select Build > Deploy Solution.
|
||||
|
||||
### Deploying and running the sample
|
||||
|
||||
- To debug the sample and then run it, press F5 or select Debug > Start Debugging. To run the sample without debugging, press Ctrl+F5 or selectDebug > Start Without Debugging.
|
|
@ -0,0 +1,180 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SDKTemplate</RootNamespace>
|
||||
<AssemblyName>ContactCardIntegration</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.10240.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<EnableDotNetNativeCompatibleProfile>true</EnableDotNetNativeCompatibleProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\SharedContent\cs\App.xaml.cs">
|
||||
<Link>App.xaml.cs</Link>
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\SharedContent\cs\MainPage.xaml.cs">
|
||||
<Link>MainPage.xaml.cs</Link>
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\SharedContent\cs\AssemblyInfo.cs">
|
||||
<Link>Properties\AssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SampleConfiguration.cs" />
|
||||
<Compile Include="Scenario1_CreateContacts.xaml.cs">
|
||||
<DependentUpon>Scenario1_CreateContacts.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scenario2_ReceiveContact.xaml.cs">
|
||||
<DependentUpon>Scenario2_ReceiveContact.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="..\..\..\SharedContent\xaml\App.xaml">
|
||||
<Link>App.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="..\..\..\SharedContent\cs\MainPage.xaml">
|
||||
<Link>MainPage.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario1_CreateContacts.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario2_ReceiveContact.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\..\SharedContent\cs\Default.rd.xml">
|
||||
<Link>Properties\Default.rd.xml</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
<Link>Assets\microsoft-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\smalltile-sdk.png">
|
||||
<Link>Assets\smallTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\splash-sdk.png">
|
||||
<Link>Assets\splash-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\squaretile-sdk.png">
|
||||
<Link>Assets\squareTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\storelogo-sdk.png">
|
||||
<Link>Assets\storeLogo-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\tile-sdk.png">
|
||||
<Link>Assets\tile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\windows-sdk.png">
|
||||
<Link>Assets\windows-sdk.png</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ContactCardIntegration", "ContactCardIntegration.csproj", "{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.Build.0 = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.Build.0 = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.Build.0 = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.ActiveCfg = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.Build.0 = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.Deploy.0 = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.ActiveCfg = Release|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.Build.0 = Release|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.Deploy.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
IgnorableNamespaces="uap mp">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.SDKSamples.ContactCardIntegration.CS"
|
||||
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="b9d4fe1b-c990-4d36-9df1-bdd8b5b31509" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Contact Card Integration C# Sample</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo-sdk.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="ContactCardIntegration.App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="ContactCardIntegration.App">
|
||||
<uap:VisualElements
|
||||
DisplayName="Contact Card Integration C# sample"
|
||||
Square150x150Logo="Assets\squareTile-sdk.png"
|
||||
Square44x44Logo="Assets\SmallTile-sdk.png"
|
||||
Description="Contact Card Integration C# sample"
|
||||
BackgroundColor="#00b2f0">
|
||||
<uap:SplashScreen Image="Assets\Splash-sdk.png" />
|
||||
<uap:DefaultTile>
|
||||
<uap:ShowNameOnTiles>
|
||||
<uap:ShowOn Tile="square150x150Logo" />
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="ms-voip-call">
|
||||
<uap:DisplayName>TestCallApp</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="ms-voip-video">
|
||||
<uap:DisplayName>TestVideoApp</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="ms-ipmessaging">
|
||||
<uap:DisplayName>TestMsgApp</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="ms-contact-profile">
|
||||
<uap:DisplayName>TestProfileApp</uap:DisplayName>
|
||||
</uap:Protocol>
|
||||
</uap:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<uap:Capability Name="contacts" />
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,71 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
public const string FEATURE_NAME = "Contact Card integration C# Sample";
|
||||
|
||||
List<Scenario> scenarios = new List<Scenario>
|
||||
{
|
||||
new Scenario() { Title="Create contacts with annotations", ClassType=typeof(Scenario1_CreateContacts)},
|
||||
new Scenario() { Title="Receiving a contact", ClassType=typeof(Scenario2_ReceiveContact) },
|
||||
};
|
||||
|
||||
public void NavigateToPageWithParameter(int pageIndex, object parameter)
|
||||
{
|
||||
ScenarioControl.SelectedIndex = pageIndex;
|
||||
ScenarioFrame.Navigate(scenarios[pageIndex].ClassType, parameter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class Scenario
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public Type ClassType { get; set; }
|
||||
}
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
// Handle protocol activations.
|
||||
/// </summary>
|
||||
protected override void OnActivated(IActivatedEventArgs e)
|
||||
{
|
||||
if (e.Kind == ActivationKind.Protocol)
|
||||
{
|
||||
Frame rootFrame = CreateRootFrame();
|
||||
|
||||
if (rootFrame.Content == null)
|
||||
{
|
||||
if (!rootFrame.Navigate(typeof(MainPage)))
|
||||
{
|
||||
throw new Exception("Failed to create initial page");
|
||||
}
|
||||
}
|
||||
|
||||
var p = rootFrame.Content as MainPage;
|
||||
p.NavigateToPageWithParameter(1, e);
|
||||
|
||||
// Ensure the current window is active
|
||||
Window.Current.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario1_CreateContacts"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<ScrollViewer Margin="12,20,12,12">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
Create test contacts with annotations
|
||||
</TextBlock>
|
||||
|
||||
<Button Content="Create test contacts" Click="{x:Bind CreateTestContacts}" Margin="0,10,0,0"/>
|
||||
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
In the People app, go to the contact named TestContact1.
|
||||
Viewing that contact's profile launches this application.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
In the People app, go to the contact named TestContact2.
|
||||
You can send a message using this app, place a call via this app,
|
||||
or make a video call via this app.
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
When an app such as the ContactCards sample displays a mini contact card
|
||||
for TestContact1 or TestContact2,
|
||||
this app will appear as an available action.
|
||||
</TextBlock>
|
||||
|
||||
<Button Content="Delete test contacts" Click="{x:Bind DeleteTestContacts}" Margin="0,10,0,0"/>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,196 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.ApplicationModel.Contacts;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Scenario1_CreateContacts : Page
|
||||
{
|
||||
private MainPage rootPage = MainPage.Current;
|
||||
|
||||
public Scenario1_CreateContacts()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private async Task<ContactList> _GetContactList()
|
||||
{
|
||||
ContactStore store = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AppContactsReadWrite);
|
||||
if (null == store)
|
||||
{
|
||||
rootPage.NotifyUser("Unable to get a contacts store.", NotifyType.ErrorMessage);
|
||||
return null;
|
||||
}
|
||||
|
||||
ContactList contactList;
|
||||
IReadOnlyList<ContactList> contactLists = await store.FindContactListsAsync();
|
||||
if (0 == contactLists.Count)
|
||||
{
|
||||
contactList = await store.CreateContactListAsync("TestContactList");
|
||||
}
|
||||
else
|
||||
{
|
||||
contactList = contactLists[0];
|
||||
}
|
||||
|
||||
return contactList;
|
||||
}
|
||||
|
||||
private async Task<ContactAnnotationList> _GetContactAnnotationList()
|
||||
{
|
||||
ContactAnnotationStore annotationStore = await ContactManager.RequestAnnotationStoreAsync(ContactAnnotationStoreAccessType.AppAnnotationsReadWrite);
|
||||
if (null == annotationStore)
|
||||
{
|
||||
rootPage.NotifyUser("Unable to get an annotations store.", NotifyType.ErrorMessage);
|
||||
return null;
|
||||
}
|
||||
|
||||
ContactAnnotationList annotationList;
|
||||
IReadOnlyList<ContactAnnotationList> annotationLists = await annotationStore.FindAnnotationListsAsync();
|
||||
if (0 == annotationLists.Count)
|
||||
{
|
||||
annotationList = await annotationStore.CreateAnnotationListAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
annotationList = annotationLists[0];
|
||||
}
|
||||
|
||||
return annotationList;
|
||||
}
|
||||
|
||||
private async void CreateTestContacts()
|
||||
{
|
||||
//
|
||||
// Creating two test contacts with email address and phone number.
|
||||
//
|
||||
|
||||
Contact contact1 = new Contact();
|
||||
contact1.FirstName = "TestContact1";
|
||||
|
||||
ContactEmail email1 = new ContactEmail();
|
||||
email1.Address = "TestContact1@contoso.com";
|
||||
contact1.Emails.Add(email1);
|
||||
|
||||
ContactPhone phone1 = new ContactPhone();
|
||||
phone1.Number = "4255550100";
|
||||
contact1.Phones.Add(phone1);
|
||||
|
||||
Contact contact2 = new Contact();
|
||||
contact2.FirstName = "TestContact2";
|
||||
|
||||
ContactEmail email2 = new ContactEmail();
|
||||
email2.Address = "TestContact2@contoso.com";
|
||||
email2.Kind = ContactEmailKind.Other;
|
||||
contact2.Emails.Add(email2);
|
||||
|
||||
ContactPhone phone2 = new ContactPhone();
|
||||
phone2.Number = "4255550101";
|
||||
phone2.Kind = ContactPhoneKind.Mobile;
|
||||
contact2.Phones.Add(phone2);
|
||||
|
||||
// Save the contacts
|
||||
ContactList contactList = await _GetContactList();
|
||||
if (null == contactList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await contactList.SaveContactAsync(contact1);
|
||||
await contactList.SaveContactAsync(contact2);
|
||||
|
||||
//
|
||||
// Create annotations for those test contacts.
|
||||
// Annotation is the contact meta data that allows People App to generate deep links
|
||||
// in the contact card that takes the user back into this app.
|
||||
//
|
||||
|
||||
ContactAnnotationList annotationList = await _GetContactAnnotationList();
|
||||
if (null == annotationList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ContactAnnotation annotation = new ContactAnnotation();
|
||||
annotation.ContactId = contact1.Id;
|
||||
|
||||
// Remote ID: The identifier of the user relevant for this app. When this app is
|
||||
// launched into from the People App, this id will be provided as context on which user
|
||||
// the operation (e.g. ContactProfile) is for.
|
||||
annotation.RemoteId = "user12";
|
||||
|
||||
// The supported operations flags indicate that this app can fulfill these operations
|
||||
// for this contact. These flags are read by apps such as the People App to create deep
|
||||
// links back into this app. This app must also be registered for the relevant
|
||||
// protocols in the Package.appxmanifest (in this case, ms-contact-profile).
|
||||
annotation.SupportedOperations = ContactAnnotationOperations.ContactProfile;
|
||||
|
||||
if (!await annotationList.TrySaveAnnotationAsync(annotation))
|
||||
{
|
||||
rootPage.NotifyUser("Failed to save annotation for TestContact1 to the store.", NotifyType.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
annotation = new ContactAnnotation();
|
||||
annotation.ContactId = contact2.Id;
|
||||
annotation.RemoteId = "user22";
|
||||
|
||||
// You can also specify multiple supported operations for a contact in a single
|
||||
// annotation. In this case, this annotation indicates that the user can be
|
||||
// communicated via VOIP call, Video Call, or IM via this application.
|
||||
annotation.SupportedOperations = ContactAnnotationOperations.Message |
|
||||
ContactAnnotationOperations.AudioCall |
|
||||
ContactAnnotationOperations.VideoCall;
|
||||
|
||||
if (!await annotationList.TrySaveAnnotationAsync(annotation))
|
||||
{
|
||||
rootPage.NotifyUser("Failed to save annotation for TestContact2 to the store.", NotifyType.ErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
rootPage.NotifyUser("Sample data created successfully.", NotifyType.StatusMessage);
|
||||
}
|
||||
|
||||
private async void DeleteTestContacts()
|
||||
{
|
||||
ContactList contactList = null;
|
||||
ContactStore store = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AppContactsReadWrite);
|
||||
if (null != store)
|
||||
{
|
||||
IReadOnlyList<ContactList> contactLists = await store.FindContactListsAsync();
|
||||
if (0 < contactLists.Count)
|
||||
{
|
||||
contactList = contactLists[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (null != contactList)
|
||||
{
|
||||
await contactList.DeleteAsync();
|
||||
rootPage.NotifyUser("Sample data deleted.", NotifyType.StatusMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootPage.NotifyUser("Could not delete sample data.", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.Scenario2_ReceiveContact"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<ScrollViewer Margin="12,20,12,12">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
Receiving a contact
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Style="{StaticResource BasicTextStyle}" TextWrapping="Wrap" Margin="0,10,0,0">
|
||||
Use the People app or a mini contact card as described in Scenario 1
|
||||
to activate this application with a contact.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,47 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// Page to handle the protocol activation. In this case we simply show the URI and the
|
||||
/// parameters passed to it.
|
||||
/// </summary>
|
||||
public sealed partial class Scenario2_ReceiveContact : Page
|
||||
{
|
||||
public Scenario2_ReceiveContact()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
var args = e.Parameter as ProtocolActivatedEventArgs;
|
||||
// Display the result of the protocol activation if we got here as a result of being activated for a protocol.
|
||||
if (args != null)
|
||||
{
|
||||
// Parse the URI to extract the protocol and the contact ids
|
||||
Uri uri = args.Uri;
|
||||
|
||||
MainPage.Current.NotifyUser("Activated with protocol = " + uri.Scheme + ", parameters = " + uri.Query, NotifyType.StatusMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"uap10.0": {}
|
||||
},
|
||||
"runtimes": {
|
||||
"win10-arm": {},
|
||||
"win10-arm-aot": {},
|
||||
"win10-x86": {},
|
||||
"win10-x86-aot": {},
|
||||
"win10-x64": {},
|
||||
"win10-x64-aot": {}
|
||||
}
|
||||
}
|
|
@ -120,21 +120,28 @@ namespace Tasks
|
|||
|
||||
StorageFile logFile = await localFolder.CreateFileAsync(logFileName, CreationCollisionOption.OpenIfExists);
|
||||
|
||||
// Create a protected file
|
||||
ProtectedFileCreateResult result = await FileProtectionManager.CreateProtectedAndOpenAsync(localFolder,
|
||||
fileName, m_EnterpriseID, CreationCollisionOption.ReplaceExisting);
|
||||
// Create a protected file allows the creation of new files even after keys are dropped so that application
|
||||
// can create and continue writing to files under lock
|
||||
|
||||
m_fileProtStatus = result.ProtectionInfo.Status.ToString();
|
||||
|
||||
// Write to File
|
||||
|
||||
using (IOutputStream outputStream = result.Stream.GetOutputStreamAt(0))
|
||||
var result = await FileProtectionManager.CreateProtectedAndOpenAsync(localFolder,
|
||||
fileName,
|
||||
m_EnterpriseID,
|
||||
CreationCollisionOption.ReplaceExisting
|
||||
);
|
||||
using (var stream = result.Stream)
|
||||
{
|
||||
DataWriter writer = new DataWriter(outputStream);
|
||||
for (int i = 0; i < 100; i++)
|
||||
m_fileProtStatus = result.ProtectionInfo.Status.ToString();
|
||||
|
||||
// Write to File
|
||||
|
||||
using (IOutputStream outputStream = result.Stream.GetOutputStreamAt(0))
|
||||
{
|
||||
writer.WriteString(textBody);
|
||||
await writer.StoreAsync();
|
||||
DataWriter writer = new DataWriter(outputStream);
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
writer.WriteString(textBody);
|
||||
await writer.StoreAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Name="RootPage">
|
||||
SizeChanged="FileOpenPickerPage_SizeChanged">
|
||||
|
||||
<Page.Resources>
|
||||
<Style x:Key="BaseStatusStyle" TargetType="TextBlock">
|
||||
|
@ -79,6 +79,7 @@
|
|||
<TextBlock x:Name="ScenarioListLabel" Text="Select Scenario:" Grid.Row="1" Style="{StaticResource SubheaderTextBlockStyle}" Margin="0,5,0,0" />
|
||||
<ListBox x:Name="Scenarios" Margin="0,0,20,0" Grid.Row="2" AutomationProperties.Name="Scenarios" HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
SelectionChanged="{x:Bind OnScenarioSelectionChanged}"
|
||||
AutomationProperties.LabeledBy="{Binding ElementName=ScenarioListLabel}" MaxHeight="125">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
|
|
@ -28,9 +28,6 @@ namespace SDKTemplate
|
|||
{
|
||||
public const string FEATURE_NAME = "File Open Picker Page";
|
||||
|
||||
public event System.EventHandler ScenarioLoaded;
|
||||
public event EventHandler<FileOpenPickerPageSizeChangedEventArgs> FileOpenPickerPageResized;
|
||||
|
||||
public Windows.ApplicationModel.Activation.LaunchActivatedEventArgs LaunchArgs;
|
||||
|
||||
public static FileOpenPickerPage Current;
|
||||
|
@ -70,22 +67,13 @@ namespace SDKTemplate
|
|||
HiddenFrame.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
|
||||
LayoutRoot.Children.Add(HiddenFrame);
|
||||
|
||||
// Populate the sample title from the constant in the GlobalVariables.cs file.
|
||||
// Populate the sample title from the constant in this file.
|
||||
SetFeatureName(FEATURE_NAME);
|
||||
|
||||
Scenarios.SelectionChanged += Scenarios_SelectionChanged;
|
||||
SizeChanged += FileOpenPickerPage_SizeChanged;
|
||||
}
|
||||
|
||||
void FileOpenPickerPage_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
InvalidateSize();
|
||||
if (FileOpenPickerPageResized != null)
|
||||
{
|
||||
FileOpenPickerPageSizeChangedEventArgs args = new FileOpenPickerPageSizeChangedEventArgs();
|
||||
args.Width = this.ActualWidth;
|
||||
FileOpenPickerPageResized(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -220,11 +208,6 @@ namespace SDKTemplate
|
|||
// Populate the input and output sections with the newly loaded content.
|
||||
InputSection.Content = input;
|
||||
OutputSection.Content = output;
|
||||
|
||||
if (ScenarioLoaded != null)
|
||||
{
|
||||
ScenarioLoaded(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -236,7 +219,7 @@ namespace SDKTemplate
|
|||
|
||||
}
|
||||
|
||||
void Scenarios_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
void OnScenarioSelectionChanged()
|
||||
{
|
||||
if (Scenarios.SelectedItem != null)
|
||||
{
|
||||
|
@ -284,15 +267,4 @@ namespace SDKTemplate
|
|||
FeatureName.Text = str;
|
||||
}
|
||||
}
|
||||
|
||||
public class FileOpenPickerPageSizeChangedEventArgs : EventArgs
|
||||
{
|
||||
private double width;
|
||||
|
||||
public double Width
|
||||
{
|
||||
get { return width; }
|
||||
set { width = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Identity Name="Microsoft.SDKSamples.WebAccountManagement.CPP" Publisher="CN=3B3B1E5A-CE36-4D75-AEDA-6809021F7AA1" Version="1.1.0.8" />
|
||||
<mp:PhoneIdentity PhoneProductId="35ba7848-dfec-48e3-9197-ac01fd9d3e74" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Identity Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Name="Microsoft.SDKSamples.WebAccountManagement.CPP" Version="1.0.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="ecd494c6-8bb7-4f4e-b04a-a4e96b9eba60" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Web Account Manager C++ Sample</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace SDKTemplate
|
|||
|
||||
Platform::String^ MICROSOFT_PROVIDER_ID = "https://login.microsoft.com";
|
||||
Platform::String^ CONSUMER_AUTHORITY = "consumers";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "service::wl.basic::DELEGATION";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "wl.basic";
|
||||
Platform::String^ MSA_CLIENT_ID = "none";
|
||||
Platform::String^ STORED_ACCOUNT_ID_KEY = "accountid";
|
||||
Windows::Security::Credentials::WebAccountProvider^ m_provider;
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace SDKTemplate
|
|||
Platform::String^ MICROSOFT_PROVIDER_ID = "https://login.microsoft.com";
|
||||
Platform::String^ CONSUMER_AUTHORITY = "consumers";
|
||||
Platform::String^ ORGANIZATION_AUTHORITY = "organizations";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "service::wl.basic::DELEGATION";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "wl.basic";
|
||||
Platform::String^ MSA_CLIENT_ID = "none";
|
||||
Platform::String^ AAD_SCOPE_REQUESTED = "";
|
||||
Platform::String^ AAD_CLIENT_ID = "c8542080-6873-47b5-bf71-d56378567ceb";
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace SDKTemplate
|
|||
Platform::String^ MICROSOFT_PROVIDER_ID = "https://login.microsoft.com";
|
||||
Platform::String^ CONSUMER_AUTHORITY = "consumers";
|
||||
Platform::String^ ORGANIZATION_AUTHORITY = "organizations";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "service::wl.basic::DELEGATION";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "wl.basic";
|
||||
Platform::String^ MSA_CLIENT_ID = "none";
|
||||
Platform::String^ AAD_SCOPE_REQUESTED = "";
|
||||
Platform::String^ AAD_CLIENT_ID = "c8542080-6873-47b5-bf71-d56378567ceb";
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace SDKTemplate
|
|||
Platform::String^ MICROSOFT_PROVIDER_ID = "https://login.microsoft.com";
|
||||
Platform::String^ CONSUMER_AUTHORITY = "consumers";
|
||||
Platform::String^ ORGANIZATION_AUTHORITY = "organizations";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "service::wl.basic::DELEGATION";
|
||||
Platform::String^ MSA_SCOPE_REQUESTED = "wl.basic";
|
||||
Platform::String^ MSA_CLIENT_ID = "none";
|
||||
Platform::String^ AAD_SCOPE_REQUESTED = "";
|
||||
Platform::String^ AAD_CLIENT_ID = "c8542080-6873-47b5-bf71-d56378567ceb";
|
||||
|
|
|
@ -251,9 +251,6 @@
|
|||
<Link>Assets\windows-sdk.png</Link>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SDKReference Include="WindowsDesktop, Version=10.0.10240.0" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace Accounts
|
|||
const string AzureActiveDirectoryAuthority = "organizations";
|
||||
|
||||
const string MicrosoftAccountClientId = "none";
|
||||
const string MicrosoftAccountScopeRequested = "service::wl.basic::DELEGATION";
|
||||
const string MicrosoftAccountScopeRequested = "wl.basic";
|
||||
|
||||
// To obtain azureAD tokens, you must register this app on the AzureAD portal, and obtain the client ID
|
||||
const string AzureActiveDirectoryClientId = "";
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace Accounts
|
|||
const string AzureActiveDirectoryAuthority = "organizations";
|
||||
|
||||
const string MicrosoftAccountClientId = "none";
|
||||
const string MicrosoftAccountScopeRequested = "service::wl.basic::DELEGATION";
|
||||
const string MicrosoftAccountScopeRequested = "wl.basic";
|
||||
|
||||
// To obtain azureAD tokens, you must register this app on the AzureAD portal, and obtain the client ID
|
||||
const string AzureActiveDirectoryClientId = "";
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace Accounts
|
|||
const string StoredAuthorityKey = "authority";
|
||||
|
||||
const string MicrosoftAccountClientId = "none";
|
||||
const string MicrosoftAccountScopeRequested = "service::wl.basic::DELEGATION";
|
||||
const string MicrosoftAccountScopeRequested = "wl.basic";
|
||||
|
||||
// To obtain azureAD tokens, you must register this app on the AzureAD portal, and obtain the client ID
|
||||
const string AzureActiveDirectoryClientId = "";
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace Accounts
|
|||
// Then, you must associate the app with the store.
|
||||
const string MicrosoftAccountProviderId = "https://login.microsoft.com";
|
||||
const string ConsumerAuthority = "consumers";
|
||||
const string AccountScopeRequested = "service::wl.basic::DELEGATION";
|
||||
const string AccountScopeRequested = "wl.basic";
|
||||
const string AccountClientId = "none";
|
||||
const string StoredAccountKey = "accountid";
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
<!---
|
||||
category: ControlsLayoutAndText
|
||||
--->
|
||||
|
||||
# Data virtualization sample
|
||||
|
||||
This sample shows how to implement a data source for XAML list controls that implements data virtualization.
|
||||
|
||||
Specifically, this sample shows how to:
|
||||
|
||||
- **Implement IItemsRangeInfo:** Use the IItemsRangeInfo interface to understand which items are in view or the buffer area for a list view, and to manage a cache containing those items.
|
||||
- **Implement ISelectionInfo:** Use the ISelectionInfo interface to manage the selection of items in the list, accounting for the data virtualization.
|
||||
|
||||
**Note** The Windows universal samples require Visual Studio 2015 to build and Windows 10 to execute.
|
||||
|
||||
To obtain information about Windows 10, go to [Windows 10](http://go.microsoft.com/fwlink/?LinkID=532421)
|
||||
|
||||
To obtain information about Microsoft Visual Studio 2015 and the tools for developing Windows apps, go to [Visual Studio 2015](http://go.microsoft.com/fwlink/?LinkID=532422)
|
||||
|
||||
## Related topics
|
||||
[ListView and GridView UI optimization](https://msdn.microsoft.com/en-us/library/windows/apps/mt204776.aspx)
|
||||
|
||||
### Samples
|
||||
|
||||
[XAML data binding](https://code.msdn.microsoft.com/windowsapps/Data-Binding-7b1d67b5/)
|
||||
|
||||
[XAML data binding with x:Bind](../XamlBind)
|
||||
|
||||
### Reference
|
||||
|
||||
<!-- Add links to related API -->
|
||||
|
||||
[IItemsRangeInfo](https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.data.iitemsrangeinfo.aspx)
|
||||
|
||||
[ISelectionInfo](https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.data.iselectioninfo.aspx)
|
||||
|
||||
## System requirements
|
||||
|
||||
**Client:** Windows 10
|
||||
|
||||
**Server:** Windows Server 2016 Technical Preview
|
||||
|
||||
**Phone:** Windows 10
|
||||
|
||||
## Build the sample
|
||||
|
||||
1. Start Microsoft Visual Studio 2015 and select **File** \> **Open** \> **Project/Solution**.
|
||||
2. Go to the directory to which you unzipped the sample. Double-click the Visual Studio 2015 Solution (.sln) file.
|
||||
3. Press Ctrl+Shift+B, or select **Build** \> **Build Solution**.
|
||||
|
||||
## Run the sample
|
||||
|
||||
The next steps depend on whether you just want to deploy the sample or you want to both deploy and run it.
|
||||
|
||||
### Deploying the sample
|
||||
|
||||
- Select Build > Deploy Solution.
|
||||
|
||||
### Deploying and running the sample
|
||||
|
||||
- To debug the sample and then run it, press F5 or select Debug > Start Debugging. To run the sample without debugging, press Ctrl+F5 or selectDebug > Start Without Debugging.
|
||||
- The sample uses the StorageFolder API to read the contents of the Pictures folder, if you don't see any results in the sample, its likely that no pictures could be found.
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DataVirtualization</RootNamespace>
|
||||
<AssemblyName>DataVirtualization</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.10240.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<EnableProjectNCompatibleProfile>true</EnableProjectNCompatibleProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseProjectNToolchain>true</UseProjectNToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NETFX_CORE;WINDOWS_UAP;CODE_ANALYSIS</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseProjectNToolchain>true</UseProjectNToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NETFX_CORE;WINDOWS_UAP;CODE_ANALYSIS</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UAP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseProjectNToolchain>true</UseProjectNToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\..\SharedContent\cs\App.xaml.cs">
|
||||
<Link>App.xaml.cs</Link>
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="..\..\..\SharedContent\cs\AssemblyInfo.cs">
|
||||
<Link>Properties\AssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SampleConfiguration.cs" />
|
||||
<Compile Include="Scenario1\ItemCacheManager.cs" />
|
||||
<Compile Include="Scenario1\FileDataSource.cs" />
|
||||
<Compile Include="Scenario1\FileItem.cs" />
|
||||
<Compile Include="Scenario1\ItemIndexRangeExtensions.cs" />
|
||||
<Compile Include="Scenario1\ItemIndexRangeList.cs" />
|
||||
<Compile Include="Scenario2\FileDataSource2.cs" />
|
||||
<Compile Include="Scenario1\Scenario1.xaml.cs">
|
||||
<DependentUpon>Scenario1.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Scenario2\Scenario2.xaml.cs">
|
||||
<DependentUpon>Scenario2.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="..\..\..\SharedContent\xaml\App.xaml">
|
||||
<Link>App.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario1\Scenario1.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Scenario2\Scenario2.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="..\..\..\SharedContent\xaml\Styles.xaml">
|
||||
<Link>Styles\Styles.xaml</Link>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\..\SharedContent\media\microsoft-sdk.png">
|
||||
<Link>Assets\microsoft-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\smalltile-sdk.png">
|
||||
<Link>Assets\smallTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\splash-sdk.png">
|
||||
<Link>Assets\splash-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\squaretile-sdk.png">
|
||||
<Link>Assets\squareTile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\storelogo-sdk.png">
|
||||
<Link>Assets\storeLogo-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\tile-sdk.png">
|
||||
<Link>Assets\tile-sdk.png</Link>
|
||||
</Content>
|
||||
<Content Include="..\..\..\SharedContent\media\windows-sdk.png">
|
||||
<Link>Assets\windows-sdk.png</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.22823.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataVirtualization", "DataVirtualization.csproj", "{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.Build.0 = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.Build.0 = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.Build.0 = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.ActiveCfg = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.Build.0 = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x64.Deploy.0 = Release|x64
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.ActiveCfg = Release|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.Build.0 = Release|x86
|
||||
{DC30CE66-DAEE-4CCF-BD02-8837FE918B6F}.Release|x86.Deploy.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(TeamFoundationVersionControl) = preSolution
|
||||
SccNumberOfProjects = 2
|
||||
SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C}
|
||||
SccTeamFoundationServer = https://funnyfarm.visualstudio.com/defaultcollection
|
||||
SccLocalPath0 = .
|
||||
SccProjectUniqueName1 = DataVirtualizationSample\\DataVirtualizationSample.csproj
|
||||
SccProjectName1 = DataVirtualizationSample
|
||||
SccLocalPath1 = DataVirtualizationSample
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,101 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="SDKTemplate.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:SDKTemplate"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
x:Name="Main"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<local:ScenarioBindingConverter x:Key="ScenarioConverter"></local:ScenarioBindingConverter>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup>
|
||||
<VisualState x:Name="wideState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="641" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Splitter.DisplayMode" Value="Inline"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="narrowState">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="0" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Splitter.DisplayMode" Value="Overlay"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<SplitView x:Name="Splitter" IsPaneOpen="True" Grid.Column="1">
|
||||
<SplitView.Pane>
|
||||
<RelativePanel Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
||||
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal">
|
||||
<Image x:Name="WindowsLogo" Stretch="None" Source="Assets/windows-sdk.png" Margin="0,15,0,0" />
|
||||
<TextBlock x:Name="Header" Text="Windows platform sample" Style="{StaticResource TagLineTextStyle}" Margin="0,15,0,0" />
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="SampleTitle" Text="Sample Title Here" Style="{StaticResource SampleHeaderTextStyle}" TextWrapping="Wrap" RelativePanel.Below="HeaderPanel" Margin="0,10,0,0"/>
|
||||
<ListBox x:Name="ScenarioControl" SelectionChanged="ScenarioControl_SelectionChanged"
|
||||
SelectionMode="Single" HorizontalAlignment="Left" Style="{StaticResource ScenarioListBoxStyle}"
|
||||
VerticalAlignment="Top" RelativePanel.Below="SampleTitle" Margin="0,10,0,0" RelativePanel.Above="FooterPanel">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Converter={StaticResource ScenarioConverter}}" Style="{StaticResource ListItemTextStyle}"/>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<StackPanel x:Name="FooterPanel" Orientation="Vertical" RelativePanel.AlignBottomWithPanel="True">
|
||||
<Image Source="Assets/microsoft-sdk.png" AutomationProperties.Name="Microsoft Logo" Stretch="None" HorizontalAlignment="Left" Margin="10,0,0,0"/>
|
||||
<TextBlock x:Name="Copyright" Text="© Microsoft Corporation. All rights reserved." Style="{StaticResource CopyrightTextStyle}"
|
||||
RelativePanel.Above="LinksPanel" Margin="10,10,0,0"
|
||||
TextWrapping="Wrap"/>
|
||||
<StackPanel x:Name="LinksPanel" Orientation="Horizontal" Margin="10,10,0,10">
|
||||
<HyperlinkButton Content="Trademarks" Tag="http://www.microsoft.com/About/Legal/EN/US/IntellectualProperty/Trademarks/EN-US.aspx"
|
||||
Click="Footer_Click" FontSize="12" Style="{StaticResource HyperlinkStyle}" />
|
||||
<TextBlock Text="|" Style="{StaticResource SeparatorStyle}" VerticalAlignment="Center" />
|
||||
<HyperlinkButton x:Name="PrivacyLink" Content="Privacy" Tag="http://privacy.microsoft.com" Click="Footer_Click" FontSize="12" Style="{StaticResource HyperlinkStyle}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</SplitView.Pane>
|
||||
<RelativePanel>
|
||||
<Frame x:Name="ScenarioFrame" Margin="0,5,0,0" RelativePanel.AlignTopWithPanel="True" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" RelativePanel.Above="StatusPanel"/>
|
||||
<StackPanel x:Name="StatusPanel" Orientation="Vertical" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
|
||||
<TextBlock x:Name="StatusLabel" Margin="0,0,0,10" TextWrapping="Wrap" Text="Status:" />
|
||||
<Border x:Name="StatusBorder" Margin="0,0,0,0" Visibility="Collapsed" >
|
||||
<TextBlock x:Name="StatusBlock" FontWeight="Bold" MaxHeight="200" MinWidth="{Binding ElementName=Splitter, Path=ActualWidth}" TextTrimming="CharacterEllipsis" Margin="20,10,10,20" TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</SplitView>
|
||||
<Border Background="{ThemeResource SystemControlBackgroundChromeMediumBrush}">
|
||||
<ToggleButton Style="{StaticResource SymbolButton}" Click="Button_Click" VerticalAlignment="Top" Foreground="{ThemeResource ApplicationForegroundThemeBrush}">
|
||||
<ToggleButton.Content>
|
||||
<FontIcon x:Name="Hamburger" FontFamily="Segoe MDL2 Assets" Glyph="" Margin="0,10,0,0"/>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,136 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
public static MainPage Current;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
// This is a static public property that allows downstream pages to get a handle to the MainPage instance
|
||||
// in order to call methods that are in this class.
|
||||
Current = this;
|
||||
SampleTitle.Text = FEATURE_NAME;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
// Populate the scenario list from the SampleConfiguration.cs file
|
||||
ScenarioControl.ItemsSource = scenarios;
|
||||
if (Window.Current.Bounds.Width < 640)
|
||||
{
|
||||
ScenarioControl.SelectedIndex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScenarioControl.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the user changes selection in the scenarios list. This method will navigate to the respective
|
||||
/// sample scenario page.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ScenarioControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// Clear the status block when navigating scenarios.
|
||||
NotifyUser(String.Empty, NotifyType.StatusMessage);
|
||||
|
||||
ListBox scenarioListBox = sender as ListBox;
|
||||
Scenario s = scenarioListBox.SelectedItem as Scenario;
|
||||
if (s != null)
|
||||
{
|
||||
ScenarioFrame.Navigate(s.ClassType);
|
||||
if (Window.Current.Bounds.Width < 640)
|
||||
{
|
||||
Splitter.IsPaneOpen = false;
|
||||
StatusBorder.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Scenario> Scenarios
|
||||
{
|
||||
get { return this.scenarios; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to display messages to the user
|
||||
/// </summary>
|
||||
/// <param name="strMessage"></param>
|
||||
/// <param name="type"></param>
|
||||
public void NotifyUser(string strMessage, NotifyType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case NotifyType.StatusMessage:
|
||||
StatusBorder.Background = new SolidColorBrush(Windows.UI.Colors.Green);
|
||||
break;
|
||||
case NotifyType.ErrorMessage:
|
||||
StatusBorder.Background = new SolidColorBrush(Windows.UI.Colors.Red);
|
||||
break;
|
||||
}
|
||||
StatusBlock.Text = strMessage;
|
||||
|
||||
// Collapse the StatusBlock if it has no text to conserve real estate.
|
||||
StatusBorder.Visibility = (StatusBlock.Text != String.Empty) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
async void Footer_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(new Uri(((HyperlinkButton)sender).Tag.ToString()));
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Splitter.IsPaneOpen = !Splitter.IsPaneOpen;
|
||||
StatusBorder.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
public enum NotifyType
|
||||
{
|
||||
StatusMessage,
|
||||
ErrorMessage
|
||||
};
|
||||
|
||||
public class ScenarioBindingConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
Scenario s = value as Scenario;
|
||||
return (MainPage.Current.Scenarios.IndexOf(s) + 1) + ") " + s.Title;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
IgnorableNamespaces="uap mp">
|
||||
|
||||
<Identity
|
||||
Name="Microsoft.SDKSamples.DataVirtualization.CS"
|
||||
Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="2084f1ac-a7cf-4c1d-a82b-d9a560ab5b48" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>Data Virtualization C# Sample</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo-sdk.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="DataVirtualization.App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="DataVirtualization.App">
|
||||
<uap:VisualElements
|
||||
DisplayName="Data Virtualization C# Sample"
|
||||
Square150x150Logo="Assets\squareTile-sdk.png"
|
||||
Square44x44Logo="Assets\SmallTile-sdk.png"
|
||||
Description="Data Virtualization C# Sample"
|
||||
BackgroundColor="#00b2f0">
|
||||
<uap:SplashScreen Image="Assets\Splash-sdk.png" />
|
||||
<uap:DefaultTile>
|
||||
<uap:ShowNameOnTiles>
|
||||
<uap:ShowOn Tile="square150x150Logo" />
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<uap:Capability Name="picturesLibrary"/>
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,35 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using DataVirtualizationSample;
|
||||
|
||||
namespace SDKTemplate
|
||||
{
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
public const string FEATURE_NAME = "Data Virtualization Sample";
|
||||
|
||||
List<Scenario> scenarios = new List<Scenario>
|
||||
{
|
||||
new Scenario() { Title="Data Virtualization", ClassType=typeof(Scenario1)},
|
||||
new Scenario() { Title="Selection Management", ClassType=typeof(Scenario2)},
|
||||
};
|
||||
}
|
||||
|
||||
public class Scenario
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public Type ClassType { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Search;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
//********************************************************************************************
|
||||
//*
|
||||
//* Note: This sample uses a custom compiler constant to enable tracing. If you add
|
||||
//* TRACE_DATASOURCE to the Conditional compilation symbols of the Build tab of the
|
||||
//* Project Properties window, then the application will spit out trace data to the
|
||||
//* Output window while debugging.
|
||||
//*
|
||||
//********************************************************************************************
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A custom datasource over the file system that supports data virtualization
|
||||
/// </summary>
|
||||
class FileDataSource : INotifyCollectionChanged, System.Collections.IList, IItemsRangeInfo
|
||||
{
|
||||
// Folder that we are browsing
|
||||
private StorageFolder _folder;
|
||||
// Query object that will tell us if the folder content changed
|
||||
private StorageFileQueryResult _queryResult;
|
||||
// Dispatcher so we can marshal calls back to the UI thread
|
||||
private CoreDispatcher _dispatcher;
|
||||
|
||||
// Cache for the file data that is currently being used
|
||||
private ItemCacheManager<FileItem> itemCache;
|
||||
|
||||
// Total number of files available
|
||||
private int _count = 1;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
private FileDataSource()
|
||||
{
|
||||
//Setup the dispatcher for the UI thread
|
||||
_dispatcher = Windows.UI.Xaml.Window.Current.Dispatcher;
|
||||
|
||||
// The ItemCacheManager does most of the heavy lifting. We pass it a callback that it will use to actually fetch data, and the max size of a request
|
||||
this.itemCache = new ItemCacheManager<FileItem>(fetchDataCallback, 50);
|
||||
this.itemCache.CacheChanged += ItemCache_CacheChanged;
|
||||
}
|
||||
|
||||
// Factory method to create the datasource
|
||||
// Requires async work which is why it needs a factory rather than being part of the constructor
|
||||
public static async Task<FileDataSource> GetDataSoure(string path)
|
||||
{
|
||||
FileDataSource ds = new FileDataSource();
|
||||
StorageFolder f = await StorageFolder.GetFolderFromPathAsync(path);
|
||||
await ds.SetFolder(f);
|
||||
return ds;
|
||||
}
|
||||
|
||||
// Set functionality for the folder
|
||||
public async Task SetFolder(StorageFolder folder)
|
||||
{
|
||||
//unhook the old contents changed event if applicable
|
||||
if (_queryResult != null)
|
||||
{
|
||||
_queryResult.ContentsChanged -= QueryResult_ContentsChanged;
|
||||
}
|
||||
// Initialize the query and register for changes
|
||||
_folder = folder;
|
||||
QueryOptions options = new QueryOptions();
|
||||
// options.IndexerOption = IndexerOption.DoNotUseIndexer;
|
||||
_queryResult = _folder.CreateFileQueryWithOptions(options);
|
||||
_queryResult.ContentsChanged += QueryResult_ContentsChanged;
|
||||
await UpdateCount();
|
||||
}
|
||||
|
||||
// Handler for when the filesystem notifies us of a change to the file list
|
||||
private void QueryResult_ContentsChanged(IStorageQueryResultBase sender, object args)
|
||||
{
|
||||
// This callback can occur on a different thread so we need to marshal it back to the UI thread
|
||||
if (!_dispatcher.HasThreadAccess)
|
||||
{
|
||||
var t = _dispatcher.RunAsync(CoreDispatcherPriority.Normal, ResetCollection);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetCollection();
|
||||
}
|
||||
}
|
||||
|
||||
// Handles a change notification for the list of files from the OS
|
||||
private void ResetCollection()
|
||||
{
|
||||
// Unhook the old change notification
|
||||
if (itemCache != null)
|
||||
{
|
||||
this.itemCache.CacheChanged -= ItemCache_CacheChanged;
|
||||
}
|
||||
|
||||
// Create a new instance of the cache manager
|
||||
this.itemCache = new ItemCacheManager<FileItem>(fetchDataCallback, 50);
|
||||
this.itemCache.CacheChanged += ItemCache_CacheChanged;
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
async Task UpdateCount()
|
||||
{
|
||||
_count = (int)await _queryResult.GetItemCountAsync();
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
#region IList Implementation
|
||||
|
||||
public bool Contains(object value)
|
||||
{
|
||||
return IndexOf(value) != -1;
|
||||
}
|
||||
|
||||
public int IndexOf(object value)
|
||||
{
|
||||
return (value != null) ? itemCache.IndexOf((FileItem)value) : -1;
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
// The cache will return null if it doesn't have the item. Once the item is fetched it will fire a changed event so that we can inform the list control
|
||||
return itemCache[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
get { return _count; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
//Required for the IItemsRangeInfo interface
|
||||
public void Dispose()
|
||||
{
|
||||
itemCache = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary method for IItemsRangeInfo interface
|
||||
/// Is called when the list control's view is changed
|
||||
/// </summary>
|
||||
/// <param name="visibleRange">The range of items that are actually visible</param>
|
||||
/// <param name="trackedItems">Additional set of ranges that the list is using, for example the buffer regions and focussed element</param>
|
||||
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList<ItemIndexRange> trackedItems)
|
||||
{
|
||||
#if TRACE_DATASOURCE
|
||||
string s = string.Format("* RangesChanged fired: Visible {0}->{1}", visibleRange.FirstIndex, visibleRange.LastIndex);
|
||||
foreach (ItemIndexRange r in trackedItems) { s += string.Format(" {0}->{1}", r.FirstIndex, r.LastIndex); }
|
||||
Debug.WriteLine(s);
|
||||
#endif
|
||||
// We know that the visible range is included in the broader range so don't need to hand it to the UpdateRanges call
|
||||
// Update the cache of items based on the new set of ranges. It will callback for additional data if required
|
||||
itemCache.UpdateRanges(trackedItems.ToArray());
|
||||
}
|
||||
|
||||
// Callback from itemcache that it needs items to be retrieved
|
||||
// Using this callback model abstracts the details of this specific datasource from the cache implementation
|
||||
private async Task<FileItem[]> fetchDataCallback(ItemIndexRange batch, CancellationToken ct)
|
||||
{
|
||||
// Fetch file objects from filesystem
|
||||
IReadOnlyList<StorageFile> results = await _queryResult.GetFilesAsync((uint)batch.FirstIndex, Math.Max(batch.Length, 20)).AsTask(ct);
|
||||
List<FileItem> files = new List<FileItem>();
|
||||
if (results != null)
|
||||
{
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
// Check if request has been cancelled, if so abort getting additional data
|
||||
ct.ThrowIfCancellationRequested();
|
||||
// Create our FileItem object with the file data and thumbnail
|
||||
FileItem newItem = await FileItem.fromStorageFile(results[i], ct);
|
||||
files.Add(newItem);
|
||||
}
|
||||
}
|
||||
return files.ToArray();
|
||||
}
|
||||
|
||||
// Event fired when items are inserted in the cache
|
||||
// Used to fire our collection changed event
|
||||
private void ItemCache_CacheChanged(object sender, CacheChangedEventArgs<FileItem> args)
|
||||
{
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, args.oldItem, args.newItem, args.itemIndex));
|
||||
}
|
||||
}
|
||||
|
||||
#region Parts of IList Not Implemented
|
||||
|
||||
public int Add(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Insert(int index, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Remove(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public System.Collections.IEnumerator GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using System.Diagnostics;
|
||||
using Windows.Storage.Search;
|
||||
using Windows.Storage.FileProperties;
|
||||
using Windows.UI.Core;
|
||||
using System.Threading;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a file and its properties
|
||||
/// </summary>
|
||||
class FileItem
|
||||
{
|
||||
public string Filename { get; set; }
|
||||
public int Size { get; set; }
|
||||
public BitmapImage ImageData { get; set; }
|
||||
|
||||
public string Key { get; private set; }
|
||||
|
||||
// Needed to ensure only one request is in progress at once
|
||||
private static SemaphoreSlim gettingFileProperties = new SemaphoreSlim(1);
|
||||
|
||||
// Fetches all the data for the specified file
|
||||
public async static Task<FileItem> fromStorageFile(StorageFile f, CancellationToken ct)
|
||||
{
|
||||
FileItem item = new FileItem();
|
||||
item.Filename = f.DisplayName;
|
||||
|
||||
// Block to make sure we only have one request outstanding
|
||||
await gettingFileProperties.WaitAsync();
|
||||
|
||||
BasicProperties bp = null;
|
||||
try
|
||||
{
|
||||
bp = await f.GetBasicPropertiesAsync().AsTask(ct);
|
||||
}
|
||||
catch (Exception) { }
|
||||
finally
|
||||
{
|
||||
gettingFileProperties.Release();
|
||||
}
|
||||
|
||||
ct.ThrowIfCancellationRequested();
|
||||
|
||||
item.Size = (int)bp.Size;
|
||||
item.Key = f.FolderRelativeId;
|
||||
|
||||
StorageItemThumbnail thumb = await f.GetThumbnailAsync(ThumbnailMode.SingleItem).AsTask(ct);
|
||||
ct.ThrowIfCancellationRequested();
|
||||
BitmapImage img = new BitmapImage();
|
||||
await img.SetSourceAsync(thumb).AsTask(ct);
|
||||
|
||||
item.ImageData = img;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
// EventArgs class for the CacheChanged event
|
||||
public class CacheChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
public T oldItem { get; set; }
|
||||
public T newItem { get; set; }
|
||||
public int itemIndex { get; set; }
|
||||
}
|
||||
|
||||
// Implements a relatively simple cache for items based on a set of ranges
|
||||
class ItemCacheManager<T>
|
||||
{
|
||||
// data structure to hold all the items that are in the ranges the cache manager is looking after
|
||||
private List<CacheEntryBlock<T>> cacheBlocks;
|
||||
|
||||
// List of ranges for items that are not present in the cache
|
||||
internal ItemIndexRangeList requests;
|
||||
|
||||
// list of ranges for items that are present in the cache
|
||||
private ItemIndexRangeList cachedResults;
|
||||
// Range of items that is currently being requested
|
||||
private ItemIndexRange requestInProgress;
|
||||
// Used to be able to cancel outstanding requests
|
||||
private CancellationTokenSource cancelTokenSource;
|
||||
// Callback that will be used to request data
|
||||
private fetchDataCallbackHandler fetchDataCallback;
|
||||
// Maximum number of items that can be fetched in one batch
|
||||
private int maxBatchFetchSize;
|
||||
// Timer to optimize the the fetching of data so we throttle requests if the list is still changing
|
||||
private Windows.UI.Xaml.DispatcherTimer timer;
|
||||
|
||||
#if DEBUG
|
||||
// Name for trace messages, and when debugging so you know which instance of the cache manager you are dealing with
|
||||
string debugName = string.Empty;
|
||||
#endif
|
||||
public ItemCacheManager(fetchDataCallbackHandler callback, int batchsize = 50, string debugName = "ItemCacheManager")
|
||||
{
|
||||
cacheBlocks = new List<CacheEntryBlock<T>>();
|
||||
requests = new ItemIndexRangeList();
|
||||
cachedResults = new ItemIndexRangeList();
|
||||
fetchDataCallback = callback;
|
||||
maxBatchFetchSize = batchsize;
|
||||
//set up a timer that is used to delay fetching data so that we can catch up if the list is scrolling fast
|
||||
timer = new Windows.UI.Xaml.DispatcherTimer();
|
||||
timer.Tick += (sender, args) =>
|
||||
{
|
||||
fetchData();
|
||||
};
|
||||
timer.Interval = new TimeSpan(20 * 10000);
|
||||
|
||||
#if DEBUG
|
||||
this.debugName = debugName;
|
||||
#endif
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine(debugName + "* Cache initialized/reset");
|
||||
#endif
|
||||
}
|
||||
|
||||
public event TypedEventHandler<object, CacheChangedEventArgs<T>> CacheChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Indexer for access to the item cache
|
||||
/// </summary>
|
||||
/// <param name="index">Item Index</param>
|
||||
/// <returns></returns>
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
// iterates through the cache blocks to find the item
|
||||
foreach (CacheEntryBlock<T> block in cacheBlocks)
|
||||
{
|
||||
if (index >= block.FirstIndex && index <= block.lastIndex)
|
||||
{
|
||||
return block.Items[index - block.FirstIndex];
|
||||
}
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
set
|
||||
{
|
||||
// iterates through the cache blocks to find the right block
|
||||
for (int i = 0; i < cacheBlocks.Count; i++)
|
||||
{
|
||||
CacheEntryBlock<T> block = cacheBlocks[i];
|
||||
if (index >= block.FirstIndex && index <= block.lastIndex)
|
||||
{
|
||||
block.Items[index - block.FirstIndex] = value;
|
||||
//register that we have the result in the cache
|
||||
if (value != null) { cachedResults.Add((uint)index, 1); }
|
||||
return;
|
||||
}
|
||||
// We have moved past the block where the item is supposed to live
|
||||
if (block.FirstIndex > index)
|
||||
{
|
||||
AddOrExtendBlock(index, value, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No blocks exist, so creating a new block
|
||||
AddOrExtendBlock(index, value, cacheBlocks.Count);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Extends an existing block if the item fits at the end, or creates a new block
|
||||
private void AddOrExtendBlock(int index, T value, int insertBeforeBlock)
|
||||
{
|
||||
if (insertBeforeBlock > 0)
|
||||
{
|
||||
CacheEntryBlock<T> block = cacheBlocks[insertBeforeBlock - 1];
|
||||
if (block.lastIndex == index - 1)
|
||||
{
|
||||
T[] newItems = new T[block.Length + 1];
|
||||
Array.Copy(block.Items, newItems, (int)block.Length);
|
||||
newItems[block.Length] = value;
|
||||
block.Length++;
|
||||
block.Items = newItems;
|
||||
return;
|
||||
}
|
||||
}
|
||||
CacheEntryBlock<T> newBlock = new CacheEntryBlock<T>() { FirstIndex = index, Length = 1, Items = new T[] { value } };
|
||||
cacheBlocks.Insert(insertBeforeBlock, newBlock);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the desired item range of the cache, discarding items that are not needed, and figuring out which items need to be requested. It will then kick off a fetch if required.
|
||||
/// </summary>
|
||||
/// <param name="ranges">New set of ranges the cache should hold</param>
|
||||
public void UpdateRanges(ItemIndexRange[] ranges)
|
||||
{
|
||||
//Normalize ranges to get a unique set of discontinuous ranges
|
||||
ranges = NormalizeRanges(ranges);
|
||||
|
||||
// Fail fast if the ranges haven't changed
|
||||
if (!HasRangesChanged(ranges)) { return; }
|
||||
|
||||
//To make the cache update easier, we'll create a new set of CacheEntryBlocks
|
||||
List<CacheEntryBlock<T>> newCacheBlocks = new List<CacheEntryBlock<T>>();
|
||||
foreach (ItemIndexRange range in ranges)
|
||||
{
|
||||
CacheEntryBlock<T> newBlock = new CacheEntryBlock<T>() { FirstIndex = range.FirstIndex, Length = range.Length, Items = new T[range.Length] };
|
||||
newCacheBlocks.Add(newBlock);
|
||||
}
|
||||
|
||||
#if TRACE_DATASOURCE
|
||||
string s = "┌ " + debugName + ".UpdateRanges: ";
|
||||
foreach (ItemIndexRange range in ranges)
|
||||
{
|
||||
s += range.FirstIndex + "->" + range.LastIndex + " ";
|
||||
}
|
||||
Debug.WriteLine(s);
|
||||
#endif
|
||||
//Copy over data to the new cache blocks from the old ones where there is overlap
|
||||
int lastTransferred = 0;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
CacheEntryBlock<T> newBlock = newCacheBlocks[i];
|
||||
ItemIndexRange range = ranges[i];
|
||||
int j = lastTransferred;
|
||||
while (j < this.cacheBlocks.Count && this.cacheBlocks[j].FirstIndex <= ranges[i].LastIndex)
|
||||
{
|
||||
ItemIndexRange overlap, oldEntryRange;
|
||||
ItemIndexRange[] added, removed;
|
||||
CacheEntryBlock<T> oldBlock = this.cacheBlocks[j];
|
||||
oldEntryRange = new ItemIndexRange(oldBlock.FirstIndex, oldBlock.Length);
|
||||
bool hasOverlap = oldEntryRange.DiffRanges(range, out overlap, out removed, out added);
|
||||
if (hasOverlap)
|
||||
{
|
||||
Array.Copy(oldBlock.Items, overlap.FirstIndex - oldBlock.FirstIndex, newBlock.Items, overlap.FirstIndex - range.FirstIndex, (int)overlap.Length);
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine("│ Transfering cache items " + overlap.FirstIndex + "->" + overlap.LastIndex);
|
||||
#endif
|
||||
}
|
||||
j++;
|
||||
if (ranges.Length > i + 1 && oldBlock.lastIndex < ranges[i + 1].FirstIndex) { lastTransferred = j; }
|
||||
}
|
||||
}
|
||||
//swap over to the new cache
|
||||
this.cacheBlocks = newCacheBlocks;
|
||||
|
||||
//figure out what items need to be fetched because we don't have them in the cache
|
||||
this.requests = new ItemIndexRangeList(ranges);
|
||||
ItemIndexRangeList newCachedResults = new ItemIndexRangeList();
|
||||
|
||||
// Use the previous knowlege of what we have cached to form the new list
|
||||
foreach (ItemIndexRange range in ranges)
|
||||
{
|
||||
foreach (ItemIndexRange cached in this.cachedResults)
|
||||
{
|
||||
ItemIndexRange overlap;
|
||||
ItemIndexRange[] added, removed;
|
||||
bool hasOverlap = cached.DiffRanges(range, out overlap, out removed, out added);
|
||||
if (hasOverlap) { newCachedResults.Add(overlap); }
|
||||
}
|
||||
}
|
||||
// remove the data we know we have cached from the results
|
||||
foreach (ItemIndexRange range in newCachedResults)
|
||||
{
|
||||
this.requests.Subtract(range);
|
||||
}
|
||||
this.cachedResults = newCachedResults;
|
||||
|
||||
startFetchData();
|
||||
|
||||
#if TRACE_DATASOURCE
|
||||
s = "└ Pending requests: ";
|
||||
foreach (ItemIndexRange range in this.requests)
|
||||
{
|
||||
s += range.FirstIndex + "->" + range.LastIndex + " ";
|
||||
}
|
||||
Debug.WriteLine(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compares the new ranges against the previous ones to see if they have changed
|
||||
private bool HasRangesChanged(ItemIndexRange[] ranges)
|
||||
{
|
||||
if (ranges.Length != cacheBlocks.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
ItemIndexRange r = ranges[i];
|
||||
CacheEntryBlock<T> block = this.cacheBlocks[i];
|
||||
if (r.FirstIndex != block.FirstIndex || r.LastIndex != block.lastIndex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Gets the first block of items that we don't have values for
|
||||
public ItemIndexRange GetFirstRequestBlock(int maxsize = 50)
|
||||
{
|
||||
if (this.requests.Count > 0)
|
||||
{
|
||||
ItemIndexRange range = this.requests[0];
|
||||
if (range.Length > 50) range = new ItemIndexRange(range.FirstIndex, 50);
|
||||
return range;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// Throttling function for fetching data. Forces a wait of 20ms before making the request.
|
||||
// If another fetch is requested in that time, it will reset the timer, so we don't fetch data if the view is actively scrolling
|
||||
public void startFetchData()
|
||||
{
|
||||
// Verify if an active request is still needed
|
||||
if (this.requestInProgress != null)
|
||||
{
|
||||
if (this.requests.Intersects(requestInProgress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//cancel the existing request
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine("> " + debugName + " Cancelling request: " + requestInProgress.FirstIndex + "->" + requestInProgress.LastIndex);
|
||||
#endif
|
||||
cancelTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
//Using a timer to delay fetching data by 20ms, if another range comes in that time, then the timer is reset.
|
||||
timer.Stop();
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public delegate Task<T[]> fetchDataCallbackHandler(ItemIndexRange range, CancellationToken ct);
|
||||
|
||||
// Called by the timer to make a request for data
|
||||
public async void fetchData()
|
||||
{
|
||||
//Stop the timer so we don't get fired again unless data is requested
|
||||
timer.Stop();
|
||||
if (this.requestInProgress != null)
|
||||
{
|
||||
// Verify if an active request is still needed
|
||||
if (this.requests.Intersects(requestInProgress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cancel the existing request
|
||||
Debug.WriteLine(">" + debugName + " Cancelling request: " + requestInProgress.FirstIndex + "->" + requestInProgress.LastIndex);
|
||||
cancelTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
ItemIndexRange nextRequest = GetFirstRequestBlock(maxBatchFetchSize);
|
||||
if (nextRequest != null)
|
||||
{
|
||||
cancelTokenSource = new CancellationTokenSource();
|
||||
CancellationToken ct = cancelTokenSource.Token;
|
||||
requestInProgress = nextRequest;
|
||||
T[] data = null;
|
||||
try
|
||||
{
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine(">" + debugName + " Fetching items " + nextRequest.FirstIndex + "->" + nextRequest.LastIndex);
|
||||
#endif
|
||||
// Use the callback to get the data, passing in a cancellation token
|
||||
data = await fetchDataCallback(nextRequest, ct);
|
||||
|
||||
if (!ct.IsCancellationRequested)
|
||||
{
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine(">" + debugName + " Inserting items into cache at: " + nextRequest.FirstIndex + "->" + (nextRequest.FirstIndex + data.Length - 1));
|
||||
#endif
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
int cacheIndex = (int)(nextRequest.FirstIndex + i);
|
||||
|
||||
T oldItem = this[cacheIndex];
|
||||
T newItem = data[i];
|
||||
|
||||
if (!newItem.Equals(oldItem))
|
||||
{
|
||||
this[cacheIndex] = newItem;
|
||||
|
||||
// Fire CacheChanged so that the datasource can fire its INCC event, and do other work based on the item having data
|
||||
if (CacheChanged != null)
|
||||
{
|
||||
CacheChanged(this, new CacheChangedEventArgs<T>() { oldItem = oldItem, newItem = newItem, itemIndex = cacheIndex });
|
||||
}
|
||||
}
|
||||
}
|
||||
requests.Subtract(new ItemIndexRange(nextRequest.FirstIndex, (uint)data.Length));
|
||||
}
|
||||
}
|
||||
// Try/Catch is needed as cancellation is via an exception
|
||||
catch (OperationCanceledException) { }
|
||||
finally
|
||||
{
|
||||
requestInProgress = null;
|
||||
// Start another request if required
|
||||
fetchData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Merges a set of ranges to form a new set of non-contiguous ranges
|
||||
/// </summary>
|
||||
/// <param name="ranges">The list of ranges to merge</param>
|
||||
/// <returns>A smaller set of merged ranges</returns>
|
||||
private ItemIndexRange[] NormalizeRanges(ItemIndexRange[] ranges)
|
||||
{
|
||||
List<ItemIndexRange> results = new List<ItemIndexRange>();
|
||||
foreach (ItemIndexRange range in ranges)
|
||||
{
|
||||
bool handled = false;
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
ItemIndexRange existing = results[i];
|
||||
if (range.ContiguousOrOverlaps(existing))
|
||||
{
|
||||
results[i] = existing.Combine(range);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
else if (range.FirstIndex < existing.FirstIndex)
|
||||
{
|
||||
results.Insert(i, range);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) { results.Add(range); }
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
|
||||
// Sees if the value is in our cache if so it returns the index
|
||||
public int IndexOf(T value)
|
||||
{
|
||||
foreach (CacheEntryBlock<T> entry in cacheBlocks)
|
||||
{
|
||||
int index = Array.IndexOf<T>(entry.Items, value);
|
||||
if (index != -1) return index + entry.FirstIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Type for the cache blocks
|
||||
class CacheEntryBlock<ITEMTYPE>
|
||||
{
|
||||
public int FirstIndex;
|
||||
public uint Length;
|
||||
public ITEMTYPE[] Items;
|
||||
|
||||
public int lastIndex { get { return FirstIndex + (int)Length - 1; } }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using System.Diagnostics;
|
||||
using Windows.Storage.Search;
|
||||
using Windows.Storage.FileProperties;
|
||||
using Windows.UI.Core;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
// Extension methods for ItemIndexRange
|
||||
public static class ItemIndexRangeExtensions
|
||||
{
|
||||
public static bool Equals(this ItemIndexRange This, ItemIndexRange range)
|
||||
{
|
||||
return (This.FirstIndex == range.FirstIndex && This.Length == range.Length);
|
||||
}
|
||||
|
||||
public static bool ContiguousOrOverlaps(this ItemIndexRange This, ItemIndexRange range)
|
||||
{
|
||||
return (range.FirstIndex >= This.FirstIndex && range.FirstIndex <= This.LastIndex + 1) || (range.LastIndex + 1 >= This.FirstIndex && range.LastIndex <= This.LastIndex);
|
||||
}
|
||||
|
||||
public static bool Intersects(this ItemIndexRange This, ItemIndexRange range)
|
||||
{
|
||||
return (range.FirstIndex >= This.FirstIndex && range.FirstIndex <= This.LastIndex) || (range.LastIndex >= This.FirstIndex && range.LastIndex <= This.LastIndex);
|
||||
}
|
||||
|
||||
public static bool Intersects(this ItemIndexRange This, int FirstIndex, uint Length)
|
||||
{
|
||||
int LastIndex = FirstIndex + (int)Length - 1;
|
||||
return (FirstIndex >= This.FirstIndex && FirstIndex <= This.LastIndex) || (LastIndex >= This.FirstIndex && LastIndex <= This.LastIndex);
|
||||
}
|
||||
|
||||
public static ItemIndexRange Combine(this ItemIndexRange This, ItemIndexRange range)
|
||||
{
|
||||
int start = Math.Min(This.FirstIndex, range.FirstIndex);
|
||||
int end = Math.Max(This.LastIndex, range.LastIndex);
|
||||
|
||||
return new ItemIndexRange(start, 1 + (uint)Math.Abs(end - start));
|
||||
}
|
||||
|
||||
public static bool DiffRanges(this ItemIndexRange RangeA, ItemIndexRange RangeB, out ItemIndexRange InBothAandB, out ItemIndexRange[] OnlyInRangeA, out ItemIndexRange[] OnlyInRangeB)
|
||||
{
|
||||
List<ItemIndexRange> exA = new List<ItemIndexRange>();
|
||||
List<ItemIndexRange> exB = new List<ItemIndexRange>();
|
||||
int i, j;
|
||||
i = Math.Max(RangeA.FirstIndex, RangeB.FirstIndex);
|
||||
j = Math.Min(RangeA.LastIndex, RangeB.LastIndex);
|
||||
|
||||
if (i <= j)
|
||||
{
|
||||
// Ranges intersect
|
||||
InBothAandB = new ItemIndexRange(i, (uint)(1 + j - i));
|
||||
if (RangeA.FirstIndex < i) exA.Add(new ItemIndexRange(RangeA.FirstIndex, (uint)(i - RangeA.FirstIndex)));
|
||||
if (RangeA.LastIndex > j) exA.Add(new ItemIndexRange(j + 1, (uint)(RangeA.LastIndex - j)));
|
||||
if (RangeB.FirstIndex < i) exB.Add(new ItemIndexRange(RangeB.FirstIndex, (uint)(i - RangeB.FirstIndex)));
|
||||
if (RangeB.LastIndex > j) exB.Add(new ItemIndexRange(j + 1, (uint)(RangeB.LastIndex - j)));
|
||||
OnlyInRangeA = exA.ToArray();
|
||||
OnlyInRangeB = exB.ToArray();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
InBothAandB = default(ItemIndexRange);
|
||||
OnlyInRangeA = new ItemIndexRange[] { RangeA };
|
||||
OnlyInRangeB = new ItemIndexRange[] { RangeB };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemIndexRange Overlap(this ItemIndexRange RangeA, ItemIndexRange RangeB)
|
||||
{
|
||||
int i, j;
|
||||
i = Math.Max(RangeA.FirstIndex, RangeB.FirstIndex);
|
||||
j = Math.Min(RangeA.LastIndex, RangeB.LastIndex);
|
||||
|
||||
if (i <= j)
|
||||
{
|
||||
// Ranges intersect
|
||||
return new ItemIndexRange(i, (uint)(1 + j - i));
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sorted collection of discontiguous ItemIndexRanges
|
||||
///
|
||||
/// </summary>
|
||||
class ItemIndexRangeList : IList<ItemIndexRange>
|
||||
{
|
||||
private List<ItemIndexRange> _ranges;
|
||||
|
||||
|
||||
public ItemIndexRangeList()
|
||||
{
|
||||
this._ranges = new List<ItemIndexRange>();
|
||||
}
|
||||
|
||||
public ItemIndexRangeList(List<ItemIndexRange> ranges)
|
||||
{
|
||||
this._ranges = NormalizeRanges(ranges);
|
||||
}
|
||||
|
||||
public ItemIndexRangeList(ItemIndexRange[] ranges)
|
||||
{
|
||||
this._ranges = NormalizeRanges(ranges);
|
||||
}
|
||||
|
||||
public List<ItemIndexRange> ToList()
|
||||
{
|
||||
return this._ranges;
|
||||
}
|
||||
|
||||
public ItemIndexRange[] ToArray()
|
||||
{
|
||||
return this._ranges.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the range into the rangelist, combining with existing ranges if necessary
|
||||
/// </summary>
|
||||
/// <param name="newrange">Range to merge into the collection</param>
|
||||
public void Add(ItemIndexRange newrange)
|
||||
{
|
||||
for (int i = 0; i < this._ranges.Count; i++)
|
||||
{
|
||||
ItemIndexRange existing = this._ranges[i];
|
||||
if (newrange.ContiguousOrOverlaps(existing))
|
||||
{
|
||||
existing = existing.Combine(newrange);
|
||||
for (int j = i + 1; j < this._ranges.Count; j++)
|
||||
{
|
||||
ItemIndexRange next = this._ranges[j];
|
||||
if (existing.ContiguousOrOverlaps(next))
|
||||
{
|
||||
existing = existing.Combine(next);
|
||||
this._ranges.RemoveAt(i + 1);
|
||||
}
|
||||
}
|
||||
this._ranges[i] = existing;
|
||||
return;
|
||||
}
|
||||
else if (newrange.LastIndex < existing.FirstIndex)
|
||||
{
|
||||
this._ranges.Insert(i, newrange);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._ranges.Add(newrange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges the range into the rangelist, combining with existing ranges if necessary
|
||||
/// </summary>
|
||||
public void Add(uint FirstIndex, uint Length)
|
||||
{
|
||||
this.Add(new ItemIndexRange((int)FirstIndex, Length));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a range from the collection, splitting existing ranges if necessary
|
||||
/// </summary>
|
||||
public void Subtract(ItemIndexRange range)
|
||||
{
|
||||
for (int idx = 0; idx < this._ranges.Count; idx++)
|
||||
{
|
||||
ItemIndexRange existing = this._ranges[idx];
|
||||
if (existing.FirstIndex > range.LastIndex) return;
|
||||
|
||||
int i, j;
|
||||
i = Math.Max(existing.FirstIndex, range.FirstIndex);
|
||||
j = Math.Min(existing.LastIndex, range.LastIndex);
|
||||
|
||||
if (i <= j)
|
||||
{
|
||||
if (existing.FirstIndex < i && existing.LastIndex > j)
|
||||
{
|
||||
//range is in the middle of existing range, so split existing into two
|
||||
this._ranges[idx] = (new ItemIndexRange(existing.FirstIndex, (uint)(i - existing.FirstIndex)));
|
||||
this._ranges.Insert(idx + 1, new ItemIndexRange(j + 1, (uint)(existing.LastIndex - j)));
|
||||
return;
|
||||
}
|
||||
else if (existing.LastIndex > j)
|
||||
{
|
||||
//range ends before existing so trim existing to be the remainder
|
||||
this._ranges[idx] = new ItemIndexRange(j + 1, (uint)(existing.LastIndex - j));
|
||||
return;
|
||||
}
|
||||
else if (existing.FirstIndex < i)
|
||||
{
|
||||
//range starts after existing so trim existing to the part before range
|
||||
this._ranges[idx] = new ItemIndexRange(existing.FirstIndex, (uint)(i - existing.FirstIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
//existing is overlapped by range, so remove it.
|
||||
this._ranges.RemoveAt(idx);
|
||||
}
|
||||
//trim the subtracted range to the remainder, and exit if complete
|
||||
if (range.LastIndex > j)
|
||||
{
|
||||
range = new ItemIndexRange(j + 1, (uint)(range.LastIndex - j));
|
||||
}
|
||||
else { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Subtract(uint FirstIndex, uint Length)
|
||||
{
|
||||
this.Subtract(new ItemIndexRange((int)FirstIndex, Length));
|
||||
}
|
||||
|
||||
public bool Intersects(ItemIndexRange range)
|
||||
{
|
||||
foreach (ItemIndexRange r in this._ranges)
|
||||
{
|
||||
if (r.Intersects(range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Merges contiguous or overlapping ranges to ensure the collection is discontiguous
|
||||
// Also sorts the ranges so they start in index order
|
||||
private List<ItemIndexRange> NormalizeRanges(IEnumerable<ItemIndexRange> ranges)
|
||||
{
|
||||
List<ItemIndexRange> results = new List<ItemIndexRange>();
|
||||
foreach (ItemIndexRange range in ranges)
|
||||
{
|
||||
bool handled = false;
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
ItemIndexRange existing = results[i];
|
||||
if (range.ContiguousOrOverlaps(existing))
|
||||
{
|
||||
results[i] = existing.Combine(range);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
else if (range.FirstIndex < existing.FirstIndex)
|
||||
{
|
||||
results.Insert(i, range);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!handled) results.Add(range);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
#region IList implementation
|
||||
public int Count { get { return ((IList<ItemIndexRange>)_ranges).Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return ((IList<ItemIndexRange>)_ranges).IsReadOnly; } }
|
||||
|
||||
public ItemIndexRange this[int index]
|
||||
{
|
||||
get { return ((IList<ItemIndexRange>)_ranges)[index]; }
|
||||
set { ((IList<ItemIndexRange>)_ranges)[index] = value; }
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator<ItemIndexRange> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<ItemIndexRange>)_ranges).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<ItemIndexRange>)_ranges).GetEnumerator();
|
||||
}
|
||||
|
||||
public int IndexOf(ItemIndexRange item)
|
||||
{
|
||||
return ((IList<ItemIndexRange>)_ranges).IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert(int index, ItemIndexRange item)
|
||||
{
|
||||
((IList<ItemIndexRange>)_ranges).Insert(index, item);
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
((IList<ItemIndexRange>)_ranges).RemoveAt(index);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
((IList<ItemIndexRange>)_ranges).Clear();
|
||||
}
|
||||
|
||||
public bool Contains(ItemIndexRange item)
|
||||
{
|
||||
return ((IList<ItemIndexRange>)_ranges).Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(ItemIndexRange[] array, int arrayIndex)
|
||||
{
|
||||
((IList<ItemIndexRange>)_ranges).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(ItemIndexRange item)
|
||||
{
|
||||
return ((IList<ItemIndexRange>)_ranges).Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="DataVirtualizationSample.Scenario1"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:DataVirtualizationSample"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid" Margin="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,0,0,10">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
This scenario demonstartes data virtualization with a custom datasource over the contents of the default pictures folder. The datasource implements IItemsRangeInfo.RangesChanged to get notifications of the items the grid control is displaying. It will fetch and the cache the items the view needs, and update the cache as the view is changed.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<GridView x:Name="Grid1" SelectionMode="Single" ShowsScrollingPlaceholders="False">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:FileItem">
|
||||
<Grid Width="200" Height="80">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.RowSpan="3" Source="{x:Bind ImageData}" Width="70" Height="70" Margin="0,0,5,0" />
|
||||
<TextBlock Text="{x:Bind Filename}" Grid.Column="1" />
|
||||
<TextBlock Text="{x:Bind Size}" Grid.Column="1" Grid.Row="1"/>
|
||||
<TextBlock x:Name="idx" Grid.Column="1" Grid.Row="2"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</Grid>
|
||||
|
||||
<!-- Status Block for providing messages to the user. Use the
|
||||
NotifyUser() method to populate the message -->
|
||||
<Border x:Name="ErrorBorder" Background="Red" Grid.Row="2"/>
|
||||
<TextBlock x:Name="StatusBlock" Grid.Row="2" Margin="12, 10, 12, 10" Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,66 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using SDKTemplate;
|
||||
using Windows.Storage;
|
||||
|
||||
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Scenario1 : Page
|
||||
{
|
||||
|
||||
public Scenario1()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
Grid1.ContainerContentChanging += Grid1_ContainerContentChanging;
|
||||
initdata();
|
||||
}
|
||||
|
||||
private void Grid1_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
||||
{
|
||||
if (!args.InRecycleQueue)
|
||||
{
|
||||
// Sets a textblock in each item indicating its index
|
||||
FrameworkElement ctr = (FrameworkElement)args.ItemContainer.ContentTemplateRoot;
|
||||
if (ctr != null)
|
||||
{
|
||||
TextBlock t = (TextBlock)ctr.FindName("idx");
|
||||
t.Text = args.ItemIndex.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void initdata()
|
||||
{
|
||||
StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
|
||||
string path = pictures.SaveFolder.Path;
|
||||
|
||||
FileDataSource ds = await FileDataSource.GetDataSoure(path);
|
||||
if (ds.Count > 0)
|
||||
{
|
||||
Grid1.ItemsSource = ds;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainPage.Current.NotifyUser("Error: The pictures folder doesn't contain any files", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,405 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Search;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
//********************************************************************************************
|
||||
//*
|
||||
//* Note: This sample uses a custom compiler constant to enable tracing. If you add
|
||||
//* TRACE_DATASOURCE to the Conditional compilation symbols of the Build tab of the
|
||||
//* Project Properties window, then the application will spit out trace data to the
|
||||
//* Output window while debugging.
|
||||
//*
|
||||
//********************************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// A custom datasource over the file system that supports data virtualization
|
||||
/// </summary>
|
||||
class FileDataSource2 : INotifyCollectionChanged, System.Collections.IList, IItemsRangeInfo, ISelectionInfo
|
||||
{
|
||||
// Folder that we are browsing
|
||||
private StorageFolder _folder;
|
||||
// Query object that will tell us if the folder content changed
|
||||
private StorageFileQueryResult _queryResult;
|
||||
// Dispatcher so we can marshal calls back to the UI thread
|
||||
private CoreDispatcher _dispatcher;
|
||||
|
||||
// Cache for the file data that is currently being used
|
||||
private ItemCacheManager<FileItem> itemCache;
|
||||
|
||||
// Total number of files available
|
||||
private int _count = 1;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
private FileDataSource2()
|
||||
{
|
||||
//Setup the dispatcher for the UI thread
|
||||
_dispatcher = Windows.UI.Xaml.Window.Current.Dispatcher;
|
||||
|
||||
// The ItemCacheManager does most of the heavy lifting. We pass it a callback that it will use to actually fetch data, and the max size of a request
|
||||
this.itemCache = new ItemCacheManager<FileItem>(fetchDataCallback, 50);
|
||||
this.itemCache.CacheChanged += ItemCache_CacheChanged;
|
||||
|
||||
// Create another ItemCacheManager for the keys for selection
|
||||
this.selectionCache = new ItemCacheManager<string>(fetchSelectionDataCallback, 50, "selectionCache");
|
||||
}
|
||||
|
||||
// Handles a change notification for the list of files from the OS
|
||||
public async void ResetCollection()
|
||||
{
|
||||
// Update the selection cache to match the filesystem after the updates
|
||||
await RemapSelection();
|
||||
|
||||
// Unhook the old change notification
|
||||
if (itemCache != null)
|
||||
{
|
||||
this.itemCache.CacheChanged -= ItemCache_CacheChanged;
|
||||
}
|
||||
|
||||
// Create a new instance of the cache manager
|
||||
this.itemCache = new ItemCacheManager<FileItem>(fetchDataCallback, 50);
|
||||
this.itemCache.CacheChanged += ItemCache_CacheChanged;
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implementation of ISelectionInfo
|
||||
//
|
||||
|
||||
// Storage for the keys of the selected items
|
||||
private ItemCacheManager<string> selectionCache;
|
||||
// List of ranges that form the selection
|
||||
private ItemIndexRangeList selection = new ItemIndexRangeList();
|
||||
|
||||
// Called when an item (or items) are selected
|
||||
public void SelectRange(ItemIndexRange range)
|
||||
{
|
||||
selection.Add(range);
|
||||
selectionCache.UpdateRanges(selection.ToArray());
|
||||
}
|
||||
|
||||
// Called when an item (or items) are deselected
|
||||
public void DeselectRange(ItemIndexRange range)
|
||||
{
|
||||
selection.Subtract(range);
|
||||
selectionCache.UpdateRanges(selection.ToArray());
|
||||
}
|
||||
|
||||
// Called to determine if an item is selected
|
||||
public bool IsSelected(int index)
|
||||
{
|
||||
foreach (ItemIndexRange range in selection)
|
||||
{
|
||||
if (index >= range.FirstIndex && index <= range.LastIndex) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called to get the selected ranges
|
||||
public IReadOnlyList<ItemIndexRange> GetSelectedRanges()
|
||||
{
|
||||
return selection.ToList();
|
||||
}
|
||||
|
||||
// Callback from the selection cache manager
|
||||
// Retrieves the keys for selected items
|
||||
private async Task<string[]> fetchSelectionDataCallback(ItemIndexRange batch, CancellationToken ct)
|
||||
{
|
||||
#if TRACE_DATASOURCE
|
||||
Debug.WriteLine("# SelectionDataCallback: " + batch.FirstIndex + "->" + batch.LastIndex);
|
||||
#endif
|
||||
// See if we already have the item in the data cache, if so get the key from there so we don't need to go to the filesystem
|
||||
var file = itemCache[batch.FirstIndex];
|
||||
if (file != null)
|
||||
{
|
||||
return new string[] { file.Key };
|
||||
}
|
||||
|
||||
// Go get the keys from the file system if necessary
|
||||
IReadOnlyList<StorageFile> results = await _queryResult.GetFilesAsync((uint)batch.FirstIndex, batch.Length).AsTask(ct);
|
||||
List<string> keys = new List<string>();
|
||||
if (results != null)
|
||||
{
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
keys.Add(results[i].FolderRelativeId);
|
||||
}
|
||||
}
|
||||
return keys.ToArray();
|
||||
}
|
||||
|
||||
// Called after a reset to figure out the new indexes for the selected items
|
||||
private async Task RemapSelection()
|
||||
{
|
||||
ItemIndexRangeList oldSelection = selection;
|
||||
ItemCacheManager<string> oldSelectionCache = selectionCache;
|
||||
ItemIndexRangeList newSelection = new ItemIndexRangeList();
|
||||
ItemCacheManager<string> newSelectionCache = new ItemCacheManager<string>(fetchSelectionDataCallback, 50, "newSelectionCache");
|
||||
|
||||
foreach (ItemIndexRange r in oldSelection)
|
||||
{
|
||||
IReadOnlyList<StorageFile> results = null;
|
||||
int lastResultOffset = 0, lastResultsItemIndex = 0;
|
||||
for (int i = 0; i < r.Length; i++)
|
||||
{
|
||||
int origIndex = r.FirstIndex + i;
|
||||
string fileid = oldSelectionCache[origIndex];
|
||||
bool matched = false;
|
||||
|
||||
// Optimization to be able to work in batches. Once we get a batch of files from the filesystem we use that to hunt
|
||||
// for matches rather than having to go ask for the index of each file
|
||||
if (results != null)
|
||||
{
|
||||
for (int j = lastResultOffset + 1; j < results.Count; j++)
|
||||
{
|
||||
if (results[j].FolderRelativeId == fileid)
|
||||
{
|
||||
lastResultOffset = j;
|
||||
int itemIndex = lastResultsItemIndex + j;
|
||||
newSelection.Add((uint)itemIndex, 1);
|
||||
newSelectionCache[itemIndex] = oldSelectionCache[origIndex];
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
{
|
||||
// Get a starting point for the index of the file
|
||||
lastResultsItemIndex = (int)(await _queryResult.FindStartIndexAsync(fileid.Substring(fileid.LastIndexOf('\\') + 1)));
|
||||
lastResultOffset = 0;
|
||||
// Get the files at that point and see if the keys actually match
|
||||
results = await _queryResult.GetFilesAsync((uint)lastResultsItemIndex, 50);
|
||||
if (results[lastResultOffset].FolderRelativeId == fileid)
|
||||
{
|
||||
newSelection.Add((uint)lastResultsItemIndex, 1);
|
||||
newSelectionCache[lastResultsItemIndex] = oldSelectionCache[origIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't find the item, so its no longer part of the selection
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the fields for the new selection
|
||||
selection = newSelection;
|
||||
selectionCache = newSelectionCache;
|
||||
}
|
||||
|
||||
#region Filesystem specific implementation details
|
||||
|
||||
// Factory method to create the datasource
|
||||
// Requires async work which is why it needs a factory rather than being part of the constructor
|
||||
public static async Task<FileDataSource2> GetDataSoure(string path)
|
||||
{
|
||||
FileDataSource2 ds = new FileDataSource2();
|
||||
StorageFolder f = await StorageFolder.GetFolderFromPathAsync(path);
|
||||
await ds.SetFolder(f);
|
||||
return ds;
|
||||
}
|
||||
|
||||
// Set functionality for the folder
|
||||
public async Task SetFolder(StorageFolder folder)
|
||||
{
|
||||
//unhook the old contents changed event if applicable
|
||||
if (_queryResult != null)
|
||||
{
|
||||
_queryResult.ContentsChanged -= QueryResult_ContentsChanged;
|
||||
}
|
||||
// Initialize the query and register for changes
|
||||
_folder = folder;
|
||||
QueryOptions options = new QueryOptions();
|
||||
// options.IndexerOption = IndexerOption.DoNotUseIndexer;
|
||||
_queryResult = _folder.CreateFileQueryWithOptions(options);
|
||||
_queryResult.ContentsChanged += QueryResult_ContentsChanged;
|
||||
await UpdateCount();
|
||||
}
|
||||
|
||||
// Handler for when the filesystem notifies us of a change to the file list
|
||||
private void QueryResult_ContentsChanged(IStorageQueryResultBase sender, object args)
|
||||
{
|
||||
// This callback can occur on a different thread so we need to marshal it back to the UI thread
|
||||
if (!_dispatcher.HasThreadAccess)
|
||||
{
|
||||
var t = _dispatcher.RunAsync(CoreDispatcherPriority.Normal, ResetCollection);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetCollection();
|
||||
}
|
||||
}
|
||||
async Task UpdateCount()
|
||||
{
|
||||
_count = (int)await _queryResult.GetItemCountAsync();
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
}
|
||||
|
||||
//Event fired when items are inserted in the cache
|
||||
//Used to fire our collection changed event
|
||||
private void ItemCache_CacheChanged(object sender, CacheChangedEventArgs<FileItem> args)
|
||||
{
|
||||
if (CollectionChanged != null)
|
||||
{
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, args.oldItem, args.newItem, args.itemIndex));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IList Implementation
|
||||
|
||||
public bool Contains(object value)
|
||||
{
|
||||
return IndexOf(value) != -1;
|
||||
}
|
||||
|
||||
public int IndexOf(object value)
|
||||
{
|
||||
return (value != null) ? itemCache.IndexOf((FileItem)value) : -1;
|
||||
}
|
||||
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
// The cache will return null if it doesn't have the item. Once the item is fetched it will fire a changed event so that we can inform the list control
|
||||
return itemCache[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
get { return _count; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IItemsRangeInfo Implementation
|
||||
//Required for the IItemsRangeInfo interface
|
||||
public void Dispose()
|
||||
{
|
||||
itemCache = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Primary method for IItemsRangeInfo interface
|
||||
/// Is called when the list control's view is changed
|
||||
/// </summary>
|
||||
/// <param name="visibleRange">The range of items that are actually visible</param>
|
||||
/// <param name="trackedItems">Additional set of ranges that the list is using, for example the buffer regions and focussed element</param>
|
||||
public void RangesChanged(ItemIndexRange visibleRange, IReadOnlyList<ItemIndexRange> trackedItems)
|
||||
{
|
||||
#if TRACE_DATASOURCE
|
||||
string s = string.Format("* RangesChanged fired: Visible {0}->{1}", visibleRange.FirstIndex, visibleRange.LastIndex);
|
||||
foreach (ItemIndexRange r in trackedItems) { s += string.Format(" {0}->{1}", r.FirstIndex, r.LastIndex); }
|
||||
Debug.WriteLine(s);
|
||||
#endif
|
||||
// We know that the visible range is included in the broader range so don't need to hand it to the UpdateRanges call
|
||||
// Update the cache of items based on the new set of ranges. It will callback for additional data if required
|
||||
itemCache.UpdateRanges(trackedItems.ToArray());
|
||||
}
|
||||
|
||||
|
||||
// Callback from itemcache that it needs items to be retrieved
|
||||
private async Task<FileItem[]> fetchDataCallback(ItemIndexRange batch, CancellationToken ct)
|
||||
{
|
||||
// Fetch file objects from filesystem
|
||||
IReadOnlyList<StorageFile> results = await _queryResult.GetFilesAsync((uint)batch.FirstIndex, Math.Max(batch.Length, 20)).AsTask(ct);
|
||||
List<FileItem> files = new List<FileItem>();
|
||||
if (results != null)
|
||||
{
|
||||
for (int i = 0; i < results.Count; i++)
|
||||
{
|
||||
ct.ThrowIfCancellationRequested();
|
||||
// Create our FileItem object with the file data and thumbnail
|
||||
FileItem newItem = await FileItem.fromStorageFile(results[i], ct);
|
||||
files.Add(newItem);
|
||||
}
|
||||
}
|
||||
return files.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parts of IList Not Implemented
|
||||
|
||||
public int Add(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Insert(int index, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsFixedSize
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public void Remove(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsSynchronized
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public object SyncRoot
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
public System.Collections.IEnumerator GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
-->
|
||||
<Page
|
||||
x:Class="DataVirtualizationSample.Scenario2"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:DataVirtualizationSample"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="RootGrid" Margin="12,10,12,12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Margin="0,0,0,10">
|
||||
<TextBlock Text="Description:" Style="{StaticResource SampleHeaderTextStyle}"/>
|
||||
<TextBlock Style="{StaticResource ScenarioDescriptionTextStyle}" TextWrapping="Wrap">
|
||||
This scenario demonstrates selection with data virtualization with a custom datasource over the contents of the default pictures folder. The datasource implements ISelectionInfo to provide custom handling of selection. The datasource builds on top of the functionality in Scenario 1 implementing the IItemsRangeInfo interface.
|
||||
</TextBlock>
|
||||
<Button x:Name="ForceReset" Click="ForceReset_Click">Force a reset</Button>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<GridView x:Name="Grid1" SelectionMode="Extended" ShowsScrollingPlaceholders="False">
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:FileItem">
|
||||
<Grid Width="200" Height="80">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Image Grid.RowSpan="3" Source="{x:Bind ImageData}" Width="70" Height="70" Margin="0,0,5,0" />
|
||||
<TextBlock Text="{x:Bind Filename}" Grid.Column="1" />
|
||||
<TextBlock Text="{x:Bind Size}" Grid.Column="1" Grid.Row="1"/>
|
||||
<TextBlock x:Name="idx" Grid.Column="1" Grid.Row="2"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</Grid>
|
||||
|
||||
<!-- Status Block for providing messages to the user. Use the
|
||||
NotifyUser() method to populate the message -->
|
||||
<Border x:Name="ErrorBorder" Background="Red" Grid.Row="2"/>
|
||||
<TextBlock x:Name="StatusBlock" Grid.Row="2" Margin="12, 10, 12, 10" Visibility="Collapsed"/>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,65 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License (MIT).
|
||||
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
|
||||
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
|
||||
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using SDKTemplate;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace DataVirtualizationSample
|
||||
{
|
||||
public sealed partial class Scenario2 : Page
|
||||
{
|
||||
public Scenario2()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
Grid1.ContainerContentChanging += Grid1_ContainerContentChanging;
|
||||
initdata();
|
||||
}
|
||||
|
||||
private void Grid1_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
|
||||
{
|
||||
if (!args.InRecycleQueue)
|
||||
{
|
||||
FrameworkElement ctr = (FrameworkElement)args.ItemContainer.ContentTemplateRoot;
|
||||
if (ctr != null)
|
||||
{
|
||||
TextBlock t = (TextBlock)ctr.FindName("idx");
|
||||
t.Text = args.ItemIndex.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void initdata()
|
||||
{
|
||||
StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
|
||||
string path = pictures.SaveFolder.Path;
|
||||
|
||||
FileDataSource2 ds = await FileDataSource2.GetDataSoure(path);
|
||||
if (ds.Count > 0)
|
||||
{
|
||||
Grid1.ItemsSource = ds;
|
||||
}
|
||||
else
|
||||
{
|
||||
MainPage.Current.NotifyUser("Error: The pictures folder doesn't contain any files", NotifyType.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void ForceReset_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
FileDataSource2 ds = Grid1.ItemsSource as FileDataSource2;
|
||||
if (ds != null) ds.ResetCollection();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0"
|
||||
},
|
||||
"frameworks": {
|
||||
"uap10.0": {}
|
||||
},
|
||||
"runtimes": {
|
||||
"win10-arm": {},
|
||||
"win10-arm-aot": {},
|
||||
"win10-x86": {},
|
||||
"win10-x86-aot": {},
|
||||
"win10-x64": {},
|
||||
"win10-x64-aot": {}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче