Merge branch 'master' into muxtestinfra
This commit is contained in:
Коммит
514a419172
|
@ -8,10 +8,8 @@
|
|||
|
||||
Markdown: Allows you to parse a Markdown String into a Markdown Document, and then Render it with a Markdown Renderer.
|
||||
|
||||
RSS: Allows you to parse an RSS content String into an RSS Schema.
|
||||
|
||||
</Description>
|
||||
<PackageTags>UWP Toolkit Windows Parsers Parsing Markdown RSS</PackageTags>
|
||||
<PackageTags>UWP Toolkit Windows Parsers Parsing Markdown</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Toolkit.Extensions;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// Parser for Atom endpoints.
|
||||
/// </summary>
|
||||
internal class AtomParser : BaseRssParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Atom reader implementation to parse Atom content.
|
||||
/// </summary>
|
||||
/// <param name="doc">XDocument to parse.</param>
|
||||
/// <returns>Strong typed response.</returns>
|
||||
public override IEnumerable<RssSchema> LoadFeed(XDocument doc)
|
||||
{
|
||||
Collection<RssSchema> feed = new Collection<RssSchema>();
|
||||
|
||||
if (doc.Root == null)
|
||||
{
|
||||
return feed;
|
||||
}
|
||||
|
||||
var items = doc.Root.Elements(doc.Root.GetDefaultNamespace() + "entry").Select(item => GetRssSchema(item)).ToList<RssSchema>();
|
||||
|
||||
feed = new Collection<RssSchema>(items);
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves strong type for passed item.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement to parse.</param>
|
||||
/// <returns>Strong typed object.</returns>
|
||||
private static RssSchema GetRssSchema(XElement item)
|
||||
{
|
||||
RssSchema rssItem = new RssSchema
|
||||
{
|
||||
Author = GetItemAuthor(item),
|
||||
Title = item.GetSafeElementString("title").Trim().DecodeHtml(),
|
||||
ImageUrl = GetItemImage(item),
|
||||
PublishDate = item.GetSafeElementDate("published"),
|
||||
FeedUrl = item.GetLink("alternate"),
|
||||
};
|
||||
|
||||
var content = GetItemContent(item);
|
||||
|
||||
// Removes scripts from html
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
rssItem.Summary = ProcessHtmlSummary(content);
|
||||
rssItem.Content = ProcessHtmlContent(content);
|
||||
}
|
||||
|
||||
string id = item.GetSafeElementString("guid").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = item.GetSafeElementString("id").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = rssItem.FeedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
rssItem.InternalID = id;
|
||||
return rssItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves item author from XElement.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>String of Item Author.</returns>
|
||||
private static string GetItemAuthor(XElement item)
|
||||
{
|
||||
var content = string.Empty;
|
||||
|
||||
if (item != null && item.Element(item.GetDefaultNamespace() + "author") != null)
|
||||
{
|
||||
content = item.Element(item.GetDefaultNamespace() + "author").GetSafeElementString("name");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("author");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns item image from XElement item.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>String pointing to item image.</returns>
|
||||
private static string GetItemImage(XElement item)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.GetSafeElementString("image")))
|
||||
{
|
||||
return item.GetSafeElementString("image");
|
||||
}
|
||||
|
||||
return item.GetImage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns item content from XElement item.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>String of item content.</returns>
|
||||
private static string GetItemContent(XElement item)
|
||||
{
|
||||
var content = item.GetSafeElementString("description");
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("content");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("summary");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Toolkit.Extensions;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for RSS Parser(s).
|
||||
/// </summary>
|
||||
internal abstract class BaseRssParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieve feed type from XDocument.
|
||||
/// </summary>
|
||||
/// <param name="doc">XDocument doc.</param>
|
||||
/// <returns>Return feed type.</returns>
|
||||
public static RssType GetFeedType(XDocument doc)
|
||||
{
|
||||
if (doc.Root == null)
|
||||
{
|
||||
return RssType.Unknown;
|
||||
}
|
||||
|
||||
XNamespace defaultNamespace = doc.Root.GetDefaultNamespace();
|
||||
return defaultNamespace.NamespaceName.EndsWith("Atom") ? RssType.Atom : RssType.Rss;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract method to be override by specific implementations of the reader.
|
||||
/// </summary>
|
||||
/// <param name="doc">XDocument doc.</param>
|
||||
/// <returns>Returns list of strongly typed results.</returns>
|
||||
public abstract IEnumerable<RssSchema> LoadFeed(XDocument doc);
|
||||
|
||||
/// <summary>
|
||||
/// Fix up the HTML content.
|
||||
/// </summary>
|
||||
/// <param name="htmlContent">Content to be fixed up.</param>
|
||||
/// <returns>Fixed up content.</returns>
|
||||
protected internal static string ProcessHtmlContent(string htmlContent)
|
||||
{
|
||||
return htmlContent.FixHtml().SanitizeString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a summary of the HTML content.
|
||||
/// </summary>
|
||||
/// <param name="htmlContent">Content to be processed.</param>
|
||||
/// <returns>Summary of the content.</returns>
|
||||
protected internal static string ProcessHtmlSummary(string htmlContent)
|
||||
{
|
||||
return htmlContent.DecodeHtml().Trim().Truncate(500).SanitizeString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of RSS.
|
||||
/// </summary>
|
||||
internal enum RssType
|
||||
{
|
||||
/// <summary>
|
||||
/// Atom
|
||||
/// </summary>
|
||||
Atom,
|
||||
|
||||
/// <summary>
|
||||
/// RSS
|
||||
/// </summary>
|
||||
Rss,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
/// </summary>
|
||||
Unknown
|
||||
}
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Toolkit.Extensions;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// RSS reader implementation to parse RSS content.
|
||||
/// </summary>
|
||||
internal class Rss2Parser : BaseRssParser
|
||||
{
|
||||
/// <summary>
|
||||
/// RDF Namespace Uri.
|
||||
/// </summary>
|
||||
private static readonly XNamespace NsRdfNamespaceUri = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
||||
|
||||
/// <summary>
|
||||
/// RDF Elements Namespace Uri.
|
||||
/// </summary>
|
||||
private static readonly XNamespace NsRdfElementsNamespaceUri = "http://purl.org/dc/elements/1.1/";
|
||||
|
||||
/// <summary>
|
||||
/// RDF Content Namespace Uri.
|
||||
/// </summary>
|
||||
private static readonly XNamespace NsRdfContentNamespaceUri = "http://purl.org/rss/1.0/modules/content/";
|
||||
|
||||
/// <summary>
|
||||
/// This override load and parses the document and return a list of RssSchema values.
|
||||
/// </summary>
|
||||
/// <param name="doc">XDocument to be loaded.</param>
|
||||
/// <returns>Strongly typed list of feeds.</returns>
|
||||
public override IEnumerable<RssSchema> LoadFeed(XDocument doc)
|
||||
{
|
||||
bool isRDF = false;
|
||||
var feed = new Collection<RssSchema>();
|
||||
XNamespace defaultNamespace = string.Empty;
|
||||
|
||||
if (doc.Root != null)
|
||||
{
|
||||
isRDF = doc.Root.Name == (NsRdfNamespaceUri + "RDF");
|
||||
defaultNamespace = doc.Root.GetDefaultNamespace();
|
||||
}
|
||||
|
||||
foreach (var item in doc.Descendants(defaultNamespace + "item"))
|
||||
{
|
||||
var rssItem = isRDF ? ParseRDFItem(item) : ParseRssItem(item);
|
||||
feed.Add(rssItem);
|
||||
}
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses XElement item into strong typed object.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item to parse.</param>
|
||||
/// <returns>Strong typed object.</returns>
|
||||
private static RssSchema ParseItem(XElement item)
|
||||
{
|
||||
var rssItem = new RssSchema();
|
||||
rssItem.Title = item.GetSafeElementString("title").Trim().DecodeHtml();
|
||||
rssItem.FeedUrl = item.GetSafeElementString("link");
|
||||
|
||||
rssItem.Author = GetItemAuthor(item);
|
||||
|
||||
string content = item.GetSafeElementString("encoded", NsRdfContentNamespaceUri);
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("description");
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("content");
|
||||
}
|
||||
}
|
||||
|
||||
var summary = item.GetSafeElementString("description");
|
||||
if (string.IsNullOrEmpty(summary))
|
||||
{
|
||||
summary = item.GetSafeElementString("encoded", NsRdfContentNamespaceUri);
|
||||
}
|
||||
|
||||
// Removes scripts from html
|
||||
if (!string.IsNullOrEmpty(summary))
|
||||
{
|
||||
rssItem.Summary = ProcessHtmlSummary(summary);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
rssItem.Content = ProcessHtmlContent(content);
|
||||
}
|
||||
|
||||
string id = item.GetSafeElementString("guid").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = item.GetSafeElementString("id").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = rssItem.FeedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
rssItem.InternalID = id;
|
||||
|
||||
return rssItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses RSS version 1.0 objects.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>Strong typed object.</returns>
|
||||
private static RssSchema ParseRDFItem(XElement item)
|
||||
{
|
||||
XNamespace ns = "http://search.yahoo.com/mrss/";
|
||||
var rssItem = ParseItem(item);
|
||||
|
||||
rssItem.PublishDate = item.GetSafeElementDate("date", NsRdfElementsNamespaceUri);
|
||||
|
||||
string image = item.GetSafeElementString("image");
|
||||
if (string.IsNullOrEmpty(image) && item.Elements(ns + "thumbnail").LastOrDefault() != null)
|
||||
{
|
||||
var element = item.Elements(ns + "thumbnail").Last();
|
||||
image = element.Attribute("url").Value;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image) && item.ToString().Contains("thumbnail"))
|
||||
{
|
||||
image = item.GetSafeElementString("thumbnail");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image))
|
||||
{
|
||||
image = item.GetImage();
|
||||
}
|
||||
|
||||
rssItem.ImageUrl = image;
|
||||
|
||||
return rssItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses RSS version 2.0 objects.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>Strong typed object.</returns>
|
||||
private static RssSchema ParseRssItem(XElement item)
|
||||
{
|
||||
XNamespace ns = "http://search.yahoo.com/mrss/";
|
||||
var rssItem = ParseItem(item);
|
||||
|
||||
rssItem.PublishDate = item.GetSafeElementDate("pubDate");
|
||||
|
||||
string image = item.GetSafeElementString("image");
|
||||
if (string.IsNullOrEmpty(image))
|
||||
{
|
||||
image = item.GetImageFromEnclosure();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image) && item.Elements(ns + "content").LastOrDefault() != null)
|
||||
{
|
||||
var element = item.Elements(ns + "content").Last();
|
||||
if (element.Attribute("type") != null && element.Attribute("type").Value.Contains("image/"))
|
||||
{
|
||||
image = element.Attribute("url").Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image) && item.Elements(ns + "thumbnail").LastOrDefault() != null)
|
||||
{
|
||||
var element = item.Elements(ns + "thumbnail").Last();
|
||||
image = element.Attribute("url").Value;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image) && item.ToString().Contains("thumbnail"))
|
||||
{
|
||||
image = item.GetSafeElementString("thumbnail");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(image))
|
||||
{
|
||||
image = item.GetImage();
|
||||
}
|
||||
|
||||
rssItem.Categories = item.GetSafeElementsString("category");
|
||||
|
||||
rssItem.ImageUrl = image;
|
||||
|
||||
return rssItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve item author from item.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>String of item author.</returns>
|
||||
private static string GetItemAuthor(XElement item)
|
||||
{
|
||||
var content = item.GetSafeElementString("creator", NsRdfElementsNamespaceUri).Trim();
|
||||
if (string.IsNullOrEmpty(content))
|
||||
{
|
||||
content = item.GetSafeElementString("author");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,460 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// Class with utilities for RSS related works.
|
||||
/// </summary>
|
||||
internal static class RssHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// String for regular expression for image pattern.
|
||||
/// </summary>
|
||||
private const string ImagePattern = @"<img.*?src=[\""'](.+?)[\""'].*?>";
|
||||
|
||||
/// <summary>
|
||||
/// String for regular expression for hyperlink pattern.
|
||||
/// </summary>
|
||||
private const string HyperlinkPattern = @"<a\s+(?:[^>]*?\s+)?href=""([^ ""]*)""";
|
||||
|
||||
/// <summary>
|
||||
/// String for regular expression for height pattern.
|
||||
/// </summary>
|
||||
private const string HeightPattern = @"height=(?:(['""])(?<height>(?:(?!\1).)*)\1|(?<height>\S+))";
|
||||
|
||||
/// <summary>
|
||||
/// String for regular expression for width pattern.
|
||||
/// </summary>
|
||||
private const string WidthPattern = @"width=(?:(['""])(?<width>(?:(?!\1).)*)\1|(?<width>\S+))";
|
||||
|
||||
/// <summary>
|
||||
/// Regular expression for image pattern.
|
||||
/// </summary>
|
||||
private static readonly Regex RegexImages = new Regex(ImagePattern, RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Regular expression for hyperlink pattern.
|
||||
/// </summary>
|
||||
private static readonly Regex RegexLinks = new Regex(HyperlinkPattern, RegexOptions.IgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Regular expression for height pattern.
|
||||
/// </summary>
|
||||
private static readonly Regex RegexHeight = new Regex(HeightPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// Regular expression for width pattern.
|
||||
/// </summary>
|
||||
private static readonly Regex RegexWidth = new Regex(WidthPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// Removes \t characters in the string and trim additional space and carriage returns.
|
||||
/// </summary>
|
||||
/// <param name="text">Text string.</param>
|
||||
/// <returns>Sanitized string.</returns>
|
||||
public static string SanitizeString(this string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var textArray = text.Split(new[] { "\t" }, StringSplitOptions.RemoveEmptyEntries);
|
||||
string sanitizedText = string.Empty;
|
||||
foreach (var item in textArray.ToList())
|
||||
{
|
||||
sanitizedText += item.Trim();
|
||||
}
|
||||
|
||||
sanitizedText = string.Join(" ", Regex.Split(sanitizedText, @"(?:\r\n|\n|\r)"));
|
||||
|
||||
return sanitizedText;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item date from <see cref="XElement"/> and element name.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of element.</param>
|
||||
/// <returns>Item date.</returns>
|
||||
public static DateTime GetSafeElementDate(this XElement item, string elementName)
|
||||
{
|
||||
return GetSafeElementDate(item, elementName, item.GetDefaultNamespace());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item date from <see cref="XElement"/>, element name and <see cref="XNamespace"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of element.</param>
|
||||
/// <param name="xNamespace">XNamespace namespace.</param>
|
||||
/// <returns>Item date.</returns>
|
||||
public static DateTime GetSafeElementDate(this XElement item, string elementName, XNamespace xNamespace)
|
||||
{
|
||||
DateTime date;
|
||||
XElement element = item.Element(xNamespace + elementName);
|
||||
if (element == null)
|
||||
{
|
||||
return DateTime.Now;
|
||||
}
|
||||
|
||||
if (TryParseDateTime(element.Value, out date))
|
||||
{
|
||||
return date;
|
||||
}
|
||||
|
||||
return DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item string value for <see cref="XElement"/> and element name.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of element.</param>
|
||||
/// <returns>Safe string.</returns>
|
||||
public static string GetSafeElementString(this XElement item, string elementName)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return GetSafeElementString(item, elementName, item.GetDefaultNamespace());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item string values for <see cref="XElement"/> and element name.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of the element.</param>
|
||||
/// <returns>Safe list of string values.</returns>
|
||||
public static IEnumerable<string> GetSafeElementsString(this XElement item, string elementName)
|
||||
{
|
||||
return GetSafeElementsString(item, elementName, item.GetDefaultNamespace());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item string values for <see cref="XElement"/>, element name and namespace.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of element.</param>
|
||||
/// <param name="xNamespace">XNamespace namespace.</param>
|
||||
/// <returns>Safe list of string values.</returns>
|
||||
public static IEnumerable<string> GetSafeElementsString(this XElement item, string elementName, XNamespace xNamespace)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
IEnumerable<XElement> values = item.Elements(xNamespace + elementName);
|
||||
return values.Where(f => !string.IsNullOrEmpty(f.Value))
|
||||
.Select(f => f.Value);
|
||||
}
|
||||
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get item string value for <see cref="XElement"/>, element name and namespace.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="elementName">Name of element.</param>
|
||||
/// <param name="xNamespace">XNamespace namespace.</param>
|
||||
/// <returns>Safe string.</returns>
|
||||
public static string GetSafeElementString(this XElement item, string elementName, XNamespace xNamespace)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
XElement value = item.Element(xNamespace + elementName);
|
||||
if (value != null)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get feed url to see full original information.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <param name="rel">rel attribute value.</param>
|
||||
/// <returns>String link.</returns>
|
||||
public static string GetLink(this XElement item, string rel)
|
||||
{
|
||||
IEnumerable<XElement> links = item.Elements(item.GetDefaultNamespace() + "link");
|
||||
var xElements = links as XElement[] ?? links.ToArray();
|
||||
IEnumerable<string> link = from l in xElements
|
||||
let xAttribute = l.Attribute("rel")
|
||||
where xAttribute != null && xAttribute.Value == rel
|
||||
let attribute = l.Attribute("href")
|
||||
where attribute != null
|
||||
select attribute.Value;
|
||||
var enumerable = link as string[] ?? link.ToArray();
|
||||
if (!enumerable.Any() && xElements.Any())
|
||||
{
|
||||
return xElements.FirstOrDefault().Attributes().First().Value;
|
||||
}
|
||||
|
||||
return enumerable.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get feed image.
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>Feed data image.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The general catch is intended to avoid breaking the Data Provider by a Html decode exception")]
|
||||
public static string GetImage(this XElement item)
|
||||
{
|
||||
string feedDataImage = null;
|
||||
try
|
||||
{
|
||||
feedDataImage = GetImagesInHTMLString(item.Value).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(feedDataImage) && feedDataImage.EndsWith("'"))
|
||||
{
|
||||
feedDataImage = feedDataImage.Remove(feedDataImage.Length - 1);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
return feedDataImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the item image from the enclosure element http://www.w3schools.com/rss/rss_tag_enclosure.asp
|
||||
/// </summary>
|
||||
/// <param name="item">XElement item.</param>
|
||||
/// <returns>Feed data image.</returns>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The general catch is intended to avoid breaking the Data Provider by a Html decode exception")]
|
||||
public static string GetImageFromEnclosure(this XElement item)
|
||||
{
|
||||
string feedDataImage = null;
|
||||
try
|
||||
{
|
||||
XElement element = item.Element(item.GetDefaultNamespace() + "enclosure");
|
||||
if (element == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var typeAttribute = element.Attribute("type");
|
||||
if (!string.IsNullOrEmpty(typeAttribute?.Value) && typeAttribute.Value.StartsWith("image"))
|
||||
{
|
||||
var urlAttribute = element.Attribute("url");
|
||||
feedDataImage = (!string.IsNullOrEmpty(urlAttribute?.Value)) ?
|
||||
urlAttribute.Value : string.Empty;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
return feedDataImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to parse the original string to a datetime format.
|
||||
/// </summary>
|
||||
/// <param name="s">Input string.</param>
|
||||
/// <param name="result">Parsed datetime.</param>
|
||||
/// <returns>True if success</returns>
|
||||
public static bool TryParseDateTime(string s, out DateTime result)
|
||||
{
|
||||
if (DateTime.TryParse(s, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces, out result))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int tzIndex = s.LastIndexOf(" ");
|
||||
if (tzIndex >= 0)
|
||||
{
|
||||
string tz = s.Substring(tzIndex, s.Length - tzIndex);
|
||||
string offset = TimeZoneToOffset(tz);
|
||||
if (offset != null)
|
||||
{
|
||||
string offsetDate = string.Format("{0} {1}", s.Substring(0, tzIndex), offset);
|
||||
return TryParseDateTime(offsetDate, out result);
|
||||
}
|
||||
}
|
||||
|
||||
result = default(DateTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate and return timezone.
|
||||
/// </summary>
|
||||
/// <param name="tz">Input string.</param>
|
||||
/// <returns>Parsed timezone.</returns>
|
||||
public static string TimeZoneToOffset(string tz)
|
||||
{
|
||||
if (tz == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
tz = tz.ToUpper().Trim();
|
||||
|
||||
if (TimeZones.ContainsKey(tz))
|
||||
{
|
||||
return TimeZones[tz].First();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve images from HTML string.
|
||||
/// </summary>
|
||||
/// <param name="htmlString">String of HTML.</param>
|
||||
/// <returns>List of images.</returns>
|
||||
private static IEnumerable<string> GetImagesInHTMLString(string htmlString)
|
||||
{
|
||||
var images = new List<string>();
|
||||
foreach (Match match in RegexImages.Matches(htmlString))
|
||||
{
|
||||
bool include = true;
|
||||
string tag = match.Value;
|
||||
|
||||
// Ignores images with low size
|
||||
var matchHeight = RegexHeight.Match(tag);
|
||||
if (matchHeight.Success)
|
||||
{
|
||||
var heightValue = matchHeight.Groups["height"].Value;
|
||||
if (int.TryParse(heightValue, out var heightIntValue) && heightIntValue < 10)
|
||||
{
|
||||
include = false;
|
||||
}
|
||||
}
|
||||
|
||||
var matchWidth = RegexWidth.Match(tag);
|
||||
if (matchWidth.Success)
|
||||
{
|
||||
var widthValue = matchWidth.Groups["width"].Value;
|
||||
if (int.TryParse(widthValue, out var widthIntValue) && widthIntValue < 10)
|
||||
{
|
||||
include = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (include)
|
||||
{
|
||||
images.Add(match.Groups[1].Value);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Match match in RegexLinks.Matches(htmlString))
|
||||
{
|
||||
var value = match.Groups[1].Value;
|
||||
if (value.Contains(".jpg") || value.Contains(".png"))
|
||||
{
|
||||
images.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dictionary of timezones.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string[]> TimeZones = new Dictionary<string, string[]>
|
||||
{
|
||||
{ "ACDT", new[] { "-1030", "Australian Central Daylight" } },
|
||||
{ "ACST", new[] { "-0930", "Australian Central Standard" } },
|
||||
{ "ADT", new[] { "+0300", "(US) Atlantic Daylight" } },
|
||||
{ "AEDT", new[] { "-1100", "Australian East Daylight" } },
|
||||
{ "AEST", new[] { "-1000", "Australian East Standard" } },
|
||||
{ "AHDT", new[] { "+0900", string.Empty } },
|
||||
{ "AHST", new[] { "+1000", string.Empty } },
|
||||
{ "AST", new[] { "+0400", "(US) Atlantic Standard" } },
|
||||
{ "AT", new[] { "+0200", "Azores" } },
|
||||
{ "AWDT", new[] { "-0900", "Australian West Daylight" } },
|
||||
{ "AWST", new[] { "-0800", "Australian West Standard" } },
|
||||
{ "BAT", new[] { "-0300", "Baghdad" } },
|
||||
{ "BDST", new[] { "-0200", "British Double Summer" } },
|
||||
{ "BET", new[] { "+1100", "Bering Standard" } },
|
||||
{ "BST", new[] { "+0300", "Brazil Standard" } },
|
||||
{ "BT", new[] { "-0300", "Baghdad" } },
|
||||
{ "BZT2", new[] { "+0300", "Brazil Zone 2" } },
|
||||
{ "CADT", new[] { "-1030", "Central Australian Daylight" } },
|
||||
{ "CAST", new[] { "-0930", "Central Australian Standard" } },
|
||||
{ "CAT", new[] { "+1000", "Central Alaska" } },
|
||||
{ "CCT", new[] { "-0800", "China Coast" } },
|
||||
{ "CDT", new[] { "+0500", "(US) Central Daylight" } },
|
||||
{ "CED", new[] { "-0200", "Central European Daylight" } },
|
||||
{ "CET", new[] { "-0100", "Central European" } },
|
||||
{ "CST", new[] { "+0600", "(US) Central Standard" } },
|
||||
{ "EAST", new[] { "-1000", "Eastern Australian Standard" } },
|
||||
{ "EDT", new[] { "+0400", "(US) Eastern Daylight" } },
|
||||
{ "EED", new[] { "-0300", "Eastern European Daylight" } },
|
||||
{ "EET", new[] { "-0200", "Eastern Europe" } },
|
||||
{ "EEST", new[] { "-0300", "Eastern Europe Summer" } },
|
||||
{ "EST", new[] { "+0500", "(US) Eastern Standard" } },
|
||||
{ "FST", new[] { "-0200", "French Summer" } },
|
||||
{ "FWT", new[] { "-0100", "French Winter" } },
|
||||
{ "GMT", new[] { "+0000", "Greenwich Mean" } },
|
||||
{ "GST", new[] { "-1000", "Guam Standard" } },
|
||||
{ "HDT", new[] { "+0900", "Hawaii Daylight" } },
|
||||
{ "HST", new[] { "+1000", "Hawaii Standard" } },
|
||||
{ "IDLE", new[] { "-1200", "International Date Line East" } },
|
||||
{ "IDLW", new[] { "+1200", "International Date Line West" } },
|
||||
{ "IST", new[] { "-0530", "Indian Standard" } },
|
||||
{ "IT", new[] { "-0330", "Iran" } },
|
||||
{ "JST", new[] { "-0900", "Japan Standard" } },
|
||||
{ "JT", new[] { "-0700", "Java" } },
|
||||
{ "MDT", new[] { "+0600", "(US) Mountain Daylight" } },
|
||||
{ "MED", new[] { "-0200", "Middle European Daylight" } },
|
||||
{ "MET", new[] { "-0100", "Middle European" } },
|
||||
{ "MEST", new[] { "-0200", "Middle European Summer" } },
|
||||
{ "MEWT", new[] { "-0100", "Middle European Winter" } },
|
||||
{ "MST", new[] { "+0700", "(US) Mountain Standard" } },
|
||||
{ "MT", new[] { "-0800", "Moluccas" } },
|
||||
{ "NDT", new[] { "+0230", "Newfoundland Daylight" } },
|
||||
{ "NFT", new[] { "+0330", "Newfoundland" } },
|
||||
{ "NT", new[] { "+1100", "Nome" } },
|
||||
{ "NST", new[] { "-0630", "North Sumatra" } },
|
||||
{ "NZ", new[] { "-1100", "New Zealand " } },
|
||||
{ "NZST", new[] { "-1200", "New Zealand Standard" } },
|
||||
{ "NZDT", new[] { "-1300", "New Zealand Daylight" } },
|
||||
{ "NZT", new[] { "-1200", "New Zealand" } },
|
||||
{ "PDT", new[] { "+0700", "(US) Pacific Daylight" } },
|
||||
{ "PST", new[] { "+0800", "(US) Pacific Standard" } },
|
||||
{ "ROK", new[] { "-0900", "Republic of Korea" } },
|
||||
{ "SAD", new[] { "-1000", "South Australia Daylight" } },
|
||||
{ "SAST", new[] { "-0900", "South Australia Standard" } },
|
||||
{ "SAT", new[] { "-0900", "South Australia Standard" } },
|
||||
{ "SDT", new[] { "-1000", "South Australia Daylight" } },
|
||||
{ "SST", new[] { "-0200", "Swedish Summer" } },
|
||||
{ "SWT", new[] { "-0100", "Swedish Winter" } },
|
||||
{ "USZ3", new[] { "-0400", "Volga Time (Russia)" } },
|
||||
{ "USZ4", new[] { "-0500", "Ural Time (Russia)" } },
|
||||
{ "USZ5", new[] { "-0600", "West-Siberian Time (Russia) " } },
|
||||
{ "USZ6", new[] { "-0700", "Yenisei Time (Russia)" } },
|
||||
{ "UT", new[] { "+0000", "Universal Coordinated" } },
|
||||
{ "UTC", new[] { "+0000", "Universal Coordinated" } },
|
||||
{ "UZ10", new[] { "-1100", "Okhotsk Time (Russia)" } },
|
||||
{ "WAT", new[] { "+0100", "West Africa" } },
|
||||
{ "WET", new[] { "+0000", "West European" } },
|
||||
{ "WST", new[] { "-0800", "West Australian Standard" } },
|
||||
{ "YDT", new[] { "+0800", "Yukon Daylight" } },
|
||||
{ "YST", new[] { "+0900", "Yukon Standard" } }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// The RSS Parser allows you to parse an RSS content String into RSS Schema.
|
||||
/// </summary>
|
||||
public class RssParser : IParser<RssSchema>
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse an RSS content string into RSS Schema.
|
||||
/// </summary>
|
||||
/// <param name="data">Input string.</param>
|
||||
/// <returns>Strong type.</returns>
|
||||
public IEnumerable<RssSchema> Parse(string data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var doc = XDocument.Parse(data);
|
||||
var type = BaseRssParser.GetFeedType(doc);
|
||||
|
||||
BaseRssParser rssParser;
|
||||
if (type == RssType.Rss)
|
||||
{
|
||||
rssParser = new Rss2Parser();
|
||||
}
|
||||
else
|
||||
{
|
||||
rssParser = new AtomParser();
|
||||
}
|
||||
|
||||
return rssParser.LoadFeed(doc);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Toolkit.Parsers.Rss
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of the RssSchema class.
|
||||
/// </summary>
|
||||
public class RssSchema : SchemaBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets title.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets summary.
|
||||
/// </summary>
|
||||
public string Summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets content.
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets image Url.
|
||||
/// </summary>
|
||||
public string ImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets extra Image Url.
|
||||
/// </summary>
|
||||
public string ExtraImageUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets media Url.
|
||||
/// </summary>
|
||||
public string MediaUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets feed Url.
|
||||
/// </summary>
|
||||
public string FeedUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets author.
|
||||
/// </summary>
|
||||
public string Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets publish Date.
|
||||
/// </summary>
|
||||
public DateTime PublishDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets item's categories.
|
||||
/// </summary>
|
||||
public IEnumerable<string> Categories { get; set; }
|
||||
}
|
||||
}
|
|
@ -5,9 +5,6 @@
|
|||
<Title>Windows Community Toolkit .NET Standard Services</Title>
|
||||
<Description>
|
||||
This .NET standard library enables access to different data sources such as Microsoft Graph, OneDrive, Twitter, Microsoft Translator, and LinkedIn. It is part of the Windows Community Toolkit.
|
||||
|
||||
Namespace:
|
||||
- Facebook: Album, DataConfig, DataHost, OAuthTokens, Permissions, Photo, Picture, PictureData, PlatformImageSource, Post, RequestSource, Service.
|
||||
</Description>
|
||||
<PackageTags>UWP Community Toolkit Windows Microsoft Graph OneDrive Twitter Translator LinkedIn service login OAuth</PackageTags>
|
||||
|
||||
|
|
|
@ -154,17 +154,6 @@ namespace Microsoft.Toolkit.Services.LinkedIn
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of LinkedIn.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
LogoutAsync();
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of LinkedIn.
|
||||
/// </summary>
|
||||
|
|
|
@ -92,15 +92,6 @@ namespace Microsoft.Toolkit.Services.LinkedIn
|
|||
return Provider.ShareDataAsync<LinkedInShareRequest, LinkedInShareResponse>(shareRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of LinkedIn.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
Provider.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of LinkedIn.
|
||||
/// </summary>
|
||||
|
|
|
@ -273,17 +273,6 @@ namespace Microsoft.Toolkit.Services.Twitter
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Twitter.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
LogoutAsync();
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Twitter.
|
||||
/// </summary>
|
||||
|
|
|
@ -400,15 +400,6 @@ namespace Microsoft.Toolkit.Services.Twitter
|
|||
return Provider.LoginAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Twitter.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
Provider.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Twitter.
|
||||
/// </summary>
|
||||
|
|
|
@ -163,17 +163,6 @@ namespace Microsoft.Toolkit.Services.Weibo
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Weibo.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
LogoutAsync();
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Weibo.
|
||||
/// </summary>
|
||||
|
|
|
@ -183,15 +183,6 @@ namespace Microsoft.Toolkit.Services.Weibo
|
|||
return Provider.LoginAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Weibo.
|
||||
/// </summary>
|
||||
[Obsolete("Logout is deprecated, please use LogoutAsync instead.", true)]
|
||||
public void Logout()
|
||||
{
|
||||
Provider.Logout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log user out of Weibo.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -1,65 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" 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)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{292D34E8-0F01-4FA8-951D-8232F75A88D5}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>DifferencesGen</RootNamespace>
|
||||
<AssemblyName>DifferencesGen</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\DifferencesGen.xml</DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>10.0.3</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="Pack">
|
||||
<!-- No-op to avoid build error when packing solution from commandline -->
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,256 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
|
||||
namespace DifferencesGen
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static HashSet<string> enumTypes = new HashSet<string>();
|
||||
private static HashSet<string> typeEvents = new HashSet<string>();
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
string min = null;
|
||||
string max = null;
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (arg.StartsWith("/min:"))
|
||||
{
|
||||
min = arg.Replace("/min:", string.Empty);
|
||||
}
|
||||
else if (arg.StartsWith("/max:"))
|
||||
{
|
||||
max = arg.Replace("/max:", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
Version.TryParse(min, out Version minVersion);
|
||||
Version.TryParse(max, out Version maxVersion);
|
||||
|
||||
if (minVersion == null || maxVersion == null)
|
||||
{
|
||||
Console.WriteLine("The differences generator needs to be run as follows:");
|
||||
Console.WriteLine("DifferencesGen /min:4.0.0.0 /max:5.0.0.0");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string folderPath = @"C:\Program Files (x86)\Windows Kits\10\References";
|
||||
|
||||
string universalApiFile = "Windows.Foundation.UniversalApiContract.winmd";
|
||||
|
||||
string universalApiDifferencesCompressedFile = "Differences-{0}.gz";
|
||||
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (sender, eventArgs) => Assembly.ReflectionOnlyLoad(eventArgs.Name);
|
||||
WindowsRuntimeMetadata.ReflectionOnlyNamespaceResolve += (sender, eventArgs) =>
|
||||
{
|
||||
string path =
|
||||
WindowsRuntimeMetadata.ResolveNamespace(eventArgs.NamespaceName, Enumerable.Empty<string>())
|
||||
.FirstOrDefault();
|
||||
if (path == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
eventArgs.ResolvedAssemblies.Add(Assembly.ReflectionOnlyLoadFrom(path));
|
||||
};
|
||||
|
||||
DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
|
||||
|
||||
FileInfo[] files = directoryInfo.GetFiles(universalApiFile, SearchOption.AllDirectories);
|
||||
|
||||
List<Tuple<Version, Assembly>> assemblyList = new List<Tuple<Version, Assembly>>();
|
||||
|
||||
if (files.Length > 0)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
var assembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
|
||||
|
||||
var nameParts = assembly.FullName.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var versionParts = nameParts[1].Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var version = Version.Parse(versionParts[1]);
|
||||
|
||||
if (version >= minVersion && version <= maxVersion)
|
||||
{
|
||||
assemblyList.Add(new Tuple<Version, Assembly>(version, assembly));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (assemblyList.Count >= 2)
|
||||
{
|
||||
var orderedList = assemblyList.OrderBy(t => t.Item1).ToList();
|
||||
|
||||
for (int i = 1; i < orderedList.Count; i++)
|
||||
{
|
||||
var previousVersionAssembly = orderedList[i - 1].Item2;
|
||||
var newerVersionAssembly = orderedList[i].Item2;
|
||||
|
||||
var version = orderedList[i].Item1;
|
||||
|
||||
var previousVersionTypes = ProcessAssembly(previousVersionAssembly);
|
||||
var newerVersionTypes = ProcessAssembly(newerVersionAssembly);
|
||||
|
||||
var addedTypes = new Dictionary<string, List<string>>();
|
||||
|
||||
foreach (var type in newerVersionTypes)
|
||||
{
|
||||
if (!previousVersionTypes.ContainsKey(type.Key))
|
||||
{
|
||||
addedTypes.Add(type.Key, null);
|
||||
|
||||
if (enumTypes.Contains(type.Key))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"New enum {type.Key}");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
HashSet<string> previousVersionTypeMembers = new HashSet<string>(previousVersionTypes[type.Key]);
|
||||
HashSet<string> newerVersionTypeMembers = new HashSet<string>(type.Value);
|
||||
|
||||
newerVersionTypeMembers.ExceptWith(previousVersionTypeMembers);
|
||||
|
||||
if (newerVersionTypeMembers.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (enumTypes.Contains(type.Key))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Enum {type.Key} has new members: {string.Join(",", newerVersionTypeMembers)}");
|
||||
}
|
||||
|
||||
foreach (var member in newerVersionTypeMembers)
|
||||
{
|
||||
if (typeEvents.Contains($"{type.Key}-{member}"))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Type {type.Key} has new event: {member}");
|
||||
}
|
||||
}
|
||||
|
||||
addedTypes.Add(type.Key, newerVersionTypeMembers.ToList());
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
using (var compressedFS = File.Create(Path.Combine(AssemblyDirectory, string.Format(universalApiDifferencesCompressedFile, version.ToString()))))
|
||||
{
|
||||
using (var compressionFS = new GZipStream(compressedFS, CompressionMode.Compress))
|
||||
{
|
||||
using (var writer = new StreamWriter(compressionFS))
|
||||
{
|
||||
foreach (var addedType in addedTypes)
|
||||
{
|
||||
stringBuilder.Clear();
|
||||
|
||||
stringBuilder.Append(addedType.Key);
|
||||
|
||||
if (addedType.Value != null && addedType.Value.Count > 0)
|
||||
{
|
||||
stringBuilder.Append(':');
|
||||
stringBuilder.Append(string.Join(",", addedType.Value));
|
||||
}
|
||||
|
||||
writer.WriteLine(stringBuilder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stringBuilder.Length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string AssemblyDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
|
||||
UriBuilder uri = new UriBuilder(codeBase);
|
||||
string path = Uri.UnescapeDataString(uri.Path);
|
||||
return Path.GetDirectoryName(path);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> ProcessAssembly(Assembly assembly)
|
||||
{
|
||||
var types = new Dictionary<string, List<string>>();
|
||||
|
||||
foreach (var exportedType in assembly.ExportedTypes)
|
||||
{
|
||||
var members = new List<string>();
|
||||
|
||||
if (exportedType.IsEnum)
|
||||
{
|
||||
if (!enumTypes.Contains(exportedType.FullName))
|
||||
{
|
||||
enumTypes.Add(exportedType.FullName);
|
||||
}
|
||||
|
||||
foreach (var member in exportedType.GetFields())
|
||||
{
|
||||
if (member.Name.Equals("value__"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
members.Add(member.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var methodInfo in exportedType.GetMethods())
|
||||
{
|
||||
if (!methodInfo.IsPublic)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (methodInfo.Name.StartsWith("get_") ||
|
||||
methodInfo.Name.StartsWith("set_") ||
|
||||
methodInfo.Name.StartsWith("put_") ||
|
||||
methodInfo.Name.StartsWith("add_") ||
|
||||
methodInfo.Name.StartsWith("remove_"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
members.Add($"{methodInfo.Name}#{methodInfo.GetParameters().Length}");
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in exportedType.GetProperties())
|
||||
{
|
||||
members.Add(propertyInfo.Name);
|
||||
}
|
||||
|
||||
foreach (var eventInfo in exportedType.GetEvents())
|
||||
{
|
||||
typeEvents.Add($"{exportedType.FullName}-{eventInfo.Name}");
|
||||
members.Add(eventInfo.Name);
|
||||
}
|
||||
}
|
||||
|
||||
types.Add(exportedType.FullName, members);
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,357 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class offers loads platform differences for use by Code Analyzer and Code Fixer.
|
||||
/// </summary>
|
||||
public static class Analyzer
|
||||
{
|
||||
internal enum TypePresenceIndicator
|
||||
{
|
||||
New,
|
||||
Changes,
|
||||
NotFound,
|
||||
}
|
||||
|
||||
private static Dictionary<string, Dictionary<string, List<NewMember>>> _differencesDictionary = null;
|
||||
|
||||
/// <summary>
|
||||
/// Embedded differences between API contract version 4 and 5.
|
||||
/// </summary>
|
||||
public const string N1DifferencesRes = "Differences-5.0.0.0.gz";
|
||||
|
||||
/// <summary>
|
||||
/// Embedded differences between API contract version 5 and 6.
|
||||
/// </summary>
|
||||
public const string N0DifferencesRes = "Differences-6.0.0.0.gz";
|
||||
|
||||
/// <summary>
|
||||
/// Earliest supported SDK version.
|
||||
/// </summary>
|
||||
public const string N2SDKVersion = "15063";
|
||||
|
||||
/// <summary>
|
||||
/// Intermediate SDK version.
|
||||
/// </summary>
|
||||
public const string N1SDKVersion = "16299";
|
||||
|
||||
/// <summary>
|
||||
/// Latest SDK version.
|
||||
/// </summary>
|
||||
public const string N0SDKVersion = "17134";
|
||||
|
||||
/// <summary>
|
||||
/// Platform related diagnostic descriptor
|
||||
/// </summary>
|
||||
public static readonly DiagnosticDescriptor PlatformRule = new DiagnosticDescriptor("UWP001", "Platform-specific", "Platform-specific code detected. Consider using ApiInformation.IsTypePresent to guard against failure", "Safety", DiagnosticSeverity.Warning, true);
|
||||
|
||||
/// <summary>
|
||||
/// Version related diagnostic descriptor
|
||||
/// </summary>
|
||||
public static readonly DiagnosticDescriptor VersionRule = new DiagnosticDescriptor("UWP002", "Version-specific", "Version-specific code detected. Consider using ApiInformation.IsTypePresent / ApiInformation.IsMethodPresent / ApiInformation.IsPropertyPresent to guard against failure", "Safety", DiagnosticSeverity.Warning, true);
|
||||
|
||||
private static char[] typeMemberSeparator = { ':' };
|
||||
private static char[] memberSeparator = { ',' };
|
||||
|
||||
static Analyzer()
|
||||
{
|
||||
_differencesDictionary = new Dictionary<string, Dictionary<string, List<NewMember>>>();
|
||||
_differencesDictionary.Add(N0DifferencesRes, GetApiAdditions(N0DifferencesRes));
|
||||
_differencesDictionary.Add(N1DifferencesRes, GetApiAdditions(N1DifferencesRes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the API differences from specified resource.
|
||||
/// </summary>
|
||||
/// <param name="resourceName">name of embedded resource</param>
|
||||
/// <returns>Dictionary with Fully qualified name of type as key and list of new members as value</returns>
|
||||
public static Dictionary<string, List<NewMember>> GetUniversalApiAdditions(string resourceName)
|
||||
{
|
||||
return _differencesDictionary[resourceName];
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<NewMember>> GetApiAdditions(string resourceName)
|
||||
{
|
||||
Dictionary<string, List<NewMember>> apiAdditionsDictionary = new Dictionary<string, List<NewMember>>();
|
||||
|
||||
Assembly assembly = typeof(Analyzer).GetTypeInfo().Assembly;
|
||||
|
||||
var resource = assembly.GetManifestResourceStream("Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer." + resourceName);
|
||||
|
||||
if (resource == null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Resource {resourceName} not found.");
|
||||
return new Dictionary<string, List<NewMember>>();
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Resource {resourceName} found.");
|
||||
Dictionary<string, List<string>> differencesDictionary = new Dictionary<string, List<string>>();
|
||||
|
||||
using (GZipStream decompressionStream = new GZipStream(resource, CompressionMode.Decompress))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(decompressionStream))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var typeDetails = reader.ReadLine();
|
||||
|
||||
var typeMemberParts = typeDetails.Split(typeMemberSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (typeMemberParts.Length == 1)
|
||||
{
|
||||
differencesDictionary.Add(typeMemberParts[0], null);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var membersAddedToType = typeMemberParts[1].Split(memberSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
differencesDictionary.Add(typeMemberParts[0], new List<string>(membersAddedToType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (differencesDictionary == null)
|
||||
{
|
||||
return apiAdditionsDictionary;
|
||||
}
|
||||
|
||||
foreach (var kvp in differencesDictionary)
|
||||
{
|
||||
var list = new List<NewMember>();
|
||||
if (kvp.Value != null)
|
||||
{
|
||||
list.AddRange(kvp.Value.Select(v => new NewMember(v)));
|
||||
}
|
||||
|
||||
apiAdditionsDictionary.Add(kvp.Key, list);
|
||||
}
|
||||
|
||||
return apiAdditionsDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function tells which version/platform the symbol is from.
|
||||
/// </summary>
|
||||
/// <param name="symbol">represents a compiler <see cref="ISymbol"/></param>
|
||||
/// <returns>instance of <see cref="Platform"/></returns>
|
||||
public static Platform GetPlatformForSymbol(ISymbol symbol)
|
||||
{
|
||||
if (symbol == null)
|
||||
{
|
||||
return new Platform(PlatformKind.Unchecked);
|
||||
}
|
||||
|
||||
if (symbol.ContainingNamespace != null && symbol.ContainingNamespace.ToDisplayString().StartsWith("Windows."))
|
||||
{
|
||||
var assembly = symbol.ContainingAssembly.Name;
|
||||
var version = symbol.ContainingAssembly.Identity.Version.Major;
|
||||
|
||||
// Any call to ApiInformation.* is allowed without warning
|
||||
if (symbol.ContainingType?.Name == "ApiInformation")
|
||||
{
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N2SDKVersion);
|
||||
}
|
||||
|
||||
// Don't want to give warning when analyzing code in an PCL project.
|
||||
// In those two targets, every Windows type is found in Windows.winmd, so that's how we'll suppress it:
|
||||
if (assembly == "Windows")
|
||||
{
|
||||
return new Platform(PlatformKind.Unchecked);
|
||||
}
|
||||
|
||||
// Some WinRT types like Windows.UI.Color get projected to come from .NET assemblies, always present:
|
||||
if (assembly.StartsWith("System.Runtime."))
|
||||
{
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N2SDKVersion);
|
||||
}
|
||||
|
||||
// Some things are emphatically part of UWP.10240
|
||||
if (assembly == "Windows.Foundation.FoundationContract" || (assembly == "Windows.Foundation.UniversalApiContract" && version == 1))
|
||||
{
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N2SDKVersion);
|
||||
}
|
||||
|
||||
if (assembly == "Windows.Foundation.UniversalApiContract")
|
||||
{
|
||||
var isType = symbol.Kind == SymbolKind.NamedType;
|
||||
|
||||
var typeName = isType ? symbol.ToDisplayString() : symbol.ContainingType.ToDisplayString();
|
||||
|
||||
TypePresenceIndicator presentInN0ApiDiff = CheckCollectionForType(Analyzer.GetUniversalApiAdditions(Analyzer.N0DifferencesRes), typeName, symbol);
|
||||
|
||||
if (presentInN0ApiDiff == TypePresenceIndicator.New)
|
||||
{
|
||||
// the entire type was found in Target Version
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N0SDKVersion);
|
||||
}
|
||||
else if (presentInN0ApiDiff == TypePresenceIndicator.Changes)
|
||||
{
|
||||
// the entire type was found in Target Version with matching parameter lengths
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N0SDKVersion, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypePresenceIndicator presentInN1ApiDiff = CheckCollectionForType(Analyzer.GetUniversalApiAdditions(Analyzer.N1DifferencesRes), typeName, symbol);
|
||||
|
||||
if (presentInN1ApiDiff == TypePresenceIndicator.New)
|
||||
{
|
||||
// the entire type was found in Target Version
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N1SDKVersion);
|
||||
}
|
||||
else if (presentInN1ApiDiff == TypePresenceIndicator.Changes)
|
||||
{
|
||||
// the entire type was found in Target Version with matching parameter lengths
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N1SDKVersion, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// the type was in Min version
|
||||
return new Platform(PlatformKind.Uwp, Analyzer.N2SDKVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All other Windows.* types come from platform-specific extensions
|
||||
return new Platform(PlatformKind.ExtensionSDK);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Platform(PlatformKind.Unchecked);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns instance of <see cref="HowToGuard"/> for <see cref="ISymbol"/>
|
||||
/// </summary>
|
||||
/// <param name="target">instance of <see cref="ISymbol"/></param>
|
||||
/// <returns>instance of <see cref="HowToGuard"/></returns>
|
||||
public static HowToGuard GetGuardForSymbol(ISymbol target)
|
||||
{
|
||||
var plat = Analyzer.GetPlatformForSymbol(target);
|
||||
|
||||
switch (plat.Kind)
|
||||
{
|
||||
case PlatformKind.ExtensionSDK:
|
||||
return new HowToGuard()
|
||||
{
|
||||
TypeToCheck = target.Kind == SymbolKind.NamedType ? target.ToDisplayString() : target.ContainingType.ToDisplayString(),
|
||||
KindOfCheck = "IsTypePresent"
|
||||
};
|
||||
case PlatformKind.Uwp:
|
||||
if (target.Kind == SymbolKind.NamedType)
|
||||
{
|
||||
return new HowToGuard()
|
||||
{
|
||||
TypeToCheck = target.ToDisplayString(),
|
||||
KindOfCheck = "IsTypePresent"
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var g = new HowToGuard
|
||||
{
|
||||
TypeToCheck = target.ContainingType.ToDisplayString()
|
||||
};
|
||||
|
||||
var d0 = Analyzer.GetUniversalApiAdditions(Analyzer.N0DifferencesRes);
|
||||
var d1 = Analyzer.GetUniversalApiAdditions(Analyzer.N1DifferencesRes);
|
||||
|
||||
if (!d0.TryGetValue(g.TypeToCheck, out List<NewMember> newMembers))
|
||||
{
|
||||
d1.TryGetValue(g.TypeToCheck, out newMembers);
|
||||
}
|
||||
|
||||
if (newMembers == null)
|
||||
{
|
||||
throw new InvalidOperationException("oops! expected this UWP version API to be in the dictionary of new things");
|
||||
}
|
||||
|
||||
g.MemberToCheck = target.Name;
|
||||
|
||||
if (target.Kind == SymbolKind.Field)
|
||||
{
|
||||
// the only fields in WinRT are enum fields
|
||||
g.KindOfCheck = "IsEnumNamedValuePresent";
|
||||
}
|
||||
else if (target.Kind == SymbolKind.Event)
|
||||
{
|
||||
g.KindOfCheck = "IsEventPresent";
|
||||
}
|
||||
else if (target.Kind == SymbolKind.Property)
|
||||
{
|
||||
// TODO: if SDK starts introducing additional accessors on properties, we'll have to change this
|
||||
g.KindOfCheck = "IsPropertyPresent";
|
||||
}
|
||||
else if (target.Kind == SymbolKind.Method)
|
||||
{
|
||||
g.KindOfCheck = "IsMethodPresent";
|
||||
|
||||
if (target.Kind == SymbolKind.Method && plat.ByParameterCount)
|
||||
{
|
||||
g.ParameterCountToCheck = (target as IMethodSymbol).Parameters.Length;
|
||||
}
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("oops! don't know why I was asked to check something that's fine");
|
||||
}
|
||||
}
|
||||
|
||||
private static TypePresenceIndicator CheckCollectionForType(Dictionary<string, List<NewMember>> collection, string typeName, ISymbol symbol)
|
||||
{
|
||||
if (!collection.TryGetValue(typeName, out var newMembers))
|
||||
{
|
||||
return TypePresenceIndicator.NotFound;
|
||||
}
|
||||
|
||||
if (newMembers == null || newMembers.Count == 0)
|
||||
{
|
||||
return TypePresenceIndicator.New;
|
||||
}
|
||||
|
||||
if (symbol.Kind == SymbolKind.NamedType)
|
||||
{
|
||||
return TypePresenceIndicator.NotFound;
|
||||
}
|
||||
|
||||
var memberName = symbol.Name;
|
||||
|
||||
foreach (var newMember in newMembers)
|
||||
{
|
||||
if (memberName == newMember.Name && !newMember.ParameterCount.HasValue)
|
||||
{
|
||||
return TypePresenceIndicator.New;
|
||||
}
|
||||
|
||||
// this member was new in collection
|
||||
if (symbol.Kind != SymbolKind.Method)
|
||||
{
|
||||
// TODO: Continue For... Warning!!! not translated
|
||||
}
|
||||
|
||||
if (memberName == newMember.Name && ((IMethodSymbol)symbol).Parameters.Length == newMember.ParameterCount)
|
||||
{
|
||||
return TypePresenceIndicator.Changes;
|
||||
}
|
||||
}
|
||||
|
||||
// this member existed in a different collection
|
||||
return TypePresenceIndicator.NotFound;
|
||||
}
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -1,32 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// The struct provides guard related info.
|
||||
/// </summary>
|
||||
public struct HowToGuard
|
||||
{
|
||||
/// <summary>
|
||||
/// Type being checked
|
||||
/// </summary>
|
||||
public string TypeToCheck;
|
||||
|
||||
/// <summary>
|
||||
/// Member being checked
|
||||
/// </summary>
|
||||
public string MemberToCheck;
|
||||
|
||||
/// <summary>
|
||||
/// Whether parameter count will be used for the check
|
||||
/// </summary>
|
||||
public int? ParameterCountToCheck;
|
||||
|
||||
/// <summary>
|
||||
/// Type of check
|
||||
/// </summary>
|
||||
public string KindOfCheck;
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Title>Windows Community Toolkit UWP Platform Specific Analyzer</Title>
|
||||
<Description>This standard library provides code analysis and code fixers (on CS and VB) to ensure that version / platform specific code is well guarded.</Description>
|
||||
<PackageTags>UWP Toolkit Windows Platform Specific Analyzer</PackageTags>
|
||||
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Differences-6.0.0.0.gz" />
|
||||
<None Remove="Differences-7.0.0.0.gz" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Differences-6.0.0.0.gz" />
|
||||
<EmbeddedResource Include="Differences-7.0.0.0.gz" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="NETStandard.Library" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="tools\*.ps1" CopyToOutputDirectory="Always" Pack="true" PackagePath="" />
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/vb" Visible="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.8.2" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,46 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class wraps a new data members
|
||||
/// </summary>
|
||||
public struct NewMember
|
||||
{
|
||||
private static char[] methodCountSeparator = { '#' };
|
||||
|
||||
/// <summary>
|
||||
/// Member name
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// Parameter count (if its a method)
|
||||
/// </summary>
|
||||
public int? ParameterCount;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NewMember"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="s">data containing name and optionally parameter count</param>
|
||||
public NewMember(string s)
|
||||
{
|
||||
string[] parts = s.Split(methodCountSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
Name = parts[0];
|
||||
ParameterCount = int.Parse(parts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Name = s;
|
||||
ParameterCount = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple struct to hold platform / version / param count info for given symbol
|
||||
/// </summary>
|
||||
public struct Platform
|
||||
{
|
||||
/// <summary>
|
||||
/// Platform Kind
|
||||
/// </summary>
|
||||
public PlatformKind Kind;
|
||||
|
||||
/// <summary>
|
||||
/// For UWP, this is version 15063 or 16299etc. For User, the fully qualified name of the attribute in use
|
||||
/// </summary>
|
||||
public string Version;
|
||||
|
||||
/// <summary>
|
||||
/// For UWP only
|
||||
/// </summary>
|
||||
public bool ByParameterCount;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Platform"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="kind"><see cref="PlatformKind"/></param>
|
||||
/// <param name="version">version</param>
|
||||
/// <param name="byParameterCount">boolean</param>
|
||||
public Platform(PlatformKind kind, string version = null, bool byParameterCount = false)
|
||||
{
|
||||
Kind = kind;
|
||||
Version = version;
|
||||
ByParameterCount = byParameterCount;
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
case PlatformKind.Unchecked:
|
||||
if (version != null)
|
||||
{
|
||||
throw new ArgumentException("No version expected");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PlatformKind.Uwp:
|
||||
break;
|
||||
|
||||
case PlatformKind.ExtensionSDK:
|
||||
if (version != null)
|
||||
{
|
||||
throw new ArgumentException("Don't specify versions for extension SDKs");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (byParameterCount && kind != PlatformKind.Uwp)
|
||||
{
|
||||
throw new ArgumentException("Only UWP can be distinguished by parameter count");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// Platform kind enum
|
||||
/// </summary>
|
||||
public enum PlatformKind
|
||||
{
|
||||
/// <summary>
|
||||
/// .NET and Pre-UWP WinRT
|
||||
/// </summary>
|
||||
Unchecked,
|
||||
|
||||
/// <summary>
|
||||
/// Core UWP platform
|
||||
/// </summary>
|
||||
Uwp,
|
||||
|
||||
/// <summary>
|
||||
/// Desktop, Mobile, IOT, Xbox extension SDK
|
||||
/// </summary>
|
||||
ExtensionSDK
|
||||
}
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is a Roslyn code analyzer that checks for types / members that should be guarded against.
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
public class PlatformSpecificAnalyzerCS : DiagnosticAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets supported diagnostics
|
||||
/// </summary>
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
|
||||
{
|
||||
get { return ImmutableArray.Create(Analyzer.PlatformRule, Analyzer.VersionRule); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets instance of symbol from syntax node
|
||||
/// </summary>
|
||||
/// <param name="node">instance of <see cref="SyntaxNode"/></param>
|
||||
/// <param name="semanticModel"><see cref="SemanticModel"/></param>
|
||||
/// <returns><see cref="ISymbol"/></returns>
|
||||
public static ISymbol GetTargetOfNode(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
var parentKind = node.Parent.Kind();
|
||||
|
||||
if (parentKind == SyntaxKind.InvocationExpression && node == ((InvocationExpressionSyntax)node.Parent).Expression)
|
||||
{
|
||||
// <target>(...)
|
||||
// points to the method after overload resolution
|
||||
return semanticModel.GetSymbolInfo((InvocationExpressionSyntax)node.Parent).Symbol;
|
||||
}
|
||||
else if (parentKind == SyntaxKind.ObjectCreationExpression && node == ((ObjectCreationExpressionSyntax)node.Parent).Type)
|
||||
{
|
||||
// New <target>
|
||||
var objectCreationExpression = (ObjectCreationExpressionSyntax)node.Parent;
|
||||
var target = semanticModel.GetSymbolInfo(objectCreationExpression).Symbol;
|
||||
|
||||
// points to the constructor after overload resolution
|
||||
return target;
|
||||
}
|
||||
else
|
||||
{
|
||||
// f<target>(...)
|
||||
// <target> x = ...
|
||||
// Action x = <target> -- note that following code does pick the right overload
|
||||
// <target> += delegate -- the following code does recognize events
|
||||
// nameof(<target>) -- I think it's nicer to report on this, even if not technically needed
|
||||
// Field access? I'll disallow it for enum values, and allow it for everything else
|
||||
var target = semanticModel.GetSymbolInfo(node).Symbol;
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var targetKind = target.Kind;
|
||||
|
||||
if (targetKind == SymbolKind.Method || targetKind == SymbolKind.Event || targetKind == SymbolKind.Property || targetKind == SymbolKind.NamedType)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (targetKind == SymbolKind.Field && target.ContainingType.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the analyzer, registering for code analysis.
|
||||
/// </summary>
|
||||
/// <param name="context"><see cref="AnalysisContext"/></param>
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
ConcurrentDictionary<int, Diagnostic> reportsDictionary = new ConcurrentDictionary<int, Diagnostic>();
|
||||
|
||||
context.RegisterSyntaxNodeAction((c) => AnalyzeExpression(c, reportsDictionary), SyntaxKind.VariableDeclaration, SyntaxKind.FieldDeclaration, SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.QualifiedName);
|
||||
}
|
||||
|
||||
private static IEnumerable<ISymbol> GetGuards(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
foreach (var condition in GetConditions(node))
|
||||
{
|
||||
// First check for invocations of ApiInformation.IsTypePresent
|
||||
foreach (var invocation in condition.DescendantNodesAndSelf(i => i is InvocationExpressionSyntax))
|
||||
{
|
||||
var targetMethod = semanticModel.GetSymbolInfo(invocation).Symbol;
|
||||
|
||||
if (targetMethod?.ContainingType?.Name == "ApiInformation")
|
||||
{
|
||||
yield return targetMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// Next check for any property/field access
|
||||
var accesses1 = condition.DescendantNodesAndSelf(d => d is MemberAccessExpressionSyntax).Select(n => semanticModel.GetSymbolInfo(n).Symbol);
|
||||
var accesses2 = condition.DescendantNodesAndSelf(d => d is IdentifierNameSyntax).Select(n => semanticModel.GetSymbolInfo(n).Symbol);
|
||||
|
||||
foreach (var symbol in accesses1.Concat(accesses2))
|
||||
{
|
||||
var symbolKind = symbol.Kind;
|
||||
|
||||
if (symbolKind == SymbolKind.Field || symbolKind == SymbolKind.Property)
|
||||
{
|
||||
yield return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ExpressionSyntax> GetConditions(SyntaxNode node)
|
||||
{
|
||||
var check = node.FirstAncestorOrSelf<IfStatementSyntax>();
|
||||
|
||||
while (check != null)
|
||||
{
|
||||
yield return check.Condition;
|
||||
check = check.Parent.FirstAncestorOrSelf<IfStatementSyntax>();
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeExpression(SyntaxNodeAnalysisContext context, ConcurrentDictionary<int, Diagnostic> reports)
|
||||
{
|
||||
var parentKind = context.Node.Parent.Kind();
|
||||
|
||||
// will be handled at higher level
|
||||
if (parentKind == SyntaxKind.SimpleMemberAccessExpression || parentKind == SyntaxKind.QualifiedName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var target = GetTargetOfNode(context.Node, context.SemanticModel);
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var platform = Analyzer.GetPlatformForSymbol(target);
|
||||
|
||||
// Some quick escapes
|
||||
if (platform.Kind == PlatformKind.Unchecked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (platform.Kind == PlatformKind.Uwp && platform.Version == Analyzer.N2SDKVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this expression inside a method/constructor/property that claims to be specific?
|
||||
var containingBlock = context.Node.FirstAncestorOrSelf<BlockSyntax>();
|
||||
|
||||
// for constructors and methods
|
||||
MemberDeclarationSyntax containingMember = containingBlock?.FirstAncestorOrSelf<BaseMethodDeclarationSyntax>();
|
||||
|
||||
if (containingBlock == null || containingBlock?.Parent is AccessorDeclarationSyntax)
|
||||
{
|
||||
containingMember = context.Node.FirstAncestorOrSelf<PropertyDeclarationSyntax>();
|
||||
}
|
||||
|
||||
// Is this invocation properly guarded? See readme.md for explanations.
|
||||
if (IsProperlyGuarded(context.Node, context.SemanticModel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (containingBlock != null)
|
||||
{
|
||||
foreach (var ret in containingBlock.DescendantNodes().OfType<ReturnStatementSyntax>())
|
||||
{
|
||||
if (IsProperlyGuarded(ret, context.SemanticModel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We'll report only a single diagnostic per line, the first.
|
||||
var loc = context.Node.GetLocation();
|
||||
if (!loc.IsInSource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var line = loc.GetLineSpan().StartLinePosition.Line;
|
||||
if (reports.TryGetValue(line, out var diagnostic) && diagnostic.Location.SourceSpan.Start <= loc.SourceSpan.Start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
diagnostic = Diagnostic.Create(platform.Kind == PlatformKind.Uwp ? Analyzer.VersionRule : Analyzer.PlatformRule, loc);
|
||||
|
||||
reports[line] = diagnostic;
|
||||
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
private bool IsProperlyGuarded(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
foreach (var symbol in GetGuards(node, semanticModel))
|
||||
{
|
||||
if (symbol.ContainingType?.Name == "ApiInformation")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,254 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.VisualBasic;
|
||||
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is a Roslyn code analyzer that checks for types / members that should be guarded against.
|
||||
/// </summary>
|
||||
[DiagnosticAnalyzer(LanguageNames.VisualBasic)]
|
||||
public class PlatformSpecificAnalyzerVB : DiagnosticAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets supported diagnostics
|
||||
/// </summary>
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
|
||||
{
|
||||
get { return ImmutableArray.Create(Analyzer.PlatformRule, Analyzer.VersionRule); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets instance of symbol from syntax node
|
||||
/// </summary>
|
||||
/// <param name="node">instance of <see cref="SyntaxNode"/></param>
|
||||
/// <param name="semanticModel"><see cref="SemanticModel"/></param>
|
||||
/// <returns><see cref="ISymbol"/></returns>
|
||||
public static ISymbol GetTargetOfNode(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
var parentKind = node.Parent.Kind();
|
||||
|
||||
if (parentKind == SyntaxKind.InvocationExpression && node == ((InvocationExpressionSyntax)node.Parent).Expression)
|
||||
{
|
||||
// <target>(...)
|
||||
// points to the method after overload resolution
|
||||
return semanticModel.GetSymbolInfo((InvocationExpressionSyntax)node.Parent).Symbol;
|
||||
}
|
||||
else if (parentKind == SyntaxKind.AddressOfExpression)
|
||||
{
|
||||
// AddressOf <target>
|
||||
return semanticModel.GetSymbolInfo(node).Symbol; // points to the method after overload resolution
|
||||
}
|
||||
else if (parentKind == SyntaxKind.ObjectCreationExpression && node == ((ObjectCreationExpressionSyntax)node.Parent).Type)
|
||||
{
|
||||
// New <target>
|
||||
var objectCreationExpression = (ObjectCreationExpressionSyntax)node.Parent;
|
||||
var target = semanticModel.GetSymbolInfo(objectCreationExpression).Symbol;
|
||||
|
||||
// points to the constructor after overload resolution
|
||||
return target;
|
||||
}
|
||||
else if (parentKind == SyntaxKind.AddHandlerStatement && node == ((AddRemoveHandlerStatementSyntax)node.Parent).EventExpression)
|
||||
{
|
||||
// AddHandler <target>, delegate
|
||||
return semanticModel.GetSymbolInfo(node).Symbol; // points to the event
|
||||
}
|
||||
else if (parentKind == SyntaxKind.NameOfExpression)
|
||||
{
|
||||
// NameOf(<target>)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// f(Of <target>)(...) -- no warning
|
||||
// Dim x As <target> = ... -- no warning
|
||||
// property access -- warning
|
||||
// field access -- only warning on enum fields
|
||||
// method access without arguments -- warning
|
||||
var target = semanticModel.GetSymbolInfo(node).Symbol;
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var targetKind = target.Kind;
|
||||
|
||||
if (targetKind == SymbolKind.Method || targetKind == SymbolKind.Property || targetKind == SymbolKind.NamedType)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (targetKind == SymbolKind.Field && target.ContainingType.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the analyzer, registering for code analysis.
|
||||
/// </summary>
|
||||
/// <param name="context"><see cref="AnalysisContext"/></param>
|
||||
public override void Initialize(AnalysisContext context)
|
||||
{
|
||||
ConcurrentDictionary<int, Diagnostic> reportsDictionary = new ConcurrentDictionary<int, Diagnostic>();
|
||||
|
||||
context.RegisterSyntaxNodeAction((c) => AnalyzeExpression(c, reportsDictionary), SyntaxKind.LocalDeclarationStatement, SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.QualifiedName);
|
||||
}
|
||||
|
||||
private static IEnumerable<ISymbol> GetGuards(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
foreach (var condition in GetConditions(node))
|
||||
{
|
||||
// First check for invocations of ApiInformation.IsTypePresent
|
||||
foreach (var invocation in condition.DescendantNodesAndSelf(i => i is InvocationExpressionSyntax))
|
||||
{
|
||||
var targetMethod = semanticModel.GetSymbolInfo(invocation).Symbol;
|
||||
|
||||
if (targetMethod?.ContainingType?.Name == "ApiInformation")
|
||||
{
|
||||
yield return targetMethod;
|
||||
}
|
||||
}
|
||||
|
||||
// Next check for any property/field access
|
||||
var accesses1 = condition.DescendantNodesAndSelf(d => d is MemberAccessExpressionSyntax).Select(n => semanticModel.GetSymbolInfo(n).Symbol);
|
||||
var accesses2 = condition.DescendantNodesAndSelf(d => d is IdentifierNameSyntax).Select(n => semanticModel.GetSymbolInfo(n).Symbol);
|
||||
|
||||
foreach (var symbol in accesses1.Concat(accesses2))
|
||||
{
|
||||
if (symbol == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var symbolKind = symbol.Kind;
|
||||
|
||||
if (symbolKind == SymbolKind.Field || symbolKind == SymbolKind.Property)
|
||||
{
|
||||
yield return symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ExpressionSyntax> GetConditions(SyntaxNode node)
|
||||
{
|
||||
var check1 = node.FirstAncestorOrSelf<MultiLineIfBlockSyntax>();
|
||||
|
||||
while (check1 != null)
|
||||
{
|
||||
yield return check1.IfStatement.Condition;
|
||||
check1 = check1.Parent.FirstAncestorOrSelf<MultiLineIfBlockSyntax>();
|
||||
}
|
||||
|
||||
var check2 = node.FirstAncestorOrSelf<SingleLineIfStatementSyntax>();
|
||||
|
||||
while (check2 != null)
|
||||
{
|
||||
yield return check2.Condition;
|
||||
check2 = check2.Parent.FirstAncestorOrSelf<SingleLineIfStatementSyntax>();
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeExpression(SyntaxNodeAnalysisContext context, ConcurrentDictionary<int, Diagnostic> reports)
|
||||
{
|
||||
var parentKind = context.Node.Parent.Kind();
|
||||
|
||||
// will be handled at higher level
|
||||
if (parentKind == SyntaxKind.SimpleMemberAccessExpression || parentKind == SyntaxKind.QualifiedName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var target = GetTargetOfNode(context.Node, context.SemanticModel);
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var platform = Analyzer.GetPlatformForSymbol(target);
|
||||
|
||||
// Some quick escapes
|
||||
if (platform.Kind == PlatformKind.Unchecked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (platform.Kind == PlatformKind.Uwp && platform.Version == Analyzer.N2SDKVersion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this expression inside a method/constructor/property that claims to be specific?
|
||||
DeclarationStatementSyntax containingMember = context.Node.FirstAncestorOrSelf<MethodBlockBaseSyntax>();
|
||||
|
||||
if (containingMember is AccessorBlockSyntax)
|
||||
{
|
||||
containingMember = containingMember.FirstAncestorOrSelf<PropertyBlockSyntax>();
|
||||
}
|
||||
|
||||
// Is this invocation properly guarded? See readme.md for explanations.
|
||||
if (IsProperlyGuarded(context.Node, context.SemanticModel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (containingMember != null)
|
||||
{
|
||||
foreach (var ret in containingMember.DescendantNodes().OfType<ReturnStatementSyntax>())
|
||||
{
|
||||
if (IsProperlyGuarded(ret, context.SemanticModel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We'll report only a single diagnostic per line, the first.
|
||||
var loc = context.Node.GetLocation();
|
||||
if (!loc.IsInSource)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var line = loc.GetLineSpan().StartLinePosition.Line;
|
||||
if (reports.TryGetValue(line, out var diagnostic) && diagnostic.Location.SourceSpan.Start <= loc.SourceSpan.Start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
diagnostic = Diagnostic.Create(platform.Kind == PlatformKind.Uwp ? Analyzer.VersionRule : Analyzer.PlatformRule, loc);
|
||||
|
||||
reports[line] = diagnostic;
|
||||
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
|
||||
private bool IsProperlyGuarded(SyntaxNode node, SemanticModel semanticModel)
|
||||
{
|
||||
foreach (var symbol in GetGuards(node, semanticModel))
|
||||
{
|
||||
if (symbol.ContainingType?.Name == "ApiInformation")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Formatting;
|
||||
using Microsoft.CodeAnalysis.Simplification;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides guard suggestion and can make the suggested changes.
|
||||
/// </summary>
|
||||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PlatformSpecificFixerCS))]
|
||||
[Shared]
|
||||
public class PlatformSpecificFixerCS : CodeFixProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of Diagnostics that can be fixed.
|
||||
/// </summary>
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds
|
||||
{
|
||||
get { return ImmutableArray.Create(Analyzer.PlatformRule.Id, Analyzer.VersionRule.Id); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Fix All provider
|
||||
/// </summary>
|
||||
/// <returns><see cref="WellKnownFixAllProviders"/></returns>
|
||||
public sealed override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers for code fix.
|
||||
/// </summary>
|
||||
/// <param name="context"><see cref="CodeFixContext"/></param>
|
||||
/// <returns>awaitable <see cref="Task"/></returns>
|
||||
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
||||
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Which node are we interested in? -- if the squiggle is over A.B().C,
|
||||
// then we need the largest IdentifierName/SimpleMemberAccess/QualifiedName
|
||||
// that encompasses "C" itself
|
||||
var diagnostic = context.Diagnostics.First();
|
||||
var span = new TextSpan(diagnostic.Location.SourceSpan.End - 1, 1);
|
||||
var node = root.FindToken(span.Start).Parent;
|
||||
|
||||
SyntaxKind nodeKind = node.Kind();
|
||||
|
||||
while (nodeKind != SyntaxKind.IdentifierName && nodeKind != SyntaxKind.SimpleMemberAccessExpression && nodeKind != SyntaxKind.QualifiedName)
|
||||
{
|
||||
node = node.Parent;
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nodeKind = node.Kind();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (node.Parent?.Kind() == SyntaxKind.SimpleMemberAccessExpression)
|
||||
{
|
||||
node = node.Parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.Parent?.Kind() == SyntaxKind.QualifiedName)
|
||||
{
|
||||
node = node.Parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
var target = PlatformSpecificAnalyzerCS.GetTargetOfNode(node, semanticModel);
|
||||
var g = Analyzer.GetGuardForSymbol(target);
|
||||
|
||||
// Introduce a guard? (only if it is a method/accessor/constructor, i.e. somewhere that allows code)
|
||||
var containingBlock = node.FirstAncestorOrSelf<BlockSyntax>();
|
||||
if (containingBlock != null)
|
||||
{
|
||||
var act1 = CodeAction.Create($"Add 'If ApiInformation.{g.KindOfCheck}'", (c) => IntroduceGuardAsync(context.Document, node, g, c), "PlatformSpecificGuard");
|
||||
context.RegisterCodeFix(act1, diagnostic);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Document> IntroduceGuardAsync(Document document, SyntaxNode node, HowToGuard g, CancellationToken cancellationToken)
|
||||
{
|
||||
// + if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent(targetContainingType))
|
||||
// {
|
||||
// old-statement
|
||||
// + }
|
||||
try
|
||||
{
|
||||
var oldStatement = node.FirstAncestorOrSelf<StatementSyntax>();
|
||||
var oldLeadingTrivia = oldStatement.GetLeadingTrivia();
|
||||
|
||||
var conditionReceiver = SyntaxFactory.ParseName($"Windows.Foundation.Metadata.ApiInformation.{g.KindOfCheck}").WithAdditionalAnnotations(Simplifier.Annotation);
|
||||
ArgumentListSyntax conditionArgument = null;
|
||||
|
||||
if (g.MemberToCheck == null)
|
||||
{
|
||||
var conditionString1 = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(g.TypeToCheck));
|
||||
conditionArgument = SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(SyntaxFactory.Argument(conditionString1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
var conditionString1 = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(g.TypeToCheck));
|
||||
var conditionString2 = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(g.MemberToCheck));
|
||||
var conditionInt3 = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(g.ParameterCountToCheck ?? 0));
|
||||
|
||||
IEnumerable<ArgumentSyntax> conditions = null;
|
||||
|
||||
if (g.ParameterCountToCheck.HasValue)
|
||||
{
|
||||
conditions = new ArgumentSyntax[] { SyntaxFactory.Argument(conditionString1), SyntaxFactory.Argument(conditionString2), SyntaxFactory.Argument(conditionInt3) };
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions = new ArgumentSyntax[] { SyntaxFactory.Argument(conditionString1), SyntaxFactory.Argument(conditionString2) };
|
||||
}
|
||||
|
||||
conditionArgument = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(conditions));
|
||||
}
|
||||
|
||||
var condition = SyntaxFactory.InvocationExpression(conditionReceiver, conditionArgument);
|
||||
|
||||
var thenStatements = SyntaxFactory.Block(oldStatement.WithoutLeadingTrivia());
|
||||
var ifStatement = SyntaxFactory.IfStatement(condition, thenStatements).WithLeadingTrivia(oldLeadingTrivia).WithAdditionalAnnotations(Formatter.Annotation);
|
||||
|
||||
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
|
||||
var newRoot = oldRoot.ReplaceNode(oldStatement, ifStatement);
|
||||
|
||||
return document.WithSyntaxRoot(newRoot);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CodeActions;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
using Microsoft.CodeAnalysis.Formatting;
|
||||
using Microsoft.CodeAnalysis.Simplification;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Microsoft.CodeAnalysis.VisualBasic;
|
||||
using Microsoft.CodeAnalysis.VisualBasic.Syntax;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class provides guard suggestion and can make the suggested changes.
|
||||
/// </summary>
|
||||
[ExportCodeFixProvider(LanguageNames.VisualBasic, Name = nameof(PlatformSpecificFixerCS))]
|
||||
[Shared]
|
||||
public class PlatformSpecificFixerVB : CodeFixProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of Diagnostics that can be fixed.
|
||||
/// </summary>
|
||||
public sealed override ImmutableArray<string> FixableDiagnosticIds
|
||||
{
|
||||
get { return ImmutableArray.Create(Analyzer.PlatformRule.Id, Analyzer.VersionRule.Id); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Fix All provider
|
||||
/// </summary>
|
||||
/// <returns><see cref="WellKnownFixAllProviders"/></returns>
|
||||
public sealed override FixAllProvider GetFixAllProvider()
|
||||
{
|
||||
return WellKnownFixAllProviders.BatchFixer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers for code fix.
|
||||
/// </summary>
|
||||
/// <param name="context"><see cref="CodeFixContext"/></param>
|
||||
/// <returns>awaitable <see cref="Task"/></returns>
|
||||
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
||||
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Which node are we interested in? -- if the squiggle is over A.B().C,
|
||||
// then we need the largest IdentifierName/SimpleMemberAccess/QualifiedName
|
||||
// that encompasses "C" itself
|
||||
var diagnostic = context.Diagnostics.First();
|
||||
var span = new TextSpan(diagnostic.Location.SourceSpan.End - 1, 1);
|
||||
var node = root.FindToken(span.Start).Parent;
|
||||
|
||||
SyntaxKind nodeKind = node.Kind();
|
||||
|
||||
while (nodeKind != SyntaxKind.IdentifierName && nodeKind != SyntaxKind.SimpleMemberAccessExpression && nodeKind != SyntaxKind.QualifiedName)
|
||||
{
|
||||
node = node.Parent;
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
nodeKind = node.Kind();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (node.Parent?.Kind() == SyntaxKind.SimpleMemberAccessExpression)
|
||||
{
|
||||
node = node.Parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (node.Parent?.Kind() == SyntaxKind.QualifiedName)
|
||||
{
|
||||
node = node.Parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
var target = PlatformSpecificAnalyzerVB.GetTargetOfNode(node, semanticModel);
|
||||
var g = Analyzer.GetGuardForSymbol(target);
|
||||
|
||||
// Introduce a guard? (only if it is a method/accessor/constructor, i.e. somewhere that allows code)
|
||||
var containingBlock = node.FirstAncestorOrSelf<MethodBlockBaseSyntax>();
|
||||
if (containingBlock != null)
|
||||
{
|
||||
var act1 = CodeAction.Create($"Add 'If ApiInformation.{g.KindOfCheck}'", (c) => IntroduceGuardAsync(context.Document, node, g, c), "PlatformSpecificGuard");
|
||||
context.RegisterCodeFix(act1, diagnostic);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<Document> IntroduceGuardAsync(Document document, SyntaxNode node, HowToGuard g, CancellationToken cancellationToken)
|
||||
{
|
||||
// + If Windows.Foundation.Metadata.ApiInformation.IsTypePresent(targetContainingType) Then
|
||||
// old-statement
|
||||
// + End If
|
||||
try
|
||||
{
|
||||
var oldStatement = node.FirstAncestorOrSelf<StatementSyntax>();
|
||||
var oldLeadingTrivia = oldStatement.GetLeadingTrivia();
|
||||
|
||||
var conditionReceiver = SyntaxFactory.ParseName($"Windows.Foundation.Metadata.ApiInformation.{g.KindOfCheck}").WithAdditionalAnnotations(Simplifier.Annotation);
|
||||
ArgumentListSyntax conditionArgument = null;
|
||||
|
||||
if (g.MemberToCheck == null)
|
||||
{
|
||||
var conditionString1 = SyntaxFactory.StringLiteralExpression(SyntaxFactory.StringLiteralToken($"\"{g.TypeToCheck}\"", g.TypeToCheck));
|
||||
conditionArgument = SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(SyntaxFactory.SimpleArgument(conditionString1)));
|
||||
}
|
||||
else
|
||||
{
|
||||
var conditionString1 = SyntaxFactory.StringLiteralExpression(SyntaxFactory.StringLiteralToken($"\"{g.TypeToCheck}\"", g.TypeToCheck));
|
||||
var conditionString2 = SyntaxFactory.StringLiteralExpression(SyntaxFactory.StringLiteralToken($"\"{g.MemberToCheck}\"", g.MemberToCheck));
|
||||
var conditionInt3 = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(g.ParameterCountToCheck ?? 0));
|
||||
|
||||
IEnumerable<ArgumentSyntax> conditions = null;
|
||||
|
||||
if (g.ParameterCountToCheck.HasValue)
|
||||
{
|
||||
conditions = new ArgumentSyntax[] { SyntaxFactory.SimpleArgument(conditionString1), SyntaxFactory.SimpleArgument(conditionString2), SyntaxFactory.SimpleArgument(conditionInt3) };
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions = new ArgumentSyntax[] { SyntaxFactory.SimpleArgument(conditionString1), SyntaxFactory.SimpleArgument(conditionString2) };
|
||||
}
|
||||
|
||||
conditionArgument = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(conditions));
|
||||
}
|
||||
|
||||
var condition = SyntaxFactory.InvocationExpression(conditionReceiver, conditionArgument);
|
||||
|
||||
var ifStatement = SyntaxFactory.IfStatement(condition);
|
||||
var thenStatements = SyntaxFactory.SingletonList(oldStatement.WithoutLeadingTrivia());
|
||||
var ifBlock = SyntaxFactory.MultiLineIfBlock(ifStatement).WithStatements(thenStatements).WithLeadingTrivia(oldLeadingTrivia).WithAdditionalAnnotations(Formatter.Annotation);
|
||||
|
||||
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
|
||||
var newRoot = oldRoot.ReplaceNode(oldStatement, ifBlock);
|
||||
|
||||
return document.WithSyntaxRoot(newRoot);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
if($project.Object.SupportsPackageDependencyResolution)
|
||||
{
|
||||
if($project.Object.SupportsPackageDependencyResolution())
|
||||
{
|
||||
# Do not install analyzers via install.ps1, instead let the project system handle it.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
|
||||
|
||||
foreach($analyzersPath in $analyzersPaths)
|
||||
{
|
||||
if (Test-Path $analyzersPath)
|
||||
{
|
||||
# Install the language agnostic analyzers.
|
||||
foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
|
||||
{
|
||||
if($project.Object.AnalyzerReferences)
|
||||
{
|
||||
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# $project.Type gives the language name like (C# or VB.NET)
|
||||
$languageFolder = ""
|
||||
if($project.Type -eq "C#")
|
||||
{
|
||||
$languageFolder = "cs"
|
||||
}
|
||||
if($project.Type -eq "VB.NET")
|
||||
{
|
||||
$languageFolder = "vb"
|
||||
}
|
||||
if($languageFolder -eq "")
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
foreach($analyzersPath in $analyzersPaths)
|
||||
{
|
||||
# Install language specific analyzers.
|
||||
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
|
||||
if (Test-Path $languageAnalyzersPath)
|
||||
{
|
||||
foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
|
||||
{
|
||||
if($project.Object.AnalyzerReferences)
|
||||
{
|
||||
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
if($project.Object.SupportsPackageDependencyResolution)
|
||||
{
|
||||
if($project.Object.SupportsPackageDependencyResolution())
|
||||
{
|
||||
# Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
|
||||
|
||||
foreach($analyzersPath in $analyzersPaths)
|
||||
{
|
||||
# Uninstall the language agnostic analyzers.
|
||||
if (Test-Path $analyzersPath)
|
||||
{
|
||||
foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
|
||||
{
|
||||
if($project.Object.AnalyzerReferences)
|
||||
{
|
||||
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# $project.Type gives the language name like (C# or VB.NET)
|
||||
$languageFolder = ""
|
||||
if($project.Type -eq "C#")
|
||||
{
|
||||
$languageFolder = "cs"
|
||||
}
|
||||
if($project.Type -eq "VB.NET")
|
||||
{
|
||||
$languageFolder = "vb"
|
||||
}
|
||||
if($languageFolder -eq "")
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
foreach($analyzersPath in $analyzersPaths)
|
||||
{
|
||||
# Uninstall language specific analyzers.
|
||||
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
|
||||
if (Test-Path $languageAnalyzersPath)
|
||||
{
|
||||
foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
|
||||
{
|
||||
if($project.Object.AnalyzerReferences)
|
||||
{
|
||||
try
|
||||
{
|
||||
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -280,7 +280,6 @@
|
|||
<Content Include="SamplePages\RemoteDeviceHelper\RemoteDeviceHelper.png" />
|
||||
<Content Include="SamplePages\ImageCropper\ImageCropper.png" />
|
||||
<Content Include="SamplePages\StaggeredLayout\StaggeredLayout.png" />
|
||||
<Content Include="SamplePages\TabView\TabView.png" />
|
||||
<Content Include="SamplePages\PeoplePicker\PeoplePicker.png" />
|
||||
<Content Include="SamplePages\PersonView\PersonView.png" />
|
||||
<Content Include="SamplePages\TokenizingTextBox\TokenizingTextBox.png" />
|
||||
|
@ -301,7 +300,6 @@
|
|||
<Content Include="SamplePages\DataGrid\DataGrid.png" />
|
||||
<Content Include="SamplePages\DispatcherQueueHelper\DispatchQueueHelper.png" />
|
||||
<Content Include="SamplePages\DockPanel\DockPanel.png" />
|
||||
<Content Include="SamplePages\Facebook Service\FacebookLogo.png" />
|
||||
<Content Include="SamplePages\LoginButton\LoginButton.png" />
|
||||
<Content Include="SamplePages\FadeHeader\FadeHeaderBehavior.png" />
|
||||
<Content Include="SamplePages\Fade\FadeBehavior.png" />
|
||||
|
@ -318,7 +316,6 @@
|
|||
<Content Include="SamplePages\AlignmentGrid\AlignmentGrid.png" />
|
||||
<Content Include="SamplePages\HeaderedContentControl\HeaderedContentControl.png" />
|
||||
<Content Include="SamplePages\HeaderedItemsControl\HeaderedItemsControl.png" />
|
||||
<Content Include="SamplePages\HeaderedTextBlock\HeaderedTextBlock.png" />
|
||||
<Content Include="SamplePages\ImageBlendBrush\ImageBlendBrush.png" />
|
||||
<Content Include="SamplePages\ImageCache\ImageCache.png" />
|
||||
<Content Include="SamplePages\ImageEx\ImageEx.png" />
|
||||
|
@ -368,8 +365,6 @@
|
|||
<Content Include="SamplePages\Twitter Service\TwitterLogo.png" />
|
||||
<Content Include="SamplePages\Twitter Service\TwitterCode.bind" />
|
||||
<Content Include="SamplePages\Twitter Service\icon.png" />
|
||||
<Content Include="SamplePages\Facebook Service\FacebookCode.bind" />
|
||||
<Content Include="SamplePages\HeaderedTextBlock\HeaderedTextBlockCode.bind" />
|
||||
<Content Include="SamplePages\ViewExtensions\ViewExtensions.png" />
|
||||
<Content Include="SamplePages\ViewportBehavior\ViewportBehavior.png" />
|
||||
<Content Include="SamplePages\Visual Extensions\VisualExtensions.png" />
|
||||
|
@ -512,9 +507,6 @@
|
|||
<Content Include="SamplePages\RemoteDevicePicker\RemoteDevicePickerCode.bind" />
|
||||
<Content Include="SamplePages\DataGrid\DataGridCode.bind" />
|
||||
<Content Include="SamplePages\ViewportBehavior\ViewportBehaviorCode.bind" />
|
||||
<Content Include="SamplePages\TabView\TabViewXaml.bind">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="SamplePages\Weibo Service\WeiboCode.bind" />
|
||||
<Compile Include="Common\TextBlockHyperlinkBehavior.cs" />
|
||||
<Compile Include="SamplePages\AutoFocusBehavior\AutoFocusBehaviorPage.xaml.cs">
|
||||
|
@ -664,9 +656,6 @@
|
|||
<Compile Include="Models\LandingPageResource.cs" />
|
||||
<Compile Include="Models\LandingPageLink.cs" />
|
||||
<Compile Include="Models\PaneState.cs" />
|
||||
<Compile Include="SamplePages\TabView\TabViewPage.xaml.cs">
|
||||
<DependentUpon>TabViewPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\UniformGrid\UniformGridPage.xaml.cs">
|
||||
<DependentUpon>UniformGridPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -781,9 +770,6 @@
|
|||
<Compile Include="SamplePages\RemoteDevicePicker\RemoteDevicePickerControlPage.xaml.cs">
|
||||
<DependentUpon>RemoteDevicePickerControlPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\RssParser\RssParserPage.xaml.cs">
|
||||
<DependentUpon>RssParserPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\RadialGradientBrush\RadialGradientBrushPage.xaml.cs">
|
||||
<DependentUpon>RadialGradientBrushPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -929,9 +915,6 @@
|
|||
<Compile Include="SamplePages\Fade\FadeBehaviorPage.xaml.cs">
|
||||
<DependentUpon>FadeBehaviorPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\HeaderedTextBlock\HeaderedTextBlockPage.xaml.cs">
|
||||
<DependentUpon>HeaderedTextBlockPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="SamplePages\ImageEx\ImageExPage.xaml.cs">
|
||||
<DependentUpon>ImageExPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -1072,10 +1055,6 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\TabView\TabViewPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\PeoplePicker\PeoplePickerPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
@ -1260,10 +1239,6 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\RssParser\RssParserPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\ScrollViewerExtensions\ScrollViewerExtensionsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
@ -1440,10 +1415,6 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SamplePages\HeaderedTextBlock\HeaderedTextBlockPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="SamplePages\ImageEx\ImageExPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// Initialize service
|
||||
FacebookService.Instance.Initialize(AppIDText.Text);
|
||||
|
||||
// Login to Facebook
|
||||
if (!await FacebookService.Instance.LoginAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get user's feed
|
||||
ListView.ItemsSource = await FacebookService.Instance.RequestAsync(FacebookDataConfig.MyFeed, 50);
|
||||
|
||||
// Get current user profile picture
|
||||
ProfileImage.DataContext = await FacebookService.Instance.GetUserPictureInfoAsync();
|
||||
|
||||
// Post a message on your wall using Facebook Dialog
|
||||
await FacebookService.Instance.PostToFeedWithDialogAsync(TitleText.Text, DescriptionText.Text, UrlText.Text);
|
||||
|
||||
// Get current user's photo albums
|
||||
await FacebookService.Instance.GetUserAlbumsAsync();
|
||||
|
||||
// Get current user's photos by album Id
|
||||
await FacebookService.Instance.GetUserPhotosByAlbumIdAsync(addedItem.Id);
|
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.3 KiB |
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 871 B |
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 3.2 KiB |
|
@ -1,16 +0,0 @@
|
|||
<Page
|
||||
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"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<controls:HeaderedTextBlock
|
||||
Header="@[Header:String:Name]"
|
||||
Text="@[Text:String:Windows Community Toolkit]"
|
||||
Orientation="@[Orientation:Enum:Orientation.Vertical]"
|
||||
Margin="20,10,0,0" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,9 +0,0 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.HeaderedTextBlockPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
</Page>
|
|
@ -1,18 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.SampleApp.Models;
|
||||
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public sealed partial class HeaderedTextBlockPage
|
||||
{
|
||||
public HeaderedTextBlockPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
<Page
|
||||
x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.RssParserPage"
|
||||
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"
|
||||
xmlns:rss="using:Microsoft.Toolkit.Parsers.Rss"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel>
|
||||
<TextBox x:Name="UrlBox"
|
||||
Header="Url"
|
||||
Text="{x:Bind Url, Mode=TwoWay}" />
|
||||
|
||||
<Button
|
||||
Content="Load"
|
||||
Margin="0,10,0,0"
|
||||
Click="{x:Bind ParseRSS}" />
|
||||
|
||||
<TextBlock Text="Feed" Margin="0,10,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<ListView
|
||||
x:Name="RSSList"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind RSSFeed}"
|
||||
SelectionChanged="RSSList_SelectionChanged">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="rss:RssSchema">
|
||||
<StackPanel Padding="10">
|
||||
<TextBlock
|
||||
Text="{x:Bind Title}"
|
||||
FontSize="18"
|
||||
FontWeight="Bold" />
|
||||
<TextBlock Text="{x:Bind Summary}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,70 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Toolkit.Parsers.Rss;
|
||||
using Windows.System;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public sealed partial class RssParserPage : Page
|
||||
{
|
||||
public ObservableCollection<RssSchema> RSSFeed { get; } = new ObservableCollection<RssSchema>();
|
||||
|
||||
public RssParserPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
ParseRSS();
|
||||
}
|
||||
|
||||
public string Url { get; set; } = "https://visualstudiomagazine.com/rss-feeds/news.aspx";
|
||||
|
||||
public async void ParseRSS()
|
||||
{
|
||||
string feed = null;
|
||||
RSSFeed.Clear();
|
||||
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
feed = await client.GetStringAsync(Url);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (feed != null)
|
||||
{
|
||||
var parser = new RssParser();
|
||||
var rss = parser.Parse(feed);
|
||||
|
||||
foreach (var element in rss)
|
||||
{
|
||||
RSSFeed.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void RSSList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (RSSList.SelectedItem is RssSchema rssItem)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri(rssItem.FeedUrl));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
RSSList.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabView/TabView.png
Двоичные данные
Microsoft.Toolkit.Uwp.SampleApp/SamplePages/TabView/TabView.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -1,19 +0,0 @@
|
|||
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.TabViewPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid x:Name="XamlRoot"/>
|
||||
<controls:InAppNotification x:Name="TabViewNotification"
|
||||
ShowDismissButton="True"
|
||||
AnimationDuration="00:00:00.1000000"
|
||||
VerticalOffset="100"
|
||||
HorizontalOffset="0"
|
||||
StackMode="Replace" />
|
||||
</Grid>
|
||||
</Page>
|
|
@ -1,73 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
|
||||
{
|
||||
public sealed partial class TabViewPage : Page, IXamlRenderListener
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
private TabView _tabs;
|
||||
|
||||
private int _counter = 1;
|
||||
|
||||
public TabViewPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public void OnXamlRendered(FrameworkElement control)
|
||||
{
|
||||
_tabs = control.FindChildByName("Tabs") as TabView;
|
||||
if (_tabs != null)
|
||||
{
|
||||
_tabs.TabDraggedOutside += Tabs_TabDraggedOutside;
|
||||
_tabs.TabClosing += Tabs_TabClosing;
|
||||
}
|
||||
|
||||
var btn = control.FindDescendantByName("AddTabButtonUpper") as Button;
|
||||
if (btn != null)
|
||||
{
|
||||
btn.Click += AddUpperTabClick;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddUpperTabClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_tabs.Items.Add(new TabViewItem()
|
||||
{
|
||||
Header = "Untitled " + _counter,
|
||||
Icon = new SymbolIcon(Symbol.Document),
|
||||
Content = "This is new tab #" + _counter++ + "."
|
||||
});
|
||||
}
|
||||
|
||||
private void Tabs_TabClosing(object sender, TabClosingEventArgs e)
|
||||
{
|
||||
TabViewNotification.Show("You're closing the '" + e.Tab.Header + "' tab.", 2000);
|
||||
}
|
||||
|
||||
private void Tabs_TabDraggedOutside(object sender, TabDraggedOutsideEventArgs e)
|
||||
{
|
||||
// The sample app let's you drag items from a static TabView with TabViewItem's pre-defined.
|
||||
// In the case of data bound scenarios e.Item should be your data item, and e.Tab should always be the TabViewItem.
|
||||
var str = e.Item.ToString();
|
||||
|
||||
if (e.Tab != null)
|
||||
{
|
||||
str = e.Tab.Header.ToString();
|
||||
}
|
||||
|
||||
TabViewNotification.Show("Tore Tab '" + str + "' Outside of TabView.", 2000);
|
||||
}
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.SampleApp.SamplePages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI.Extensions"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<controls:TabView x:Name="Tabs"
|
||||
TabWidthBehavior="@[Tab Width Behavior:Enum:TabWidthMode.Actual]"
|
||||
CanCloseTabs="@[Can Close Tabs:Bool:False]"
|
||||
IsCloseButtonOverlay="@[Overlay Close Button:Bool:False]"
|
||||
CanDragItems="@[Drag and Drop Enabled:Bool:False]"
|
||||
CanReorderItems="@[Drag and Drop Enabled]"
|
||||
AllowDrop="@[Drag and Drop Enabled]"
|
||||
SelectedTabWidth="200">
|
||||
<controls:TabView.Resources>
|
||||
<x:Double x:Key="TabViewItemHeaderMinHeight">40</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderMinWidth">48</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderMaxWidth">200</x:Double>
|
||||
</controls:TabView.Resources>
|
||||
<controls:TabView.Header>
|
||||
<TextBlock Padding="16,8,0,8" FontSize="16" FontWeight="Bold" Text="TabView Control Header"/>
|
||||
</controls:TabView.Header>
|
||||
<controls:TabView.TabStartHeader>
|
||||
<Button Width="48"
|
||||
Height="{StaticResource TabViewItemHeaderMinHeight}"
|
||||
Margin="0,0,-1,0"
|
||||
BorderThickness="1"
|
||||
Background="Transparent"
|
||||
Style="{StaticResource ButtonRevealStyle}"
|
||||
Padding="2,2,0,0">
|
||||
<Viewbox MaxWidth="16" MaxHeight="16">
|
||||
<SymbolIcon Symbol="AddFriend"/>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</controls:TabView.TabStartHeader>
|
||||
|
||||
<!-- Tabs -->
|
||||
<controls:TabViewItem Header="Home" Icon="Home">
|
||||
<TextBlock Padding="16">The TabView control has multiple uses.</TextBlock>
|
||||
</controls:TabViewItem>
|
||||
<controls:TabViewItem Header="Tab 2 Has Longer Text" Icon="Audio">
|
||||
<TextBlock Padding="16">It has a lot of versatility out of the box for different scenarios.</TextBlock>
|
||||
</controls:TabViewItem>
|
||||
<controls:TabViewItem Header="Tab 3" Icon="Video">
|
||||
<TextBlock Padding="16">You can enable drag-and-drop and reorder the tabs too.</TextBlock>
|
||||
</controls:TabViewItem>
|
||||
<controls:TabViewItem Header="Not Closable" Icon="Calendar" IsClosable="False">
|
||||
<TextBlock Padding="16">This tab isn't closable because its IsClosable property is set to False, even when CanCloseTabs is True.</TextBlock>
|
||||
</controls:TabViewItem>
|
||||
|
||||
<controls:TabView.TabActionHeader>
|
||||
<Button x:Name="AddTabButtonUpper"
|
||||
Width="48"
|
||||
Height="{StaticResource TabViewItemHeaderMinHeight}"
|
||||
Margin="-1,0,0,0"
|
||||
BorderThickness="1"
|
||||
Background="Transparent"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Viewbox MaxWidth="16"
|
||||
MaxHeight="16">
|
||||
<FontIcon FontFamily="Segoe MDL2 Assets"
|
||||
Glyph="" />
|
||||
</Viewbox>
|
||||
</Button>
|
||||
|
||||
</controls:TabView.TabActionHeader>
|
||||
|
||||
<controls:TabView.TabEndHeader>
|
||||
<Button Width="48"
|
||||
Height="{StaticResource TabViewItemHeaderMinHeight}"
|
||||
Margin="-1,0,0,0"
|
||||
BorderThickness="1"
|
||||
Background="Transparent"
|
||||
Style="{StaticResource ButtonRevealStyle}">
|
||||
<Viewbox MaxWidth="16" MaxHeight="16">
|
||||
<SymbolIcon Symbol="Setting"/>
|
||||
</Viewbox>
|
||||
</Button>
|
||||
</controls:TabView.TabEndHeader>
|
||||
|
||||
<controls:TabView.Footer>
|
||||
<TextBlock Padding="16,8,16,8"
|
||||
HorizontalAlignment="Right"
|
||||
FontSize="16" FontWeight="Bold"
|
||||
Text="TabView Control Footer" />
|
||||
</controls:TabView.Footer>
|
||||
</controls:TabView>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -14,18 +14,6 @@
|
|||
"Icon": "/SamplePages/TextToolbar/TextToolbar.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/TextToolbar.md"
|
||||
},
|
||||
{
|
||||
"Name": "TabView",
|
||||
"Type": "TabViewPage",
|
||||
"Subcategory": "Layout",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "Please migrate to the TabView control from WinUI, this control will be removed in a future release. https://aka.ms/winui",
|
||||
"About": "A control for displaying multiple items in the same space and allows a user to easily switch between them.",
|
||||
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/TabView",
|
||||
"XamlCodeFile": "TabViewXaml.bind",
|
||||
"Icon": "/SamplePages/TabView/TabView.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/TabView.md"
|
||||
},
|
||||
{
|
||||
"Name": "DataGrid",
|
||||
"Type": "DataGridPage",
|
||||
|
@ -86,18 +74,6 @@
|
|||
"Icon": "/SamplePages/ImageEx/ImageEx.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/ImageEx.md"
|
||||
},
|
||||
{
|
||||
"Name": "HeaderedTextBlock",
|
||||
"Type": "HeaderedTextBlockPage",
|
||||
"Subcategory": "Layout",
|
||||
"About": "The HeaderedTextBlock control is designed to provide a header for read only text. This control is useful for displaying read only forms.",
|
||||
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/HeaderedTextBlock",
|
||||
"XamlCodeFile": "HeaderedTextBlockCode.bind",
|
||||
"Icon": "/SamplePages/HeaderedTextBlock/HeaderedTextBlock.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/controls/HeaderedTextBlock.md",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "The HeaderedTextBlock has been replaced with the HeaderedContentControl and will be removed in a future major release."
|
||||
},
|
||||
{
|
||||
"Name": "MasterDetailsView",
|
||||
"Type": "MasterDetailsViewPage",
|
||||
|
@ -658,16 +634,6 @@
|
|||
"Name": "Services",
|
||||
"Icon": "Icons/Services.png",
|
||||
"Samples": [
|
||||
{
|
||||
"Name": "Facebook Service",
|
||||
"About": "The Facebook Service allows you to retrieve or publish data to Facebook graph.",
|
||||
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.Services/Services/Facebook",
|
||||
"CodeFile": "FacebookCode.bind",
|
||||
"Icon": "/SamplePages/Facebook Service/FacebookLogo.png",
|
||||
"BadgeUpdateVersionRequired": "DEPRECATED",
|
||||
"DeprecatedWarning": "The underlying library, winsdkfb, which the FacebookService relies on is not currently maintained.",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/services/Facebook.md"
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft Graph Service",
|
||||
"About": "These providers help you easily authenticate and connect to the Microsoft Graph.",
|
||||
|
@ -909,14 +875,6 @@
|
|||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/MarkdownParser.md"
|
||||
},
|
||||
{
|
||||
"Name": "RSS Parser",
|
||||
"Type": "RssParserPage",
|
||||
"Subcategory": "Parser",
|
||||
"About": "The RSS Parser allows you to parse an RSS content String into RSS Schema.",
|
||||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/RssParser.md"
|
||||
},
|
||||
{
|
||||
"Name": "LiveTile",
|
||||
"Type": "LiveTilePage",
|
||||
|
@ -964,14 +922,6 @@
|
|||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/high-performance/Introduction.md"
|
||||
},
|
||||
{
|
||||
"Name": "PlatformSpecificAnalyzer",
|
||||
"Subcategory": "Developer",
|
||||
"About": "Platform Specific Analyzer is a Roslyn analyzer that analyzes and suggests code fixes to ensure that any version / platform specific API are guarded by correct runtime checks",
|
||||
"CodeUrl": "https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer",
|
||||
"Icon": "/Assets/Helpers.png",
|
||||
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/platform-specific/PlatformSpecificAnalyzer.md"
|
||||
},
|
||||
{
|
||||
"Name": "CompareStateTrigger",
|
||||
"Type": "CompareStateTriggerPage",
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>uap10.0.16299</TargetFramework>
|
||||
<Title>Windows Community Toolkit Services</Title>
|
||||
<Description>This library enables access to Facebook. It is part of the Windows Community Toolkit.</Description>
|
||||
<PackageTags>UWP Toolkit Windows OAuth Facebook</PackageTags>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="winsdkfb" Version="0.12.20161020.4" />
|
||||
|
||||
<!-- This is here to prevent a conflict in certain circumstances -->
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
|
||||
|
||||
<ProjectReference Include="..\Microsoft.Toolkit.Uwp\Microsoft.Toolkit.Uwp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,4 +0,0 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cmicrosoftgraph_005Cmessage/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cmicrosoftgraph_005Cuser/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="Microsoft.Toolkit.Uwp.Services">
|
||||
</Library>
|
||||
</Directives>
|
|
@ -1,42 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly types Facebook Album object. Partial for extending properties.
|
||||
/// </summary>
|
||||
public partial class FacebookAlbum
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string description of the strongly typed properties in this model.
|
||||
/// </summary>
|
||||
public static string Fields => "id, name, description, cover_photo, picture";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets id property.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets name property.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets description property.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets cover_photo property.
|
||||
/// </summary>
|
||||
public FacebookPhoto Cover_Photo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets picture property.
|
||||
/// </summary>
|
||||
public FacebookPictureData Picture { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Configuration object for specifying richer query information.
|
||||
/// </summary>
|
||||
public class FacebookDataConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a predefined config to get user feed. The feed of posts (including status updates) and links published by this person, or by others on this person's profile
|
||||
/// </summary>
|
||||
public static FacebookDataConfig MyFeed => new FacebookDataConfig { Query = "/me/feed" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a predefined config to show only the posts that were published by this person
|
||||
/// </summary>
|
||||
public static FacebookDataConfig MyPosts => new FacebookDataConfig { Query = "/me/posts" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets a predefined config to show only the posts that this person was tagged in
|
||||
/// </summary>
|
||||
public static FacebookDataConfig MyTagged => new FacebookDataConfig { Query = "/me/tagged" };
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the query string for filtering service results.
|
||||
/// </summary>
|
||||
public string Query { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Class used to store JSON data response from Facebook
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the inner data</typeparam>
|
||||
internal class FacebookDataHost<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets internal data.
|
||||
/// </summary>
|
||||
public T Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Facebook OAuth tokens.
|
||||
/// </summary>
|
||||
public class FacebookOAuthTokens
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets facebook AppId.
|
||||
/// </summary>
|
||||
public string AppId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets Windows Store ID.
|
||||
/// </summary>
|
||||
public string WindowsStoreId { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// List of user related data permissions
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FacebookPermissions
|
||||
{
|
||||
/// <summary>
|
||||
/// Public profile
|
||||
/// </summary>
|
||||
PublicProfile = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Email
|
||||
/// </summary>
|
||||
Email = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Publish actions
|
||||
/// </summary>
|
||||
PublishActions = 4,
|
||||
|
||||
/// <summary>
|
||||
/// About me
|
||||
/// </summary>
|
||||
UserAboutMe = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Birthday
|
||||
/// </summary>
|
||||
UserBirthday = 16,
|
||||
|
||||
/// <summary>
|
||||
/// Education history
|
||||
/// </summary>
|
||||
UserEducationHistory = 32,
|
||||
|
||||
/// <summary>
|
||||
/// Friends
|
||||
/// </summary>
|
||||
UserFriends = 64,
|
||||
|
||||
/// <summary>
|
||||
/// Games activity
|
||||
/// </summary>
|
||||
UserGamesActivity = 128,
|
||||
|
||||
/// <summary>
|
||||
/// Hometown
|
||||
/// </summary>
|
||||
UserHometown = 256,
|
||||
|
||||
/// <summary>
|
||||
/// Likes
|
||||
/// </summary>
|
||||
UserLikes = 512,
|
||||
|
||||
/// <summary>
|
||||
/// Location
|
||||
/// </summary>
|
||||
UserLocation = 1024,
|
||||
|
||||
/// <summary>
|
||||
/// Photos
|
||||
/// </summary>
|
||||
UserPhotos = 2048,
|
||||
|
||||
/// <summary>
|
||||
/// Posts
|
||||
/// </summary>
|
||||
UserPosts = 4096,
|
||||
|
||||
/// <summary>
|
||||
/// Relationship details
|
||||
/// </summary>
|
||||
UserRelationshipDetails = 8192,
|
||||
|
||||
/// <summary>
|
||||
/// Relationships
|
||||
/// </summary>
|
||||
UserRelationships = 16384,
|
||||
|
||||
/// <summary>
|
||||
/// Religion and politics
|
||||
/// </summary>
|
||||
UserReligionPolitics = 32768,
|
||||
|
||||
/// <summary>
|
||||
/// Status
|
||||
/// </summary>
|
||||
UserStatus = 65536,
|
||||
|
||||
/// <summary>
|
||||
/// Tagged places
|
||||
/// </summary>
|
||||
UserTaggedPlaces = 131072,
|
||||
|
||||
/// <summary>
|
||||
/// Videos
|
||||
/// </summary>
|
||||
UserVideos = 262144,
|
||||
|
||||
/// <summary>
|
||||
/// Website
|
||||
/// </summary>
|
||||
UserWebsite = 524288,
|
||||
|
||||
/// <summary>
|
||||
/// WorkHistory
|
||||
/// </summary>
|
||||
UserWorkHistory = 1048576
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly types Facebook Photo object. Partial for extending properties.
|
||||
/// </summary>
|
||||
public partial class FacebookPhoto
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string description of the strongly typed properties in this model.
|
||||
/// </summary>
|
||||
public static string Fields => "id, album, link, created_time, name, images, picture";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets id property.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets album property.
|
||||
/// </summary>
|
||||
public FacebookAlbum Album { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a link to the entity instance.
|
||||
/// </summary>
|
||||
public string Link { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets time the entity instance was created.
|
||||
/// </summary>
|
||||
public DateTime Created_Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets name property.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets images property.
|
||||
/// </summary>
|
||||
public List<FacebookPlatformImageSource> Images { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets picture property.
|
||||
/// </summary>
|
||||
public string Picture { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed object for presenting picture data returned from service provider.
|
||||
/// </summary>
|
||||
public class FacebookPicture
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the picture is a silhouette or not.
|
||||
/// </summary>
|
||||
public bool Is_Silhouette { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an url to the picture.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the picture.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the url of the page with the picture.
|
||||
/// </summary>
|
||||
public string Link { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly types Facebook Picture object. Partial for extending properties.
|
||||
/// </summary>
|
||||
public partial class FacebookPictureData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets data property.
|
||||
/// </summary>
|
||||
public FacebookPicture Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly types Facebook PlatformImageSource object. Partial for extending properties.
|
||||
/// </summary>
|
||||
public partial class FacebookPlatformImageSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string description of the strongly typed properties in this model.
|
||||
/// </summary>
|
||||
public static string Fields => "height, source, width";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets height property.
|
||||
/// </summary>
|
||||
public string Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets source property.
|
||||
/// </summary>
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets width property.
|
||||
/// </summary>
|
||||
public string Width { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Strongly typed object for presenting post data returned from service provider.
|
||||
/// </summary>
|
||||
public class FacebookPost
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a string description of the strongly typed properties in this model.
|
||||
/// </summary>
|
||||
public static string Fields => "id, message, created_time, link, full_picture";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets id property.
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets message or post text.
|
||||
/// </summary>
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets time the entity instance was created.
|
||||
/// </summary>
|
||||
public DateTime Created_Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a link to the entity instance.
|
||||
/// </summary>
|
||||
public string Link { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a link to the accompanying image.
|
||||
/// </summary>
|
||||
public string Full_Picture { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Foundation.Collections;
|
||||
using winsdkfb;
|
||||
using winsdkfb.Graph;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Type to handle paged requests to Facebook Graph.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Strong type to return.</typeparam>
|
||||
public class FacebookRequestSource<T> : Collections.IIncrementalSource<T>
|
||||
{
|
||||
private bool _isFirstCall = true;
|
||||
|
||||
private FBPaginatedArray _paginatedArray;
|
||||
|
||||
private FacebookDataConfig _config;
|
||||
|
||||
private string _fields;
|
||||
|
||||
private PropertySet _propertySet;
|
||||
|
||||
private FBJsonClassFactory _factory;
|
||||
|
||||
private string _limit;
|
||||
|
||||
private int _maxPages;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FacebookRequestSource{T}"/> class.
|
||||
/// </summary>
|
||||
public FacebookRequestSource()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FacebookRequestSource{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="config">Config containing query information.</param>
|
||||
/// <param name="fields">Comma-separated list of properties expected in the JSON response. Accompanying properties must be found on the strong-typed T.</param>
|
||||
/// <param name="limit">A string representation of the number of records for page - i.e. pageSize.</param>
|
||||
/// <param name="maxPages">Upper limit of pages to return.</param>
|
||||
public FacebookRequestSource(FacebookDataConfig config, string fields, string limit, int maxPages)
|
||||
{
|
||||
_config = config;
|
||||
_fields = fields;
|
||||
_limit = limit;
|
||||
_maxPages = maxPages;
|
||||
|
||||
_propertySet = new PropertySet { { "fields", _fields }, { "limit", _limit } };
|
||||
|
||||
_factory = new FBJsonClassFactory(s => JsonConvert.DeserializeObject(s, typeof(T)));
|
||||
|
||||
// FBPaginatedArray does not allow us to set page size per request so we must go with first supplied - see https://github.com/Microsoft/winsdkfb/issues/221
|
||||
_paginatedArray = new FBPaginatedArray(_config.Query, _propertySet, _factory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns strong typed page of data.
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Page number.</param>
|
||||
/// <param name="pageSize">Size of page.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Strong typed page of data.</returns>
|
||||
public async Task<IEnumerable<T>> GetPagedItemsAsync(int pageIndex, int pageSize, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (_isFirstCall)
|
||||
{
|
||||
var result = await _paginatedArray.FirstAsync();
|
||||
|
||||
return ProcessResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_paginatedArray.HasNext && (pageIndex < _maxPages))
|
||||
{
|
||||
var result = await _paginatedArray.NextAsync();
|
||||
return ProcessResult(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<T> ProcessResult(FBResult result)
|
||||
{
|
||||
List<T> items = new List<T>();
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
IReadOnlyList<object> processedResults = (IReadOnlyList<object>)result.Object;
|
||||
|
||||
foreach (T processedResult in processedResults)
|
||||
{
|
||||
items.Add(processedResult);
|
||||
}
|
||||
|
||||
_isFirstCall = false;
|
||||
return items;
|
||||
}
|
||||
|
||||
throw new Exception(result.ErrorInfo?.Message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,515 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.Security.Authentication.Web;
|
||||
using Windows.Storage.Streams;
|
||||
using winsdkfb;
|
||||
using winsdkfb.Graph;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.Services.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for connecting to Facebook.
|
||||
/// </summary>
|
||||
[Obsolete("The underlying library, winsdkfb, which the FacebookService relies on is not currently maintained.")]
|
||||
public class FacebookService
|
||||
{
|
||||
/// <summary>
|
||||
/// Field for tracking initialization status.
|
||||
/// </summary>
|
||||
private bool isInitialized;
|
||||
|
||||
/// <summary>
|
||||
/// List of permissions required by the app.
|
||||
/// </summary>
|
||||
private FBPermissions permissions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a Windows Store ID associated with the current app
|
||||
/// </summary>
|
||||
public string WindowsStoreId => WebAuthenticationBroker.GetCurrentApplicationCallbackUri().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FacebookService"/> class.
|
||||
/// </summary>
|
||||
public FacebookService()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize underlying provider with relevant token information.
|
||||
/// </summary>
|
||||
/// <param name="oAuthTokens">Token instance.</param>
|
||||
/// <param name="requiredPermissions">List of required permissions. public_profile and user_posts permissions will be used by default.</param>
|
||||
/// <returns>Success or failure.</returns>
|
||||
public bool Initialize(FacebookOAuthTokens oAuthTokens, FacebookPermissions requiredPermissions = FacebookPermissions.PublicProfile | FacebookPermissions.UserPosts)
|
||||
{
|
||||
if (oAuthTokens == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(oAuthTokens));
|
||||
}
|
||||
|
||||
return Initialize(oAuthTokens.AppId, requiredPermissions, oAuthTokens.WindowsStoreId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize underlying provider with relevant token information.
|
||||
/// </summary>
|
||||
/// <param name="appId">Application ID (Provided by Facebook developer site)</param>
|
||||
/// <param name="requiredPermissions">List of required permissions. public_profile and user_posts permissions will be used by default.</param>
|
||||
/// <param name="windowsStoreId">Windows Store SID</param>
|
||||
/// <returns>Success or failure.</returns>
|
||||
public bool Initialize(string appId, FacebookPermissions requiredPermissions = FacebookPermissions.PublicProfile | FacebookPermissions.UserPosts, string windowsStoreId = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appId))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(appId));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(windowsStoreId))
|
||||
{
|
||||
windowsStoreId = WindowsStoreId;
|
||||
}
|
||||
|
||||
isInitialized = true;
|
||||
|
||||
Provider.FBAppId = appId;
|
||||
Provider.WinAppId = windowsStoreId;
|
||||
|
||||
// Permissions
|
||||
var permissionList = new List<string>();
|
||||
|
||||
foreach (FacebookPermissions value in Enum.GetValues(typeof(FacebookPermissions)))
|
||||
{
|
||||
if ((requiredPermissions & value) != 0)
|
||||
{
|
||||
var name = value.ToString();
|
||||
var finalName = new StringBuilder();
|
||||
|
||||
foreach (var c in name)
|
||||
{
|
||||
if (char.IsUpper(c))
|
||||
{
|
||||
if (finalName.Length > 0)
|
||||
{
|
||||
finalName.Append('_');
|
||||
}
|
||||
|
||||
finalName.Append(char.ToLower(c));
|
||||
}
|
||||
else
|
||||
{
|
||||
finalName.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
permissionList.Add(finalName.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
permissions = new FBPermissions(permissionList);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private singleton field.
|
||||
/// </summary>
|
||||
private static FacebookService instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets public singleton property.
|
||||
/// </summary>
|
||||
public static FacebookService Instance => instance ?? (instance = new FacebookService());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to an instance of the underlying data provider.
|
||||
/// </summary>
|
||||
public FBSession Provider
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
throw new InvalidOperationException("Provider not initialized.");
|
||||
}
|
||||
|
||||
return FBSession.ActiveSession;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current logged user name.
|
||||
/// </summary>
|
||||
public string LoggedUser => !Provider.LoggedIn ? null : FBSession.ActiveSession.User.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Login with set of required requiredPermissions.
|
||||
/// </summary>
|
||||
/// <returns>Success or failure.</returns>
|
||||
public async Task<bool> LoginAsync()
|
||||
{
|
||||
if (Provider != null)
|
||||
{
|
||||
var result = await Provider.LoginAsync(permissions, SessionLoginBehavior.WebView);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result.ErrorInfo != null)
|
||||
{
|
||||
Debug.WriteLine(string.Format("Error logging in: {0}", result.ErrorInfo.Message));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Debug.WriteLine("Error logging in - no Active session found");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log out of the underlying service instance.
|
||||
/// </summary>
|
||||
/// <returns>Task to support await of async call.</returns>
|
||||
public Task LogoutAsync()
|
||||
{
|
||||
return Provider.LogoutAsync().AsTask();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request list data from service provider based upon a given config / query.
|
||||
/// </summary>
|
||||
/// <param name="config">FacebookDataConfig instance.</param>
|
||||
/// <param name="maxRecords">Upper limit of records to return.</param>
|
||||
/// <returns>Strongly typed list of data returned from the service.</returns>
|
||||
public Task<List<FacebookPost>> RequestAsync(FacebookDataConfig config, int maxRecords = 20)
|
||||
{
|
||||
return RequestAsync<FacebookPost>(config, maxRecords, FacebookPost.Fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request list data from service provider based upon a given config / query.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Strong type of model.</typeparam>
|
||||
/// <param name="config">FacebookDataConfig instance.</param>
|
||||
/// <param name="maxRecords">Upper limit of records to return.</param>
|
||||
/// <param name="fields">A comma separated string of required fields, which will have strongly typed representation in the model passed in.</param>
|
||||
/// <returns>Strongly typed list of data returned from the service.</returns>
|
||||
public async Task<List<T>> RequestAsync<T>(FacebookDataConfig config, int maxRecords = 20, string fields = "id,message,from,created_time,link,full_picture")
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var requestSource = new FacebookRequestSource<T>(config, fields, maxRecords.ToString(), 1);
|
||||
|
||||
var list = await requestSource.GetPagedItemsAsync(0, maxRecords);
|
||||
|
||||
return new List<T>(list);
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await RequestAsync<T>(config, maxRecords, fields);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request list data from service provider based upon a given config / query.
|
||||
/// </summary>
|
||||
/// <param name="config">FacebookDataConfig instance.</param>
|
||||
/// <param name="pageSize">Upper limit of records to return.</param>
|
||||
/// <param name="maxPages">Upper limit of pages to return.</param>
|
||||
/// <returns>Strongly typed list of data returned from the service.</returns>
|
||||
public Task<IncrementalLoadingCollection<FacebookRequestSource<FacebookPost>, FacebookPost>> RequestAsync(FacebookDataConfig config, int pageSize, int maxPages)
|
||||
{
|
||||
return RequestAsync<FacebookPost>(config, pageSize, maxPages, FacebookPost.Fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request generic list data from service provider based upon a given config / query.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Strong type of model.</typeparam>
|
||||
/// <param name="config">FacebookDataConfig instance.</param>
|
||||
/// <param name="pageSize">Upper limit of records to return.</param>
|
||||
/// <param name="maxPages">Upper limit of pages to return.</param>
|
||||
/// <param name="fields">A comma separated string of required fields, which will have strongly typed representation in the model passed in.</param>
|
||||
/// <returns>Strongly typed list of data returned from the service.</returns>
|
||||
public async Task<IncrementalLoadingCollection<FacebookRequestSource<T>, T>> RequestAsync<T>(FacebookDataConfig config, int pageSize, int maxPages, string fields = "id,message,from,created_time,link,full_picture")
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var requestSource = new FacebookRequestSource<T>(config, fields, pageSize.ToString(), maxPages);
|
||||
|
||||
return new IncrementalLoadingCollection<FacebookRequestSource<T>, T>(requestSource);
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await RequestAsync<T>(config, pageSize, maxPages, fields);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="FacebookPicture"/> object associated with the logged user
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="FacebookPicture"/> object</returns>
|
||||
public async Task<FacebookPicture> GetUserPictureInfoAsync()
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var factory = new FBJsonClassFactory(JsonConvert.DeserializeObject<FacebookDataHost<FacebookPicture>>);
|
||||
|
||||
PropertySet propertySet = new PropertySet { { "redirect", "0" } };
|
||||
var singleValue = new FBSingleValue("/me/picture", propertySet, factory);
|
||||
|
||||
var result = await singleValue.GetAsync();
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return ((FacebookDataHost<FacebookPicture>)result.Object).Data;
|
||||
}
|
||||
|
||||
throw new Exception(result.ErrorInfo?.Message);
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await GetUserPictureInfoAsync();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves list of user photo albums.
|
||||
/// </summary>
|
||||
/// <param name="maxRecords">Upper limit of records to return.</param>
|
||||
/// <param name="fields">Custom list of Album fields to retrieve.</param>
|
||||
/// <returns>List of User Photo Albums.</returns>
|
||||
public async Task<List<FacebookAlbum>> GetUserAlbumsAsync(int maxRecords = 20, string fields = null)
|
||||
{
|
||||
fields = fields ?? FacebookAlbum.Fields;
|
||||
var config = new FacebookDataConfig { Query = "/me/albums" };
|
||||
|
||||
return await RequestAsync<FacebookAlbum>(config, maxRecords, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves list of user photo albums.
|
||||
/// </summary>
|
||||
/// <param name="pageSize">Number of records to retrieve per page.</param>
|
||||
/// <param name="maxPages">Upper limit of pages to return.</param>
|
||||
/// <param name="fields">Custom list of Album fields to retrieve.</param>
|
||||
/// <returns>List of User Photo Albums.</returns>
|
||||
public async Task<IncrementalLoadingCollection<FacebookRequestSource<FacebookAlbum>, FacebookAlbum>> GetUserAlbumsAsync(int pageSize, int maxPages, string fields = null)
|
||||
{
|
||||
fields = fields ?? FacebookAlbum.Fields;
|
||||
var config = new FacebookDataConfig { Query = "/me/albums" };
|
||||
|
||||
return await RequestAsync<FacebookAlbum>(config, pageSize, maxPages, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves list of user photos by album id.
|
||||
/// </summary>
|
||||
/// <param name="albumId">Albums Id for photos.</param>
|
||||
/// <param name="maxRecords">Upper limit of records to return</param>
|
||||
/// <param name="fields">Custom list of Photo fields to retrieve.</param>
|
||||
/// <returns>List of User Photos.</returns>
|
||||
public async Task<List<FacebookPhoto>> GetUserPhotosByAlbumIdAsync(string albumId, int maxRecords = 20, string fields = null)
|
||||
{
|
||||
fields = fields ?? FacebookPhoto.Fields;
|
||||
var config = new FacebookDataConfig { Query = $"/{albumId}/photos" };
|
||||
|
||||
return await RequestAsync<FacebookPhoto>(config, maxRecords, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves list of user photos by album id.
|
||||
/// </summary>
|
||||
/// <param name="albumId">Albums Id for photos.</param>
|
||||
/// <param name="pageSize">Number of records to retrieve per page.</param>
|
||||
/// <param name="maxPages">Upper limit of pages to return.</param>
|
||||
/// <param name="fields">Custom list of Photo fields to retrieve.</param>
|
||||
/// <returns>List of User Photos.</returns>
|
||||
public async Task<IncrementalLoadingCollection<FacebookRequestSource<FacebookPhoto>, FacebookPhoto>> GetUserPhotosByAlbumIdAsync(string albumId, int pageSize, int maxPages, string fields = null)
|
||||
{
|
||||
fields = fields ?? FacebookPhoto.Fields;
|
||||
var config = new FacebookDataConfig { Query = $"/{albumId}/photos" };
|
||||
|
||||
return await RequestAsync<FacebookPhoto>(config, pageSize, maxPages, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a photo by id.
|
||||
/// </summary>
|
||||
/// <param name="photoId">Photo Id for the photo.</param>
|
||||
/// <returns>A single photo.</returns>
|
||||
public async Task<FacebookPhoto> GetPhotoByPhotoIdAsync(string photoId)
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var factory = new FBJsonClassFactory(JsonConvert.DeserializeObject<FacebookPhoto>);
|
||||
|
||||
PropertySet propertySet = new PropertySet { { "fields", "images" } };
|
||||
var singleValue = new FBSingleValue($"/{photoId}", propertySet, factory);
|
||||
|
||||
var result = await singleValue.GetAsync();
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return (FacebookPhoto)result.Object;
|
||||
}
|
||||
|
||||
throw new Exception(result.ErrorInfo?.Message);
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await GetPhotoByPhotoIdAsync(photoId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables direct posting data to the timeline.
|
||||
/// </summary>
|
||||
/// <param name="link">Link contained as part of the post. Cannot be null.</param>
|
||||
/// <returns>Task to support await of async call.</returns>
|
||||
[Deprecated("The underlying publish_action permission is no longer supported by Facebook. Please see https://developers.facebook.com/blog/post/2018/04/24/new-facebook-platform-product-changes-policy-updates/ for details.", DeprecationType.Deprecate, 4)]
|
||||
public async Task<bool> PostToFeedAsync(string link)
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var parameters = new PropertySet { { "link", link } };
|
||||
|
||||
string path = FBSession.ActiveSession.User.Id + "/feed";
|
||||
var factory = new FBJsonClassFactory(JsonConvert.DeserializeObject<FacebookPost>);
|
||||
|
||||
var singleValue = new FBSingleValue(path, parameters, factory);
|
||||
var result = await singleValue.PostAsync();
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var postResponse = result.Object as FacebookPost;
|
||||
if (postResponse != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine(string.Format("Could not post. {0}", result.ErrorInfo?.ErrorUserMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await PostToFeedAsync(link);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables posting data to the timeline using Facebook dialog.
|
||||
/// </summary>
|
||||
/// <param name="link">Link contained as part of the post. Cannot be null.</param>
|
||||
/// <returns>Task to support await of async call.</returns>
|
||||
public async Task<bool> PostToFeedWithDialogAsync(string link)
|
||||
{
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var parameters = new PropertySet { { "link", link } };
|
||||
|
||||
var result = await Provider.ShowFeedDialogAsync(parameters);
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.WriteLine(string.Format("Could not post. {0}", result.ErrorInfo?.ErrorUserMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await PostToFeedWithDialogAsync(link);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables posting a picture to the timeline
|
||||
/// </summary>
|
||||
/// <param name="title">Title of the post.</param>
|
||||
/// <param name="pictureName">Picture name.</param>
|
||||
/// <param name="pictureStream">Picture stream to upload.</param>
|
||||
/// <returns>Return ID of the picture</returns>
|
||||
[Deprecated("The underlying publish_action permission is no longer supported by Facebook. Please see https://developers.facebook.com/blog/post/2018/04/24/new-facebook-platform-product-changes-policy-updates/ for details.", DeprecationType.Deprecate, 4)]
|
||||
public async Task<string> PostPictureToFeedAsync(string title, string pictureName, IRandomAccessStreamWithContentType pictureStream)
|
||||
{
|
||||
if (pictureStream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Provider.LoggedIn)
|
||||
{
|
||||
var facebookPictureStream = new FBMediaStream(pictureName, pictureStream);
|
||||
var parameters = new PropertySet
|
||||
{
|
||||
{ "source", facebookPictureStream },
|
||||
{ "name", title }
|
||||
};
|
||||
|
||||
string path = FBSession.ActiveSession.User.Id + "/photos";
|
||||
var factory = new FBJsonClassFactory(JsonConvert.DeserializeObject<FacebookPicture>);
|
||||
|
||||
var singleValue = new FBSingleValue(path, parameters, factory);
|
||||
var result = await singleValue.PostAsync();
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var photoResponse = result.Object as FacebookPicture;
|
||||
if (photoResponse != null)
|
||||
{
|
||||
return photoResponse.Id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var isLoggedIn = await LoginAsync();
|
||||
if (isLoggedIn)
|
||||
{
|
||||
return await PostPictureToFeedAsync(title, pictureName, pictureStream);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -361,22 +361,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the object that will be used during next Frame navigation for
|
||||
/// Connected Animation involving a list control (item must be an element of
|
||||
/// ListViewBase.ItemsSource collection).
|
||||
/// Useful if the parameter used during page navigation is different from the
|
||||
/// data item in the list control. Also useful during back navigation if the
|
||||
/// item navigating back to is different from the item that was navigated from.
|
||||
/// </summary>
|
||||
/// <param name="frame">The Frame handling the navigation</param>
|
||||
/// <param name="item">The data item from a list control to be animated during next frame navigation</param>
|
||||
[Obsolete("Method is replaced by SetListDataItemForNextConnectedAnimation")]
|
||||
public static void SetListDataItemForNextConnectedAnnimation(this Frame frame, object item)
|
||||
{
|
||||
GetConnectedAnimationHelper(frame)?.SetParameterForNextFrameNavigation(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the object that will be used during next Frame navigation for
|
||||
/// Connected Animation involving a list control (item must be an element of
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Design.Common;
|
||||
using Microsoft.Windows.Design;
|
||||
using Microsoft.Windows.Design.Features;
|
||||
using Microsoft.Windows.Design.Metadata;
|
||||
using Microsoft.Windows.Design.Model;
|
||||
using Microsoft.Windows.Design.PropertyEditing;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
|
||||
{
|
||||
internal class HeaderedTextBlockDefaults : DefaultInitializer
|
||||
{
|
||||
public override void InitializeDefaults(ModelItem item)
|
||||
{
|
||||
item.Properties[nameof(HeaderedTextBlock.Header)].SetValue(string.Empty);
|
||||
item.Properties[nameof(HeaderedTextBlock.Text)].SetValue(string.Empty);
|
||||
}
|
||||
}
|
||||
internal class HeaderedTextBlockMetadata : AttributeTableBuilder
|
||||
{
|
||||
public HeaderedTextBlockMetadata()
|
||||
: base()
|
||||
{
|
||||
AddCallback(typeof(Microsoft.Toolkit.Uwp.UI.Controls.HeaderedTextBlock),
|
||||
b =>
|
||||
{
|
||||
b.AddCustomAttributes(new FeatureAttribute(typeof(HeaderedTextBlockDefaults)));
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.HeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryAppearance),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.TextStyle),
|
||||
new CategoryAttribute(Properties.Resources.CategoryAppearance),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.Header), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.Text), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.Orientation), new CategoryAttribute(Properties.Resources.CategoryAppearance));
|
||||
b.AddCustomAttributes(nameof(HeaderedTextBlock.HideTextIfEmpty), new CategoryAttribute(Properties.Resources.CategoryAppearance));
|
||||
b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -85,7 +85,6 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="TabViewItemMetadata.cs" />
|
||||
<Compile Include="BladeItemMetadata.cs" />
|
||||
<Compile Include="BladeViewMetadata.cs" />
|
||||
<Compile Include="CarouselMetadata.cs" />
|
||||
|
@ -93,7 +92,6 @@
|
|||
<Compile Include="AdaptiveGridViewMetadata.cs" />
|
||||
<Compile Include="ExpanderMetadata.cs" />
|
||||
<Compile Include="GridSplitterMetadata.cs" />
|
||||
<Compile Include="HeaderedTextBlockMetadata.cs" />
|
||||
<Compile Include="ImageExMetadata.cs" />
|
||||
<Compile Include="InAppNotificationMetadata.cs" />
|
||||
<Compile Include="LayoutTransitionControlMetadata.cs" />
|
||||
|
@ -104,7 +102,6 @@
|
|||
<Compile Include="MetadataRegistration.cs" />
|
||||
<Compile Include="Common\MetadataRegistrationBase.cs" />
|
||||
<Compile Include="Common\PlatformTypes.cs" />
|
||||
<Compile Include="TabViewMetadata.cs" />
|
||||
<Compile Include="OrbitViewMetadata.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Design.Common;
|
||||
using Microsoft.Windows.Design;
|
||||
using Microsoft.Windows.Design.Features;
|
||||
using Microsoft.Windows.Design.Metadata;
|
||||
using Microsoft.Windows.Design.Model;
|
||||
using Microsoft.Windows.Design.PropertyEditing;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
|
||||
{
|
||||
|
||||
internal class TabViewItemMetadata : AttributeTableBuilder
|
||||
{
|
||||
public TabViewItemMetadata()
|
||||
: base()
|
||||
{
|
||||
AddCallback(typeof(TabViewItem),
|
||||
b =>
|
||||
{
|
||||
b.AddCustomAttributes(nameof(TabViewItem.Header),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabViewItem.HeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabViewItem.IsClosable),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabViewItem.Icon),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon)
|
||||
);
|
||||
|
||||
b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.Toolkit.Uwp.UI.Controls.Design.Common;
|
||||
using Microsoft.Windows.Design;
|
||||
using Microsoft.Windows.Design.Metadata;
|
||||
using Microsoft.Windows.Design.PropertyEditing;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls.Design
|
||||
{
|
||||
internal class TabViewMetadata : AttributeTableBuilder
|
||||
{
|
||||
public TabViewMetadata()
|
||||
: base()
|
||||
{
|
||||
AddCallback(typeof(Microsoft.Toolkit.Uwp.UI.Controls.TabView),
|
||||
b =>
|
||||
{
|
||||
// Layout
|
||||
b.AddCustomAttributes(nameof(TabView.TabWidthBehavior), new CategoryAttribute(Properties.Resources.CategoryLayout));
|
||||
b.AddCustomAttributes(nameof(TabView.SelectedTabWidth), new CategoryAttribute(Properties.Resources.CategoryLayout));
|
||||
b.AddCustomAttributes(nameof(TabView.IsCloseButtonOverlay), new CategoryAttribute(Properties.Resources.CategoryLayout));
|
||||
|
||||
// Interactions
|
||||
b.AddCustomAttributes(nameof(TabView.CanCloseTabs), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
|
||||
// Templates
|
||||
b.AddCustomAttributes(nameof(TabView.ItemHeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabView.TabActionHeader), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
b.AddCustomAttributes(nameof(TabView.TabActionHeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabView.TabEndHeader), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
b.AddCustomAttributes(nameof(TabView.TabEndHeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(nameof(TabView.TabStartHeader), new CategoryAttribute(Properties.Resources.CategoryCommon));
|
||||
b.AddCustomAttributes(nameof(TabView.TabStartHeaderTemplate),
|
||||
new CategoryAttribute(Properties.Resources.CategoryCommon),
|
||||
new EditorBrowsableAttribute(EditorBrowsableState.Advanced)
|
||||
);
|
||||
b.AddCustomAttributes(new ToolboxCategoryAttribute(ToolboxCategoryPaths.Toolkit, false));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the properties for the <see cref="HeaderedTextBlock"/> control.
|
||||
/// </summary>
|
||||
public partial class HeaderedTextBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="HeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
|
||||
nameof(HeaderTemplate),
|
||||
typeof(DataTemplate),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="TextStyle"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TextStyleProperty = DependencyProperty.Register(
|
||||
nameof(TextStyle),
|
||||
typeof(Style),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Header"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
|
||||
nameof(Header),
|
||||
typeof(string),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(null, (d, e) => { ((HeaderedTextBlock)d).UpdateVisibility(); }));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Text"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
|
||||
nameof(Text),
|
||||
typeof(string),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(null, (d, e) => { ((HeaderedTextBlock)d).UpdateVisibility(); }));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Orientation"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
|
||||
nameof(Orientation),
|
||||
typeof(Orientation),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(Orientation.Vertical, (d, e) => { ((HeaderedTextBlock)d).UpdateForOrientation((Orientation)e.NewValue); }));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="HideTextIfEmpty"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty HideTextIfEmptyProperty = DependencyProperty.Register(
|
||||
nameof(HideTextIfEmpty),
|
||||
typeof(bool),
|
||||
typeof(HeaderedTextBlock),
|
||||
new PropertyMetadata(false, (d, e) => { ((HeaderedTextBlock)d).UpdateVisibility(); }));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header style.
|
||||
/// </summary>
|
||||
public DataTemplate HeaderTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DataTemplate)GetValue(HeaderTemplateProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(HeaderTemplateProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text style.
|
||||
/// </summary>
|
||||
public Style TextStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Style)GetValue(TextStyleProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(TextStyleProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header.
|
||||
/// </summary>
|
||||
public string Header
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)GetValue(HeaderProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(HeaderProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return (string)GetValue(TextProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(TextProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of inline text elements within a Windows.UI.Xaml.Controls.TextBlock.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A collection that holds all inline text elements from the Windows.UI.Xaml.Controls.TextBlock. The default is an empty collection.</returns>
|
||||
public InlineCollectionWrapper Inlines { get; } = new InlineCollectionWrapper();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the orientation.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Orientation)GetValue(OrientationProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(OrientationProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Text TextBlock is hidden if its value is empty
|
||||
/// </summary>
|
||||
public bool HideTextIfEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)GetValue(HideTextIfEmptyProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(HideTextIfEmptyProperty, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Markup;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a control for providing a header for read-only text.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = "HeaderContentPresenter", Type = typeof(ContentPresenter))]
|
||||
[ContentProperty(Name = nameof(Inlines))]
|
||||
[Obsolete("The HeaderedTextBlock has been replaced with the more generic HeaderedContentControl.")]
|
||||
public partial class HeaderedTextBlock : Control
|
||||
{
|
||||
private ContentPresenter _headerContentPresenter;
|
||||
private TextBlock _textContent;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeaderedTextBlock"/> class.
|
||||
/// </summary>
|
||||
public HeaderedTextBlock()
|
||||
{
|
||||
DefaultStyleKey = typeof(HeaderedTextBlock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when applying the control template.
|
||||
/// </summary>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_headerContentPresenter = GetTemplateChild("HeaderContentPresenter") as ContentPresenter;
|
||||
_textContent = GetTemplateChild("TextContent") as TextBlock;
|
||||
|
||||
UpdateVisibility();
|
||||
Inlines.AddItemsToTextBlock(_textContent);
|
||||
UpdateForOrientation(this.Orientation);
|
||||
}
|
||||
|
||||
private void UpdateVisibility()
|
||||
{
|
||||
if (_headerContentPresenter != null)
|
||||
{
|
||||
_headerContentPresenter.Visibility = _headerContentPresenter.Content == null
|
||||
? Visibility.Collapsed
|
||||
: Visibility.Visible;
|
||||
}
|
||||
|
||||
if (_textContent != null)
|
||||
{
|
||||
_textContent.Visibility = string.IsNullOrWhiteSpace(_textContent.Text) && HideTextIfEmpty
|
||||
? Visibility.Collapsed
|
||||
: Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateForOrientation(Orientation orientationValue)
|
||||
{
|
||||
switch (orientationValue)
|
||||
{
|
||||
case Orientation.Vertical:
|
||||
VisualStateManager.GoToState(this, "Vertical", true);
|
||||
break;
|
||||
case Orientation.Horizontal:
|
||||
VisualStateManager.GoToState(this, "Horizontal", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<Style x:Key="HeaderedTextBlockTextStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}" />
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextSelectionHighlightColorThemeBrush}" />
|
||||
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="TextLineBounds" Value="TrimToBaseline" />
|
||||
<Setter Property="OpticalMarginAlignment" Value="TrimSideBearings" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
<Setter Property="FontWeight" Value="Light" />
|
||||
<Setter Property="LineHeight" Value="28" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:HeaderedTextBlock">
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="Orientation" Value="Vertical" />
|
||||
<Setter Property="TextStyle" Value="{ThemeResource HeaderedTextBlockTextStyle}" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="controls:HeaderedTextBlock">
|
||||
<StackPanel x:Name="Panel"
|
||||
Orientation="{TemplateBinding Orientation}">
|
||||
<ContentPresenter x:Name="HeaderContentPresenter"
|
||||
Grid.ColumnSpan="4"
|
||||
x:DeferLoadStrategy="Lazy"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
AutomationProperties.Name="Header content"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}"
|
||||
IsHitTestVisible="False"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<TextBlock x:Name="TextContent"
|
||||
AutomationProperties.Name="Text content"
|
||||
Style="{TemplateBinding TextStyle}"
|
||||
Text="{TemplateBinding Text}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="OrientationStates">
|
||||
<VisualState x:Name="Vertical" />
|
||||
<VisualState x:Name="Horizontal">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="TextContent.Margin" Value="10,0,0,0" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</StackPanel>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
|
@ -17,7 +17,6 @@
|
|||
- GridSplitter: A the control that redistributes space between columns or rows of a Grid control.
|
||||
- HeaderedContentControl: Provides a header to content.
|
||||
- HeaderedItemsControl: Provides a header to items.
|
||||
- HeaderedTextBlock: Provide a header for read-only text.
|
||||
- ImageCropper: ImageCropper control allows user to crop image freely.
|
||||
- ImageEx: Images are downloaded asynchronously showing a load indicator and can be stored in a local cache.
|
||||
- InAppNotification: Show local notifications in your application.
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=gridsplitter/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=hamburgermenu/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=hamburgermenu_005Cmenuitems/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=headeredtextblock/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=imageex/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pulltorefreshlistview/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=radialgauge/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
|
@ -213,14 +213,6 @@
|
|||
<value>Notification</value>
|
||||
<comment>The landmark name for the InAppNotification control. It is said by the narrator when using landmark navigation.</comment>
|
||||
</data>
|
||||
<data name="WindowsCommunityToolkit_TabView_CloseButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
<comment>Narrator Resource for TabView Close Button.</comment>
|
||||
</data>
|
||||
<data name="WindowsCommunityToolkit_TabView_CloseButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
|
||||
<value>Close tab</value>
|
||||
<comment>Tooltip for TabView Close Button.</comment>
|
||||
</data>
|
||||
<data name="WindowsCommunityToolkit_TokenizingTextBoxItem_MenuFlyout_Remove" xml:space="preserve">
|
||||
<value>Remove</value>
|
||||
<comment>Label for TokenizingTextBox MenuFlyout 'Remove' option.</comment>
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Event arguments for <see cref="TabView.TabClosing"/> event.
|
||||
/// </summary>
|
||||
public class TabClosingEventArgs : CancelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabClosingEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="item">Item being closed.</param>
|
||||
/// <param name="tab"><see cref="TabViewItem"/> container being closed.</param>
|
||||
public TabClosingEventArgs(object item, TabViewItem tab)
|
||||
{
|
||||
Item = item;
|
||||
Tab = tab;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Item being closed.
|
||||
/// </summary>
|
||||
public object Item { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Tab being closed.
|
||||
/// </summary>
|
||||
public TabViewItem Tab { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A class used by the <see cref="TabView"/> TabDraggedOutside Event
|
||||
/// </summary>
|
||||
public class TabDraggedOutsideEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabDraggedOutsideEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="item">data context of element dragged</param>
|
||||
/// <param name="tab"><see cref="TabViewItem"/> container being dragged.</param>
|
||||
public TabDraggedOutsideEventArgs(object item, TabViewItem tab)
|
||||
{
|
||||
Item = item;
|
||||
Tab = tab;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Item/Data Context of the item being dragged outside of the <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
public object Item { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Tab being dragged outside of the <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
public TabViewItem Tab { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,221 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// TabView methods related to calculating the width of the <see cref="TabViewItem"/> Headers.
|
||||
/// </summary>
|
||||
public partial class TabView
|
||||
{
|
||||
// Attached property for storing widths of tabs if set by other means during layout.
|
||||
private static double GetOriginalWidth(TabViewItem obj)
|
||||
{
|
||||
return (double)obj.GetValue(OriginalWidthProperty);
|
||||
}
|
||||
|
||||
private static void SetOriginalWidth(TabViewItem obj, double value)
|
||||
{
|
||||
obj.SetValue(OriginalWidthProperty, value);
|
||||
}
|
||||
|
||||
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
|
||||
private static readonly DependencyProperty OriginalWidthProperty =
|
||||
DependencyProperty.RegisterAttached("OriginalWidth", typeof(double), typeof(TabView), new PropertyMetadata(null));
|
||||
|
||||
private static void OnLayoutEffectingPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var tabview = sender as TabView;
|
||||
if (tabview != null && tabview._hasLoaded)
|
||||
{
|
||||
tabview.TabView_SizeChanged(tabview, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void TabView_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
// We need to do this calculation here in Size Changed as the
|
||||
// Columns don't have their Actual Size calculated in Measure or Arrange.
|
||||
if (_hasLoaded && _tabViewContainer != null)
|
||||
{
|
||||
// Look for our special columns to calculate size of other 'stuff'
|
||||
var taken = _tabViewContainer.ColumnDefinitions.Sum(cd => GetIgnoreColumn(cd) ? 0 : cd.ActualWidth);
|
||||
|
||||
// Get the column we want to work on for available space
|
||||
var tabc = _tabViewContainer.ColumnDefinitions.FirstOrDefault(cd => GetConstrainColumn(cd));
|
||||
if (tabc != null)
|
||||
{
|
||||
var available = ActualWidth - taken;
|
||||
var required = 0.0;
|
||||
var mintabwidth = double.MaxValue;
|
||||
|
||||
if (TabWidthBehavior == TabWidthMode.Actual)
|
||||
{
|
||||
if (_tabScroller != null)
|
||||
{
|
||||
// If we have a scroll container, get its size.
|
||||
required = _tabScroller.ExtentWidth;
|
||||
}
|
||||
|
||||
// Restore original widths
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var tab = ContainerFromItem(item) as TabViewItem;
|
||||
if (tab == null)
|
||||
{
|
||||
continue; // container not generated yet
|
||||
}
|
||||
|
||||
if (tab.ReadLocalValue(OriginalWidthProperty) != DependencyProperty.UnsetValue)
|
||||
{
|
||||
tab.Width = GetOriginalWidth(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (available > 0)
|
||||
{
|
||||
// Calculate the width for each tab from the provider and determine how much space they take.
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var tab = ContainerFromItem(item) as TabViewItem;
|
||||
if (tab == null)
|
||||
{
|
||||
continue; // container not generated yet
|
||||
}
|
||||
|
||||
mintabwidth = Math.Min(mintabwidth, tab.MinWidth);
|
||||
|
||||
double width = double.NaN;
|
||||
|
||||
switch (TabWidthBehavior)
|
||||
{
|
||||
case TabWidthMode.Equal:
|
||||
width = ProvideEqualWidth(tab, item, available);
|
||||
break;
|
||||
case TabWidthMode.Compact:
|
||||
width = ProvideCompactWidth(tab, item, available);
|
||||
break;
|
||||
}
|
||||
|
||||
if (tab.ReadLocalValue(OriginalWidthProperty) == DependencyProperty.UnsetValue)
|
||||
{
|
||||
SetOriginalWidth(tab, tab.Width);
|
||||
}
|
||||
|
||||
if (width > double.Epsilon)
|
||||
{
|
||||
tab.Width = width;
|
||||
required += Math.Max(Math.Min(width, tab.MaxWidth), tab.MinWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
tab.Width = GetOriginalWidth(tab);
|
||||
required += tab.ActualWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fix negative bounds.
|
||||
available = 0.0;
|
||||
|
||||
// Still need to determine a 'minimum' width (if available)
|
||||
// TODO: Consolidate this logic with above better?
|
||||
foreach (var item in Items)
|
||||
{
|
||||
var tab = ContainerFromItem(item) as TabViewItem;
|
||||
if (tab == null)
|
||||
{
|
||||
continue; // container not generated yet
|
||||
}
|
||||
|
||||
mintabwidth = Math.Min(mintabwidth, tab.MinWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(mintabwidth < double.MaxValue))
|
||||
{
|
||||
mintabwidth = 0.0; // No Containers, no visual, 0 size.
|
||||
}
|
||||
|
||||
if (available > mintabwidth)
|
||||
{
|
||||
// Constrain the column based on our required and available space
|
||||
tabc.MaxWidth = available;
|
||||
}
|
||||
|
||||
//// TODO: If it's less, should we move the selected tab to only be the one shown by default?
|
||||
|
||||
if (available <= mintabwidth || Math.Abs(available - mintabwidth) < double.Epsilon)
|
||||
{
|
||||
tabc.Width = new GridLength(mintabwidth);
|
||||
}
|
||||
else if (required >= available)
|
||||
{
|
||||
// Fix size as we don't have enough space for all the tabs.
|
||||
tabc.Width = new GridLength(available);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We haven't filled up our space, so we want to expand to take as much as needed.
|
||||
tabc.Width = GridLength.Auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double ProvideEqualWidth(TabViewItem tab, object item, double availableWidth)
|
||||
{
|
||||
if (double.IsNaN(SelectedTabWidth))
|
||||
{
|
||||
if (Items.Count <= 1)
|
||||
{
|
||||
return availableWidth;
|
||||
}
|
||||
|
||||
return Math.Max(tab.MinWidth, availableWidth / Items.Count);
|
||||
}
|
||||
else if (Items.Count() <= 1)
|
||||
{
|
||||
// Default case of a single tab, make it full size.
|
||||
return Math.Min(SelectedTabWidth, availableWidth);
|
||||
}
|
||||
else
|
||||
{
|
||||
var width = (availableWidth - SelectedTabWidth) / (Items.Count - 1);
|
||||
|
||||
// Constrain between Min and Selected (Max)
|
||||
if (width < tab.MinWidth)
|
||||
{
|
||||
width = tab.MinWidth;
|
||||
}
|
||||
else if (width > SelectedTabWidth)
|
||||
{
|
||||
width = SelectedTabWidth;
|
||||
}
|
||||
|
||||
// If it's selected make it full size, otherwise whatever the size should be.
|
||||
return tab.IsSelected
|
||||
? Math.Min(SelectedTabWidth, availableWidth)
|
||||
: width;
|
||||
}
|
||||
}
|
||||
|
||||
private double ProvideCompactWidth(TabViewItem tab, object item, double availableWidth)
|
||||
{
|
||||
// If we're selected and have a value for that, then just return that.
|
||||
if (tab.IsSelected && !double.IsNaN(SelectedTabWidth))
|
||||
{
|
||||
return SelectedTabWidth;
|
||||
}
|
||||
|
||||
// Otherwise use min size.
|
||||
return tab.MinWidth;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Reflection;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// TabView methods related to tracking Items and ItemsSource changes.
|
||||
/// </summary>
|
||||
public partial class TabView
|
||||
{
|
||||
// Temporary tracking of previous collections for removing events.
|
||||
private MethodInfo _removeItemsSourceMethod;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnItemsChanged(object e)
|
||||
{
|
||||
IVectorChangedEventArgs args = (IVectorChangedEventArgs)e;
|
||||
|
||||
base.OnItemsChanged(e);
|
||||
|
||||
if (args.CollectionChange == CollectionChange.ItemRemoved && SelectedIndex == -1)
|
||||
{
|
||||
// If we remove the selected item we should select the previous item
|
||||
int startIndex = (int)args.Index + 1;
|
||||
if (startIndex > Items.Count)
|
||||
{
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
SelectedIndex = FindNextTabIndex(startIndex, -1);
|
||||
}
|
||||
|
||||
// Update Sizing (in case there are less items now)
|
||||
TabView_SizeChanged(this, null);
|
||||
}
|
||||
|
||||
private void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
|
||||
{
|
||||
var action = (CollectionChange)e.Action;
|
||||
if (action == CollectionChange.Reset)
|
||||
{
|
||||
// Reset collection to reload later.
|
||||
_hasLoaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetInitialSelection()
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
{
|
||||
// If we have an index, but didn't get the selection, make the selection
|
||||
if (SelectedIndex >= 0 && SelectedIndex < Items.Count)
|
||||
{
|
||||
SelectedItem = Items[SelectedIndex];
|
||||
}
|
||||
|
||||
// Otherwise, select the first item by default
|
||||
else if (Items.Count >= 1)
|
||||
{
|
||||
SelectedItem = Items[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finds the next visible & enabled tab index.
|
||||
private int FindNextTabIndex(int startIndex, int direction)
|
||||
{
|
||||
int index = startIndex;
|
||||
if (direction != 0)
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
index += direction;
|
||||
|
||||
if (index >= Items.Count)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
else if (index < 0)
|
||||
{
|
||||
index = Items.Count - 1;
|
||||
}
|
||||
|
||||
var tabItem = ContainerFromIndex(index) as TabViewItem;
|
||||
if (tabItem != null && tabItem.IsEnabled && tabItem.Visibility == Visibility.Visible)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private void ItemsSource_PropertyChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
// Use reflection to store a 'Remove' method of any possible collection in ItemsSource
|
||||
// Cache for efficiency later.
|
||||
if (ItemsSource != null)
|
||||
{
|
||||
_removeItemsSourceMethod = ItemsSource.GetType().GetMethod("Remove");
|
||||
}
|
||||
else
|
||||
{
|
||||
_removeItemsSourceMethod = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// TabView properties.
|
||||
/// </summary>
|
||||
public partial class TabView
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content to appear to the left or above the tab strip.
|
||||
/// </summary>
|
||||
public object TabStartHeader
|
||||
{
|
||||
get { return (object)GetValue(TabStartHeaderProperty); }
|
||||
set { SetValue(TabStartHeaderProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabStartHeader"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabStartHeader"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabStartHeaderProperty =
|
||||
DependencyProperty.Register(nameof(TabStartHeader), typeof(object), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> for the <see cref="TabStartHeader"/>.
|
||||
/// </summary>
|
||||
public DataTemplate TabStartHeaderTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(TabStartHeaderTemplateProperty); }
|
||||
set { SetValue(TabStartHeaderTemplateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabStartHeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabStartHeaderTemplate"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabStartHeaderTemplateProperty =
|
||||
DependencyProperty.Register(nameof(TabStartHeaderTemplate), typeof(DataTemplate), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content to appear next to the tab strip.
|
||||
/// </summary>
|
||||
public object TabActionHeader
|
||||
{
|
||||
get { return (object)GetValue(TabActionHeaderProperty); }
|
||||
set { SetValue(TabActionHeaderProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabActionHeader"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabActionHeader"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabActionHeaderProperty =
|
||||
DependencyProperty.Register(nameof(TabActionHeader), typeof(object), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> for the <see cref="TabActionHeader"/>.
|
||||
/// </summary>
|
||||
public DataTemplate TabActionHeaderTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(TabActionHeaderTemplateProperty); }
|
||||
set { SetValue(TabActionHeaderTemplateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabActionHeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabActionHeaderTemplate"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabActionHeaderTemplateProperty =
|
||||
DependencyProperty.Register(nameof(TabActionHeaderTemplate), typeof(DataTemplate), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content to appear to the right or below the tab strip.
|
||||
/// </summary>
|
||||
public object TabEndHeader
|
||||
{
|
||||
get { return (object)GetValue(TabEndHeaderProperty); }
|
||||
set { SetValue(TabEndHeaderProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabEndHeader"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabEndHeader"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabEndHeaderProperty =
|
||||
DependencyProperty.Register(nameof(TabEndHeader), typeof(object), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="DataTemplate"/> for the <see cref="TabEndHeader"/>.
|
||||
/// </summary>
|
||||
public DataTemplate TabEndHeaderTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(TabEndHeaderTemplateProperty); }
|
||||
set { SetValue(TabEndHeaderTemplateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabEndHeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabEndHeaderTemplate"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabEndHeaderTemplateProperty =
|
||||
DependencyProperty.Register(nameof(TabEndHeaderTemplate), typeof(DataTemplate), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default <see cref="DataTemplate"/> for the <see cref="TabViewItem.HeaderTemplate"/>.
|
||||
/// </summary>
|
||||
public DataTemplate ItemHeaderTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(ItemHeaderTemplateProperty); }
|
||||
set { SetValue(ItemHeaderTemplateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ItemHeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabStartHeaderTemplate"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty ItemHeaderTemplateProperty =
|
||||
DependencyProperty.Register(nameof(ItemHeaderTemplate), typeof(DataTemplate), typeof(TabView), new PropertyMetadata(null, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether by default a Tab can be closed or not if no value to <see cref="TabViewItem.IsClosable"/> is provided.
|
||||
/// </summary>
|
||||
public bool CanCloseTabs
|
||||
{
|
||||
get { return (bool)GetValue(CanCloseTabsProperty); }
|
||||
set { SetValue(CanCloseTabsProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="CanCloseTabs"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="CanCloseTabs"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty CanCloseTabsProperty =
|
||||
DependencyProperty.Register(nameof(CanCloseTabs), typeof(bool), typeof(TabView), new PropertyMetadata(false, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a <see cref="TabViewItem"/> Close Button should be included in layout calculations.
|
||||
/// </summary>
|
||||
public bool IsCloseButtonOverlay
|
||||
{
|
||||
get { return (bool)GetValue(IsCloseButtonOverlayProperty); }
|
||||
set { SetValue(IsCloseButtonOverlayProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsCloseButtonOverlay"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="IsCloseButtonOverlay"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty IsCloseButtonOverlayProperty =
|
||||
DependencyProperty.Register(nameof(IsCloseButtonOverlay), typeof(bool), typeof(TabView), new PropertyMetadata(false, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating the size of the selected tab. By default this is set to Auto and the selected tab size doesn't change.
|
||||
/// </summary>
|
||||
public double SelectedTabWidth
|
||||
{
|
||||
get { return (double)GetValue(SelectedTabWidthProperty); }
|
||||
set { SetValue(SelectedTabWidthProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="SelectedTabWidth"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="SelectedTabWidth"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty SelectedTabWidthProperty =
|
||||
DependencyProperty.Register(nameof(SelectedTabWidth), typeof(double), typeof(TabView), new PropertyMetadata(double.NaN, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current <see cref="TabWidthMode"/> which determines how tab headers' width behave.
|
||||
/// </summary>
|
||||
public TabWidthMode TabWidthBehavior
|
||||
{
|
||||
get { return (TabWidthMode)GetValue(TabWidthBehaviorProperty); }
|
||||
set { SetValue(TabWidthBehaviorProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="TabWidthBehavior"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="TabWidthBehavior"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty TabWidthBehaviorProperty =
|
||||
DependencyProperty.Register(nameof(TabWidthBehavior), typeof(TabWidthMode), typeof(TabView), new PropertyMetadata(TabWidthMode.Actual, OnLayoutEffectingPropertyChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attached property value to indicate if this grid column should be ignored when calculating header sizes.
|
||||
/// </summary>
|
||||
/// <param name="obj">Grid Column.</param>
|
||||
/// <returns>Boolean indicating if this column is ignored by TabViewHeader logic.</returns>
|
||||
public static bool GetIgnoreColumn(ColumnDefinition obj)
|
||||
{
|
||||
return (bool)obj.GetValue(IgnoreColumnProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the attached property value for <see cref="IgnoreColumnProperty"/>
|
||||
/// </summary>
|
||||
/// <param name="obj">Grid Column.</param>
|
||||
/// <param name="value">Boolean value</param>
|
||||
public static void SetIgnoreColumn(ColumnDefinition obj, bool value)
|
||||
{
|
||||
obj.SetValue(IgnoreColumnProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IgnoreColumnProperty"/> attached property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the IgnoreColumn attached property.</returns>
|
||||
public static readonly DependencyProperty IgnoreColumnProperty =
|
||||
DependencyProperty.RegisterAttached("IgnoreColumn", typeof(bool), typeof(TabView), new PropertyMetadata(false));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attached value indicating this column should be restricted for the <see cref="TabViewItem"/> headers.
|
||||
/// </summary>
|
||||
/// <param name="obj">Grid Column.</param>
|
||||
/// <returns>True if this column should be constrained.</returns>
|
||||
public static bool GetConstrainColumn(ColumnDefinition obj)
|
||||
{
|
||||
return (bool)obj.GetValue(ConstrainColumnProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the attached property value for the <see cref="ConstrainColumnProperty"/>
|
||||
/// </summary>
|
||||
/// <param name="obj">Grid Column.</param>
|
||||
/// <param name="value">Boolean value.</param>
|
||||
public static void SetConstrainColumn(ColumnDefinition obj, bool value)
|
||||
{
|
||||
obj.SetValue(ConstrainColumnProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="ConstrainColumnProperty"/> attached property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the ConstrainColumn attached property.</returns>
|
||||
public static readonly DependencyProperty ConstrainColumnProperty =
|
||||
DependencyProperty.RegisterAttached("ConstrainColumn", typeof(bool), typeof(TabView), new PropertyMetadata(false));
|
||||
}
|
||||
}
|
|
@ -1,320 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Uwp.UI.Extensions;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// TabView is a control for displaying a set of tabs and their content.
|
||||
/// </summary>
|
||||
[Obsolete("Please migrate to the TabView control from WinUI, this control will be removed in a future release. https://aka.ms/winui")]
|
||||
[TemplatePart(Name = TabContentPresenterName, Type = typeof(ContentPresenter))]
|
||||
[TemplatePart(Name = TabViewContainerName, Type = typeof(Grid))]
|
||||
[TemplatePart(Name = TabsItemsPresenterName, Type = typeof(ItemsPresenter))]
|
||||
[TemplatePart(Name = TabsScrollViewerName, Type = typeof(ScrollViewer))]
|
||||
[TemplatePart(Name = TabsScrollBackButtonName, Type = typeof(ButtonBase))]
|
||||
[TemplatePart(Name = TabsScrollForwardButtonName, Type = typeof(ButtonBase))]
|
||||
public partial class TabView : ListViewBase
|
||||
{
|
||||
private const int ScrollAmount = 50; // TODO: Should this be based on TabWidthMode
|
||||
|
||||
private const string TabContentPresenterName = "TabContentPresenter";
|
||||
private const string TabViewContainerName = "TabViewContainer";
|
||||
private const string TabsItemsPresenterName = "TabsItemsPresenter";
|
||||
private const string TabsScrollViewerName = "ScrollViewer";
|
||||
private const string TabsScrollBackButtonName = "ScrollBackButton";
|
||||
private const string TabsScrollForwardButtonName = "ScrollForwardButton";
|
||||
|
||||
private ContentPresenter _tabContentPresenter;
|
||||
private Grid _tabViewContainer;
|
||||
private ItemsPresenter _tabItemsPresenter;
|
||||
private ScrollViewer _tabScroller;
|
||||
private ButtonBase _tabScrollBackButton;
|
||||
private ButtonBase _tabScrollForwardButton;
|
||||
|
||||
private bool _hasLoaded;
|
||||
private bool _isDragging;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabView"/> class.
|
||||
/// </summary>
|
||||
public TabView()
|
||||
{
|
||||
DefaultStyleKey = typeof(TabView);
|
||||
|
||||
// Container Generation Hooks
|
||||
RegisterPropertyChangedCallback(ItemsSourceProperty, ItemsSource_PropertyChanged);
|
||||
ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
|
||||
|
||||
// Drag and Layout Hooks
|
||||
DragItemsStarting += TabView_DragItemsStarting;
|
||||
DragItemsCompleted += TabView_DragItemsCompleted;
|
||||
SizeChanged += TabView_SizeChanged;
|
||||
|
||||
// Selection Hook
|
||||
SelectionChanged += TabView_SelectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a tab is dragged by the user outside of the <see cref="TabView"/>. Generally, this paradigm is used to create a new-window with the torn-off tab.
|
||||
/// The creation and handling of the new-window is left to the app's developer.
|
||||
/// </summary>
|
||||
public event EventHandler<TabDraggedOutsideEventArgs> TabDraggedOutside;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a tab's Close button is clicked. Set <see cref="CancelEventArgs.Cancel"/> to true to prevent automatic Tab Closure.
|
||||
/// </summary>
|
||||
public event EventHandler<TabClosingEventArgs> TabClosing;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override DependencyObject GetContainerForItemOverride() => new TabViewItem();
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool IsItemItsOwnContainerOverride(object item)
|
||||
{
|
||||
return item is TabViewItem;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
if (_tabItemsPresenter != null)
|
||||
{
|
||||
_tabItemsPresenter.SizeChanged -= TabView_SizeChanged;
|
||||
}
|
||||
|
||||
if (_tabScroller != null)
|
||||
{
|
||||
_tabScroller.Loaded -= ScrollViewer_Loaded;
|
||||
}
|
||||
|
||||
_tabContentPresenter = GetTemplateChild(TabContentPresenterName) as ContentPresenter;
|
||||
_tabViewContainer = GetTemplateChild(TabViewContainerName) as Grid;
|
||||
_tabItemsPresenter = GetTemplateChild(TabsItemsPresenterName) as ItemsPresenter;
|
||||
_tabScroller = GetTemplateChild(TabsScrollViewerName) as ScrollViewer;
|
||||
|
||||
if (_tabItemsPresenter != null)
|
||||
{
|
||||
_tabItemsPresenter.SizeChanged += TabView_SizeChanged;
|
||||
}
|
||||
|
||||
if (_tabScroller != null)
|
||||
{
|
||||
_tabScroller.Loaded += ScrollViewer_Loaded;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_tabScroller.Loaded -= ScrollViewer_Loaded;
|
||||
|
||||
if (_tabScrollBackButton != null)
|
||||
{
|
||||
_tabScrollBackButton.Click -= ScrollTabBackButton_Click;
|
||||
}
|
||||
|
||||
if (_tabScrollForwardButton != null)
|
||||
{
|
||||
_tabScrollForwardButton.Click -= ScrollTabForwardButton_Click;
|
||||
}
|
||||
|
||||
_tabScrollBackButton = _tabScroller.FindDescendantByName(TabsScrollBackButtonName) as ButtonBase;
|
||||
_tabScrollForwardButton = _tabScroller.FindDescendantByName(TabsScrollForwardButtonName) as ButtonBase;
|
||||
|
||||
if (_tabScrollBackButton != null)
|
||||
{
|
||||
_tabScrollBackButton.Click += ScrollTabBackButton_Click;
|
||||
}
|
||||
|
||||
if (_tabScrollForwardButton != null)
|
||||
{
|
||||
_tabScrollForwardButton.Click += ScrollTabForwardButton_Click;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScrollTabBackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_tabScroller.ChangeView(Math.Max(0, _tabScroller.HorizontalOffset - ScrollAmount), null, null);
|
||||
}
|
||||
|
||||
private void ScrollTabForwardButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_tabScroller.ChangeView(Math.Min(_tabScroller.ScrollableWidth, _tabScroller.HorizontalOffset + ScrollAmount), null, null);
|
||||
}
|
||||
|
||||
private void TabView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (_isDragging)
|
||||
{
|
||||
// Skip if we're dragging, we'll reset when we're done.
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tabContentPresenter != null)
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
{
|
||||
_tabContentPresenter.Content = null;
|
||||
_tabContentPresenter.ContentTemplate = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ContainerFromItem(SelectedItem) is TabViewItem container)
|
||||
{
|
||||
_tabContentPresenter.Content = container.Content;
|
||||
_tabContentPresenter.ContentTemplate = container.ContentTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If our width can be effected by the selection, need to run algorithm.
|
||||
if (!double.IsNaN(SelectedTabWidth))
|
||||
{
|
||||
TabView_SizeChanged(sender, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
|
||||
var tabitem = element as TabViewItem;
|
||||
|
||||
tabitem.Loaded -= TabViewItem_Loaded;
|
||||
tabitem.Closing -= TabViewItem_Closing;
|
||||
tabitem.Loaded += TabViewItem_Loaded;
|
||||
tabitem.Closing += TabViewItem_Closing;
|
||||
|
||||
if (tabitem.Header == null)
|
||||
{
|
||||
tabitem.Header = item;
|
||||
}
|
||||
|
||||
if (tabitem.HeaderTemplate == null)
|
||||
{
|
||||
var headertemplatebinding = new Binding()
|
||||
{
|
||||
Source = this,
|
||||
Path = new PropertyPath(nameof(ItemHeaderTemplate)),
|
||||
Mode = BindingMode.OneWay
|
||||
};
|
||||
tabitem.SetBinding(TabViewItem.HeaderTemplateProperty, headertemplatebinding);
|
||||
}
|
||||
|
||||
if (tabitem.IsClosable != true && tabitem.ReadLocalValue(TabViewItem.IsClosableProperty) == DependencyProperty.UnsetValue)
|
||||
{
|
||||
var iscloseablebinding = new Binding()
|
||||
{
|
||||
Source = this,
|
||||
Path = new PropertyPath(nameof(CanCloseTabs)),
|
||||
Mode = BindingMode.OneWay,
|
||||
};
|
||||
tabitem.SetBinding(TabViewItem.IsClosableProperty, iscloseablebinding);
|
||||
}
|
||||
}
|
||||
|
||||
private void TabViewItem_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var tabitem = sender as TabViewItem;
|
||||
|
||||
tabitem.Loaded -= TabViewItem_Loaded;
|
||||
|
||||
// Only need to do this once.
|
||||
if (!_hasLoaded)
|
||||
{
|
||||
_hasLoaded = true;
|
||||
|
||||
// Need to set a tab's selection on load, otherwise ListView resets to null.
|
||||
SetInitialSelection();
|
||||
|
||||
// Need to make sure ContentPresenter is set to content based on selection.
|
||||
TabView_SelectionChanged(this, null);
|
||||
|
||||
// Need to make sure we've registered our removal method.
|
||||
ItemsSource_PropertyChanged(this, null);
|
||||
|
||||
// Make sure we complete layout now.
|
||||
TabView_SizeChanged(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void TabViewItem_Closing(object sender, TabClosingEventArgs e)
|
||||
{
|
||||
var item = ItemFromContainer(e.Tab);
|
||||
|
||||
var args = new TabClosingEventArgs(item, e.Tab);
|
||||
TabClosing?.Invoke(this, args);
|
||||
|
||||
if (!args.Cancel)
|
||||
{
|
||||
if (ItemsSource != null)
|
||||
{
|
||||
_removeItemsSourceMethod?.Invoke(ItemsSource, new object[] { item });
|
||||
}
|
||||
else
|
||||
{
|
||||
Items.Remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TabView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
|
||||
{
|
||||
// Keep track of drag so we don't modify content until done.
|
||||
_isDragging = true;
|
||||
}
|
||||
|
||||
private void TabView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
|
||||
{
|
||||
_isDragging = false;
|
||||
|
||||
// args.DropResult == None when outside of area (e.g. create new window)
|
||||
if (args.DropResult == DataPackageOperation.None)
|
||||
{
|
||||
var item = args.Items.FirstOrDefault();
|
||||
var tab = ContainerFromItem(item) as TabViewItem;
|
||||
|
||||
if (tab == null && item is FrameworkElement fe)
|
||||
{
|
||||
tab = fe.FindParent<TabViewItem>();
|
||||
}
|
||||
|
||||
if (tab == null)
|
||||
{
|
||||
// We still don't have a TabViewItem, most likely is a static TabViewItem in the template being dragged and not selected.
|
||||
// This is a fallback scenario for static tabs.
|
||||
// Note: This can be wrong if two TabViewItems share the exact same Content (i.e. a string), this should be unlikely in any practical scenario.
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var tabItem = ContainerFromIndex(i) as TabViewItem;
|
||||
if (ReferenceEquals(tabItem.Content, item))
|
||||
{
|
||||
tab = tabItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TabDraggedOutside?.Invoke(this, new TabDraggedOutsideEventArgs(item, tab));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If dragging the active tab, there's an issue with the CP blanking.
|
||||
TabView_SelectionChanged(this, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,736 +0,0 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
|
||||
xmlns:ex="using:Microsoft.Toolkit.Uwp.UI.Extensions"
|
||||
xmlns:local="using:Microsoft.Toolkit.Uwp.UI.Controls">
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPointerOver"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPressed"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<StaticResource x:Key="TabViewItemHeaderBackground"
|
||||
ResourceKey="SystemControlTransparentBrush" />
|
||||
<StaticResource x:Key="TabViewSelectionIndicatorForeground"
|
||||
ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForeground"
|
||||
ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPressed"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundSelected"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPointerOver"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundDisabled"
|
||||
ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderRevealBorderBrush"
|
||||
ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPointerOver"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPressed"
|
||||
Color="{StaticResource SystemChromeLowColor}" />
|
||||
<StaticResource x:Key="TabViewItemHeaderBackground"
|
||||
ResourceKey="SystemControlTransparentBrush" />
|
||||
<StaticResource x:Key="TabViewSelectionIndicatorForeground"
|
||||
ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForeground"
|
||||
ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPressed"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundSelected"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPointerOver"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundDisabled"
|
||||
ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderRevealBorderBrush"
|
||||
ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="TabViewBackground"
|
||||
Color="{ThemeResource SystemChromeLowColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundSelected"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPointerOver"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<SolidColorBrush x:Key="TabViewItemHeaderBackgroundPressed"
|
||||
Color="{ThemeResource SystemColorHighlightColor}" />
|
||||
<StaticResource x:Key="TabViewItemHeaderBackground"
|
||||
ResourceKey="SystemControlTransparentBrush" />
|
||||
<StaticResource x:Key="TabViewSelectionIndicatorForeground"
|
||||
ResourceKey="SystemControlForegroundAccentBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForeground"
|
||||
ResourceKey="SystemControlForegroundBaseMediumBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPressed"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundSelected"
|
||||
ResourceKey="SystemControlForegroundBaseHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundPointerOver"
|
||||
ResourceKey="SystemControlForegroundBaseMediumHighBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderForegroundDisabled"
|
||||
ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
|
||||
<StaticResource x:Key="TabViewItemHeaderRevealBorderBrush"
|
||||
ResourceKey="SystemControlBackgroundTransparentRevealBorderBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<Thickness x:Key="TabViewItemHeaderMargin">0</Thickness>
|
||||
<Thickness x:Key="TabViewItemHeaderIconMargin">0,0,8,0</Thickness>
|
||||
<Thickness x:Key="TabViewItemHeaderCloseMargin">8,0,-8,0</Thickness>
|
||||
<Thickness x:Key="TabViewItemHeaderBorderThickness">0,1,1,0</Thickness>
|
||||
<x:Double x:Key="TabViewItemHeaderIconSize">16</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderMinWidth">48</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderMinHeight">40</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderMaxWidth">NaN</x:Double>
|
||||
<x:Double x:Key="TabViewItemHeaderCloseWidth">32</x:Double>
|
||||
|
||||
<converters:EmptyObjectToObjectConverter x:Key="NullVisibilityConverter"
|
||||
EmptyValue="Collapsed"
|
||||
NotEmptyValue="Visible" />
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
|
||||
<!-- 65 is the size of both scroll buttons + 1 for layout rounding. -->
|
||||
<converters:DoubleToVisibilityConverter x:Key="GreaterThanToleranceVisibilityConverter"
|
||||
GreaterThan="65.0" />
|
||||
|
||||
<converters:BoolToObjectConverter x:Key="CloseCollapsingSizeConverter"
|
||||
FalseValue="{StaticResource TabViewItemHeaderCloseWidth}"
|
||||
TrueValue="NaN" />
|
||||
|
||||
<!-- Default style for compatibility with WPF migrators. -->
|
||||
<Style TargetType="local:TabView">
|
||||
<Setter Property="Background" Value="{ThemeResource TabViewBackground}" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="TabNavigation" Value="Local" />
|
||||
<Setter Property="IsSwipeEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Enabled" />
|
||||
<Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
|
||||
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
|
||||
<!-- Need UseLayoutRounding as Width being dynamically changed in same cases to fill up an entire space, otherwise gap. -->
|
||||
<Setter Property="UseLayoutRounding" Value="False" />
|
||||
<Setter Property="ItemContainerTransitions">
|
||||
<Setter.Value>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition />
|
||||
<ContentThemeTransition />
|
||||
<ReorderThemeTransition />
|
||||
<EntranceThemeTransition IsStaggeringEnabled="False" />
|
||||
</TransitionCollection>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="ItemsPanel">
|
||||
<Setter.Value>
|
||||
<ItemsPanelTemplate>
|
||||
<!-- Note: We have to use StackPanel here due to other issues using ItemsStackPanel or VirtualizingStackPanel -->
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Template" Value="{StaticResource TabViewTemplate}" />
|
||||
</Style>
|
||||
|
||||
<ControlTemplate x:Key="TabViewTemplate"
|
||||
TargetType="local:TabView">
|
||||
<Grid x:Name="TabViewContainer">
|
||||
<Grid.RowDefinitions>
|
||||
<!-- 0 Header -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 1 Tabs -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 2 Content -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- 3 Footer -->
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- 0 TabStartHeader -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- 1 Tabs -->
|
||||
<ColumnDefinition Width="Auto"
|
||||
local:TabView.ConstrainColumn="True"
|
||||
local:TabView.IgnoreColumn="True" />
|
||||
<!-- 2 Reserved: Overflow Drop-down -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- 3 Auto Add Button -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<!-- 4 Padding -->
|
||||
<ColumnDefinition Width="*"
|
||||
local:TabView.IgnoreColumn="True" />
|
||||
<!-- 5 TabEndHeader -->
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter Grid.Row="0"
|
||||
Grid.ColumnSpan="6"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}" />
|
||||
|
||||
<ContentPresenter Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Content="{TemplateBinding TabStartHeader}"
|
||||
ContentTemplate="{TemplateBinding TabStartHeaderTemplate}" />
|
||||
|
||||
<ScrollViewer x:Name="ScrollViewer"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
|
||||
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
|
||||
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
|
||||
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
|
||||
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
|
||||
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
|
||||
Style="{StaticResource TabViewScrollViewer}"
|
||||
TabNavigation="{TemplateBinding TabNavigation}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
|
||||
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
|
||||
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
|
||||
<ItemsPresenter x:Name="TabsItemsPresenter"
|
||||
Padding="{TemplateBinding Padding}" />
|
||||
</ScrollViewer>
|
||||
|
||||
<ContentPresenter Grid.Row="1"
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Left"
|
||||
Content="{TemplateBinding TabActionHeader}"
|
||||
ContentTemplate="{TemplateBinding TabActionHeaderTemplate}" />
|
||||
|
||||
<ContentPresenter Grid.Row="1"
|
||||
Grid.Column="5"
|
||||
Content="{TemplateBinding TabEndHeader}"
|
||||
ContentTemplate="{TemplateBinding TabEndHeaderTemplate}" />
|
||||
|
||||
<ContentPresenter x:Name="TabContentPresenter"
|
||||
Grid.Row="2"
|
||||
Grid.ColumnSpan="6"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}" />
|
||||
|
||||
<ContentPresenter Grid.Row="3"
|
||||
Grid.ColumnSpan="6"
|
||||
Content="{TemplateBinding Footer}"
|
||||
ContentTemplate="{TemplateBinding FooterTemplate}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
|
||||
<!-- Based on Style for ListViewItemExpanded Template - https://msdn.microsoft.com/en-us/library/windows/apps/mt299136.aspx -->
|
||||
<ControlTemplate x:Key="TabViewItemHeaderTemplate"
|
||||
TargetType="local:TabViewItem">
|
||||
<Grid x:Name="LayoutRoot"
|
||||
ex:FrameworkElementExtensions.AncestorType="local:TabView"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Control.IsTemplateFocusTarget="True"
|
||||
FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
|
||||
RenderTransformOrigin="0.5,0.5">
|
||||
<Grid.RenderTransform>
|
||||
<ScaleTransform x:Name="LayoutRootScale" />
|
||||
</Grid.RenderTransform>
|
||||
<Rectangle x:Name="SelectionIndicator"
|
||||
Height="2"
|
||||
Margin="0,-1"
|
||||
VerticalAlignment="Top"
|
||||
Fill="{ThemeResource TabViewSelectionIndicatorForeground}"
|
||||
Opacity="0" />
|
||||
|
||||
<Grid Padding="{TemplateBinding Padding}">
|
||||
<Grid x:Name="ContentPresenterGrid">
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform x:Name="ContentPresenterTranslateTransform" />
|
||||
</Grid.RenderTransform>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Modifications of default ListViewItem Template for our Tab Here -->
|
||||
<Viewbox x:Name="IconBox"
|
||||
MaxWidth="{ThemeResource TabViewItemHeaderIconSize}"
|
||||
MaxHeight="{ThemeResource TabViewItemHeaderIconSize}"
|
||||
Margin="{ThemeResource TabViewItemHeaderIconMargin}"
|
||||
Visibility="{Binding Icon, Converter={StaticResource NullVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ContentPresenter x:Name="Icon"
|
||||
Content="{TemplateBinding Icon}"
|
||||
Foreground="{TemplateBinding Foreground}" />
|
||||
</Viewbox>
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Header}"
|
||||
ContentTemplate="{TemplateBinding HeaderTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}"
|
||||
FontWeight="{TemplateBinding FontWeight}"
|
||||
OpticalMarginAlignment="TrimSideBearings" />
|
||||
<!-- Use grid to toggle visibility based on IsClosable property and inner border for hover animations. -->
|
||||
<Border x:Name="CloseButtonContainer"
|
||||
Grid.Column="2"
|
||||
Width="{Binding (ex:FrameworkElementExtensions.Ancestor).IsCloseButtonOverlay, Converter={StaticResource CloseCollapsingSizeConverter}, ElementName=LayoutRoot}"
|
||||
HorizontalAlignment="Right"
|
||||
Visibility="{Binding IsClosable, Converter={StaticResource BoolToVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<Border x:Name="CloseButtonBorder"
|
||||
Width="{StaticResource TabViewItemHeaderCloseWidth}"
|
||||
Margin="{ThemeResource TabViewItemHeaderCloseMargin}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Visibility="Collapsed">
|
||||
<Button x:Name="CloseButton"
|
||||
x:Uid="/Microsoft.Toolkit.Uwp.UI.Controls/Resources/WindowsCommunityToolkit_TabView_CloseButton"
|
||||
Style="{StaticResource TabViewItemCloseButtonStyle}">
|
||||

|
||||
</Button>
|
||||
</Border>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPointerOver}" />
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPointerOver}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<!-- Close Button -->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="Visible" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPressed}" />
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPressed}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPressed}" />
|
||||
<Setter Target="SelectionIndicator.Opacity" Value="0.4" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Background" Value="{ThemeResource TabViewItemHeaderBackgroundSelected}" />
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundSelected}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundSelected}" />
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<!-- Selected Bar -->
|
||||
<DoubleAnimation Storyboard.TargetName="SelectionIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
|
||||
<!-- Close Button -->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="Visible" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PointerOverSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Background" Value="{ThemeResource TabViewItemHeaderBackgroundPointerOver}" />
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPointerOver}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<!-- Selected Bar -->
|
||||
<DoubleAnimation Storyboard.TargetName="SelectionIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
|
||||
<!-- Close Button -->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="Visible" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="LayoutRoot.Background" Value="{ThemeResource TabViewItemHeaderBackgroundSelected}" />
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPressed}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundPressed}" />
|
||||
</VisualState.Setters>
|
||||
|
||||
<Storyboard>
|
||||
<!-- Selected Bar -->
|
||||
<DoubleAnimation Storyboard.TargetName="SelectionIndicator"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="1"
|
||||
Duration="0" />
|
||||
|
||||
<!-- Close Button -->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="CloseButtonBorder"
|
||||
Storyboard.TargetProperty="Visibility">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="Visible" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DisabledStates">
|
||||
<VisualState x:Name="Enabled" />
|
||||
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="Icon.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundDisabled}" />
|
||||
<Setter Target="ContentPresenter.Foreground" Value="{ThemeResource TabViewItemHeaderForegroundDisabled}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DataVirtualizationStates">
|
||||
<VisualState x:Name="DataAvailable" />
|
||||
|
||||
<VisualState x:Name="DataPlaceholder" />
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="ReorderHintStates">
|
||||
<VisualState x:Name="NoReorderHint" />
|
||||
|
||||
<VisualState x:Name="BottomReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation Direction="Bottom"
|
||||
ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="TopReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation Direction="Top"
|
||||
ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="RightReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation Direction="Right"
|
||||
ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="LeftReorderHint">
|
||||
<Storyboard>
|
||||
<DragOverThemeAnimation Direction="Left"
|
||||
ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
|
||||
TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition GeneratedDuration="0:0:0.2"
|
||||
To="NoReorderHint" />
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
|
||||
<VisualStateGroup x:Name="DragStates">
|
||||
<VisualState x:Name="NotDragging" />
|
||||
|
||||
<VisualState x:Name="Dragging">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="LayoutRoot"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="{ThemeResource ListViewItemDragThemeOpacity}"
|
||||
Duration="0" />
|
||||
<DragItemThemeAnimation TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="DraggingTarget" />
|
||||
|
||||
<VisualState x:Name="MultipleDraggingPrimary" />
|
||||
|
||||
<VisualState x:Name="MultipleDraggingSecondary" />
|
||||
|
||||
<VisualState x:Name="DraggedPlaceholder" />
|
||||
|
||||
<VisualState x:Name="Reordering">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="LayoutRoot"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="{ThemeResource ListViewItemReorderThemeOpacity}"
|
||||
Duration="0:0:0.240" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="ReorderingTarget">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="LayoutRoot"
|
||||
Storyboard.TargetProperty="Opacity"
|
||||
To="{ThemeResource ListViewItemReorderTargetThemeOpacity}"
|
||||
Duration="0:0:0.240" />
|
||||
<DoubleAnimation Storyboard.TargetName="LayoutRootScale"
|
||||
Storyboard.TargetProperty="ScaleX"
|
||||
To="{ThemeResource ListViewItemReorderTargetThemeScale}"
|
||||
Duration="0:0:0.240" />
|
||||
<DoubleAnimation Storyboard.TargetName="LayoutRootScale"
|
||||
Storyboard.TargetProperty="ScaleY"
|
||||
To="{ThemeResource ListViewItemReorderTargetThemeScale}"
|
||||
Duration="0:0:0.240" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="MultipleReorderingPrimary" />
|
||||
|
||||
<VisualState x:Name="ReorderedPlaceholder">
|
||||
<Storyboard>
|
||||
<FadeOutThemeAnimation TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="DragOver">
|
||||
<Storyboard>
|
||||
<DropTargetItemThemeAnimation TargetName="LayoutRoot" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition GeneratedDuration="0:0:0.2"
|
||||
To="NotDragging" />
|
||||
</VisualStateGroup.Transitions>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
|
||||
<!-- Based on Style for Windows.UI.Xaml.Controls.ListViewItem -->
|
||||
<Style TargetType="local:TabViewItem">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||
<Setter Property="FontWeight" Value="SemiBold" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TabViewItemHeaderRevealBorderBrush}" />
|
||||
<Setter Property="BorderThickness" Value="{ThemeResource TabViewItemHeaderBorderThickness}" />
|
||||
<Setter Property="Background" Value="{ThemeResource TabViewItemHeaderBackground}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource TabViewItemHeaderForeground}" />
|
||||
<Setter Property="Margin" Value="{ThemeResource TabViewItemHeaderMargin}" />
|
||||
<Setter Property="TabNavigation" Value="Local" />
|
||||
<Setter Property="IsHoldingEnabled" Value="True" />
|
||||
<Setter Property="Padding" Value="14,0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinWidth" Value="{ThemeResource TabViewItemHeaderMinWidth}" />
|
||||
<Setter Property="MinHeight" Value="{ThemeResource TabViewItemHeaderMinHeight}" />
|
||||
<Setter Property="MaxWidth" Value="{ThemeResource TabViewItemHeaderMaxWidth}" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="FocusVisualMargin" Value="0" />
|
||||
<Setter Property="IsClosable" Value="False" />
|
||||
<Setter Property="Template" Value="{StaticResource TabViewItemHeaderTemplate}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TabViewItemCloseButtonStyle"
|
||||
TargetType="Button">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumHighBrush}" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="6,0,6,0" />
|
||||
<Setter Property="HorizontalAlignment" Value="Center" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="FontFamily" Value="Segoe MDL2 Assets" />
|
||||
<Setter Property="FontWeight" Value="Normal" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid x:Name="RootGrid"
|
||||
Background="{TemplateBinding Background}">
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<Storyboard>
|
||||
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<Storyboard>
|
||||
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>-->
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid" />
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<Storyboard>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid"
|
||||
Storyboard.TargetProperty="Background">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlBackgroundBaseLowBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="Foreground">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
|
||||
Storyboard.TargetProperty="BorderBrush">
|
||||
<DiscreteObjectKeyFrame KeyTime="0"
|
||||
Value="{ThemeResource SystemControlDisabledTransparentBrush}" />
|
||||
</ObjectAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="TabViewScrollViewer"
|
||||
TargetType="ScrollViewer">
|
||||
<Setter Property="HorizontalScrollMode" Value="Auto" />
|
||||
<Setter Property="VerticalScrollMode" Value="Auto" />
|
||||
<Setter Property="IsHorizontalRailEnabled" Value="True" />
|
||||
<Setter Property="IsVerticalRailEnabled" Value="True" />
|
||||
<Setter Property="IsTabStop" Value="False" />
|
||||
<Setter Property="ZoomMode" Value="Disabled" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Top" />
|
||||
<Setter Property="VerticalScrollBarVisibility" Value="Visible" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="BorderThickness" Value="1,0,0,0" />
|
||||
<Setter Property="BorderBrush" Value="{ThemeResource TabViewItemHeaderRevealBorderBrush}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ScrollViewer">
|
||||
<Border x:Name="Root"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}">
|
||||
<Grid Background="{TemplateBinding Background}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<RepeatButton x:Name="ScrollBackButton"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderThickness="1"
|
||||
Delay="50"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Interval="100"
|
||||
Style="{StaticResource RepeatButtonRevealStyle}"
|
||||
Visibility="{Binding ScrollableWidth, Converter={StaticResource GreaterThanToleranceVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Collapsed, TargetNullValue=Collapsed}">
|
||||

|
||||
</RepeatButton>
|
||||
|
||||
<ScrollContentPresenter x:Name="ScrollContentPresenter"
|
||||
Grid.Column="1"
|
||||
TabFocusNavigation="Once"
|
||||
Margin="{TemplateBinding Padding}" />
|
||||
|
||||
<RepeatButton x:Name="ScrollForwardButton"
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderThickness="1"
|
||||
Delay="50"
|
||||
FontFamily="Segoe MDL2 Assets"
|
||||
Interval="100"
|
||||
Style="{StaticResource RepeatButtonRevealStyle}"
|
||||
Visibility="{Binding ScrollableWidth, Converter={StaticResource GreaterThanToleranceVisibilityConverter}, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Collapsed, TargetNullValue=Collapsed}">
|
||||

|
||||
</RepeatButton>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
|
@ -1,79 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Item Container for a <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
public partial class TabViewItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the header content for the tab.
|
||||
/// </summary>
|
||||
public object Header
|
||||
{
|
||||
get { return (object)GetValue(HeaderProperty); }
|
||||
set { SetValue(HeaderProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Header"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="Header"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty HeaderProperty =
|
||||
DependencyProperty.Register(nameof(Header), typeof(object), typeof(TabViewItem), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon to appear in the tab header.
|
||||
/// </summary>
|
||||
public IconElement Icon
|
||||
{
|
||||
get { return (IconElement)GetValue(IconProperty); }
|
||||
set { SetValue(IconProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Icon"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="Icon"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty IconProperty =
|
||||
DependencyProperty.Register(nameof(Icon), typeof(IconElement), typeof(TabViewItem), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template to override for the tab header.
|
||||
/// </summary>
|
||||
public DataTemplate HeaderTemplate
|
||||
{
|
||||
get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
|
||||
set { SetValue(HeaderTemplateProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="HeaderTemplate"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="HeaderTemplate"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty HeaderTemplateProperty =
|
||||
DependencyProperty.Register(nameof(HeaderTemplate), typeof(DataTemplate), typeof(TabViewItem), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the tab can be closed by the user with the close button.
|
||||
/// </summary>
|
||||
public bool IsClosable
|
||||
{
|
||||
get { return (bool)GetValue(IsClosableProperty); }
|
||||
set { SetValue(IsClosableProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsClosable"/> dependency property.
|
||||
/// </summary>
|
||||
/// <returns>The identifier for the <see cref="IsClosable"/> dependency property.</returns>
|
||||
public static readonly DependencyProperty IsClosableProperty =
|
||||
DependencyProperty.Register(nameof(IsClosable), typeof(bool), typeof(TabViewItem), new PropertyMetadata(null));
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Windows.Devices.Input;
|
||||
using Windows.System;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Input;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Input;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Item Container for a <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = TabCloseButtonName, Type = typeof(ButtonBase))]
|
||||
public partial class TabViewItem : ListViewItem
|
||||
{
|
||||
private const string TabCloseButtonName = "CloseButton";
|
||||
|
||||
private ButtonBase _tabCloseButton;
|
||||
|
||||
private bool _isMiddleClick;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TabViewItem"/> class.
|
||||
/// </summary>
|
||||
public TabViewItem()
|
||||
{
|
||||
DefaultStyleKey = typeof(TabViewItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the Tab's close button is clicked.
|
||||
/// </summary>
|
||||
public event EventHandler<TabClosingEventArgs> Closing;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
if (_tabCloseButton != null)
|
||||
{
|
||||
_tabCloseButton.Click -= TabCloseButton_Click;
|
||||
}
|
||||
|
||||
_tabCloseButton = GetTemplateChild(TabCloseButtonName) as ButtonBase;
|
||||
|
||||
if (_tabCloseButton != null)
|
||||
{
|
||||
_tabCloseButton.Click += TabCloseButton_Click;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnPointerPressed(PointerRoutedEventArgs e)
|
||||
{
|
||||
_isMiddleClick = false;
|
||||
|
||||
if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse)
|
||||
{
|
||||
PointerPoint pointerPoint = e.GetCurrentPoint(this);
|
||||
|
||||
// Record if middle button is pressed
|
||||
if (pointerPoint.Properties.IsMiddleButtonPressed)
|
||||
{
|
||||
_isMiddleClick = true;
|
||||
}
|
||||
|
||||
// Disable unwanted behavior inherited by ListViewItem:
|
||||
// Disable "Ctrl + Left click" to deselect tab
|
||||
// Or variant like "Ctrl + Shift + Left click"
|
||||
// Or "Ctrl + Alt + Left click"
|
||||
if (pointerPoint.Properties.IsLeftButtonPressed)
|
||||
{
|
||||
var ctrl = Window.Current.CoreWindow.GetKeyState(VirtualKey.Control);
|
||||
if (ctrl.HasFlag(CoreVirtualKeyStates.Down))
|
||||
{
|
||||
// return here so the event won't be picked up by the base class
|
||||
// but keep this event unhandled so it can be picked up further
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.OnPointerPressed(e);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnPointerReleased(PointerRoutedEventArgs e)
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
|
||||
// Close on Middle-Click
|
||||
if (_isMiddleClick)
|
||||
{
|
||||
TabCloseButton_Click(this, null);
|
||||
}
|
||||
|
||||
_isMiddleClick = false;
|
||||
}
|
||||
|
||||
private void TabCloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (IsClosable)
|
||||
{
|
||||
Closing?.Invoke(this, new TabClosingEventArgs(Content, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Possible modes for how to layout a <see cref="TabViewItem"/> Header's Width in the <see cref="TabView"/>.
|
||||
/// </summary>
|
||||
public enum TabWidthMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Each tab header takes up as much space as it needs. This is similar to how WPF and Visual Studio Code behave.
|
||||
/// Suggest to keep <see cref="TabView.IsCloseButtonOverlay"/> set to false.
|
||||
/// <see cref="TabView.SelectedTabWidth"/> is ignored.
|
||||
/// In this scenario, tab width behavior is effectively turned off. This can be useful when using custom styling or a custom panel for layout of <see cref="TabViewItem"/> as well.
|
||||
/// </summary>
|
||||
Actual,
|
||||
|
||||
/// <summary>
|
||||
/// Each tab header will use the minimal space set by <see cref="FrameworkElement.MinWidth"/> on the <see cref="TabViewItem"/>.
|
||||
/// Suggest to set the <see cref="TabView.SelectedTabWidth"/> to show more content for the selected item.
|
||||
/// </summary>
|
||||
Compact,
|
||||
|
||||
/// <summary>
|
||||
/// Each tab header will fill to fit the available space. If <see cref="TabView.SelectedTabWidth"/> is set, that will be used as a Maximum Width.
|
||||
/// This is similar to how Microsoft Edge behaves when used with the <see cref="TabView.SelectedTabWidth"/>.
|
||||
/// Suggest to set <see cref="TabView.IsCloseButtonOverlay"/> to true.
|
||||
/// Suggest to set <see cref="TabView.SelectedTabWidth"/> to 200 and the TabViewItemHeaderMinWidth Resource to 90.
|
||||
/// </summary>
|
||||
Equal,
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/GridSplitter/GridSplitter.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/HeaderedContentControl/HeaderedContentControl.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/HeaderedItemsControl/HeaderedItemsControl.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/HeaderedTextBlock/HeaderedTextBlock.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ImageCropper/ImageCropper.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ImageCropper/ImageCropperThumb.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ImageEx/ImageEx.xaml" />
|
||||
|
@ -26,7 +25,6 @@
|
|||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/RemoteDevicePicker/RemoteDevicePicker.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/RotatorTile/RotatorTile.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ScrollHeader/ScrollHeader.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/TabView/TabView.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ImageCropper/ImageCropperThumb.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/ImageCropper/ImageCropper.xaml" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.Toolkit.Uwp.UI.Controls/Eyedropper/Eyedropper.xaml" />
|
||||
|
|
|
@ -181,13 +181,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
|
|||
/// </summary>
|
||||
public event EventHandler ImageLoaded;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the platform supports Composition.
|
||||
/// </summary>
|
||||
[Obsolete("This property is now obsolete and will be removed in a future version of the Toolkit.")]
|
||||
public static bool IsCompositionSupported => !DesignTimeHelpers.IsRunningInLegacyDesignerMode &&
|
||||
ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 3); // SDK >= 14393
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TileControl"/> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.GridSplitter" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.HeaderedContentControl" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.HeaderedItemsControl" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.HeaderedTextBlock" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.ImageEx" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.ImageExBase" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.InAppNotification" />
|
||||
|
@ -32,8 +31,6 @@
|
|||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.RotatorTile" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.ScrollHeader" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.StaggeredPanel" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TabView" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TabViewItem" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TextToolbar" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.TileControl" />
|
||||
<Item Type="Microsoft.Toolkit.Uwp.UI.Controls.UniformGrid" />
|
||||
|
|
|
@ -86,14 +86,6 @@ namespace Microsoft.Toolkit.Extensions
|
|||
/// <returns><c>true</c> if the string contains only letters; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsCharacterString(this string str) => Regex.IsMatch(str, CharactersRegex);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of an object.
|
||||
/// </summary>
|
||||
/// <param name="value">The object to convert.</param>
|
||||
/// <returns>String representation of the object.</returns>
|
||||
[Obsolete("Use value?.ToString() instead. This will be removed in the next release of the toolkit.")]
|
||||
public static string ToSafeString(this object value) => value?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string with HTML comments, scripts, styles, and tags removed.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class to wrap around a Task to provide more information usable for UI data binding scenarios. As discussed in MSDN Magazine: https://msdn.microsoft.com/magazine/dn605875.
|
||||
/// </summary>
|
||||
/// <typeparam name="TResult">Type of result returned by task.</typeparam>
|
||||
[Obsolete("This helper will be removed in a future release, use the ObservableObject base class from Microsoft.Toolkit.Mvvm and the SetAndNotifyOnCompletion method")]
|
||||
public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotifyTaskCompletion{TResult}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="task">Task to wait on.</param>
|
||||
public NotifyTaskCompletion(Task<TResult> task)
|
||||
{
|
||||
Task = task;
|
||||
if (!task.IsCompleted)
|
||||
{
|
||||
TaskCompletion = WatchTaskAsync(task);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WatchTaskAsync(Task task)
|
||||
{
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (PropertyChanged == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCompleted)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsNotCompleted)));
|
||||
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsCanceled)));
|
||||
}
|
||||
else if (task.IsFaulted)
|
||||
{
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsFaulted)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Exception)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(InnerException)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ErrorMessage)));
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(IsSuccessfullyCompleted)));
|
||||
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(Result)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the task that is being waited on.
|
||||
/// </summary>
|
||||
public Task<TResult> Task { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the task wrapper task.
|
||||
/// </summary>
|
||||
public Task TaskCompletion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result of the given task.
|
||||
/// </summary>
|
||||
public TResult Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Task.Status == TaskStatus.RanToCompletion) ?
|
||||
Task.Result :
|
||||
default(TResult);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the task.
|
||||
/// </summary>
|
||||
public TaskStatus Status => Task.Status;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the task is completed.
|
||||
/// </summary>
|
||||
public bool IsCompleted => Task.IsCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the task is not completed.
|
||||
/// </summary>
|
||||
public bool IsNotCompleted => !Task.IsCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the task was successfully completed.
|
||||
/// </summary>
|
||||
public bool IsSuccessfullyCompleted => Task.Status == TaskStatus.RanToCompletion;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the task was canceled.
|
||||
/// </summary>
|
||||
public bool IsCanceled => Task.IsCanceled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there was an error with the task.
|
||||
/// </summary>
|
||||
public bool IsFaulted => Task.IsFaulted;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception which occurred on the task (if one occurred).
|
||||
/// </summary>
|
||||
public AggregateException Exception => Task.Exception;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the inner exception of the task.
|
||||
/// </summary>
|
||||
public Exception InnerException => Exception?.InnerException;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message of the task.
|
||||
/// </summary>
|
||||
public string ErrorMessage => InnerException?.Message ?? Exception.Message;
|
||||
|
||||
/// <summary>
|
||||
/// PropertyChanged event.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Microsoft.Toolkit.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Obsolete see https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3134.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to be used for creating the Singleton instance.</typeparam>
|
||||
/// <example>
|
||||
/// Instead of this helper, migrate your code to this pattern instead:
|
||||
/// <code>
|
||||
/// // Setup Singleton
|
||||
/// public class MyClass
|
||||
/// {
|
||||
/// public static MyClass Instance { get; } = new MyClass();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[Obsolete("This helper will be removed in a future release, see example tag for code replacement. https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3134")]
|
||||
public static class Singleton<T>
|
||||
where T : new()
|
||||
{
|
||||
// Use ConcurrentDictionary for thread safety.
|
||||
private static readonly ConcurrentDictionary<Type, T> _instances = new ConcurrentDictionary<Type, T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the Singleton class.
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
// Safely creates the first instance or retrieves the existing instance across threads.
|
||||
return _instances.GetOrAdd(typeof(T), (t) => new T());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,8 +19,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{F1AFFFA7-28FE-
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B30036C4-D514-4E5B-A323-587A061772CE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.Services", "Microsoft.Toolkit.Uwp.Services\Microsoft.Toolkit.Uwp.Services.csproj", "{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls", "Microsoft.Toolkit.Uwp.UI.Controls\Microsoft.Toolkit.Uwp.UI.Controls.csproj", "{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid", "Microsoft.Toolkit.Uwp.UI.Controls.DataGrid\Microsoft.Toolkit.Uwp.UI.Controls.DataGrid.csproj", "{DAEB9CEC-C817-33B2-74B2-BC379380DB72}"
|
||||
|
@ -78,12 +76,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Toolkit.Uwp.UI.Co
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Parsers", "Microsoft.Toolkit.Parsers\Microsoft.Toolkit.Parsers.csproj", "{42CA4935-54BE-42EA-AC19-992378C08DE6}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PlatformSpecific", "PlatformSpecific", "{096ECFD7-7035-4487-9C87-81DCE9389620}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DifferencesGen", "Microsoft.Toolkit.Uwp.PlatformDifferencesGen\DifferencesGen.csproj", "{292D34E8-0F01-4FA8-951D-8232F75A88D5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer", "Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer\Microsoft.Toolkit.Uwp.PlatformSpecificAnalyzer.csproj", "{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Toolkit.Uwp.Input.GazeInteraction", "Microsoft.Toolkit.UWP.Input.GazeInteraction\Microsoft.Toolkit.Uwp.Input.GazeInteraction.vcxproj", "{A5E98964-45B1-442D-A07A-298A3221D81E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GazeInputTest", "GazeInputTest\GazeInputTest.csproj", "{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6}"
|
||||
|
@ -317,31 +309,6 @@ Global
|
|||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B24A296C-B3EB-4E06-A64E-74AC2D1ACC91}.Release|x86.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7189A42D-6F1A-4FA3-8E00-E2C14FDF167A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E9FAABFB-D726-42C1-83C1-CB46A29FEA81}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
|
@ -730,55 +697,6 @@ Global
|
|||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{42CA4935-54BE-42EA-AC19-992378C08DE6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|ARM.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|ARM64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|x64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Native|x86.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|Any CPU.Build.0 = Debug|Win32
|
||||
{A5E98964-45B1-442D-A07A-298A3221D81E}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
|
@ -1246,8 +1164,6 @@ Global
|
|||
{EFA96B3C-857E-4659-B942-6BEF7719F4CA} = {9333C63A-F64F-4797-82B3-017422668A5D}
|
||||
{7AEFC959-ED7C-4D96-9E92-72609B40FBE0} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC}
|
||||
{6BD0BA4A-DE6D-3E87-8F83-63518C31ECD1} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC}
|
||||
{292D34E8-0F01-4FA8-951D-8232F75A88D5} = {096ECFD7-7035-4487-9C87-81DCE9389620}
|
||||
{262BB7CE-EF42-4BF7-B90C-107E6CBB57FF} = {096ECFD7-7035-4487-9C87-81DCE9389620}
|
||||
{A122EA02-4DE7-413D-BFBF-AF7DFC668DD6} = {B30036C4-D514-4E5B-A323-587A061772CE}
|
||||
{75F9EE44-3EFA-47BC-AEDD-351B9834A0AF} = {F1AFFFA7-28FE-4770-BA48-10D76F3E59BC}
|
||||
{4E9466D1-D5AA-46AC-801B-C8FDAB79F0D4} = {B30036C4-D514-4E5B-A323-587A061772CE}
|
||||
|
|
|
@ -35,12 +35,11 @@ Once you do a search, you should see a list similar to the one below (versions m
|
|||
| --- | --- |
|
||||
| Microsoft.Toolkit | .NET Standard NuGet package containing common code |
|
||||
| Microsoft.Toolkit.HighPerformance | .NET Standard and .NET Core NuGet package with performance oriented helpers, extensions, etc. |
|
||||
| Microsoft.Toolkit.Parsers | .NET Standard NuGet package containing cross-platform parsers, such as Markdown and RSS |
|
||||
| Microsoft.Toolkit.Services | .NET Standard NuGet package containing cross-platform services |
|
||||
| Microsoft.Toolkit.Parsers | .NET Standard NuGet package containing cross-platform parsers, such as Markdown |
|
||||
| Microsoft.Toolkit.Services | .NET Standard NuGet package containing cross-platform services helpers, such as LinkedIn, Microsoft Graph, Twitter and more |
|
||||
| Microsoft.Toolkit.Uwp | Main NuGet package includes code only helpers such as Colors conversion tool, Storage file handling, a Stream helper class, etc. |
|
||||
| Microsoft.Toolkit.Uwp.Notifications | Notifications Package - Generate tile, toast, and badge notifications for Windows 10 via code. Includes intellisense support to avoid having to use the XML syntax |
|
||||
| Microsoft.Toolkit.Uwp.Notifications.Javascript | Notification Packages for JavaScript |
|
||||
| Microsoft.Toolkit.Uwp.Services | Services Package - This NuGet package includes the service helpers for Facebook, LinkedIn, Microsoft Graph, Twitter and more |
|
||||
| Microsoft.Toolkit.Uwp.UI | UI Packages - XAML converters, Visual tree extensions, and other extensions and helpers for your XAML UI |
|
||||
| Microsoft.Toolkit.Uwp.UI.Animations | Animations and Composition behaviors such as Blur, Fade, Rotate, etc. |
|
||||
| Microsoft.Toolkit.Uwp.UI.Controls | XAML Controls such as RadialGauge, RangeSelector, etc. |
|
||||
|
|
Загрузка…
Ссылка в новой задаче